Files
railwaysystem/RuoYi-Vue/docs/train_register_api.md
T

36 KiB
Raw Blame History

报到机接口文档(TrainRegisterApiController)

服务模块:ruoyi-admin 控制器:com.ruoyi.web.controller.api.TrainRegisterApiController 接口前缀:/api/register/v1 文档依据:控制器源码(截至 2026-11-09),字段口径以对应 VO / 数据库字段为准 适用对象:报到机(自助终端)厂商、联调与测试人员


目录


1. 通用说明

1.1 请求地址

项 值
协议 HTTP(生产建议由网关承载 HTTPS)
Host 部署服务器地址与端口,例如 http://192.168.1.202:8080
Base Path /api/register/v1
完整示例 http://192.168.1.202:8080/api/register/v1/student/list

1.2 鉴权与请求头

  • SecurityConfig 中已将 /api/register/v1/** 配置为 permitAll,当前所有接口均无需登录令牌(控制器类注释中提到的 Bearer 鉴权已不再生效)。
  • 若生产环境在网关/边缘设备层增加了设备签名(如 HMAC)校验,以网关约定为准,不影响本文档的接口定义。
  • 请求头:
场景 Header 说明
所有请求 Accept: application/json 可选
POST 请求 Content-Type: application/json 必填,请求体为 JSON
文件/人脸 无 人脸图片以 base64 字符串放在 JSON 请求体内,不使用 multipart

1.3 统一响应结构

除个别特殊说明外,全部接口返回 AjaxResult:

{
  "code": 200,
  "msg": "操作成功",
  "data": {}
}
字段 类型 说明
code int 业务状态码,见 1.4
msg string 提示信息,成功时为“操作成功”,失败时为错误描述
data any 业务数据;失败时可能不存在

注意:data 为 null 时部分序列化配置下该字段可能被省略,客户端应做空值兜底。

1.4 状态码与错误约定

code 含义 典型场景
200 成功 正常返回
500 通用业务/参数错误 参数缺失、图片解析失败、文件写入失败、业务校验不通过
601 警告 预留,本文档接口未使用
40402 学员不存在 GET /student/{sId} 及依赖学员主档的接口(StudentErrorCode.STUDENT_NOT_FOUND)
401 未认证 当前 /api/register/v1/** 已放行,正常不会出现

重要约定:业务异常由全局异常处理器统一捕获,HTTP 状态码固定为 200,调用方必须依据响应体中的 code 判断成败,不能只看 HTTP 状态。

1.5 时间与命名约定

  • 服务端 Jackson 时区为 GMT+8,日期时间统一格式 yyyy-MM-dd HH:mm:ss,纯日期格式 yyyy-MM-dd。
  • JSON 字段以接口定义为准,部分沿用数据库下划线风格(如 dorm_room),属于既有契约,请勿自行转换。
  • 学员标识为 sId,教师标识为 tId;人脸库中教师人员 ID 统一为 tId + "_teacher"。

2. 接口总览

序号 模块 方法 路径 用途
1 学员信息 GET /student/list 学员主档列表(含报到状态)
2 学员信息 GET /student/{sId} 单个学员详情(含账号绑定信息)
3 学员信息 GET /student/{sId}/base 学员基础信息(宿舍/教室/开班/场地)
4 学员信息 GET /student/{sId}/resources 学员关联资源(在基础信息上增加课程表与课次)
5 教师信息 GET /teacher/list 教师主档列表(字段结构同学员列表)
6 教师信息 GET /teacher/{tId}/base 教师基础信息(信息/宿舍/授课课表)
7 班级 GET /class/student 进行中班级及其未报到学员名单
8 宿舍 GET /dormitory/layout 宿舍布局(楼栋→楼层→房间)
9 课表 GET /lesson/schedule 按日期(可选课程类型)查询课程安排
10 人脸 POST /student/face 上传学员人脸并更新头像、补办报到
11 人脸 POST /teacher/face 上传教师人脸并更新头像
12 人脸 POST /face 按 employeeId 后缀自动分发到学员/教师
13 报到管理 POST /report/reset 报到重置(解绑人脸 + 清门锁授权 + 复位参训状态)

3. 学员信息模块

3.1 获取学员信息列表

用途:报到机启动或刷新时拉取全部学员主档,用于本地人员检索、人脸比对名单准备;每条记录额外补入该学员最新一条报到记录的参训状态与报到时间。

  • 方法 / 路径:GET /api/register/v1/student/list
  • 请求参数:无
  • 请求体:不适用

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "sId": "S20260001",
      "sName": "张三",
      "personNo": "P0001",
      "sGender": "男",
      "sBirth": "1998-05-12",
      "deptName": "供电段",
      "cardNo": "C0001",
      "sPhone": "13800000001",
      "sEmail": "zhangsan@example.com",
      "idCardMasked": "210***********1234",
      "idCardLast4": "1234",
      "sourceType": 1,
      "status": 1,
      "avatar": "/profile/upload/2026/11/09/xxx.png",
      "dutyLevel": null,
      "id": 1,
      "deptId": 103,
      "enrollmentStatus": 0,
      "joinTime": null
    }
  ]
}

字段说明(StudentItemVO)

字段 类型 说明
sId string 学员唯一标识(主键引用口径)
sName string 姓名
personNo string 人员编号
sGender string 性别
sBirth string 出生日期 yyyy-MM-dd
deptName string 所属部门/车间名称
cardNo string 卡号
sPhone / sEmail string 手机号 / 邮箱
idCardMasked / idCardLast4 string 脱敏身份证 / 后四位(明文身份证不下发)
sourceType int 学员来源类型
status int 档案状态
avatar string 头像可访问路径(相对资源前缀)
id / deptId long 自增编号 / 部门ID
enrollmentStatus int 参训状态:0待报到、1已报到、2请假、3放弃、4已完成(取最新一条报名记录)
joinTime datetime 报到时间(取最新一条报名记录,未报到为 null)
extractStatus / extractTime / extractMsg / photoMd5 / featureData / featureMd5 / featureAlgoVersion / featureSize — 人脸特征提取的内部扩展字段,报到机通常无需使用,多为 null

错误场景

场景 code msg
数据库异常 500 系统异常提示
查询结果为空 200 data: [](空数组,非错误)

3.2 获取单个学员详情

用途:按 sId 查询单个学员的完整档案,并附带其系统账号绑定状态,供报到机核对身份或人工处理。

  • 方法 / 路径:GET /api/register/v1/student/{sId}
  • 请求参数(Path)
参数名 类型 必填 说明
sId string 是 学员唯一标识
  • 请求体:不适用

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "sId": "S20260001",
    "sName": "张三",
    "personNo": "P0001",
    "sGender": "男",
    "sBirth": "1998-05-12",
    "deptName": "供电段",
    "cardNo": "C0001",
    "sPhone": "13800000001",
    "sEmail": "zhangsan@example.com",
    "avatar": "/profile/upload/2026/11/09/xxx.png",
    "faceRef": null,
    "remark": null,
    "createBy": "admin",
    "createTime": "2026-09-01 09:00:00",
    "userId": 105,
    "userName": "student01",
    "nickName": "张三",
    "accountBindingStatus": 1
  }
}

字段说明:StudentAdminVO = StudentItemVO + StudentDetailVO + 账号绑定字段

字段 类型 说明
(基础字段) — 同 3.1 学员列表字段
faceRef string 人脸参考标识
remark string 备注
createBy / createTime string / datetime 建档人 / 建档时间
userId long 绑定的系统用户ID(未绑定为 null)
userName string 绑定的系统登录账号
nickName string 绑定的用户昵称
accountBindingStatus int 账号关联状态:0未关联、1已关联

错误场景

场景 code msg
学员不存在 40402 学员不存在
sId 为空 40402 / 500 学员不存在(按未命中处理)

说明:源码中 detail == null 分支返回 data: null,但底层 selectStudentDetail 在未命中时会抛业务异常,因此实际表现为 code=40402。


3.3 获取学员基础信息

用途:报到机刷脸/刷卡识别学员后,展示该学员的个人基础信息、所属开班、当前宿舍床位、上课教室、实训场地,用于报到确认页。

  • 方法 / 路径:GET /api/register/v1/student/{sId}/base
  • 请求参数(Path)
参数名 类型 必填 说明
sId string 是 学员唯一标识
  • 请求体:不适用
  • 数据来源:班级优先取该学员最新报到记录(train_class_report)中的 plan_id,缺失时回落到报名关系(train_enrollment)中最新的班级;教室取该班排课课次(train_lesson)中首个带教室的课次,实训场地取首个带场地的课次(两者可能来自不同课次)。

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "studInfo": {
      "sId": "S20260001",
      "sName": "张三",
      "sPhone": "13800000001",
      "sGender": "男",
      "deptName": "供电段",
      "personNo": "P0001"
    },
    "classPlan": {
      "planId": "PLAN-2026-01",
      "planName": "2026年秋季高铁房建设施高级研修班",
      "startTime": "2026-09-15 08:30:00",
      "endTime": "2026-09-30 17:30:00"
    },
    "dormitory": {
      "dormId": "DORM-0101",
      "bedNo": "2",
      "dormBuilding": "1号楼",
      "dormRoom": "101"
    },
    "classroom": {
      "classRoomId": "CR-001",
      "roomLocation": "实训基地A栋201",
      "roomName": "第一理论教室"
    },
    "trainingSite": {
      "siteId": "TS-001",
      "siteLocation": "实训基地B栋",
      "siteName": "接触网实训场",
      "major": "接触网工"
    }
  }
}

字段说明

字段 类型 说明
studInfo object 学员精简信息:sId、sName、sPhone、sGender、deptName、personNo
classPlan object | null 所属开班:planId、planName、startTime、endTime;未分配班级时为 null
dormitory object 宿舍:dormId、bedNo(有住宿记录时)、dormBuilding、dormRoom;未分配宿舍时字段可能为 null
classroom object | null 上课教室:classRoomId、roomLocation、roomName;该班无教室课次时为 null
trainingSite object | null 实训场地:siteId、siteLocation、siteName、major;该班无场地课次时为 null

本接口不返回 courses 与 lesson 明细(源码中已注释关闭),需要课表请调用 3.4 或 7.1。

错误场景

场景 code msg
学员不存在 40402 学员不存在
学员未分配班级/宿舍 200 对应字段为 null(不是错误)

3.4 获取学员关联资源

用途:在 3.3 的基础上额外返回该学员所在班级的课程清单与课次明细,供报到机展示“我的课程 / 我的课表”。

  • 方法 / 路径:GET /api/register/v1/student/{sId}/resources
  • 请求参数(Path)
参数名 类型 必填 说明
sId string 是 学员唯一标识
  • 请求体:不适用

响应示例(在 3.3 的 data 基础上增加两个字段)

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "studInfo": { "sId": "S20260001", "sName": "张三", "sPhone": "13800000001", "sGender": "男", "deptName": "供电段", "personNo": "P0001" },
    "classPlan": { "planId": "PLAN-2026-01", "planName": "2026年秋季高铁房建设施高级研修班", "startTime": "2026-09-15 08:30:00", "endTime": "2026-09-30 17:30:00" },
    "courses": [
      { "cId": "C0001", "cName": "高铁站房巡检" }
    ],
    "lesson": [
      { "startTime": "2026-09-16 09:00:00", "endTime": "2026-09-16 11:30:00", "planCourseId": 1, "cName": "高铁站房巡检" }
    ],
    "dormitory": { "dormId": "DORM-0101", "bedNo": "2", "dormBuilding": "1号楼", "dormRoom": "101" },
    "classroom": { "classRoomId": "CR-001", "roomLocation": "实训基地A栋201", "roomName": "第一理论教室" },
    "trainingSite": { "siteId": "TS-001", "siteLocation": "实训基地B栋", "siteName": "接触网实训场", "major": "接触网工" }
  }
}

新增字段说明

字段 类型 说明
courses array 该班已配置课程:cId(课程ID)、cName(课程名称);无班级时为 null
lesson array 该班全部课次:startTime、endTime、planCourseId、cName;无课次时为 null 或空数组

lesson 元素不含教师与场地字段;如需教师/场地/班牌信息,请使用 7.1 GET /lesson/schedule。

错误场景

场景 code msg
学员不存在 40402 学员不存在
无班级 200 classPlan、courses、lesson 为 null

4. 教师信息模块

4.1 获取教师信息列表

用途:拉取全部教师主档,字段结构与学员列表完全一致,便于报到机用同一套解析逻辑处理两类人员。

  • 方法 / 路径:GET /api/register/v1/teacher/list
  • 请求参数:无
  • 请求体:不适用

字段映射规则

输出字段 取值来源
sId tId + "_teacher"(与人脸库教师 employee_id 口径一致)
sName train_teacher.t_name
personNo / sGender / sBirth / deptName / cardNo / sPhone / sEmail / status / avatar / id train_teacher 对应字段
enrollmentStatus train_teacher.enrollment_status(0待报到/1已报到/2请假/3放弃/4已完成)
joinTime train_teacher.join_time(未报到为 null)

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "sId": "T101_teacher",
      "sName": "李老师",
      "personNo": "T0001",
      "sGender": "男",
      "deptName": "供电教研室",
      "sPhone": "13900000001",
      "status": 1,
      "avatar": "/profile/upload/2026/11/09/teacher.png",
      "id": 3,
      "enrollmentStatus": 1,
      "joinTime": "2026-09-14 09:10:00"
    }
  ]
}

错误场景

场景 code msg
无教师数据 200 data: []

4.2 获取教师基础信息

用途:教师刷脸后展示教师基本信息、当前宿舍、授课课表。

  • 方法 / 路径:GET /api/register/v1/teacher/{tId}/base
  • 请求参数(Path)
参数名 类型 必填 说明
tId string 是 教师工号;支持传入 tId + "_teacher",服务端会自动截去后缀
  • 请求体:不适用

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "teacherInfo": {
      "tId": "T101",
      "tName": "李老师",
      "tPhone": "13900000001",
      "tGender": "男",
      "deptName": "供电教研室",
      "personNo": "T0001",
      "avatar": "/profile/upload/2026/11/09/teacher.png"
    },
    "dormitory": {
      "dormId": "DORM-0203",
      "bedNo": "1",
      "dormBuilding": "2号楼",
      "dormRoom": "203"
    },
    "lesson": [
      {
        "lessonId": 101,
        "planCourseId": 1,
        "courseName": "高铁站房巡检",
        "tId": "T101",
        "teacherName": "李老师",
        "startTime": "2026-09-16 09:00:00",
        "endTime": "2026-09-16 11:30:00",
        "venueType": 1,
        "classRoomId": "CR-001",
        "classRoomName": "第一理论教室",
        "siteId": null,
        "siteName": null,
        "venueName": "第一理论教室",
        "boardSn": "BOARD-SN-001",
        "remark": null
      }
    ]
  }
}

字段说明

字段 类型 说明
teacherInfo object | null 教师不存在时为 null;字段:tId、tName、tPhone、tGender、deptName、personNo、avatar
dormitory object dormId、bedNo(有住宿时)、dormBuilding、dormRoom
lesson array 该教师全部授课课次(按开始时间升序),元素为 TimetableLessonItemVO,字段见下表

lesson 元素字段(TimetableLessonItemVO)

字段 类型 说明
lessonId long 课次ID
planCourseId long 计划课程实例ID
courseName string 课程名称
tId string 教师工号
teacherName string 教师姓名
startTime / endTime datetime 课次起止时间
venueType int 场地类型:1普通教室、2实训场地
classRoomId / classRoomName string 教室ID / 名称(venueType=1 时有效)
siteId / siteName string 实训场地ID / 名称(venueType=2 时有效)
venueName string 场地聚合展示名(教室名或场地名)
boardSn string 电子班牌 SN;未唯一绑定该教室时为 null
remark string 课次备注

该接口不按日期过滤,返回教师全部课次。

错误场景

场景 code msg
tId 不存在 200 teacherInfo: null、lesson: []
教师无住宿/无课次 200 对应字段为空对象/空数组

5. 班级模块

5.1 获取进行中班级的未报到学员名单

用途:报到机按班级展示待报到学员,用于现场点名/呼叫报到。

  • 方法 / 路径:GET /api/register/v1/class/student
  • 请求参数:无
  • 请求体:不适用
  • 筛选口径:班级 plan_status = 1(进行中);学员 enrollment_status = 0(待报到)

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "planId": "PLAN-2026-01",
      "planName": "2026年秋季高铁房建设施高级研修班",
      "classId": "CLASS-01",
      "className": "房建一班",
      "students": [
        { "sId": "S20260001", "sName": "张三", "deptId": 103, "deptName": "供电段" }
      ]
    }
  ]
}

字段说明

字段 类型 说明
planId / planName string 培训班ID / 名称
classId / className string 班级ID / 名称
students array 未报到学员:sId、sName、deptId、deptName

容错说明:单个学员主档缺失时不会中断整体返回,改用报名记录(train_enrollment)中的姓名/部门冗余字段兜底,此时 deptId 为 null。

错误场景

场景 code msg
无进行中班级 200 data: []

6. 宿舍模块

6.1 获取宿舍布局信息

用途:报到机展示宿舍楼栋—楼层—房间的床位占用情况(如“已分配数/总床位数”)。

  • 方法 / 路径:GET /api/register/v1/dormitory/layout
  • 请求参数:无
  • 请求体:不适用
  • 统计口径:已分配数 = train_accommodation 中住宿状态为 0(预分配)、1(已入住)的记录数;总床位 = 房间核定容量 capacity

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "1号楼": {
      "1层": {
        "101": { "id": 10, "dorm_room": "101", "accommodation": "2/4" }
      },
      "未标注楼层": {
        "102": { "id": 11, "dorm_room": "102", "accommodation": "0/2" }
      }
    }
  }
}

字段说明

层级 键 说明
一级 楼栋名称 dorm_building,为空的数据会被跳过
二级 楼层 dorm_floor,为空时固定为 未标注楼层
三级 房间号 dorm_room
叶子对象 id 房间自增编号
dorm_room 房间号(下划线命名,保持既有契约)
accommodation 已分配床位数/总床位数 字符串,如 2/4

返回使用 LinkedHashMap,键顺序与 SQL 排序(楼栋→楼层→房间号)一致;无宿舍数据时 data: {}。

错误场景

场景 code msg
无宿舍数据 200 data: {}

7. 课表模块

7.1 获取课程安排(按日期 + 课程类型)

用途:查询指定自然日的课程安排,按日期为键分组返回课程名称、课程类型、上课地点、上课教师等信息,供报到机/班牌展示当日课表。

  • 方法 / 路径:GET /api/register/v1/lesson/schedule
  • 请求参数(Query)
参数名 类型 必填 默认值 说明
startTime string 否 当天(北京时间) 课程开始时间,对应 train_lesson.start_time。支持 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、yyyy/MM/dd、ISO-8601(T 分隔、Z 结尾、+08:00 偏移)、10 位秒级 / 13 位毫秒级时间戳;服务端取该时间所在自然日
courseType string 否 不筛选 课程类型,对应 train_course.c_type:1 = 理论、2 = 实训。缺省或空串返回当日全部类型;传其它值返回参数错误
  • 请求体:不适用
  • 查询范围:train_lesson 为主表,关联 train_plan_course、train_course、train_teacher、train_classroom_info、train_training_site、train_class_plan;日期区间为 [当日 00:00:00, 次日 00:00:00),由 SQL STR_TO_DATE 在库内生成(不受应用 JVM 时区影响);培训班为逻辑删除时不参与过滤(课表属于已发生的教学事实)。

请求示例

GET /api/register/v1/lesson/schedule
GET /api/register/v1/lesson/schedule?startTime=2026-11-09
GET /api/register/v1/lesson/schedule?startTime=2026-11-09&courseType=1
GET /api/register/v1/lesson/schedule?startTime=2026-11-09T09:00:00&courseType=2

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "2026-11-09": [
      {
        "id": 101,
        "name": "高铁站房巡检",
        "courseType": "1",
        "venueType": 1,
        "startTime": "2026-11-09 09:00:00",
        "endTime": "2026-11-09 11:30:00",
        "teacher": "李老师",
        "classroom": "第一理论教室",
        "planName": "2026年秋季高铁房建设施高级研修班"
      },
      {
        "id": 102,
        "name": "接触网实操",
        "courseType": "2",
        "venueType": 2,
        "startTime": "2026-11-09 14:00:00",
        "endTime": "2026-11-09 16:30:00",
        "teacher": "王老师",
        "classroom": "接触网实训场",
        "planName": "2026年秋季高铁房建设施高级研修班"
      }
    ]
  }
}

data 结构说明

层级 说明
键 日期字符串 yyyy-MM-dd(按课次开始时间的北京时间自然日分组,升序)
值 该日课程数组,按 startTime、id 升序

课程元素字段(CourseScheduleItemVO)

字段 类型 说明
id long 课次ID(train_lesson.lesson_id)
name string 课程名称(train_course.c_name)
courseType string 课程类型,与 train_course.c_type 一致:1 理论、2 实训;课程缺失或为空时为 null
venueType int 课次场地类型:1 普通教室、2 实训场地
startTime / endTime datetime 课次起止时间 yyyy-MM-dd HH:mm:ss
teacher string 上课教师姓名(train_teacher.t_name)
classroom string 上课地点:venueType=1 取教室名称,venueType=2 取实训场地名称
planName string 所属培训班名称(train_class_plan.plan_name)

错误场景

场景 code msg 说明
startTime 格式无法识别 500 startTime 格式不正确,支持 yyyy-MM-dd / yyyy-MM-dd HH:mm:ss / ISO-8601,示例:2026-11-09 例如传入 2026年11月9日
courseType 非 1/2 500 courseType 仅支持 1(理论)或 2(实训) 例如传入 理论、3
当日无课程 200 data: {} 空对象,非错误
该类型当日无课程 200 data: {} 加了 courseType 后无匹配

服务端会输出日志 lessonSchedule 课程安排查询 day=..., courseType=..., rows=...,排障时可直接用 rows 区分“SQL 未命中”与“命中但未返回”。


8. 人脸模块

8.1 上传学员人脸照片

用途:报到机采集学员人脸 → 转 PNG 落盘 → 更新 train_student.avatar → 同步到人脸识别设备 → 回写报到状态并办理门锁入住(若已分配宿舍)。

  • 方法 / 路径:POST /api/register/v1/student/face
  • 请求头:Content-Type: application/json

请求体

字段 类型 必填 说明
sId string 是 学员唯一标识,同时作为人脸库 employee_id
faceImage string 是 base64 人脸图片,可带 data:image/png;base64, 前缀;服务端统一转码为 PNG
{
  "sId": "S20260001",
  "faceImage": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
}

业务副作用(按顺序)

  1. 图片解码并保存到 profile/upload/yyyy/MM/dd/{uuid}.png,路径写入 train_student.avatar
  2. 调用人脸设备新增/更新人员(employee_id = sId)
  3. 回查该学员报名记录,将第一条记录的参训状态置为 1(已报到)并记录报到时间
  4. 若存在有效住宿记录,调用门禁入住接口(/openapi/iot/stay/checkin)下发入住授权

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "sId": "S20260001",
    "avatar": "/profile/upload/2026/11/09/9f1c....png",
    "faceSync": "success",
    "lockCheckin": { "code": 200, "msg": "入住成功" },
    "lockAuthId": "",
    "joinTime": "2026-11-09 09:12:33"
  }
}

字段说明

字段 类型 说明
sId string 学员标识
avatar string 头像可访问路径
faceSync string 人脸设备同步结果:success、failed、skipped
faceMsg string 仅 faceSync=failed 时出现,失败原因(设备无响应/异常信息)
lockCheckin object 门锁入住接口返回结果;无有效住宿时不返回
lockAuthId string 预留字段,固定空串
joinTime datetime 实际报到时间;无住宿时可能不返回
(设备扩展字段) any 设备成功时其返回的 data 会被并入本层

错误场景

场景 code msg
缺少 sId 或 faceImage 500 参数缺失:sId 与 faceImage 均为必填
sId 为空串 500 sId 不能为空
图片非法/无法解码 500 图片解析失败:{具体原因}
目录创建失败 500 创建存储目录失败
文件写入失败 500 图片写入失败:{具体原因}
学员不存在或更新 0 行 500 学员不存在或更新失败
设备同步失败 200 仍返回成功,faceSync=failed + faceMsg(头像已保存)

8.2 上传教师人脸照片

用途:教师人脸采集与入库,更新 train_teacher.avatar,并同步人脸设备(employee_id = tId + "_teacher")。

  • 方法 / 路径:POST /api/register/v1/teacher/face
  • 请求头:Content-Type: application/json

请求体

字段 类型 必填 说明
teacherId string 是 教师工号(train_teacher.t_id),不要带 _teacher 后缀
faceImage string 是 base64 人脸图片,可带 data URI 前缀
{
  "teacherId": "T101",
  "faceImage": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
}

业务差异:教师无开班报名记录,不做报到状态更新;仅当教师已有有效住宿且已生成门锁流水号时,补发一次入住授权(有效期 15 天)。

响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "tId": "T101",
    "avatar": "/profile/upload/2026/11/09/ab34....png",
    "faceSync": "success",
    "lockCheckin": { "code": 200, "msg": "入住成功" },
    "lockAuthId": ""
  }
}

字段说明:tId、avatar、faceSync、faceMsg(失败时)、lockCheckin(有住宿且门锁可用时)、lockAuthId(固定空串)、设备扩展字段合并规则同上。

错误场景

场景 code msg
缺少 teacherId 或 faceImage 500 参数缺失:teacherId 与 faceImage 均为必填
teacherId 为空串 500 teacherId 不能为空
图片非法/无法解码 500 图片解析失败:{具体原因}
目录/文件写入失败 500 创建存储目录失败 / 图片写入失败:{具体原因}
教师不存在或更新 0 行 500 教师不存在或更新失败
设备同步失败 200 faceSync=failed + faceMsg(头像已保存)

8.3 上传人脸照片(按员工标识自动分发)

用途:统一入口。按 employeeId 后缀自动路由,设备侧无需区分学员/教师接口。

  • 方法 / 路径:POST /api/register/v1/face
  • 请求头:Content-Type: application/json

请求体

字段 类型 必填 说明
employeeId string 是 人脸库人员ID。以 _teacher 结尾 → 视为教师(去掉后缀即 tId);其它 → 视为学员 sId
faceImage string 是 base64 人脸图片
{
  "employeeId": "T101_teacher",
  "faceImage": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
}

分发规则

判定 路由 说明
employeeId 以 _teacher 结尾 → 8.2 教师人脸上传 tId = employeeId 去掉后缀;后缀匹配区分大小写、不做 trim
其它 → 8.1 学员人脸上传 sId = employeeId

响应:直接透传被调用接口(8.1 / 8.2)的响应体,结构与之一致。

错误场景

场景 code msg
缺少 employeeId 或 faceImage 500 参数缺失:employeeId 与 faceImage 均为必填
employeeId 为空串 500 employeeId 不能为空
employeeId 仅为 _teacher(工号为空) 500 employeeId 非法:教师工号(tId)不能为空
后续流程失败 500 / 200 同 8.1 / 8.2

9. 报到管理模块

9.1 报到重置

用途:误点他人信息导致人脸错绑时的补救接口。按顺序执行:解绑人脸 → 清除门锁入住授权 → 参训状态复位为 0(待报到) → 清空学员头像。宿舍分配记录保留,不做退宿。

  • 方法 / 路径:POST /api/register/v1/report/reset
  • 请求头:Content-Type: application/x-www-form-urlencoded(参数通过 Query 传递,无请求体)

请求参数(Query)

参数名 类型 必填 说明
planId string 是 培训班ID(train_enrollment.plan_id)
studentId string 是 学员ID(train_student.s_id)

请求示例

POST /api/register/v1/report/reset?planId=PLAN-2026-01&studentId=S20260001

响应示例(成功)

{
  "code": 200,
  "msg": "报到重置成功",
  "data": {
    "planId": "PLAN-2026-01",
    "studentId": "S20260001",
    "enrollmentId": 900001,
    "previousStatus": 1,
    "faceUnbind": "success",
    "lockCheckout": "success",
    "lockApiBizNo": "LOCK20261109001",
    "currentStatus": 0,
    "alreadyReset": false,
    "accommodationKept": true,
    "avatarCleared": true,
    "avatarSId": "S20260001"
  }
}

字段说明

字段 类型 说明
planId / studentId string 入参回显
enrollmentId long 参训记录ID
previousStatus int 重置前参训状态
faceUnbind string success(人脸解绑成功)
lockCheckout string success,或 skipped: 无有效住宿记录 / skipped: 缺少门锁业务流水号与宿舍信息
lockApiBizNo string 门锁业务流水号(仅实际操作门锁时返回)
currentStatus int 重置后状态,固定 0
alreadyReset boolean 重置前是否已是 0(重复调用标识)
accommodationKept boolean 是否保留了住宿分配
avatarCleared boolean 学员头像是否已清空
avatarSId string 被清空头像的学员标识

分步失败场景(返回 code=500,msg 指明失败步骤)

场景 msg
缺少 planId / studentId 参数 Spring 参数缺失提示(Required request parameter 'planId' ...)
planId 或 studentId 为空串 planId 和 studentId 不能为空
报名记录不存在 未找到学员[{studentId}]在班级[{planId}]的参训记录
学员已放弃培训(状态3) 学员已放弃培训,不允许重置报到状态
学员已结训(状态4) 学员已结训,不允许重置报到状态
步骤1 人脸解绑失败 步骤1 人脸解绑失败:{原因};参训状态未变更
步骤2 门锁响应解析失败 步骤2 门锁授权清除响应解析失败:{原因};人脸已解绑,参训状态未变更
步骤2 门锁清除失败 步骤2 门锁授权清除失败:{原因};人脸已解绑,参训状态未变更
步骤3 状态复位失败 步骤3 参训状态重置失败;人脸与门锁授权已解除,请人工核对后重试
学员档案缺失(前 3 步已完成) 报到重置已完成,但未找到有效学员档案[{sId}],avatar 未清空(失败响应中仍带 data)
清空头像异常(前 3 步已完成) 报到重置已完成,但清空学员头像失败:{原因}(失败响应中仍带 data)

该接口为非幂等敏感操作,重复调用会对已复位的数据再次执行解绑/清库动作,建议设备端加二次确认。


10. 附录

10.1 枚举值速查

字段 取值
参训/报到状态 enrollmentStatus 0 待报到、1 已报到、2 请假、3 放弃、4 已完成
场地类型 venueType 1 普通教室、2 实训场地
课程类型 courseType(train_course.c_type) 1 理论、2 实训
账号关联状态 accountBindingStatus 0 未关联、1 已关联
人脸同步状态 faceSync success / failed / skipped
房间占用 accommodation 字符串 已分配数/总床位数,如 2/4

10.2 关联数据表

模块 主要数据表
学员 train_student、train_enrollment
教师 train_teacher
开班 / 课程 train_class_plan、train_plan_course、train_course
排课 train_lesson、train_classroom_info、train_training_site、train_electronic_board
宿舍 train_dormitory_info、train_accommodation
报到 train_class_report
人脸/门锁 人脸识别服务、/openapi/iot/stay/checkin 门禁开放接口

文档结束。接口变更请同步更新本文件,并在提交说明中标注版本。