# 教务系统对接 — 学工数据只读 API 调用说明 本文档面向**教务系统**开发/联调人员,说明如何调用学工系统提供的学生基本信息、学生学籍异动两类只读接口。 --- ## 1. 概述 | 项目 | 说明 | |------|------| | 协议 | HTTP GET | | 数据格式 | JSON,`Content-Type: application/json` | | 字符编码 | UTF-8 | | 鉴权方式 | 请求头 `X-Jw-Cx-Key`(由学工方分配) | | 接口数量 | 2 个业务接口 + 1 个健康检查 | | 接口 | 路径 | 用途 | |------|------|------| | 学生基本信息 | `GET /api/v1/students` | 分页拉取全部在库学生快照 | | 学生学籍异动 | `GET /api/v1/student-changes` | 按自然年拉取已办结异动 | | 健康检查 | `GET /health` | 探测服务是否存活(无需密钥) | --- ## 2. 访问地址 | 项目 | 值 | |------|-----| | Base URL | `http://web.hzdyjsxy.cn:18081/jw-cx-api` | 完整示例: ``` http://web.hzdyjsxy.cn:18081/jw-cx-api/api/v1/students?page=1 http://web.hzdyjsxy.cn:18081/jw-cx-api/api/v1/student-changes?year=2026 http://web.hzdyjsxy.cn:18081/jw-cx-api/health ``` > 若域名、端口或路径前缀有调整,以学工方最新通知为准。 --- ## 3. 鉴权 除 `/health` 外,所有业务接口必须在 **HTTP Header** 中携带: ```http X-Jw-Cx-Key: <学工方分配的密钥> ``` | 情况 | HTTP 状态 | 响应示例 | |------|-----------|----------| | 未携带或密钥错误 | 401 | `{"code":401,"msg":"无效的密钥或未提供请求头 X-Jw-Cx-Key"}` | | 参数错误 | 400 | `{"code":400,"msg":"..."}` | | 成功 | 200 | `{"code":200,"msg":"success",...}` | | 服务端异常 | 500 | `{"code":500,"msg":"数据库查询失败",...}` | --- ## 4. 统一响应结构 ### 4.1 成功 ```json { "code": 200, "msg": "success", "data": [ /* 记录数组 */ ], "total": 100 } ``` 各接口还会在顶层返回分页或筛选元数据(见下文)。 ### 4.2 字段值约定 - 日期:`YYYY-MM-DD`(如 `"2026-04-21"`) - 日期时间:`YYYY-MM-DD HH:mm:ss` - 无数据:JSON `null`(不是空字符串 `""`) - 异动类型、学生当前状态等枚举:**中文名称**(非内部代码) --- ## 5. 接口一:学生基本信息 ### 5.1 请求 ``` GET /api/v1/students ``` **Query 参数** | 参数 | 必填 | 说明 | 默认值 | |------|------|------|--------| | `page` | 否 | 页码,从 1 开始 | `1` | | `page_size` | 否 | 每页条数,范围 1~5000 | `500` | **示例** ```http GET /api/v1/students?page=1&page_size=500 X-Jw-Cx-Key: JWCX001 ``` ### 5.2 响应元数据 | 字段 | 说明 | |------|------| | `total` | 学生总条数 | | `page` | 当前页码 | | `page_size` | 本页大小 | | `total_pages` | 总页数 | ### 5.3 拉取全量建议流程 学生数量可能达到数万条,**必须分页循环**: ``` 1. 请求 page=1,读取 total、total_pages 2. 处理 data 数组 3. page 递增,直到 page >= total_pages ``` 伪代码: ```python page = 1 page_size = 500 while True: resp = GET(f"/api/v1/students?page={page}&page_size={page_size}") # 入库或处理 resp["data"] if page >= resp["total_pages"]: break page += 1 ``` ### 5.4 `data` 每条记录字段 | 字段名 | 必填(教务) | 说明 | |--------|--------------|------| | 姓名 | 是 | | | 学号 | 是 | | | 联系电话 | 否 | | | 身份证件类型 | 否 | 有身份证号时为「居民身份证」,否则 `null` | | 身份证件号 | 否 | | | 班级信息 | 是 | `class_id` → `xg_class_msg.class_name`(优先班级表,再回退学生表冗余字段) | | 所在校区 | 是 | 学生 `class_id` → 班级 `campus_id` → `xg_dorm_campus.campus_name`,再回退班级 `campus_name`;无班级或未设置时为 `null` | | 所在年级 | 是 | | | 入学年份 | 是 | 整数,如 `2024` | | 系部信息 | 是 | `college_id` → `sys_dept.dept_name`(优先学生表 ID,其次班级表 ID,再回退冗余 `college_name`) | | 专业信息 | 是 | `major_id` → `sys_dept.dept_name`(优先学生表 ID,其次班级表 ID,再回退冗余 `major_name`) | | 班主任工号 | 是 | | | 班主任姓名 | 是 | | | 学生当前状态 | 是 | 教务中文名:在读、休学、复学、退学、毕业、结业、停学、其他 | | 学生是否在校 | 是 | 「在校」或「不在校」;退学、毕业为不在校 | | 异动类型 | 否 | 固定 `null` | | 培养层次 | 否 | 班级 `class_type` 字典值(`zsjy_education_level`):`0` 三年制中级技工、`1` 三年制技师、`2` 四年制高级工、`3` 五年制高级技工、`4` 六年制技师、`5` 中高职一体;无班级或未设置时为 `null` | | 学制 | 是 | 年数,整数 | | 毕业时间 | 是 | **年份**整数;已毕业取异动生效年,否则按入学年+学制推算 | | 毕业学年学期 | 是 | 如 `2025-2026-2`;无法推算时为 `null` | | 学习经历 | 否 | 固定 `null` | | 何年何月-何年何月 | 否 | 固定 `null` | | 在何地学习 | 否 | 固定 `null` | | 性别 | 是 | 「男」或「女」 | | 籍贯地 | 否 | | | 在校照片 | 否 | 固定 `null` | | 出生日期 | 否 | `YYYY-MM-DD` | | 家庭现住址 | 否 | | | 成员1姓名 | 否 | 父亲姓名 | | 成员2姓名 | 否 | 母亲姓名 | | 成员1关系 | 否 | 有父亲姓名时为「父亲」 | | 成员2关系 | 否 | 有母亲姓名时为「母亲」 | | 成员1联系电话 | 否 | 父亲电话 | | 成员2联系电话 | 否 | 母亲电话 | ### 5.5 响应示例(节选) ```json { "code": 200, "msg": "success", "data": [ { "姓名": "张三", "学号": "2024010101", "联系电话": "13800138000", "身份证件类型": "居民身份证", "身份证件号": "3301************", "班级信息": "24技师1班", "所在校区": "西溪校区", "所在年级": "2024", "入学年份": 2024, "系部信息": "电气工程系", "专业信息": "电气工程", "班主任工号": "T001", "班主任姓名": "李老师", "学生当前状态": "在读", "学生是否在校": "在校", "异动类型": null, "培养层次": "1", "学制": 3, "毕业时间": 2027, "毕业学年学期": "2026-2027-2", "性别": "男" } ], "total": 12500, "page": 1, "page_size": 500, "total_pages": 25 } ``` --- ## 6. 接口二:学生学籍异动 ### 6.1 请求 ``` GET /api/v1/student-changes ``` **Query 参数** | 参数 | 必填 | 说明 | 默认值 | |------|------|------|--------| | `year` | 否 | 按「异动生效时间」所在**自然年**筛选,范围 2000~2100 | 当前自然年 | **示例** ```http GET /api/v1/student-changes?year=2026 X-Jw-Cx-Key: JWCX001 ``` ### 6.2 数据范围说明 - 仅返回 **已办结** 记录(学籍异动 `process_status=1`,换班级 `status=1`) - 按 **`异动生效时间` 的年份** 过滤(非提交时间) - **不分页**,一次返回该年全部记录(数据量通常远小于学生全量) - **不包含毕业(BI_YE)** 记录 - **去重**:同一学号、同一登记日(`提交时间`)、同一异动类型仅保留一条;视图与 API 双层去重 ### 6.3 响应元数据 | 字段 | 说明 | |------|------| | `total` | 本次返回条数(等于 `data` 长度) | | `filter_year` | 实际筛选年份 | | `filter_by_column` | 固定为 `异动生效时间` | ### 6.4 `data` 每条记录字段 | 字段名 | 说明 | |--------|------| | 提交时间 | `YYYY-MM-DD` | | 异动生效学期 | 固定 `null`(本系统无学年学期) | | 异动类型 | 教务中文名:**退学、休学、复学、转班级** | | 申请人学号 | | | 姓名 | | | 异动前所在年级 / 院系 / 专业 / 班级 / 学制 | 院系/专业优先 `sys_dept`;班级优先 `xg_class_msg`;再回退快照冗余名称 | | 异动前所在校区 | 异动前班级 `campus_id` → `xg_dorm_campus.campus_name`,再回退班级 `campus_name` | | 异动后所在年级 / 班级 / 院系 / 专业 / 学制 | 同上(异动后取当前学生/新班级) | | 异动后所在校区 | 异动后班级 `campus_id` → `xg_dorm_campus.campus_name`,再回退班级 `campus_name` | | 异动后班主任工号 / 姓名 | | | 新名字 | 固定 `null` | | 审核状态 | 固定「已通过」 | ### 6.5 异动类型说明 | 接口返回值 | 含义 | |------------|------| | 退学 | 学籍异动-退学 | | 休学 | 学籍异动-休学 | | 复学 | 学籍异动-复学 | | 转班级 | 换班级记录 | > 本系统**不同步**毕业异动;毕业相关信息请从「学生基本信息」接口获取。 ### 6.6 响应示例 ```json { "code": 200, "msg": "success", "data": [ { "提交时间": "2026-03-15", "异动生效学期": null, "异动类型": "休学", "申请人学号": "2024010101", "姓名": "张三", "异动前所在年级": "2024", "异动前所在校区": "西溪校区", "异动前所在院系": "电气工程系", "异动前所在专业": "电气工程", "异动前所在班级": "24技师1班", "异动前学制": 3, "异动后所在校区": "西溪校区", "异动后所在年级": "2024", "异动后所在班级": "24技师1班", "异动后所在院系": "电气工程系", "异动后所在专业": "电气工程", "异动后学制": 3, "异动后班主任工号": "T001", "异动后班主任姓名": "李老师", "新名字": null, "审核状态": "已通过" } ], "total": 12, "filter_year": 2026, "filter_by_column": "异动生效时间" } ``` --- ## 7. 调用示例 ### 7.1 cURL ```bash # 健康检查 curl -s "http://web.hzdyjsxy.cn:18081/jw-cx-api/health" # 学生基本信息(第 1 页) curl -s -H "X-Jw-Cx-Key: JWCX001" \ "http://web.hzdyjsxy.cn:18081/jw-cx-api/api/v1/students?page=1&page_size=500" # 学生异动(2026 年) curl -s -H "X-Jw-Cx-Key: JWCX001" \ "http://web.hzdyjsxy.cn:18081/jw-cx-api/api/v1/student-changes?year=2026" ``` ### 7.2 Postman 1. Method 选 **GET** 2. URL 填入完整地址(含 Query) 3. **Headers** 增加一行: - Key: `X-Jw-Cx-Key` - Value: 学工分配的密钥 4. 点击 Send ### 7.3 Python(标准库) 项目内提供联调脚本: ```bash cd 跟教务系统同步学生异动信息/api_service python test_jw_cx_api.py --base-url http://web.hzdyjsxy.cn:18081/jw-cx-api --year 2026 ``` --- ## 8. 联调检查清单 | 步骤 | 操作 | |------|------| | 1 | 访问 `/health` 返回 `{"status":"ok"}` | | 2 | 不带密钥访问业务接口,应返回 401 | | 3 | 带正确密钥访问 `/api/v1/students?page=1`,`code=200` 且含 `total`、`total_pages` | | 4 | 循环分页直至拉取全部学生 | | 5 | 带密钥访问 `/api/v1/student-changes?year=2026`,核对异动类型中文名 | | 6 | 确认 `null` 字段(校区、异动生效学期等)教务侧可接受 | --- ## 9. 常见问题 **Q:为什么 80 端口访问 404?** A:公网 API 走 **18081** 端口及 `/jw-cx-api` 路径,请使用本文 2.1 节地址。 **Q:学生接口为什么要分页?** A:全量学生可能数万条,单次响应过大易超时;请按 `total_pages` 逐页拉取。 **Q:异动接口为何没有毕业?** A:按对接约定,毕业不同步至异动接口;毕业状态与毕业时间见学生基本信息接口。 **Q:「复学并换班级」会有几条异动?** A:可能同时存在「复学」「转班级」两条记录,请按学号+提交时间处理。 **Q:密钥如何更换?** A:由学工方在服务端配置 `[api] api_key` 后通知教务更新 Header。 --- ## 10. 相关文档 | 文档 | 说明 | |------|------| | [README.md](./README.md) | 服务部署与运维(学工侧) | | [../异动类型映射说明.md](../异动类型映射说明.md) | 异动类型详细映射 | | [../../跟教务系统同步学生基础信息/学生基本信息对接说明.md](../../跟教务系统同步学生基础信息/学生基本信息对接说明.md) | 学生字段口径与状态映射 | | [test_jw_cx_api.py](./test_jw_cx_api.py) | 联调测试脚本 | --- **文档版本**:2026-07-02 **维护**:学工系统开发组(接口变更时请同步更新本文档)