# 院校主子表导入导出接口文档（art/school）

## 1. 目标

面向前端提供“院校主表 + 子表（详情/校区/学院/专业/招生计划）”的一体化导出与导入能力，支持：

- 多 Sheet 标准模板导出
- 导入前冲突预检
- 按院校编码选择性替换（原子事务）
- 导入明细反馈（成功/失败/跳过与原因）

---

## 2. 接口清单

### 2.1 导出模板数据（含业务数据）

- **URL**: `POST /art/school/export`
- **权限**: `art:school:export`
- **Content-Type**: `application/x-www-form-urlencoded`
- **响应**: Excel 文件下载（`.xlsx`）

#### 查询参数（同院校列表）

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mainCode | string | 否 | 院校编码 |
| mainName | string | 否 | 院校名称（模糊） |
| shortName | string | 否 | 院校简称（模糊） |
| province/city/district | string | 否 | 行政区 |
| universityType | string | 否 | 大学类型 |
| educationLevel | string | 否 | 学历层次 |
| schoolNature | string | 否 | 办学性质 |

#### 导出 Sheet 结构

1. `院校基本信息`
2. `院校详情信息`
3. `校区信息`
4. `学院信息`
5. `专业信息`
6. `招生计划信息`
7. `模板说明`

> 子表均包含 `院校编码*`、`院校名称*` 用于与主表关联。

---

### 2.2 导出空模板

- **URL**: `POST /art/school/importTemplate`
- **权限**: `art:school:export`
- **响应**: 空模板 Excel（仅表头 + 模板说明）

---

### 2.3 导入预检

- **URL**: `POST /art/school/importPreview`
- **权限**: `art:school:import`
- **Content-Type**: `multipart/form-data`
- **入参**:
  - `file`（Excel 文件，必填）

#### 返回示例

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "totalSchoolCount": 10,
    "conflictCount": 3,
    "invalidCount": 1,
    "details": [
      {
        "mainCode": "1001",
        "mainName": "测试院校01",
        "status": "CONFLICT",
        "message": "系统已存在该院校数据，导入时需确认是否替换"
      },
      {
        "mainCode": "",
        "mainName": "",
        "status": "INVALID",
        "message": "必填字段缺失：院校编码/院校名称不能为空"
      }
    ]
  }
}
```

#### 状态说明

- `CONFLICT`: 系统已存在（按院校编码/名称命中）
- `INVALID`: 模板数据无效（缺失、重复、子表关联不到主表）

---

### 2.4 导入执行

- **URL**: `POST /art/school/importData`
- **权限**: `art:school:import`
- **Content-Type**: `multipart/form-data`
- **入参**:
  - `file`（Excel 文件，必填）
  - `replaceAll`（boolean，选填，默认 `false`）
  - `replaceMainCodes`（string[]，选填，可多值传参）

#### 前端推荐调用逻辑

1. 先调 `importPreview`。
2. 对 `CONFLICT` 院校弹窗询问“是否替换”。
3. 将用户同意替换的院校编码组装为 `replaceMainCodes`，再调 `importData`。
4. 用户全量替换时可直接传 `replaceAll=true`。

#### 返回示例

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "totalSchoolCount": 10,
    "successCount": 8,
    "failCount": 1,
    "skippedCount": 1,
    "details": [
      {
        "mainCode": "1001",
        "mainName": "测试院校01",
        "status": "SUCCESS",
        "message": "替换成功"
      },
      {
        "mainCode": "1002",
        "mainName": "测试院校02",
        "status": "SKIPPED",
        "message": "院校名称重复且用户取消替换"
      },
      {
        "mainCode": "1003",
        "mainName": "测试院校03",
        "status": "FAILED",
        "message": "学院保存失败"
      }
    ]
  }
}
```

#### 状态说明

- `SUCCESS`: 导入/替换成功
- `SKIPPED`: 冲突且用户未选择替换
- `FAILED`: 导入失败（事务回滚）
- `INVALID`: 文件预校验失败

---

## 3. 原子性与一致性

- 导入按“单院校”为最小事务单元：
  - 该院校主表 + 子表（详情/校区/学院/专业/招生计划）要么全部成功，要么全部回滚。
- 替换逻辑：
  1. 删除该院校历史主表及上述子表数据
  2. 插入新主表数据
  3. 插入新子表数据

---

## 4. 模板必填规则（简表）

- `院校基本信息`: `院校编码*`、`院校名称*`
- `院校详情信息`: `院校编码*`、`院校名称*`
- `校区信息`: `院校编码*`、`院校名称*`、`校区名称*`
- `学院信息`: `院校编码*`、`院校名称*`、`学院名称*`
- `专业信息`: `院校编码*`、`院校名称*`、`学院ID*`、`专业名称*`
- `招生计划信息`: `院校编码*`、`院校名称*`、`年份*`、`招生省份*`、`专业名称*`、`计划数*`

> `*` 表示模板必填字段。

---

## 5. 兼容说明

- 现有 `POST /art/school` 与 `PUT /art/school` 仍用于单院校编辑（`school + detail` 统一结构）。
- 本文档新增导入导出接口可与现有列表/编辑功能并行使用。
