docs(api): 冻结RailwaySystem一期API Contract V1.0

This commit is contained in:
wangzhibiao committed 2026-09-14 09:27:33 +08:00
1 parent c8560cf30b
commit 67543e0422
13 files changed
+1846

No files matched your search

+175
View File
@@ -0,0 +1,175 @@
# RailwaySystem Phase 2:API Contract / DTO-VO / Service Boundary Design
> **阶段状态:** Phase 2 契约设计冻结稿 (Review Round 1 修订版)
> **业务最高基线:** `doc/RailwaySystem 智慧培训系统一期 PRD V1.0.md`
> **数据持久化基线:** `doc/RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0.md` (21张表)
> **代码生成器基线:** `generator-v2-ready-20260914` (只读验证通过)
> **审查基线:** `docs/api-v2/12_contract_review_round1.md` (Review Round 1 判定通过: A)
> **设计原则:** 业务工作台驱动 / 业务流程闭环驱动,坚决抵制 21 张表单表 CRUD 堆砌。
---
## 1. 阶段目标与定位
本阶段处于数据库 V2 冻结与正式业务编码之间,目标为:
> **在不编写业务代码的前提下,把 PRD V1.0、Database V2 和业务流程正式转换成 API 契约、DTO/VO、Service 聚合边界和模块责任。**
本系统定位为**在职人员智慧培训业务系统**,其核心价值是支撑铁路培训基地开展“学员参训全生命周期”的培训管理,而不是 21 张表的后台增删改查。所有接口均紧密围绕**业务工作台**、**业务动作(Command)**与**业务查询(Resource)**展开。
---
## 2. 领域核心标识映射
数据库 V2 确立的三大业务领域主键在 API 与 DTO/VO 中全面沿用,禁止退化为旧式的多字段复合弱标识:
```text
1. enrollment_id (参训档案标识)
= 某个学员参加某个培训计划的唯一主线上下文。
考勤、报到、住宿、成绩、学习记录、画像全部挂接此 ID。
2. plan_course_id (计划课程标识)
= 某个公共课程被引入某个具体培训计划后的本期课程实例。
课次排课、成绩项目可选绑定、学习内容挂接此 ID。
3. lesson_id (课次教学活动标识)
= 本期某门课程在特定时间、特定场地、由特定教师开展的具体教学活动。
电子班牌联动、课次签到/签退考勤明细直接绑定此 ID。
```
---
## 3. 业务闭环与模块分布
系统一期紧扣 PRD V1.0 冻结的业务主流程,划分为四大业务模块:
```mermaid
flowchart LR
subgraph ModuleA["Module A: 人员与基础资源"]
A1[教师/学员主档]
A2[课程库/教学资源]
A3[教室/场地/班牌/宿舍房态]
end
subgraph ModuleB["Module B: 开班计划与排课 (核心业务)"]
B1[创建/编辑培训计划]
B2[配置本期课程]
B3[参训人员名单/导入]
B4[课次编排与冲突检测]
B5[学员报到确认]
B6[计划综合工作台]
end
subgraph ModuleC["Module C: 住宿/门禁/考勤"]
C1[批量住宿预分配/调整]
C2[门禁授权下发/重试]
C3[班牌考勤签到/签退]
C4[考勤人工修正/补录]
end
subgraph ModuleD["Module D: 成绩/学习/统计/画像"]
D1[成绩项目权重配置]
D2[成绩录入/导入/台账]
D3[外部学习记录接入]
D4[计划统计/成绩下钻]
D5[学员综合画像]
end
ModuleA --> ModuleB
ModuleB --> ModuleC
ModuleB --> ModuleD
ModuleC --> ModuleD
```
---
## 4. 文档目录导航 (docs/api-v2/)
本设计规范包含以下 13 篇核心文档,相互支撑,构成完整的开发契约:
| 序号 | 文档名称 | 核心内容 |
|:---|:---|:---|
| 00 | [`00_readme.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/00_readme.md) | 阶段总纲、架构全景、领域标识映射、最小合约视图与文档索引 |
| 01 | [`01_api_design_principles.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/01_api_design_principles.md) | API 架构原则、Resource vs Command、分页规范、返回包装与确定性/优先级定义 |
| 02 | [`02_module_a_base_resource_api.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/02_module_a_base_resource_api.md) | Module A 基础主数据 API 契约(教师、学员、课程、资源、教室、场地、班牌、宿舍) |
| 03 | [`03_module_b_plan_enrollment_schedule_api.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/03_module_b_plan_enrollment_schedule_api.md) | Module B 核心业务 API 契约(计划、工作台、本期课程、参训名单、排课冲突预检、课表、报到) |
| 04 | [`04_module_c_accommodation_attendance_api.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/04_module_c_accommodation_attendance_api.md) | Module C 过程执行 API 契约(宿舍预分配、调整、门禁同步、班牌签到/签退、考勤修正) |
| 05 | [`05_module_d_score_learning_statistics_api.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/05_module_d_score_learning_statistics_api.md) | Module D 业务成效 API 契约(成绩配置、打分台账、学习记录接入、统计下钻、学员画像) |
| 06 | [`06_dto_vo_catalog.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/06_dto_vo_catalog.md) | DTO / VO 领域模型资产全集(明确字段、类型、校验规则与用例映射) |
| 07 | [`07_service_boundary.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/07_service_boundary.md) | 业务编排 Application Service 与基础 CRUD Service 的职责与协作边界 |
| 08 | [`08_transaction_and_invariants.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/08_transaction_and_invariants.md) | 事务控制边界、并发锁策略与 5 大跨表业务不变量 + 1 项报到生命周期策略落地规范 |
| 09 | [`09_external_adapter_contract.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/09_external_adapter_contract.md) | 外部适配器契约(班牌/人脸考勤、智能门禁、VR/3D学习接入,Mock与轻量异步机制) |
| 10 | [`10_permission_and_error_codes.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/10_permission_and_error_codes.md) | RuoYi RBAC 权限字符定义与全业务错误码模型规范 |
| 11 | [`11_open_questions.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/11_open_questions.md) | PRD V-01 ~ V-14 现场验证问题映射、API 影响与隔离应对策略 |
| 12 | [`12_contract_review_round1.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/api-v2/12_contract_review_round1.md) | Contract Review Round 1 审查报告(越界修正、P0/P1/P2裁定、结论A) |
---
## 5. 一期规模与核心指标统计 (Review Round 1)
- **一期 API 总数:** 91 个
- **P0 优先级 (核心闭环必须实现):** 49 个
- **P1 优先级 (辅助支撑能力):** 40 个
- **P2 优先级 (依赖现场确认/延后):** 2 个
- **模块分布统计:**
- **Module A (人员与基础资源):** 38 个 (P0: 16, P1: 22, P2: 0)
- **Module B (开班计划与排课):** 25 个 (P0: 17, P1: 7, P2: 1)
- **Module C (住宿/门禁/考勤):** 13 个 (P0: 9, P1: 4, P2: 0)
- **Module D (成绩/学习/统计/画像):** 15 个 (P0: 7, P1: 7, P2: 1)
- **外部 Adapter 接口数量:** 3 个 (`FaceAttendanceAdapter`, `DoorAccessAdapter`, `ExternalLearningAdapter`)
- **核心业务 Command API 数量:** 24 个 (包含 P0 核心 Command API 20 个 [19 个核心业务流程 Command + 1 个 P0 计划管理更新: `PUT /train/plans/{planId}`], P1 辅助 Command 4 个)
- **核心聚合 VO 数量:** 8 个
- **DTO / VO 定义总数:** 46 个 (P0 核心 DTO/VO: 26 个)
- **业务 Application Service:** 11 个 (包含 10 个 P0 Application Service + 1 个 P1 PortraitAppService)
- **标准事务控制边界:** 8 个
- **跨表业务强制不变量 (Cross-table Invariants):** 5 项
- **业务生命周期控制策略 (Lifecycle Policy):** 1 项 (Report Deadline)
---
## 6. Phase 1 Minimal Contract (P0 核心开发基线)
为确保后续 Issue 拆分与开发聚焦于最小可用的完整培训业务闭环,特设立 **Phase 1 Minimal Contract** 视图。后续开发阶段将首先按此基线开展编码与单元测试:
### 6.1 P0 API 规模概览 (共 49 个)
- **Module A (16个):** 教师列表/详情/增/改;学员列表/详情/录入;课程列表/详情/新增;教室列表/详情/新增;场地列表/详情/新增;宿舍列表/详情/新增/选项。
- **Module B (17个):** 计划列表/增/查/改/完成/工作台;本期课程列表/增;参训名单列表/单增/批量导入/详情;新增课次/修改课次/课次详情/冲突预检/完整课表;报到确认。
- **Module C (9个):** 批量预分配宿舍/单人调房/走读设置/住宿列表;门禁同步重试;签到/签退/人工修正/课次考勤名单。
- **Module D (7个):** 成绩项目列表/权重配置;录入成绩;成绩台账大表/学员成绩单;学习记录接收;计划统计概览。
### 6.2 P0 核心 Command API 清单 (共 20 个:19 个核心业务流程 Command + 1 个 P0 计划管理更新)
```text
1. POST /train/plans (创建培训计划)
2. PUT /train/plans/{planId} (P0计划管理更新:编辑培训计划)
3. POST /train/plans/{planId}/complete (结束培训计划)
4. POST /train/plans/{planId}/courses (配置本期课程)
5. POST /train/plans/{planId}/enrollments (添加参训人员)
6. POST /train/plans/{planId}/enrollments/batch (Excel批量导入参训人员)
7. POST /train/plans/{planId}/lessons (新增课次)
8. PUT /train/lessons/{lessonId} (修改课次)
9. POST /train/lessons/check-conflict (课次冲突预检)
10. POST /train/enrollments/{enrollmentId}/report (学员报到确认)
11. POST /train/plans/{planId}/accommodations/preallocate (批量住宿预分配)
12. PUT /train/enrollments/{enrollmentId}/accommodation (单人调房)
13. PUT /train/enrollments/{enrollmentId}/accommodation/day-student (设置走读)
14. POST /train/accommodations/{id}/auth/sync (门禁授权手动同步)
15. POST /train/attendance/sign-in (课次签到)
16. POST /train/attendance/sign-out (课次签退)
17. PUT /train/attendance/{checkinId} (人工修正考勤)
18. POST /train/plans/{planId}/score-items (成绩项目与权重配置)
19. POST /train/scores/entry (录入学员成绩)
20. POST /train/study-records/receive (外部学习记录接入)
```
### 6.3 P0 核心聚合 VO 清单
```text
1. PlanWorkbenchVO (培训计划综合工作台 VO)
2. PlanTimetableVO (计划完整课程表 VO)
3. EnrollmentDetailVO (学员参训全景档案 VO)
4. LessonConflictVO (排课冲突预检结果 VO)
5. PlanScoreMatrixVO (全班成绩台账大表 VO,只包含加权综合得分,不硬编码及格线)
6. PlanStatisticsVO (计划综合统计概览 VO)
```
### 6.4 P0 Application Service 清单 (10 个 P0 业务编排 Service)
`PlanAppService`, `EnrollmentAppService`, `SchedulingAppService`, `AccommodationAppService`, `ReportAppService`, `AttendanceAppService`, `ScoreAppService`, `LearningAppService`, `StatisticsAppService`, `ExternalIntegrationAppService`。
+139
View File
@@ -0,0 +1,139 @@
# RailwaySystem API 设计原则与规范
> **状态:** Phase 2 规范冻结 (Review Round 1 修订版)
> **适用范围:** 所有 Controller、API 契约、DTO/VO 及前端调用
---
## 1. 业务驱动与工作台优先原则
### 1.1 拒绝单表 CRUD 堆砌
本系统是面向铁路培训基地的**智慧培训业务系统**,不是 21 张数据库表的 CRUD 管理后台。严禁出现以下反模式:
```text
一张数据库表
→ 一个单表 Controller
→ 一个单表 Service
→ 一套单表 CRUD 页面
```
### 1.2 业务工作台与聚合驱动
业务人员在日常管理中是以“**开班计划**”为主线推进各阶段工作的。因此系统核心页面是以 **培训计划工作台 (Plan Workbench)** 为枢纽,通过聚合视图直观展示当前培训所处的阶段(筹备中、排课中、住宿分配中、报到中、培训进行中、已结束),并提供进入各业务动作的明确入口,而不是让用户在几十个左侧菜单栏来回跳转拼接数据。
---
## 2. Resource API 与 Command API 区分
为了保持 API 语义清晰,明确操作意图与事务边界,所有接口严格区分 **资源查询 (Resource API)** 与 **业务动作 (Command API)**。
### 2.1 资源型 API (Resource API)
- **定位:** 用于获取业务实体详情、列表、看板或特定维度的只读数据。
- **HTTP 动词:** 纯 `GET`。
- **特点:** 无副作用、幂等,不触发跨表业务状态跃迁。
- **示例:**
```text
GET /train/plans/{planId} // 获取计划详情
GET /train/plans/{planId}/courses // 查询计划本期课程列表
GET /train/plans/{planId}/timetable // 获取完整课程表
GET /train/enrollments/{id} // 查询参训档案详情
GET /train/dormitory/occupancy-board // 获取宿舍房态看板
```
### 2.2 命令型 API (Command API)
- **定位:** 用于驱动业务流程流转、触发状态机跃迁、执行跨表批量操作或外部协作。
- **HTTP 动词:** 业务动作用名词或动宾短语结尾(如 `/complete`, `/preallocate`, `/sign-in`)。
- **与普通 CRUD 更新的区分:**
- 普通的字段修改(如改教师电话、改教室容量)属于标准资源更新 (`PUT /train/teacher/{id}`);
- 真正业务 Command(如 `POST /train/plans/{id}/complete`, `POST /train/plans/{id}/accommodations/preallocate`, `POST /train/attendance/sign-in`)必须具有跨表状态推动语义,受严格事务保护与不变量拦截。
---
## 3. URL 命名与路径规范
1. **统一前缀:** 所有培训业务接口统一以 `/train` 开头,兼容 RuoYi 路由机制与安全过滤器。
2. **RESTful 资源层级:** 遵循“父资源/ID/子资源”层级表达从属关系:
- `/train/plans/{planId}/courses`
- `/train/plans/{planId}/lessons`
- `/train/plans/{planId}/enrollments`
3. **领域主键显式声明:** 路径变量统一使用数据库 V2 确立的领域 ID 名称:
- `{planId}` (String, 32位)
- `{planCourseId}` (Long)
- `{enrollmentId}` (Long)
- `{lessonId}` (Long)
- `{accommodationId}` (Long)
- `{checkinId}` (Long)
- `{scoreItemId}` (Long)
4. **禁止隐式复合参数替代主键:** 严禁在 URL 或 RequestBody 中反复退化为 `plan_id + student_id + c_id + time` 查找记录。
---
## 4. 分页与列表控制规范
严禁前端通过 `pageSize=99999` 获取全量业务数据,必须严格划分分页接口与专用聚合接口:
| 分类 | 典型接口 | 控制策略 | 说明 |
|:---|:---|:---|:---|
| **常规主数据列表** | 教师、学员、公共课程库、教学资源、教室、场地、电子班牌 | **必须分页** (`pageNum`, `pageSize`) | 默认 `pageSize=10`,最大允许 `pageSize=100` |
| **大型业务流列表** | 培训计划列表、参训学员名单、课次列表、学习记录流水 | **必须分页** (`pageNum`, `pageSize`) | 参训学员按计划筛选时分页呈现,提供按状态过滤 |
| **业务聚合视图** | 培训计划工作台 (`/workbench`)、完整课程表 (`/timetable`) | **不分页** (专用聚合 VO 返回) | 课程表按时间周期或开班全程以结构化日历网格返回 |
| **业务配置项** | 本期成绩项目配置 (`/score-items`)、本期课程清单 (`/courses`) | **不分页** (全量返回) | 一个培训班课程通常为 3~10 门,成绩项为 3~6 项,数量可控 |
| **统计与看板** | 统计概览 (`/overview`)、房态看板 (`/occupancy-board`) | **不分页** (结构化聚合看板) | 服务端完成聚合计算,返回统计指标及图表数据结构 |
---
## 5. 响应体规范 (RuoYi 体系兼容)
系统全面兼容若依现有的 `AjaxResult` 和 `TableDataInfo` 标准,前端不需要额外定制拦截器:
### 5.1 单一实体 / 命令响应 / 聚合 VO
采用 `AjaxResult` 包装:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"planId": "PLAN202609001",
"planName": "沈阳局2026年第1期高速铁路综合培训班"
}
}
```
### 5.2 分页列表响应
采用若依标准 `TableDataInfo` 包装:
```json
{
"total": 45,
"rows": [
{ "enrollmentId": 1001, "studentName": "张三", "enrollmentStatus": 1 }
],
"code": 200,
"msg": "查询成功"
}
```
### 5.3 业务异常响应
发生业务校验失败时返回标准 HTTP 200 或业务错误码(如 500),由全局异常捕获器统一封装:
```json
{
"code": 40901,
"msg": "排课冲突:王老师在该时段已有授课任务(安全理论基础 09:00-11:00)",
"data": {
"conflictType": "TEACHER",
"conflictLessonId": 801
}
}
```
---
## 6. 确定性标记与 API 优先级规范
### 6.1 需求确定性分级
- **【确认】:** PRD V1.0 与第二次项目会议纪要已明确冻结的业务需求与规则,不可随意擅改。
- **【默认/建议方案(待现场确认)】:** 设计层给出的标准工程实现方案,若现场验证后甲方有微调,可平滑变更。
- **【待现场确认】:** 明确属于 PRD V-01 ~ V-14 范围的事项(如及格线判定、学员画像标签、超期报到处理策略),API 契约保持高弹性,严禁硬编码。
- **【扩展】:** 一期不开发、仅在模型或契约层预留扩展兼容能力(如分布式事务、Kafka、Outbox模式、复杂证书体系),严禁过度设计。
### 6.2 API 优先级分级 (P0 / P1 / P2)
- **P0 (一期核心闭环必须实现):** 支撑培训班从立项、选课、入班、排课、住宿、报到、上课打卡、打分、统计到结训的最小可用闭环接口。后续 Issue 拆分优先以此作为开发基线。
- **P1 (一期辅助管理能力):** 在主链路畅通后实现的辅助运维、人工修正、主档维护与报表查询接口。
- **P2 (依赖现场确认或非首轮必须):** 依赖现场硬件协议确认或面向学员端的次要门户接口,实现前需再次确认。
@@ -0,0 +1,160 @@
# Module A:人员与基础资源 API 契约
> **模块定位:** 支撑培训业务运转的基础主数据管理(Teacher, Student, Course, Resource, Classroom, Site, Board, Dormitory)。
> **设计特点:** 标准基础资料型对象,保留规范化的资源 CRUD 与关联管理接口,严格遵循敏感信息脱敏与业务校验。不引入审批流、多版本、复杂搜索等过度设计。
> **优先级统计:** 共 38 个 API (P0: 16个, P1: 22个, P2: 0个)。
---
## 1. 教师管理 (Teacher)
### 1.1 分页查询教师列表 【P0】【确认】
- **业务目的:** 为管理员提供教师名录检索,排课选教师时必用。
- **HTTP Method:** `GET`
- **Path:** `/train/teacher/list`
- **Permission:** `train:teacher:list`
- **Request (Query):** `pageNum`, `pageSize`, `tName`, `orgName`, `status`
- **Response (TableDataInfo):** 返回包含 `tId`, `tName`, `tGender`, `tBirth`, `orgName`, `jobTitle`, `tPhone`, `tEmail`, `status` 等的教师列表。
- **核心校验:** `pageSize <= 100`。
- **涉及数据表:** `train_teacher`
### 1.2 获取教师详情 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/teacher/{tId}`
- **Permission:** `train:teacher:query`
- **核心校验:** 教师 ID 存在性。
- **错误码:** `TEACHER_NOT_FOUND (40401)`
### 1.3 新增教师 【P0】【确认】
- **业务目的:** 手工录入新培训教师。
- **HTTP Method:** `POST`
- **Path:** `/train/teacher`
- **Permission:** `train:teacher:add`
- **Request (TeacherCreateDTO):** `tName`, `tGender`, `tBirth`, `orgName`, `jobTitle`, `tPhone`, `tEmail`, `remark`
- **核心校验:** `tName` 必填;电话格式校验。
- **涉及数据表:** `train_teacher`
### 1.4 修改教师 【P0】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/teacher/{tId}`
- **Permission:** `train:teacher:edit`
- **Request (TeacherUpdateDTO):** 允许变更联系方式、单位、职务、备注。
### 1.5 修改教师启停用状态 【P1】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/teacher/{tId}/status`
- **Permission:** `train:teacher:edit`
- **Request (JSON):** `{"status": 0}` (0 停用,1 启用)
---
## 2. 学员主档管理 (Student)
### 2.1 分页查询学员主档列表 【P0】【确认】
- **业务目的:** 检索系统学员库(长期主档)。
- **HTTP Method:** `GET`
- **Path:** `/train/student/list`
- **Permission:** `train:student:list`
- **Response (TableDataInfo):** 身份证号默认只返回脱敏文本 `idCardMasked` 与 `idCardLast4`。
### 2.2 获取学员详情 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/student/{sId}`
- **Permission:** `train:student:query`
- **核心校验:** 学员存在性;身份证号默认脱敏展示。
### 2.3 新增学员 【P0】【确认】
- **业务目的:** 手工建档学员主数据。
- **HTTP Method:** `POST`
- **Path:** `/train/student`
- **Permission:** `train:student:add`
- **Request (StudentCreateDTO):** `sName`, `sGender`, `sBirth`, `orgName`, `sPhone`, `sEmail`, `idCard`, `faceRef`, `remark`
- **核心校验:** 身份证号合规;后端计算 HMAC-SHA256 哈希作为 `id_card_hash` 查重;AES/SM4 可逆加密存入 `id_card_ciphertext`。
- **错误码:** `STUDENT_IDCARD_DUPLICATED (40002)`
### 2.4 修改学员主档 【P1】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/student/{sId}`
- **Permission:** `train:student:edit`
### 2.5 Excel批量导入学员主档 【P1】【确认】
- **HTTP Method:** `POST`
- **Path:** `/train/student/import`
- **Permission:** `train:student:import`
- **说明:** 基地全局学员库的独立导入入口。计划内的名单批量导入优先使用 Module B 的 `/train/plans/{id}/enrollments/batch`。
---
## 3. 课程库与教学资源管理 (Course & Resource)
### 3.1 分页查询课程库列表 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/course/list`
- **Permission:** `train:course:list`
- **说明:** 极简设计,一期删除课程多版本、审批流程、热门标签。
### 3.2 获取课程详情及绑定资源 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/course/{cId}`
- **Permission:** `train:course:query`
- **Response (CourseDetailVO):** 聚合返回课程基本信息及已关联的教学资源列表。
### 3.3 新增课程 【P0】【确认】
- **HTTP Method:** `POST`
- **Path:** `/train/course`
- **Permission:** `train:course:add`
- **Request (CourseCreateDTO):** `cCode`, `cName`, `cType`, `cDuration`, `teachingObjective`, `courseContent`
- **核心校验:** `cCode` 唯一性;`cName` 必填。
### 3.4 修改课程 【P1】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/course/{cId}`
- **Permission:** `train:course:edit`
### 3.5 课程绑定教学资源 【P1】【确认】
- **HTTP Method:** `POST`
- **Path:** `/train/course/{cId}/resources`
- **Permission:** `train:course:edit`
- **Request (JSON):** `{"resourceId": "RES001", "sortNo": 1}`
- **主要数据表:** `train_course_resource`
### 3.6 课程解绑教学资源 【P1】【确认】
- **HTTP Method:** `DELETE`
- **Path:** `/train/course/{cId}/resources/{resourceId}`
- **Permission:** `train:course:edit`
### 3.7 教学资源独立 CRUD (5个 API) 【P1】【确认】
- `GET /train/resource/list` 【P1】(分页查询教学资源)
- `GET /train/resource/{resourceId}` 【P1】(获取资源详情)
- `POST /train/resource` 【P1】(新增教学资源:支持文档、视频、VR、3D、链接)
- `PUT /train/resource/{resourceId}` 【P1】(修改教学资源)
- `DELETE /train/resource/{resourceId}` 【P1】(删除教学资源,被课程引用时阻止删除)
---
## 4. 空间与设备资源管理 (Classroom, Site, Board, Dormitory)
### 4.1 教室管理 (Classroom)
- `GET /train/classroom/list` 【P0】【确认】(分页查询教室,排课选普通教室必用)
- `GET /train/classroom/{classRoomId}` 【P0】【确认】(获取教室详情及绑定班牌)
- `POST /train/classroom` 【P0】【确认】(新增教室:`roomName`, `roomLocation`, `capacity`, `roomStatus`)
- `PUT /train/classroom/{classRoomId}` 【P1】【确认】(修改教室)
### 4.2 实训场地管理 (Training Site)
- `GET /train/training-site/list` 【P0】【确认】(分页查询实训场地:VR区、3D拆解区、实操演练区,排课必用)
- `GET /train/training-site/{siteId}` 【P0】【确认】(获取实训场地详情)
- `POST /train/training-site` 【P0】【确认】(新增实训场地:`siteName`, `siteLocation`, `capacity`, `siteDevice`)
- `PUT /train/training-site/{siteId}` 【P1】【确认】(修改实训场地)
### 4.3 电子班牌管理 (Electronic Board)
- `GET /train/electronic-board/list` 【P1】【确认】(分页查询班牌设备)
- `GET /train/electronic-board/{boardId}` 【P1】【确认】(获取班牌详情)
- `POST /train/electronic-board` 【P1】【确认】(注册班牌:`boardName`, `boardSn`, `bindClassroomId`, `boardIp`, `apiEndpoint`)
- `PUT /train/electronic-board/{boardId}` 【P1】【确认】(修改班牌配置)
### 4.4 宿舍房间管理 (Dormitory Info)
- `GET /train/dormitory/list` 【P0】【确认】(分页查询宿舍房间列表,附带容量、已住人数、剩余床位)
- `GET /train/dormitory/{dormId}` 【P0】【确认】(获取指定房间详情及当前入住人员列表)
- `POST /train/dormitory` 【P0】【确认】(新增房间:`dormBuilding`, `dormFloor`, `dormRoom`, `capacity`, `genderType`, `lockDeviceSn`)
- `PUT /train/dormitory/{dormId}` 【P1】【确认】(修改房间信息,容量缩减不可小于当前已住人数)
- `GET /train/dormitory/options` 【P0】【确认】(获取楼栋、楼层级联下拉数据,分房弹窗必需)
@@ -0,0 +1,238 @@
# Module B:开班计划与排课(核心业务模块)API 契约
> **模块定位:** 智慧培训业务核心流转引擎,贯穿“立项建班 → 课程配置 → 名单确定 → 课次编排与冲突检测 → 课程表生成 → 学员报到”。
> **设计原则:** 禁止退化为 5 张表的独立 CRUD,全面以“开班计划聚合工作台”与“业务 Command”为主线。
> **优先级统计:** 共 25 个 API (P0: 17个, P1: 7个, P2: 1个)。
---
## 1. 培训计划与聚合工作台 (ClassPlan & Workbench)
### 1.1 分页查询培训计划列表 【P0】【确认】
- **业务目的:** 管理员分页检索开班班次,支持按状态、开班时间范围筛选。
- **HTTP Method:** `GET`
- **Path:** `/train/plans`
- **Permission:** `train:plan:list`
- **Request (Query):** `pageNum`, `pageSize`, `planName`, `planStatus`, `startTimeBegin`, `startTimeEnd`
- **Response (TableDataInfo):** 返回包含计划基本信息、课程门数、实际参训人数的概览卡片数据。
### 1.2 创建培训计划 (UC-01) 【P0】【确认】
- **业务目的:** 立项创建新的培训班,明确培训起止时间、计划人数与报到截止规则。
- **HTTP Method:** `POST`
- **Path:** `/train/plans`
- **Permission:** `train:plan:add`
- **Request (PlanCreateDTO):**
```json
{
"planName": "沈阳局2026年第1期高速铁路信号检修专项培训班",
"startTime": "2026-10-10 08:30:00",
"endTime": "2026-10-24 17:30:00",
"trainAddress": "沈阳培训基地第3教学楼",
"recruitNum": 40,
"applyCondition": "各电务段在岗信号工,具备2年以上工作经验",
"reportDeadline": "2026-10-09 18:00:00",
"remark": "全脱产封闭培训"
}
```
- **核心校验:**
- `startTime < endTime` 起止时间有效性;
- `reportDeadline <= startTime` 报到截止时间不得晚于开班时间;
- `recruitNum > 0`;
- 初始 `plan_status` 强制设为 `0` (未开始)。
- **错误码:** `PLAN_TIME_INVALID (40010)`, `PLAN_NAME_DUPLICATED (40011)`
### 1.3 获取培训计划基本信息 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}`
- **Permission:** `train:plan:query`
### 1.4 编辑培训计划 (UC-02) 【P0】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/plans/{planId}`
- **Permission:** `train:plan:edit`
- **Request (PlanUpdateDTO):** 包含名称、地址、人数、报到截止时间、备注。已结训计划不可修改。
### 1.5 结束培训计划 (UC-29) 【P0】【确认】
- **业务目的:** 标志本期培训结束,将计划状态置为“已结束”,参训学员状态批量归档为“已完成”,历史签到、学习、成绩锁定留存。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/complete`
- **Permission:** `train:plan:complete`
- **事务边界:** 强事务。更新 `train_class_plan.plan_status = 2`,更新该班所有未退学学员的 `train_enrollment.enrollment_status = 4` (已完成) 及 `complete_time = NOW()`。
### 1.6 获取培训计划综合工作台 (PlanWorkbenchVO) 【P0】【确认】
- **业务目的:** 核心枢纽接口!管理员打开某个计划时调用,一次性获取计划生命周期阶段看板与各维度指标摘要,作为后续操作决策入口。
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/workbench`
- **Permission:** `train:plan:query`
- **Response (AjaxResult<PlanWorkbenchVO>):** 聚合返回开班基础信息、课程概览、参训人数统计、排课状态概览、住宿需求与预分配统计、成绩配置概览及下一步操作引导文本。
---
## 2. 本期课程配置 (Plan Course)
### 2.1 查询本期已配置课程列表 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/courses`
- **Permission:** `train:plan:query`
### 2.2 配置本期课程 (UC-03) 【P0】【确认】
- **业务目的:** 引入课程库中的一门或多门公共课程作为本期培训内容。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/courses`
- **Permission:** `train:plan:edit`
- **Request (PlanCourseAddDTO):** `{"cIds": ["C001", "C002"], "remark": "专业必修"}`
- **核心校验:** 基于 `UNIQUE(plan_id, c_id)` 防重复添加。
### 2.3 移除本期课程 【P1】【确认】
- **HTTP Method:** `DELETE`
- **Path:** `/train/plans/{planId}/courses/{planCourseId}`
- **Permission:** `train:plan:edit`
- **核心校验:** 若该课程已排定课次 (`train_lesson`) 或已关联成绩项目 (`train_score_item`),严禁删除。
---
## 3. 参训人员管理 (Enrollment)
### 3.1 查看本期参训名单 (UC-06) 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/enrollments`
- **Permission:** `train:enrollment:list`
- **Request (Query):** `pageNum`, `pageSize`, `studentName`, `enrollmentStatus`, `accommodationRequired`
### 3.2 添加单名参训人员 (UC-04) 【P0】【确认】
- **业务目的:** 管理员从已有学员库中选择某位学员加入本班。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/enrollments`
- **Permission:** `train:enrollment:add`
- **Request (EnrollmentAddDTO):** `{"studentId": "S2026001", "sourceType": 1, "accommodationRequired": 1}`
- **核心校验:** 防重复参训 (`UNIQUE(plan_id, student_id)`);人数上限告警。
### 3.3 Excel批量导入参训人员 (UC-05) 【P0】【确认】
- **业务目的:** 基地通过各站段汇总上报的名单 Excel 一次性批量导入本班学员。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/enrollments/batch`
- **Permission:** `train:enrollment:import`
- **Request (Multipart):** Excel 文件 (`file`),参数 `autoCreateStudent=true`。
- **事务边界:** 批量事务入库。
### 3.4 获取参训档案详情 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/enrollments/{enrollmentId}`
- **Permission:** `train:enrollment:query`
- **Response (AjaxResult<EnrollmentDetailVO>):** 聚合返回参训状态、脱敏身份证、宿舍分配、报到记录、门禁授权状态。
### 3.5 变更参训状态 【P1】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/enrollments/{enrollmentId}/status`
- **Permission:** `train:enrollment:edit`
- **Request (JSON):** `{"enrollmentStatus": 2, "remark": "因公请假"}` (2 请假,3 放弃)
### 3.6 移除参训人员 【P1】【确认】
- **HTTP Method:** `DELETE`
- **Path:** `/train/enrollments/{enrollmentId}`
- **Permission:** `train:enrollment:remove`
- **核心校验:** 若学员已产生考勤或成绩记录,禁止物理删除,提示转为放弃状态。
---
## 4. 课次编排与冲突检查 (Lesson & Timetable)
### 4.1 新增课次 (UC-07) 【P0】【确认】
- **业务目的:** 排定一次具体的教学活动,指定课程、授课教师、上课时间段及教室或实训场地。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/lessons`
- **Permission:** `train:lesson:add`
- **Request (LessonCreateDTO):** `planCourseId`, `tId`, `startTime`, `endTime`, `venueType`, `classRoomId`, `siteId`, `remark`
- **核心校验:** 课次时间落在计划起止时间内;场地二选一;行锁保护下的教师/教室/场地/本班四维排课冲突检查。
- **错误码:** `LESSON_TEACHER_CONFLICT (40040)`, `LESSON_CLASSROOM_CONFLICT (40041)`, `LESSON_SITE_CONFLICT (40042)`, `LESSON_PLAN_OVERLAP (40043)`
### 4.2 修改课次 (UC-08) 【P0】【确认】
- **HTTP Method:** `PUT`
- **Path:** `/train/lessons/{lessonId}`
- **Permission:** `train:lesson:edit`
- **核心校验:** 排除自身后重新进行冲突检查;已有考勤记录时禁止修改时段。
### 4.3 删除课次 (UC-09) 【P1】【确认】
- **HTTP Method:** `DELETE`
- **Path:** `/train/lessons/{lessonId}`
- **Permission:** `train:lesson:remove`
- **核心校验:** 已有打卡考勤记录时严禁删除。
### 4.4 排课冲突检查接口 (UC-10) 【P0】【确认】
- **业务目的:** 管理员在排课弹窗中选择好教师、场地、时间后,前端失焦实时调用该接口进行冲突预检,无需真正入库,返回友好的冲突原因与冲突课次列表。
- **HTTP Method:** `POST`
- **Path:** `/train/lessons/check-conflict`
- **Permission:** `train:lesson:query`
- **Response (AjaxResult<LessonConflictVO>):** 返回是否存在冲突、冲突类型、冲突详细信息及冲突课次列表。
### 4.5 获取计划完整课表 (UC-11) 【P0】【确认】
- **业务目的:** 按时间轴返回本期培训的所有课次集合,供工作台日历视图展示与打印。
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/timetable`
- **Permission:** `train:plan:query`
- **Response (AjaxResult<PlanTimetableVO>)**
### 4.6 获取教师个人课表 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/teacher/{tId}/timetable`
- **Permission:** `train:teacher:query`
---
## 5. 学员报到 (Class Report)
### 5.1 学员报到确认 (UC-15) 【P0】【确认】
- **业务目的:** 学员到达基地后,通过刷身份证、人脸识别或管理员人工核验完成到班报到。完成报到后,立即向学员展示其宿舍安排、课程表与首课教室场地。
- **HTTP Method:** `POST`
- **Path:** `/train/enrollments/{enrollmentId}/report`
- **Permission:** `train:report:confirm`
- **Request (ReportConfirmDTO):**
```json
{
"reportWay": 1,
"reportDeviceSn": "FACE-PAD-EAST-01",
"verifyNote": "身份证核验通过",
"forceOverride": false
}
```
- **生命周期策略 (Report Lifecycle Policy):**
- 【确认】:`report_deadline` 配置在 `train_class_plan` 上;报到时必须读取并检查该截止时间;
- 【默认/建议方案(待现场确认)】:若当前时间晚于 `report_deadline`,当 `forceOverride=false` 时返回警告(`REPORT_DEADLINE_WARNING`),提示管理员确认;若 `forceOverride=true` 或系统策略为宽松模式,则允许完成报到,并在报到流水中标记 `is_overdue = true`;
- 兼容现场确认可能收敛为三种模式:严格阻断 (`BLOCK`)、提示警告 (`WARNING`) 或管理员强制覆盖 (`ADMIN_OVERRIDE`)。
- **Response (AjaxResult<ReportResultVO>):**
```json
{
"code": 200,
"msg": "报到成功!",
"data": {
"enrollmentId": 1001,
"studentName": "张三",
"reportTime": "2026-10-09 14:20:00",
"isOverdue": false,
"policyAction": "NORMAL",
"dormitory": {
"building": "南区2号宿舍楼",
"room": "305室",
"bedNo": "1号床"
},
"nextLesson": {
"courseName": "信号基础理论",
"startTime": "2026-10-10 09:00:00",
"location": "CR101教室"
}
}
}
```
- **错误码:** `REPORT_ALREADY_CONFIRMED (40050)`, `REPORT_DEADLINE_WARNING (40051)`
### 5.2 查询计划报到流水列表 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/reports`
- **Permission:** `train:report:list`
### 5.3 学员端获取个人当前培训信息 【P2】【待现场确认】
- **业务目的:** 学员登录后通过手机端或平板查看本人所在培训班、报到状态、宿舍及课表。
- **HTTP Method:** `GET`
- **Path:** `/train/student/my-training`
- **Permission:** 登录学员默认可见本人信息。
- **分级理由:** 依赖学员端专用界面的排期建设,不作为一期基地管理端闭环的 P0 阻塞项,标记为 P2。
@@ -0,0 +1,120 @@
# Module C:住宿 / 门禁 / 考勤 API 契约
> **模块定位:** 培训执行过程中的关键生活与教学秩序支撑,涵盖“住宿预分配 → 住宿人工调整 → 门禁授权下发 → 课次班牌签到/签退 → 人工考勤修正”。
> **核心原则:** 房间级分配为主(床位可选)、住宿与外部门禁解耦、考勤严格基于具体课次 (`lesson_id`) 与参训档案 (`enrollment_id`)。轻量异步重试,不引入重型消息队列。
> **优先级统计:** 共 13 个 API (P0: 9个, P1: 4个, P2: 0个)。
---
## 1. 住宿安排管理 (Accommodation)
### 1.1 批量住宿预分配 (UC-12) 【P0】【确认】
- **业务目的:** 名单确定后、报到之前,管理员一键对本班有住宿需求且尚未分配的学员进行自动化预分配。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/accommodations/preallocate`
- **Permission:** `train:accommodation:assign`
- **Request (AccommodationPreallocateDTO):** `targetEnrollmentIds`, `preferredBuilding`, `genderSeparated`, `sameClassAdjacent`
- **分配策略规则【默认/建议方案】:**
1. 严格遵守性别隔离(男学员进男寝,女学员进女寝);
2. 优先填满同房间剩余床位,同班同寝、相邻房间优先;
3. 房间级分配为主,`bed_no` 按序号顺编(可选填)。
- **并发与容量保护:** 对目标房间加行级排他锁 (`SELECT FOR UPDATE`),核验 `count < capacity`。
- **错误码:** `DORM_CAPACITY_EXCEEDED (40060)`, `DORM_GENDER_CONFLICT (40061)`
### 1.2 单人住宿调整 (UC-13) 【P0】【确认】
- **业务目的:** 报到前或培训期间,针对身体不适等特殊情况,管理员手动将某个学员调换到其他房间或指定床位。
- **HTTP Method:** `PUT`
- **Path:** `/train/enrollments/{enrollmentId}/accommodation`
- **Permission:** `train:accommodation:adjust`
- **Request (AccommodationAdjustDTO):** `targetDormId`, `bedNo`, `reason`
- **核心校验:** 目标房间存在且状态正常;性别匹配;容量未满。
- **错误码:** `DORM_ROOM_FULL (40062)`, `DORM_GENDER_MISMATCH (40063)`
### 1.3 设置走读 / 不住宿 【P0】【确认】
- **业务目的:** 本地或走读学员无需住宿,将其设置为不住宿,释放其可能已占用的房间床位。
- **HTTP Method:** `PUT`
- **Path:** `/train/enrollments/{enrollmentId}/accommodation/day-student`
- **Permission:** `train:accommodation:adjust`
- **Request (JSON):** `{"isDayStudent": true, "remark": "本地职工申请走读"}`
### 1.4 查看计划住宿安排列表 (UC-14) 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/accommodations`
- **Permission:** `train:accommodation:list`
- **Request (Query):** `building`, `floor`, `room`, `studentName`, `accommodationStatus`
- **Response (TableDataInfo):** 包含学员姓名、单位、性别、入住楼栋、房间、床号、分配时间、门禁同步状态。
### 1.5 查看实时宿舍房态看板 【P1】【确认】
- **业务目的:** 供宿管或管理员全局掌控全基地宿舍楼栋各房间的占用率与空余床位。
- **HTTP Method:** `GET`
- **Path:** `/train/dormitory/occupancy-board`
- **Permission:** `train:dormitory:list`
- **Response (AjaxResult<DormOccupancyBoardVO>):** 按楼栋/楼层嵌套分组的房间网格列表。
---
## 2. 门禁授权管理 (Dorm Auth)
### 2.1 手动触发 / 重试门禁授权同步 (UC-16) 【P0】【确认】
- **业务目的:** 住宿确定后系统自动触发门禁下发;若因外部网络或门锁离线导致同步失败,管理员可在此点击手动重试下发。
- **HTTP Method:** `POST`
- **Path:** `/train/accommodations/{accommodationId}/auth/sync`
- **Permission:** `train:dormauth:sync`
- **事务与轻量解耦实现【默认/建议方案】:**
1. 数据库事务提交成功后,通过 `afterCommit` 触发后续 Adapter 调用;
2. 一期默认建议:`afterCommit` → 轻量异步执行器 → Adapter;如果未引入异步执行器,则为 `afterCommit` → 当前线程事务外调用 Adapter;
3. 两者都必须保证:外部调用失败不得回滚已经提交的本地数据库事务,而是独立更新同步状态 (`face_sync_status = 2`) 并记录 `last_error`,支持页面手动重试;
4. 明确:MQ / Outbox / 分布式事务 = 一期不引入【扩展】。
- **错误码:** `STUDENT_FACE_DATA_MISSING (40070)`, `DOOR_DEVICE_OFFLINE (40071)`
### 2.2 查询计划门禁授权下发状态 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/dorm-auths`
- **Permission:** `train:dormauth:list`
---
## 3. 课次考勤管理 (Attendance)
### 3.1 课次签到 (UC-17) 【P0】【确认】
- **业务目的:** 电子班牌人脸识别成功后回传签到事件,或管理员/教师代签到。
- **HTTP Method:** `POST`
- **Path:** `/train/attendance/sign-in`
- **Permission:** `train:attendance:checkin`
- **Request (AttendanceSignInDTO):** `enrollmentId`, `lessonId`, `boardId`, `checkinWay`, `signInTime`
- **核心校验:**
- **业务不变量 1 强制校验:** `enrollment.plan_id == lesson.plan_course.plan_id`,防止跨班串签!
- 幂等性:基于 `(enrollment_id, lesson_id)` 查重,存在已有签到记录时不重复覆盖。
- **错误码:** `ATTENDANCE_PLAN_MISMATCH (40080)`, `LESSON_NOT_FOUND (40081)`
### 3.2 课次签退 (UC-18) 【P0】【确认】
- **业务目的:** 课次结束后,学员刷班牌签退。
- **HTTP Method:** `POST`
- **Path:** `/train/attendance/sign-out`
- **Permission:** `train:attendance:checkin`
- **Request (AttendanceSignOutDTO):** `enrollmentId`, `lessonId`, `signOutTime`
### 3.3 人工修正考勤 (UC-19) 【P0】【确认】
- **业务目的:** 针对因设备识别失败、公假外出、迟到等情形,任课教师或管理员手动调整考勤状态。
- **HTTP Method:** `PUT`
- **Path:** `/train/attendance/{checkinId}`
- **Permission:** `train:attendance:edit`
- **Request (AttendanceAdjustDTO):** `attendanceStatus`, `manualFlag=1`, `remark`
- **核心校验:** 强制标记 `manual_flag = 1` 留痕。
### 3.4 人工补录课次考勤记录 【P1】【确认】
- **HTTP Method:** `POST`
- **Path:** `/train/attendance/manual-add`
- **Permission:** `train:attendance:edit`
### 3.5 查询指定课次的考勤名单 【P0】【确认】
- **业务目的:** 任课教师在上课时查看当前课次的应到人数、实到人数、缺勤名单与签到详情。
- **HTTP Method:** `GET`
- **Path:** `/train/lessons/{lessonId}/attendance`
- **Permission:** `train:attendance:list`
- **Response (AjaxResult<LessonAttendanceVO>):** 聚合返回本班所有参训人员在该课次的打卡状态与时间列表。
### 3.6 查询计划考勤统计概况 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/attendance/summary`
- **Permission:** `train:attendance:query`
@@ -0,0 +1,171 @@
# Module D:成绩 / 学习 / 统计 / 画像 API 契约
> **模块定位:** 培训业务成效与多维量化评估模块,贯穿“成绩项目与权重配置 → 成绩录入导入台账 → 外部学习数据幂等接入 → 统计概览 → 学员简单画像”。
> **核心原则:** 统计一期不建持久化表、成绩权重总和强校验 100%、学习记录事件幂等防重、加权综合分作为核心结果、及格判定与画像标签保持现场弹性。
> **优先级统计:** 共 15 个 API (P0: 7个, P1: 7个, P2: 1个)。
---
## 1. 成绩项目与成绩管理 (ScoreItem & StudentScore)
### 1.1 查询计划成绩项目配置列表 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/score-items`
- **Permission:** `train:score:query`
- **Response (AjaxResult<List<ScoreItemVO>>):** 返回包括 `scoreItemId`, `planCourseId`, `courseName`, `itemName`, `itemType` (1理论 2实操 3综合), `weight` (权重), `fullScore` (满分), `sortNo`。
### 1.2 配置本期成绩项目 (UC-20) 【P0】【确认】
- **业务目的:** 为本期培训班定义成绩构成及权重配比,支持绑定本期课程或独立综合考核。
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/score-items`
- **Permission:** `train:score:config`
- **Request (ScoreItemBatchConfigDTO):** 包含各项名称、类型、权重、满分。
- **核心校验:**
- **权重总和校验:** `SUM(weight) == 100.0`;
- `fullScore > 0`;
- **业务不变量 4 校验:** 若 `planCourseId` 非空,必须验证该课程属于本 `planId`。
- **错误码:** `SCORE_WEIGHT_SUM_INVALID (40090)`, `SCORE_ITEM_COURSE_MISMATCH (40091)`
### 1.3 录入学员单条/批量成绩 (UC-21) 【P0】【确认】
- **业务目的:** 教师或管理员在成绩录入页面对学员某一门或多门科目打分。
- **HTTP Method:** `POST`
- **Path:** `/train/scores/entry`
- **Permission:** `train:score:edit`
- **Request (ScoreEntryBatchDTO):** 包含 `enrollmentId`, `scoreItemId`, `score`, `sourceType`, `remark`。
- **核心校验:**
- **业务不变量 2 校验:** `studentScore.enrollment.plan_id == scoreItem.plan_id`,严禁跨班给分!
- **业务不变量 5 校验:** `0 <= score <= scoreItem.full_score`,严禁超过满分;
- 幂等更新:基于 `UNIQUE(enrollment_id, score_item_id)`。
- **错误码:** `SCORE_EXCEEDS_FULL_SCORE (40092)`, `SCORE_PLAN_MISMATCH (40093)`
### 1.4 Excel批量导入成绩 (UC-22) 【P1】【确认】
- **HTTP Method:** `POST`
- **Path:** `/train/plans/{planId}/scores/import`
- **Permission:** `train:score:import`
- **Request (Multipart):** Excel 成绩文件 (`file`)。
### 1.5 获取培训计划全班成绩台账矩阵 【P0】【确认】
- **业务目的:** 管理员成绩大表。横向动态展示各成绩项目得分,最后一列计算本期综合成绩。
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/scores/matrix`
- **Permission:** `train:score:query`
- **及格判定需求修正说明【重要】:**
- 【确认】:`weightedTotalScore` (加权综合成绩) 为本接口确认必须输出的业务核心结果;
- 【默认/建议方案(待现场确认)】:`passStatus` (是否及格) 字段标记为待现场确认项。根据 PRD V-07,及格线是否存在、及格分数为何值待现场确认。接口默认不强制做及格过滤,若现场需要可在入参指定 `passingScore` 动态评估。
- **Response (AjaxResult<PlanScoreMatrixVO>):**
```json
{
"code": 200,
"data": {
"planId": "PLAN202609001",
"headers": [
{ "scoreItemId": 301, "itemName": "信号理论考试", "weight": 30.0 },
{ "scoreItemId": 302, "itemName": "联锁配线实操", "weight": 40.0 },
{ "scoreItemId": 303, "itemName": "日常综合考核", "weight": 30.0 }
],
"rows": [
{
"enrollmentId": 1001,
"studentName": "张三",
"orgName": "大连工务段",
"scores": { "301": 88.5, "302": 92.0, "303": 85.0 },
"weightedTotalScore": 88.85,
"passStatus": null
}
]
}
}
```
### 1.6 获取单学员本期成绩单 (UC-23) 【P0】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/enrollments/{enrollmentId}/scores`
- **Permission:** `train:score:query`
- **Response (AjaxResult<StudentScoreReportVO>):** 返回学员各科目得分、权重、满分及加权综合分 `weightedTotalScore`。
---
## 2. 外部学习数据接入 (Study Record)
### 2.1 接收外部系统学习记录 (UC-24) 【P0】【确认】
- **业务目的:** 供 VR 巡检模拟器、3D 实训拆装台、电子阅览室等外部设备回传学员自主学习时长。
- **HTTP Method:** `POST`
- **Path:** `/train/study-records/receive`
- **Permission:** 系统级 Adapter 调用或内部 API 认证。
- **Request (StudyRecordReceiveDTO):** `enrollmentId`, `planCourseId`, `resourceId`, `learningContent`, `sourceSystem`, `sourceEventId`, `startTime`, `endTime`, `studySeconds`
- **核心幂等机制:** 基于 `UNIQUE(source_system, source_event_id)`,存在重复事件时返回 `status: "duplicate"`,不重复入库;正常入库返回 `status: "accepted"`。
- **业务不变量 3 校验:** 若 `planCourseId` 非空,校验 `enrollment.plan_id == planCourse.plan_id`。
### 2.2 多维查询学习记录流水 (UC-25) 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/study-records`
- **Permission:** `train:learning:list`
### 2.3 查询学员个人学习记录明细 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/enrollments/{enrollmentId}/study-records`
- **Permission:** `train:learning:list`
---
## 3. 统计分析与成绩下钻 (Statistics)
### 3.1 培训计划综合统计概览 (UC-26) 【P0】【确认】
- **业务目的:** 管理员查看本期培训综合报表。
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/statistics/overview`
- **Permission:** `train:statistics:query`
- **设计特点:** 一期不建持久化统计表,实时多表聚合计算。指标包括参训人数、报到人数、结训人数、平均出勤率、自主学习总时长、平均加权综合分(移除预设的及格率)。
### 3.2 成绩区间分布与学员下钻分析 【P2】【待现场确认】
- **业务目的:** 成绩梯队分段统计与点击下钻学员名单。
- **HTTP Method:** `GET`
- **Path:** `/train/plans/{planId}/statistics/score-distribution`
- **Permission:** `train:statistics:query`
- **分级理由:** 依赖现场确认及格线与分段区间(PRD V-07),作为分析类高级接口,标记为 P2。
### 3.3 学员个人全流程训练详情 (UC-27) 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/enrollments/{enrollmentId}/training-detail`
- **Permission:** `train:statistics:query`
- **Response (AjaxResult<StudentTrainingDetailVO>):** 返回学员全部出勤打卡流水、各成绩项得分明细与自主学习时长明细。
---
## 4. 学员画像 (Portrait)
### 4.1 生成 / 刷新学员画像 【P1】【确认】
- **业务目的:** 结课前后,管理员或系统根据考勤、成绩、自主学习时长生成简单画像。
- **HTTP Method:** `POST`
- **Path:** `/train/enrollments/{enrollmentId}/portrait/generate`
- **Permission:** `train:portrait:generate`
- **画像模型解耦策略【重要】:**
- 【确认】:一期存在简单学员画像数据;
- 【默认/建议方案(待现场确认)】:采用指标集 + 标签集方式存储呈现;
- 【待现场确认】:最终指标数量、指标名称、具体标签集、是否使用雷达图。
### 4.2 查询学员综合画像 (UC-28) 【P1】【确认】
- **HTTP Method:** `GET`
- **Path:** `/train/enrollments/{enrollmentId}/portrait`
- **Permission:** `train:portrait:query`
- **Response (AjaxResult<StudentPortraitVO>):**
```json
{
"code": 200,
"data": {
"enrollmentId": 1001,
"studentName": "张三",
"orgName": "大连工务段",
"metrics": [
{ "metricKey": "attendance", "metricName": "出勤情况", "scoreValue": 96.5 },
{ "metricKey": "theory", "metricName": "理论成绩", "scoreValue": 88.0 },
{ "metricKey": "practical", "metricName": "实操考核", "scoreValue": 92.5 },
{ "metricKey": "learningDuration", "metricName": "自主学习", "scoreValue": 85.0 }
],
"tags": ["出勤良好", "实操突出", "自主钻研"],
"summary": "该学员在培训期间出勤率高,实操表现优异。",
"generatedTime": "2026-10-24 16:30:00"
}
}
```
- **错误码:** `PORTRAIT_NOT_FOUND (40110)`
+160
View File
@@ -0,0 +1,160 @@
# RailwaySystem DTO / VO 领域模型目录与字段规范
> **规范原则:** 坚决禁止 Controller 直接使用数据库持久化 Domain 作为前端接口契约。
> 明确区分 **Request DTO** (前端输入参数校验)、**Command DTO** (驱动业务动作)、**Query DTO** (查询筛选条件) 和 **Response VO** (安全脱敏与视图聚合)。
> **Review Round 1 修订:** 增加 P0 / P1 / P2 优先级标注;`weightedTotalScore` 作为核心【确认】成绩结果,及格判定 `passStatus` 降级为【默认/建议方案(待现场确认)】;学员画像调整为通用弹性指标集。
---
## 1. 核心聚合视图对象 (Core Aggregate VO)
### 1.1 培训计划综合工作台:`PlanWorkbenchVO` 【P0】【确认】
- **定位:** 管理员打开培训班核心工作台时的一次性状态概览与决策入口。
- **字段规范:**
| 字段名 | 类型 | 说明 | 业务来源 |
|:---|:---|:---|:---|
| `planId` | String | 培训计划唯一标识 | `train_class_plan.plan_id` |
| `planName` | String | 培训班名称 | `train_class_plan.plan_name` |
| `planStatus` | Integer | 状态:0未开始 1进行中 2已结束 3已取消 | `train_class_plan.plan_status` |
| `startTime` | Date | 开班时间 | `train_class_plan.start_time` |
| `endTime` | Date | 结业时间 | `train_class_plan.end_time` |
| `reportDeadline` | Date | 报到截止时间 | `train_class_plan.report_deadline` |
| `trainAddress` | String | 培训地点 | `train_class_plan.train_address` |
| `courseSummary` | Object | 本期课程概览 (`courseCount`, `courseNames`) | 聚合 `train_plan_course` |
| `enrollmentSummary` | Object | 参训人数统计 (`recruitNum`, `enrolledCount`, `reportedCount`, `leaveCount`) | 聚合 `train_enrollment` |
| `scheduleSummary` | Object | 排课状态 (`totalLessons`, `scheduledHours`, `hasConflict`) | 聚合 `train_lesson` |
| `accommodationSummary` | Object | 住宿概况 (`requiredCount`, `allocatedCount`, `dayStudentCount`) | 聚合 `train_accommodation` |
| `scoreConfigSummary` | Object | 成绩配置状态 (`isConfigured`, `itemCount`, `totalWeight`) | 聚合 `train_score_item` |
| `nextActionHint` | String | 工作台下一步建议操作指引 | 业务规则动态生成 |
---
### 1.2 计划完整课程表:`PlanTimetableVO` 【P0】【确认】
- **定位:** 全局展示该班从开班到结训的所有课次日历排期。
- **字段规范:**
| 字段名 | 类型 | 说明 |
|:---|:---|:---|
| `planId` | String | 培训计划标识 |
| `planName` | String | 培训班名称 |
| `startDate` | Date | 课表开始日期 |
| `endDate` | Date | 课表结束日期 |
| `days` | List<TimetableDayVO> | 按日期分组的课次排期集合 |
| `days[].date` | String | 日期 (yyyy-MM-dd) |
| `days[].dayOfWeek` | Integer | 星期几 (1~7) |
| `days[].lessons` | List<TimetableLessonItemVO> | 当日课次列表 |
| `lessons[].lessonId` | Long | 课次 ID |
| `lessons[].courseName` | String | 本期课程名称 |
| `lessons[].teacherName` | String | 授课教师姓名 |
| `lessons[].startTime` | Date | 课次开始时间 |
| `lessons[].endTime` | Date | 课次结束时间 |
| `lessons[].venueType` | Integer | 场地类型 (1教室 2实训场地) |
| `lessons[].venueName` | String | 教室名称或实训场地名称 |
| `lessons[].boardSn` | String | 关联电子班牌 SN 号(若有) |
---
### 1.3 学员参训档案全景:`EnrollmentDetailVO` 【P0】【确认】
- **定位:** 查看某学员在某培训班的全生命周期全景档案。
- **字段规范:**
| 字段名 | 类型 | 说明 |
|:---|:---|:---|
| `enrollmentId` | Long | 参训档案标识 |
| `planId` | String | 培训计划标识 |
| `planName` | String | 培训计划名称 |
| `studentId` | String | 学员 ID |
| `studentName` | String | 学员姓名 |
| `gender` | String | 性别 |
| `idCardMasked` | String | 身份证脱敏展示 (`210102********1234`) |
| `orgName` | String | 选派单位 |
| `enrollmentStatus` | Integer | 状态:0待报到 1已报到 2请假 3放弃 4已完成 |
| `accommodationRequired` | Integer | 住宿需求 (0不住宿 1住宿) |
| `accommodation` | Object | 当前住宿分配详情 (`building`, `room`, `bedNo`, `authStatus`) |
| `report` | Object | 报到信息 (`reportTime`, `reportWay`, `verifyNote`) |
---
### 1.4 排课冲突检测结果:`LessonConflictVO` 【P0】【确认】
- **字段规范:**
- `hasConflict`: Boolean (是否存在冲突)
- `conflictType`: String (`NONE`, `TEACHER`, `CLASSROOM`, `SITE`, `PLAN_OVERLAP`)
- `conflictMessage`: String (冲突描述文本)
- `conflictingLessons`: List<ConflictItemVO> (冲突的现有课次明细)
---
### 1.5 全班综合成绩台账矩阵:`PlanScoreMatrixVO` 【P0】【确认/现场调整】
- **定位:** 全班成绩汇总大表。
- **字段规范:**
| 字段名 | 类型 | 确定性 | 说明 |
|:---|:---|:---|:---|
| `planId` | String | 【确认】 | 培训计划标识 |
| `headers` | List<ScoreHeaderVO> | 【确认】 | 表头科目定义:`scoreItemId`, `itemName`, `weight`, `fullScore` |
| `rows[].enrollmentId` | Long | 【确认】 | 参训档案标识 |
| `rows[].studentName` | String | 【确认】 | 学员姓名 |
| `rows[].orgName` | String | 【确认】 | 单位名称 |
| `rows[].scores` | Map<Long, Double> | 【确认】 | 各科目实际得分映射 (`scoreItemId -> score`) |
| `rows[].weightedTotalScore` | Double | 【确认】 | 核心业务结果:加权综合折算总分 |
| `rows[].passStatus` | Integer | 【默认/建议方案(待现场确认)】 | 及格判定:现场若确定及格线则输出(0不及格 1及格),未确定前置为 null |
---
### 1.6 培训计划综合统计概览:`PlanStatisticsVO` 【P0】【确认】
- **定位:** 计划综合成效报表。包含参训/报到/结训人数、综合出勤率、课程出勤对比、各设备自主学习时长分布、平均综合得分(不包含固定及格率)。
---
### 1.7 学员个人全流程训练详情:`StudentTrainingDetailVO` 【P1】【确认】
- **定位:** 学员全流程档案流水(出勤流水、单科成绩流水、自主学习流水)。
---
### 1.8 学员综合画像:`StudentPortraitVO` 【P1】【确认/现场调整】
- **定位:** 简单画像展示 VO。避免过早写死四维雷达图与具体标签规则。
- **字段规范:**
| 字段名 | 类型 | 确定性 | 说明 |
|:---|:---|:---|:---|
| `enrollmentId` | Long | 【确认】 | 参训档案标识 |
| `studentName` | String | 【确认】 | 学员姓名 |
| `orgName` | String | 【确认】 | 单位名称 |
| `metrics` | List<PortraitMetricVO> | 【默认/建议方案(待现场确认)】 | 指标集合(如出勤率、理论分、实操分、时长),支持前端动态渲染为雷达图或柱状图 |
| `tags` | List<String> | 【默认/建议方案(待现场确认)】 | 画像标签集合(如 `["出勤良好", "实操突出"]`),待现场确认具体标签集 |
| `summary` | String | 【默认/建议方案(待现场确认)】 | 综合评语概要 |
| `generatedTime` | Date | 【确认】 | 画像生成时间 |
---
### 1.9 实时房态看板:`DormOccupancyBoardVO` 【P1】【确认】
- 按楼栋 -> 楼层 -> 房间分组的网格视图。
---
## 2. 核心业务命令与请求 DTO (Command & Request DTO)
### 2.1 P0 核心命令与请求 DTO (14 个)
- **`PlanCreateDTO`** 【P0】: `planName`, `startTime`, `endTime`, `reportDeadline`, `recruitNum`, `trainAddress`, `remark`
- **`PlanUpdateDTO`** 【P0】: 计划修改
- **`PlanCourseAddDTO`** 【P0】: `cIds`, `remark`
- **`EnrollmentAddDTO`** 【P0】: `studentId`, `sourceType`, `accommodationRequired`
- **`EnrollmentBatchDTO`** 【P0】: `file`, `autoCreateStudent`
- **`LessonCreateDTO`** 【P0】: `planCourseId`, `tId`, `startTime`, `endTime`, `venueType`, `classRoomId`, `siteId`
- **`LessonUpdateDTO`** 【P0】: 课次修改
- **`LessonConflictCheckDTO`** 【P0】: 排课预检
- **`AccommodationPreallocateDTO`** 【P0】: `preferredBuilding`, `genderSeparated`, `sameClassAdjacent`
- **`AccommodationAdjustDTO`** 【P0】: `targetDormId`, `bedNo`, `reason`
- **`ReportConfirmDTO`** 【P0】: `reportWay`, `reportDeviceSn`, `verifyNote`, `forceOverride` (支持时限策略)
- **`AttendanceSignInDTO`** 【P0】: `enrollmentId`, `lessonId`, `boardId`, `checkinWay`, `signInTime`
- **`AttendanceSignOutDTO`** 【P0】: `enrollmentId`, `lessonId`, `signOutTime`
- **`ScoreItemBatchConfigDTO`** 【P0】: `items` (包含科目名称、类型、权重、满分)
- **`ScoreEntryBatchDTO`** 【P0】: `entries` (`enrollmentId`, `scoreItemId`, `score`)
- **`StudyRecordReceiveDTO`** 【P0】: `enrollmentId`, `sourceSystem`, `sourceEventId`, `startTime`, `endTime`, `studySeconds`
### 2.2 P1 辅助命令与请求 DTO (4 个)
- **`AttendanceAdjustDTO`** 【P1】: 考勤人工修正 (`attendanceStatus`, `manualFlag=1`, `remark`)
- **`ScoreImportDTO`** 【P1】: Excel 导入成绩
- **`PortraitGenerateDTO`** 【P1】: 画像生成请求
- **`TeacherCreateDTO` / `StudentCreateDTO`** 等基础资料录入 DTO 【P0/P1】
+124
View File
@@ -0,0 +1,124 @@
# RailwaySystem Service 聚合边界与模块责任划分
> **架构定位:** 解决传统若依代码生成后业务逻辑严重泄漏在 Controller 层的痛点。
> **分层原则:** Controller 仅负责参数接收与简单校验;所有跨表事务、业务不变量校验、状态机流转与外部系统适配编排全部收敛在 **业务编排层 (Application Service)**。底层保留轻量级的 **基础领域持久化 Service (Domain / Mapper Service)**。
---
## 1. Service 分层架构模型
```mermaid
flowchart TD
subgraph ControllerLayer["Web Controller 层"]
C1[TrainClassPlanController]
C2[TrainEnrollmentController]
C3[TrainLessonController]
C4[TrainAccommodationController]
C5[TrainAttendanceController]
C6[TrainScoreController]
end
subgraph ApplicationServiceLayer["业务编排层 (Application Service / 事务边界)"]
AS1[PlanAppService]
AS2[EnrollmentAppService]
AS3[SchedulingAppService]
AS4[AccommodationAppService]
AS5[ReportAppService]
AS6[AttendanceAppService]
AS7[ScoreAppService]
AS8[LearningAppService]
AS9[StatisticsAppService]
AS10[PortraitAppService]
AS11[ExternalIntegrationAppService]
end
subgraph DomainServiceLayer["基础持久化与数据访问层 (Domain / Mapper Service)"]
DS1[ITrainClassPlanService]
DS2[ITrainEnrollmentService]
DS3[ITrainLessonService]
DS4[ITrainAccommodationService]
DS5[ITrainCheckinService]
DS6[ITrainScoreItemService]
DS7[ITrainStudentScoreService]
DS8[ITrainDormitoryInfoService]
DS9[ITrainTeacherService]
DS10[ITrainStudentService]
end
subgraph ExternalAdapters["外部适配器层 (Mock First / 轻量异步)"]
EA1[FaceAttendanceAdapter]
EA2[DoorAccessAdapter]
EA3[ExternalLearningAdapter]
end
ControllerLayer --> ApplicationServiceLayer
ApplicationServiceLayer --> DomainServiceLayer
ApplicationServiceLayer --> ExternalAdapters
```
---
## 2. 核心业务编排 Service 职责规范
### 2.1 `PlanAppService` (培训计划生命周期编排)
- **核心职责:** 开班计划的创建、编辑、状态流转、工作台聚合数据装配、结训归档。
- **关键方法:**
- `PlanWorkbenchVO getPlanWorkbench(String planId)`: 跨表聚合本期课程、学员入班、排课、住宿、成绩配置概况。
- `String createPlan(PlanCreateDTO dto)`: 校验起止时间与人数,初始化状态为未开始。
- `void updatePlan(PlanUpdateDTO dto)`: 校验计划状态与已排课次时段边界。
- `void completePlan(String planId, String operator)`: 结束培训计划,事务内将计划状态更新为 2 (已结束),批量将该班有效参训记录更新为 4 (已完成)。
### 2.2 `EnrollmentAppService` (参训人员全流程编排)
- **核心职责:** 学员入班准入、查重、Excel批量名单解析、参训状态维护、退班清理。
- **关键方法:**
- `Long addEnrollment(String planId, EnrollmentAddDTO dto)`: 校验防重复参训 (`UNIQUE(plan_id, student_id)`),创建参训档案。
- `BatchImportResultVO batchImportEnrollments(String planId, MultipartFile file, boolean autoCreate)`: 解析 Excel 批量原子入班。
- `void updateEnrollmentStatus(Long enrollmentId, Integer status, String reason)`: 维护请假、放弃状态。
### 2.3 `SchedulingAppService` (排课编排与冲突检测)
- **核心职责:** 课次增删改、四维排课冲突检测引擎、完整课程表结构化装配。
- **关键方法:**
- `LessonConflictVO checkConflict(LessonConflictCheckDTO dto)`: 四维排课冲突预检算法(教师、教室、场地、本班)。
- `Long createLesson(String planId, LessonCreateDTO dto)`: 行锁保护下排课入库。
- `void updateLesson(Long lessonId, LessonUpdateDTO dto)`: 排除自身后冲突校验;已有考勤记录阻断改期。
- `PlanTimetableVO getPlanTimetable(String planId)`: 组装日历日程矩阵。
### 2.4 `AccommodationAppService` (住宿策略与分配编排)
- **核心职责:** 批量住宿自动化预分配算法、单人调房、走读设置、房间入住看板聚合。
- **关键方法:**
- `void preallocateAccommodations(String planId, AccommodationPreallocateDTO dto)`: 按“男女分开、同班同寝、相邻房间优先”批量预分配,行锁保护容量。
- `void adjustAccommodation(Long enrollmentId, AccommodationAdjustDTO dto)`: 单人调房,校验目标房间容量与性别匹配。
- `void setDayStudent(Long enrollmentId, boolean isDayStudent, String remark)`: 走读免宿设置,清退床位。
### 2.5 `ReportAppService` (学员报到业务编排)
- **核心职责:** 报到身份确认、时效生命周期校验、联动信息(住宿、课表、首课)即时输出。
- **关键方法:**
- `ReportResultVO confirmReport(Long enrollmentId, ReportConfirmDTO dto)`: 检查 `report_deadline` 生命周期策略(支持严格阻断、警告提示或管理员强制覆盖三种模式),写入报到记录并更新参训状态为 1 (已报到)。
### 2.6 `AttendanceAppService` (考勤事件与人工修正)
- **核心职责:** 班牌签到/签退事件接入、业务不变量 1 校验、考勤状态判定、人工补签修正。
- **关键方法:**
- `void recordSignIn(AttendanceSignInDTO dto)`: 强校验 `enrollment.plan_id == lesson.plan_course.plan_id`,幂等记录签到。
- `void recordSignOut(AttendanceSignOutDTO dto)`: 记录签退并核算本次出勤是否达标。
- `void adjustAttendance(Long checkinId, AttendanceAdjustDTO dto)`: 人工修正考勤,`manual_flag = 1` 留痕。
### 2.7 `ScoreAppService` (成绩项目权重与折算引擎)
- **核心职责:** 成绩项权重总和 100% 校验、打分录入、满分上限校验、加权综合分实时计算。
- **关键方法:**
- `void configureScoreItems(String planId, ScoreItemBatchConfigDTO dto)`: 强校验权重总和 100.0 及课程归属性。
- `void recordScores(ScoreEntryBatchDTO dto)`: 强校验 `score <= fullScore` 及跨班不变量 2,批量入库。
- `PlanScoreMatrixVO getPlanScoreMatrix(String planId)`: 动态计算每名学员的加权综合成绩 `weightedTotalScore`,及格判定作为可选扩展。
### 2.8 `LearningAppService` (外部学习记录幂等接入)
- **核心职责:** 接收 VR/3D 学习时长、防重去重、时间与时长有效性校验。
- **关键方法:**
- `StudyEventResultVO receiveLearningRecord(StudyRecordReceiveDTO dto)`: 基于 `(source_system, source_event_id)` 幂等入库,返回 accepted / duplicate / invalid。
### 2.9 `StatisticsAppService` (多维实时统计聚合)
- **核心职责:** 计划大屏综合统计、课程出勤横向对比、自主学习时长分布(一期不设统计中间表)。
### 2.10 `PortraitAppService` (学员画像计算)
- **核心职责:** 基于考勤、理论、实操、学习时长生成简单画像指标集与标签集。
### 2.11 `ExternalIntegrationAppService` (外部系统防腐与适配编排)
- **核心职责:** 统一隔离外部网络波动与设备协议差异;数据库事务提交成功后通过 `afterCommit` 触发后续 Adapter 调用(一期默认建议:`afterCommit` → 轻量异步执行器 → Adapter;若未引入异步执行器则为当前线程事务外调用 Adapter,两者皆保证外部调用失败不回滚本地已提交事务,保留待同步状态并支持手工重试),一期不引入 MQ / Outbox / 分布式事务【扩展】。
@@ -0,0 +1,121 @@
# RailwaySystem 事务边界与业务不变量设计
> **规范目的:** 数据库物理约束仅能保证单表或两表引用结构合法,无法表达业务拓扑跨表一致性。
> 本文档定义系统核心业务的 **事务控制边界 (Transaction Boundary)**、**并发锁机制 (Concurrency Lock)**、**5 大跨表硬业务不变量 (Cross-table Invariants)** 以及 **1 项报到生命周期策略 (Lifecycle Policy)**。
---
## 1. 核心事务边界设计
所有核心业务操作必须遵循明确的 ACID 事务边界。
**核心铁律:数据库短事务严禁包裹外部第三方 HTTP/网络长调用!**
```mermaid
sequenceDiagram
autonumber
actor Admin as 管理员/用户
participant AppService as AccommodationAppService
participant DB as 本地数据库 (Kingbase)
participant SyncManager as Spring 事务同步器 (afterCommit)
participant Adapter as DoorAccessAdapter
participant External as 外部门禁系统
Admin->>AppService: 请求调整学员宿舍 (enrollmentId, targetDormId)
rect rgb(240, 248, 255)
note right of AppService: 【本地事务开始 @Transactional】
AppService->>DB: 锁定目标房间行 (SELECT FOR UPDATE)
AppService->>DB: 校验容量(count < capacity)与性别匹配
AppService->>DB: 更新 train_accommodation (dorm_id, bed_no)
AppService->>DB: 插入/更新 train_dorm_auth (face_sync_status=0 待同步)
AppService->>SyncManager: 注册 afterCommit 回调钩子
AppService->>DB: 提交事务 (COMMIT)
note right of AppService: 【本地短事务结束,数据库连接立即释放】
end
rect rgb(255, 250, 240)
note right of SyncManager: 【事务提交后通过 afterCommit 触发】
SyncManager->>Adapter: 触发门禁同步 (authId, faceRef, lockSn)
Adapter->>External: 发送 HTTP 门锁授权请求 (超时阈值 5s)
External-->>Adapter: 返回授权结果
alt 同步成功
Adapter->>DB: 独立短事务更新 face_sync_status=1
else 同步失败/超时
Adapter->>DB: 独立短事务更新 face_sync_status=2, 记录 last_error
end
end
AppService-->>Admin: 返回调房成功响应 (若门禁失败,页面提示可手动重试)
```
### 1.1 事务边界清单 (8 个核心业务事务)
1. **添加参训人员 (`addEnrollment`):** 验证计划与学员状态 -> 查重 (`UNIQUE(plan_id, student_id)`) -> 插入参训档案。
2. **创建课次 (`createLesson`):** 验证课程与师资 -> 锁定教师与场地资源行 (`FOR UPDATE`) -> 冲突检测 -> 插入课次。
3. **批量住宿预分配 (`preallocateAccommodations`):** 锁定候选房间集合 (`FOR UPDATE`) -> 校验容量与性别隔离 -> 批量插入住宿与门禁待同步记录 -> 事务提交后通过 afterCommit 触发门禁同步。
4. **单人住宿调整 (`adjustAccommodation`):** 锁定目标房间 (`FOR UPDATE`) -> 校验性别与容量 -> 更新住宿记录 -> 事务提交后通过 afterCommit 触发门禁同步。
5. **学员报到确认 (`confirmReport`):** 锁定参训记录 -> 检查 `report_deadline` 生命周期策略 -> 插入报到记录 -> 更新参训状态为已报到。
6. **成绩项目与权重保存 (`configureScoreItems`):** 锁定计划成绩项 -> 强校验 `SUM(weight) == 100.0` -> 校验绑定课程归属性 -> 批量保存。
7. **成绩录入与上限校验 (`recordScores`):** 校验跨班不变量 -> 校验 `0 <= score <= full_score` -> 批量更新入库。
8. **结束培训计划 (`completePlan`):** 更新计划状态为 2 (已结束) -> 批量更新有效学员状态为 4 (已完成) -> 记录结业时间。
---
## 2. 必须由 Service 层强制落实的 5 大跨表硬业务不变量
以下 5 项跨表拓扑一致性规则属于系统“硬不变量”,任何情况下违反必须立即阻断并抛出异常,严禁跨班串线:
### 不变量 1:考勤课次与参训计划一致性【强制】
$$\text{attendance.enrollment.plan\_id} == \text{lesson.planCourse.plan\_id}$$
- **说明:** 严禁 A 班学员打卡绑定到 B 班课次上。
- **校验点:** `AttendanceAppService.recordSignIn(...)` 与 `manualAddAttendance(...)`。
### 不变量 2:成绩项与参训计划一致性【强制】
$$\text{studentScore.enrollment.plan\_id} == \text{scoreItem.plan\_id}$$
- **说明:** 严禁将 A 班的成绩考核项打在 B 班学员档案上。
- **校验点:** `ScoreAppService.recordScores(...)`。
### 不变量 3:自主学习记录与计划课程一致性【强制】
$$\text{study.enrollment.plan\_id} == \text{planCourse.plan\_id} \quad (\text{当 } planCourseId \neq null)$$
- **说明:** 外部 VR/3D 接入学习记录时,若指定了本期课程,该课程所属计划必须与学员参训计划完全一致。
- **校验点:** `LearningAppService.receiveLearningRecord(...)`。
### 不变量 4:成绩项目绑定课程与计划一致性【强制】
$$\text{scoreItem.planCourse.plan\_id} == \text{scoreItem.plan\_id} \quad (\text{当 } planCourseId \neq null)$$
- **说明:** 配置成绩项目时,所挂接的课程必须是当前培训计划已引入的本期课程。
- **校验点:** `ScoreAppService.configureScoreItems(...)`。
### 不变量 5:成绩得分非负与满分上限不变量【强制】
$$0 \le \text{studentScore.score} \le \text{scoreItem.full\_score}$$
- **说明:** 录入分数不得为负数,且绝对不可超过该项目配置的满分。
- **校验点:** `ScoreAppService.recordScores(...)`。
---
## 3. 报到时效生命周期策略 (Report Lifecycle Policy)
> **调整说明:** 原 `report.report_time <= plan.report_deadline` 属于业务生命周期控制策略,不属于跨表拓扑结构硬一致性。
### 策略定义:
- **【确认】:** `report_deadline` 配置在 `train_class_plan` 上;学员报到确认时必须读取并核对该截止时间。
- **【默认/建议方案(待现场确认)】:**
若当前时间超过 `report_deadline`,接口默认返回警告(`REPORT_DEADLINE_WARNING`),提示管理员确认;若传入 `forceOverride = true`,则允许完成报到并在流水中标记 `is_overdue = true`。
- **【待现场确认模式兼容】:**
契约与 Service 逻辑已具备完全弹性,可根据现场确认平滑切换为以下三种模式之一而无需改动数据库表:
1. `BLOCK` (严格阻断:逾期严禁自助报到,必须走线下人工特批);
2. `WARNING` (提示警告:弹窗警告提示,学员/管理员确认后仍可入班);
3. `ADMIN_OVERRIDE` (管理员强制覆盖:学员端阻断,管理员端具备特权放行)。
---
## 4. 并发控制与外部适配器轻量实现
### 4.1 排课冲突排他锁机制
排课事务中对教师 `t_id` 和空间 `class_room_id`/`site_id` 加行锁 (`FOR UPDATE`),随后执行时间重叠检测(`existing.start < new.end AND existing.end > new.start`),彻底杜绝两位管理员并发排入同一位老师或同一间教室的穿透风险。
### 4.2 宿舍容量防超卖排他锁机制
分房或调房事务中,对目标房间加行锁 (`SELECT capacity FROM train_dormitory_info WHERE dorm_id = ? FOR UPDATE`),统计 `COUNT(*) < capacity`,严格防止超员入住。
### 4.3 外部适配器调用的轻量设计(禁止重型中间件)
- **一期明确原则:** 严禁引入 MQ、Kafka、RabbitMQ、Outbox Pattern、分布式事务中间件【扩展】;
- **轻量实现方案:** 数据库事务提交成功后,通过 `afterCommit` 触发后续 Adapter 调用。
- 一期默认建议:`afterCommit` → 轻量异步执行器 → Adapter;
- 如果未引入异步执行器:`afterCommit` → 当前线程事务外调用 Adapter;
- 两者都必须保证:外部调用失败不得回滚已经提交的本地数据库事务,而是保留/更新同步状态(`face_sync_status = 2`)并支持手工重试。
+113
View File
@@ -0,0 +1,113 @@
# RailwaySystem 外部系统适配器契约设计 (Integration Contract)
> **集成原则:** 真实设备与第三方系统一期全部采用 **Mock First** 原则。业务 Service 严禁直接硬编码外部 HTTP 接口,统一通过 **防腐适配器接口 (Adapter SPI)** 进行解耦与隔离。
> **外部调用解耦原则:** 数据库事务提交成功后,通过 `afterCommit` 触发后续 Adapter 调用(一期默认建议:`afterCommit` → 轻量异步执行器 → Adapter;若未引入异步执行器则为当前线程事务外调用 Adapter;两者皆保证外部调用失败不回滚已提交的本地数据库事务,而是独立更新同步状态并支持手工重试)。**MQ / Outbox / 分布式事务 = 一期不引入【扩展】。**
---
## 1. 适配器体系与 SPI 接口清单
系统抽象出三大核心集成适配器接口:
```mermaid
classDiagram
class FaceAttendanceAdapter {
<<interface>>
+pushLessonSchedule(PushScheduleCmd cmd) PushResult
+syncFaceFeature(SyncFaceCmd cmd) SyncResult
+mockCheckinCallback(MockCallbackCmd cmd) CallbackResult
}
class DoorAccessAdapter {
<<interface>>
+grantAccess(GrantAccessCmd cmd) AccessResult
+revokeAccess(RevokeAccessCmd cmd) AccessResult
+queryDeviceStatus(String lockSn) DeviceStatusResult
}
class ExternalLearningAdapter {
<<interface>>
+validateEventId(String sourceSystem, String eventId) boolean
+parseStudyPayload(Object rawPayload) StandardStudyEvent
+mockPushStudyDuration(MockStudyCmd cmd) PushResult
}
class MockFaceAttendanceAdapterImpl {
+pushLessonSchedule()
+syncFaceFeature()
}
class MockDoorAccessAdapterImpl {
+grantAccess()
+revokeAccess()
}
class MockExternalLearningAdapterImpl {
+validateEventId()
}
FaceAttendanceAdapter <|.. MockFaceAttendanceAdapterImpl
DoorAccessAdapter <|.. MockDoorAccessAdapterImpl
ExternalLearningAdapter <|.. MockExternalLearningAdapterImpl
```
---
## 2. 电子班牌与人脸考勤适配器:`FaceAttendanceAdapter`
### 2.1 业务场景
1. **课表向班牌推送:** 当教室排定课次后,系统将排课时段、授课教师及学员人脸凭证推送给对应教室的电子班牌;
2. **班牌签到打卡回传:** 学员在班牌刷脸后,班牌通过 HTTP 回调 RailwaySystem `/train/attendance/sign-in`。
### 2.2 接口规范与 Mock 行为
- **下发排课与人脸:** `PushResult pushLessonSchedule(PushScheduleCmd cmd)`
- **一期 Mock 实现【确认】:**
一期 Mock 实现直接打印日志并返回 `success: true`,模拟班牌下发成功;支持单元测试中模拟设备超时或离线。
---
## 3. 智能门禁与门锁适配器:`DoorAccessAdapter`
### 3.1 业务场景
学员完成宿舍分配后,系统下发人脸开锁授权;调房或退宿时,注销门锁权限。
### 3.2 接口规范
- **授权下发:** `AccessResult grantAccess(GrantAccessCmd cmd)`
- **权限撤销:** `AccessResult revokeAccess(RevokeAccessCmd cmd)`
- **轻量解耦与容灾重试设计【默认/建议方案】:**
1. 数据库事务提交成功后,通过 `afterCommit` 触发后续 Adapter 调用;
2. 一期默认建议:`afterCommit` → 轻量异步执行器 → Adapter;如果未引入异步执行器:`afterCommit` → 当前线程事务外调用 Adapter;
3. 两者都必须保证:外部调用失败不得回滚已经提交的本地数据库事务,而是保留/更新同步状态(`face_sync_status = 2`)并支持手工重试;
4. 明确:MQ / Outbox / 分布式事务 = 一期不引入【扩展】;
5. 单次 HTTP 请求超时阈值设为 5 秒,若调用失败或超时,在独立短事务中更新 `train_dorm_auth.face_sync_status = 2` (失败),记录 `last_error`,管理员可在页面一键手工重试。
---
## 4. VR / 3D / 电子阅览室学习记录接入:`ExternalLearningAdapter`
### 4.1 业务场景与幂等保证
外部专业实训系统在学员自主学习结束后,异步向 RailwaySystem 推送打卡流水。
### 4.2 接口响应三态设计 (HTTP 200)
- **正常首次接收 (`status = "accepted"`):**
```json
{
"code": 200,
"msg": "学习记录接入成功",
"data": { "status": "accepted", "durId": 5001, "message": "已记录学习时长 4500 秒" }
}
```
- **重复事件推送 (`status = "duplicate"`):**
```json
{
"code": 200,
"msg": "事件已存在,忽略重复推送",
"data": { "status": "duplicate", "durId": 5001, "message": "系统已存在该事件记录,无需重复入库" }
}
```
- **参数或拓扑非法 (`status = "invalid"`):**
```json
{
"code": 400,
"msg": "学习记录参数校验失败",
"data": { "status": "invalid", "durId": null, "message": "校验失败:startTime 不能晚于 endTime" }
}
```
@@ -0,0 +1,140 @@
# RailwaySystem 权限字符与业务错误码规范
> **设计依据:** 全面沿用若依成熟的 RBAC 权限控制体系与全局异常拦截机制。
> **实施原则:** 权限字符标准化归一为 `train:*` 前缀;错误码统一收敛并与若依 `ServiceException` 及 `AjaxResult` 深度融合,拒绝重复造轮子。
> **Review Round 1 修订:** 严禁引入任何硬编码的“不及格/不合格结业”阻断错误码;报到逾期错误码调整为生命周期时限告警。
---
## 1. 权限编码规范 (RuoYi RBAC)
系统一期主要服务于三类角色:**培训管理员 (admin)**、**任课教师 (teacher)**、**参训学员 (student)**。
所有 API 严格通过 `@PreAuthorize("@ss.hasPermi('train:xxx:xxx')")` 进行鉴权拦截。
### 1.1 Module A:人员与基础资源权限 (23 个权限字符)
| 权限字符 | 权限名称 | 对应主要 API | 适用角色 |
|:---|:---|:---|:---|
| `train:teacher:list` | 教师查询/导出 | `GET /train/teacher/list` | 管理员, 教师 |
| `train:teacher:query` | 教师详情 | `GET /train/teacher/{tId}` | 管理员, 教师 |
| `train:teacher:add` | 教师新增 | `POST /train/teacher` | 管理员 |
| `train:teacher:edit` | 教师修改/启停用 | `PUT /train/teacher/{tId}` | 管理员 |
| `train:student:list` | 学员库查询 | `GET /train/student/list` | 管理员 |
| `train:student:query` | 学员详情 | `GET /train/student/{sId}` | 管理员 |
| `train:student:add` | 学员单条录入 | `POST /train/student` | 管理员 |
| `train:student:edit` | 学员档案修改 | `PUT /train/student/{sId}` | 管理员 |
| `train:student:import` | 学员Excel导入 | `POST /train/student/import` | 管理员 |
| `train:course:list` | 课程库列表 | `GET /train/course/list` | 管理员, 教师 |
| `train:course:query` | 课程详情 | `GET /train/course/{cId}` | 管理员, 教师 |
| `train:course:add` | 课程新增 | `POST /train/course` | 管理员 |
| `train:course:edit` | 课程修改/绑资源 | `PUT /train/course/{cId}` | 管理员 |
| `train:resource:list` | 教学资源列表 | `GET /train/resource/list` | 管理员, 教师 |
| `train:resource:edit` | 教学资源增删改 | `POST/PUT/DELETE /train/resource` | 管理员 |
| `train:classroom:list` | 教室列表 | `GET /train/classroom/list` | 管理员, 教师 |
| `train:classroom:edit` | 教室维护 | `POST/PUT /train/classroom` | 管理员 |
| `train:site:list` | 实训场地列表 | `GET /train/training-site/list` | 管理员, 教师 |
| `train:site:edit` | 实训场地维护 | `POST/PUT /train/training-site` | 管理员 |
| `train:board:list` | 电子班牌查询 | `GET /train/electronic-board/list` | 管理员 |
| `train:board:edit` | 电子班牌注册维护 | `POST/PUT /train/electronic-board` | 管理员 |
| `train:dormitory:list` | 宿舍房间/房态查询 | `GET /train/dormitory/list` | 管理员, 宿管 |
| `train:dormitory:edit` | 宿舍房间维护 | `POST/PUT /train/dormitory` | 管理员, 宿管 |
---
### 1.2 Module B:开班计划与排课权限 (17 个权限字符)
| 权限字符 | 权限名称 | 对应主要 API | 适用角色 |
|:---|:---|:---|:---|
| `train:plan:list` | 培训计划列表 | `GET /train/plans` | 管理员, 教师 |
| `train:plan:query` | 计划详情/工作台/课表 | `GET /train/plans/{planId}/*` | 管理员, 教师 |
| `train:plan:add` | 创建培训计划 | `POST /train/plans` | 管理员 |
| `train:plan:edit` | 编辑培训计划/配课 | `PUT /train/plans/{planId}`, `POST /courses` | 管理员 |
| `train:plan:complete` | 结束培训计划 | `POST /train/plans/{planId}/complete` | 管理员 |
| `train:enrollment:list` | 参训名单查询 | `GET /train/plans/{planId}/enrollments` | 管理员, 教师 |
| `train:enrollment:query` | 参训档案详情 | `GET /train/enrollments/{enrollmentId}` | 管理员 |
| `train:enrollment:add` | 添加参训人员 | `POST /train/plans/{planId}/enrollments` | 管理员 |
| `train:enrollment:import` | Excel批量导入参训人员 | `POST /train/plans/{planId}/enrollments/batch`| 管理员 |
| `train:enrollment:edit` | 修改参训状态 | `PUT /train/enrollments/{enrollmentId}/status` | 管理员 |
| `train:enrollment:remove`| 移除参训人员 | `DELETE /train/enrollments/{enrollmentId}` | 管理员 |
| `train:lesson:query` | 课次详情/冲突预检 | `POST /train/lessons/check-conflict` | 管理员, 教师 |
| `train:lesson:add` | 新增课次排课 | `POST /train/plans/{planId}/lessons` | 管理员 |
| `train:lesson:edit` | 修改课次排课 | `PUT /train/lessons/{lessonId}` | 管理员 |
| `train:lesson:remove` | 删除课次 | `DELETE /train/lessons/{lessonId}` | 管理员 |
| `train:report:confirm` | 学员报到确认 | `POST /train/enrollments/{enrollmentId}/report`| 管理员, 学员自助 |
| `train:report:list` | 报到记录列表 | `GET /train/plans/{planId}/reports` | 管理员 |
---
### 1.3 Module C:住宿、门禁与考勤权限 (8 个权限字符)
| 权限字符 | 权限名称 | 对应主要 API | 适用角色 |
|:---|:---|:---|:---|
| `train:accommodation:list` | 住宿安排查看 | `GET /train/plans/{planId}/accommodations` | 管理员, 宿管 |
| `train:accommodation:assign` | 批量预分配宿舍 | `POST /train/plans/{planId}/accommodations/preallocate` | 管理员, 宿管 |
| `train:accommodation:adjust` | 单人调房/走读设置 | `PUT /train/enrollments/{enrollmentId}/accommodation*` | 管理员, 宿管 |
| `train:dormauth:list` | 门禁状态查询 | `GET /train/plans/{planId}/dorm-auths` | 管理员, 宿管 |
| `train:dormauth:sync` | 门禁授权同步/重试 | `POST /train/accommodations/{accommodationId}/auth/sync` | 管理员 |
| `train:attendance:list` | 考勤查询 | `GET /train/lessons/{lessonId}/attendance` | 管理员, 教师 |
| `train:attendance:checkin` | 课次打卡签到/签退 | `POST /train/attendance/sign-in`, `sign-out` | 班牌回调, 教师 |
| `train:attendance:edit` | 人工修正/补录考勤 | `PUT /train/attendance/{checkinId}`, `POST /manual-add` | 管理员, 教师 |
---
### 1.4 Module D:成绩、学习、统计与画像权限 (8 个权限字符)
| 权限字符 | 权限名称 | 对应主要 API | 适用角色 |
|:---|:---|:---|:---|
| `train:score:query` | 成绩查询/台账矩阵 | `GET /train/plans/{planId}/scores/matrix` | 管理员, 教师 |
| `train:score:config` | 配置成绩项目与权重 | `POST /train/plans/{planId}/score-items` | 管理员 |
| `train:score:edit` | 录入单条/批量成绩 | `POST /train/scores/entry` | 管理员, 教师 |
| `train:score:import` | Excel批量导入成绩 | `POST /train/plans/{planId}/scores/import` | 管理员, 教师 |
| `train:learning:list` | 学习记录列表查询 | `GET /train/plans/{planId}/study-records` | 管理员, 教师 |
| `train:statistics:query` | 统计概览/下钻分析 | `GET /train/plans/{planId}/statistics/*` | 管理员, 领导 |
| `train:portrait:query` | 学员画像查询 | `GET /train/enrollments/{enrollmentId}/portrait` | 管理员, 教师, 学员 |
| `train:portrait:generate`| 刷新/生成学员画像 | `POST /train/enrollments/{enrollmentId}/portrait/generate` | 管理员 |
---
## 2. 统一业务错误码模型字典 (40000 ~ 40999)
系统全面兼容若依的 `ServiceException` 体系。后端业务校验不通过时,统一抛出带有特定错误码的 `ServiceException`,由若依框架现有的 `GlobalExceptionHandler` 统一捕获,转换输出为标准的 `AjaxResult.error(code, msg)`。
| 错误码 | 常量定义 | 默认错误提示信息 | 对应模块/用例 |
|:---|:---|:---|:---|
| **40001** | `TEACHER_PHONE_DUPLICATED` | 教师手机号码已存在 | 教师管理 |
| **40002** | `STUDENT_IDCARD_DUPLICATED` | 学员身份证号已建档,不可重复录入 | 学员管理 |
| **40003** | `COURSE_CODE_DUPLICATED` | 课程编码已存在 | 课程库 |
| **40004** | `RESOURCE_IN_USE` | 该教学资源已被课程引用,无法删除 | 教学资源 |
| **40005** | `BOARD_SN_DUPLICATED` | 电子班牌 SN 设备编号已存在 | 电子班牌 |
| **40006** | `DORM_ROOM_DUPLICATED` | 同一楼栋下该宿舍房号已存在 | 宿舍房间 |
| **40007** | `DORM_CAPACITY_CANNOT_SHRINK` | 房间容量不可调整至小于当前已入住人数 | 宿舍房间 |
| **40010** | `PLAN_TIME_INVALID` | 培训计划起止时间不合法 (结束时间必须晚于开始时间) | 开班计划 (UC-01) |
| **40011** | `PLAN_NAME_DUPLICATED` | 同名培训计划已存在 | 开班计划 (UC-01) |
| **40012** | `PLAN_STATUS_LOCKED` | 当前培训计划已结训或取消,不允许修改 | 开班计划 (UC-02) |
| **40013** | `PLAN_LESSON_OUT_OF_BOUNDS` | 调整培训时间导致现有已排课次超出计划周期 | 开班计划 (UC-02) |
| **40014** | `PLAN_ALREADY_COMPLETED` | 培训计划已经结训,不可重复执行结训 | 开班计划 (UC-29) |
| **40020** | `PLAN_COURSE_DUPLICATED` | 该课程已添加到本期培训计划中 | 课程配置 (UC-03) |
| **40021** | `PLAN_COURSE_HAS_LESSONS` | 该课程已排定课次,请先删除关联课次后再移除课程 | 课程配置 |
| **40022** | `PLAN_COURSE_HAS_SCORE_ITEMS` | 该课程已关联成绩考核项目,不可直接移除 | 课程配置 |
| **40030** | `ENROLLMENT_DUPLICATED` | 该学员已在本班参训名单中,不可重复添加 | 参训管理 (UC-04) |
| **40031** | `RECRUIT_LIMIT_EXCEEDED` | 参训人数已超出本期计划招生上限 | 参训管理 (UC-04) |
| **40032** | `ENROLLMENT_HAS_BUSINESS_DATA` | 该学员已产生考勤或成绩数据,不可删除参训档案 | 参训管理 |
| **40040** | `LESSON_TEACHER_CONFLICT` | 教师排课时间冲突:该教师在指定时段已有其他教学任务 | 排课冲突 (UC-07/10) |
| **40041** | `LESSON_CLASSROOM_CONFLICT` | 教室排课时间冲突:该教室在指定时段已被占用 | 排课冲突 (UC-07/10) |
| **40042** | `LESSON_SITE_CONFLICT` | 实训场地排课时间冲突:该实训场地在指定时段已被占用 | 排课冲突 (UC-07/10) |
| **40043** | `LESSON_PLAN_OVERLAP` | 本班课次时间重叠:同一培训班不可在同一时段上多门课 | 排课冲突 (UC-07/10) |
| **40044** | `LESSON_ALREADY_HAS_ATTENDANCE`| 该课次已有学员签到打卡,禁止修改上课时间 | 课次维护 (UC-08) |
| **40050** | `REPORT_ALREADY_CONFIRMED` | 该学员已完成报到确认,无需重复报到 | 学员报到 (UC-15) |
| **40051** | `REPORT_DEADLINE_WARNING` | 已超过本期培训报到截止时间,触发时限提醒 | 学员报到 (UC-15) |
| **40060** | `DORM_CAPACITY_EXCEEDED` | 候选宿舍总可用床位不足,无法满足当前分配需求 | 住宿分配 (UC-12) |
| **40061** | `DORM_GENDER_CONFLICT` | 宿舍性别属性与学员性别不符,严禁混住 | 住宿分配 (UC-12/13) |
| **40062** | `DORM_ROOM_FULL` | 目标宿舍房间床位已满,无法调入 | 住宿调整 (UC-13) |
| **40070** | `STUDENT_FACE_DATA_MISSING` | 学员未采集人脸照片,无法下发门禁授权 | 门禁同步 (UC-16) |
| **40071** | `DOOR_DEVICE_OFFLINE` | 目标门锁设备离线,门禁授权下发失败 | 门禁同步 (UC-16) |
| **40080** | `ATTENDANCE_PLAN_MISMATCH` | 业务不变量校验失败:考勤学员与课次所属培训班不一致 | 考勤管理 (UC-17) |
| **40090** | `SCORE_WEIGHT_SUM_INVALID` | 成绩项目配置不合法:各项有效权重之和必须等于100% | 成绩配置 (UC-20) |
| **40091** | `SCORE_ITEM_COURSE_MISMATCH` | 成绩项目关联的课程不属于当前培训计划 | 成绩配置 (UC-20) |
| **40092** | `SCORE_EXCEEDS_FULL_SCORE` | 录入分数超出该项目设定的满分上限 | 成绩录入 (UC-21) |
| **40093** | `SCORE_PLAN_MISMATCH` | 业务不变量校验失败:成绩录入学员工号与科目所属班次不符 | 成绩录入 (UC-21) |
| **40100** | `STUDY_EVENT_INVALID` | 外部学习记录数据不合法 (结束时间早于开始时间或时长为负) | 学习接入 (UC-24) |
| **40110** | `PORTRAIT_NOT_FOUND` | 该学员画像尚未生成,请先执行画像刷新 | 学员画像 (UC-28) |
+33
View File
@@ -0,0 +1,33 @@
# RailwaySystem 现场验证问题映射与 API 应对策略 (Open Questions)
> **基准依据:** 严格对齐 PRD V1.0 第 28 章冻结的 **V-01 ~ V-14 现场验证清单**。
> **核心原则:** 不私自发明不相关的额外问题;对每个问题明确当前默认策略、影响 API、影响 DTO/VO 以及现场确认后的数据库评估。
> **当前评估:** 大多数 V-01~V-14 的不同答案可由 API / Service / DTO / Adapter 策略吸收,预计无需修改 Database V2。但若现场确认引入新的持久化业务对象、一期范围变化、新的硬件持久化要求或审计要求,必须重新进入 Database Change Review。
---
## 1. V-01 ~ V-14 映射与全方位应对矩阵
| 编号 | 现场待确认事项 | 当前默认策略 | 影响 API | 影响 DTO/VO | 现场确认后DB影响 | 应对与隔离策略 |
|:---|:---|:---|:---|:---|:---:|:---|
| **V-01** | 学员最终真实来源系统及数据字段 | 默认支持工号、姓名、性别、身份证、单位、电话、邮箱 7 项核心字段 | `POST /train/student`, `POST /train/plans/{id}/enrollments` | `StudentCreateDTO`, `EnrollmentAddDTO` | **预计无** | 现有 `train_student` 预留 `source_type` 与 `remark` 字段,常规业务扩展字段可通过 `remark` 承载。若需引入复杂组织机构多层级模型,则需重新评估。 |
| **V-02** | 一期实际如何导入内部人员 | 默认提供通用 Excel 批量导入模板,并支持自动建档开关 | `POST /train/plans/{id}/enrollments/batch` | `EnrollmentBatchDTO` | **预计无** | 现场若提供直连只读视图或内部平台接口,仅需在 `EnrollmentAppService` 中切换数据提取实现类。 |
| **V-03** | 宿舍是否分到具体床位还是只到房间 | 默认“**房间必填,床号可选填**”,按 1~N 顺序编排可选床号 | `POST /train/plans/{id}/accommodations/preallocate` | `AccommodationPreallocateDTO`, `AccommodationAdjustDTO` | **预计无** | `train_accommodation.bed_no` 字段已允许为 NULL;若只要房间,传空即可;若要床位,直接传值。 |
| **V-04** | 请假如何影响考勤统计 | 默认请假为独立考勤状态 (`status=5`),计算出勤率时分母扣减请假课次 | `GET /train/plans/{id}/statistics/overview` | `PlanStatisticsVO`, `AttendanceSummaryVO` | **预计无** | 纯内存/SQL统计算法调整,`StatisticsAppService` 封装公式,对外数据结构完全一致。 |
| **V-05** | 迟到/早退的具体判定规则与宽限期 | 默认上课前30分至开课后10分为正常,超过10分判定迟到,教师可人工修正 | `POST /train/attendance/sign-in`, `PUT /attendance/{id}` | `AttendanceSignInDTO`, `AttendanceAdjustDTO` | **预计无** | 判定阈值通过若依系统参数 (`sys_config`) 动态配置,API 与数据库表结构不受影响。 |
| **V-06** | 正式成绩考核项目有哪些 | 采用开班级动态配置模型,由培训班自由定义项目名称、类型及权重,总权重等于100% | `POST /train/plans/{id}/score-items` | `ScoreItemBatchConfigDTO`, `PlanScoreMatrixVO` | **预计无** | `train_score_item` 支持任意门数与类型的考核项,彻底杜绝固定科目列硬编码。 |
| **V-07** | 是否存在及格线 | 默认只输出加权折算综合成绩 `weightedTotalScore`,及格判定 `passStatus` 作为可选入参动态比较 | `GET /train/plans/{id}/scores/matrix` | `PlanScoreMatrixVO` | **预计无** | 数据库不存及格状态;若现场需要固定及格线,仅在 VO 计算时与阈值(如60分)对比输出。 |
| **V-08** | 培训结束与“合格/结业”是否区分 | 默认简化为:培训时间结束即结训,有效参训学员全部更新为“已完成”,保留成绩 | `POST /train/plans/{id}/complete` | `PlanWorkbenchVO` | **可能有** | 当前状态在 `train_enrollment.enrollment_status = 4` 表达。若现场提出多级结业审批或不合格复议归档,可能需要新增审批流相关状态字段。 |
| **V-09** | 是否需要证书管理 | 明确一期不开发证书模块,API 契约不设证书接口 | 无 | 无 | **需重新评估** | 一期删除此功能;二期若现场确认需要证书持久化(如电子证书编号、发证日期),必须重新评估并引入 `train_certificate` 表。 |
| **V-10** | 学员画像最终指标与标签集 | 默认采用通用指标集 (`metrics`) + 标签集 (`tags`) 弹性结构,预置出勤、理论、实操、时长4项指标 | `GET /train/enrollments/{id}/portrait` | `StudentPortraitVO` | **预计无** | `train_student_portrait` 的文本字段与数值字段具备承载通用指标与标签能力。 |
| **V-11** | 电子班牌真实硬件协议 | 采用标准 RESTful/JSON 契约,一期使用 Mock 适配器驱动 | 适配器内部通讯 | `FaceAttendanceAdapter` | **预计无** | 硬件差异隔离在 Adapter 防腐层内,现场确定品牌型号后仅需编写厂商驱动类。 |
| **V-12** | 智能门锁真实硬件协议 | 采用基于门锁 SN 与人脸凭证的标准指令,一期使用 Mock 适配器驱动 | 适配器内部通讯 | `DoorAccessAdapter` | **预计无** | 门锁 SDK 集成仅影响 `DoorAccessAdapter` 实现类,业务主表只记录下发状态与错误日志。 |
| **V-13** | VR/3D/阅览系统真实推送协议 | 采用标准化极简 HTTP POST 接口接入:`source_system`, `source_event_id`, `study_seconds` | `POST /train/study-records/receive` | `StudyRecordReceiveDTO` | **预计无** | 契约已覆盖外部学习打卡核心要素,第三方系统无论如何定制均可映射到该通用 DTO。 |
| **V-14** | 对其他系统需要开放哪些数据查询接口 | 提供只读查询接口(学员课表、成绩单、训练详情),通过若依 Token 鉴权与白名单过滤 | 现有 Resource Query API | `PlanTimetableVO`, `StudentScoreReportVO` | **需重新评估** | 常规只读查询直接复用现有接口;若现场提出第三方系统专用的推送回调队列或独立数据交换审计日志,需重新评估。 |
---
## 2. 结论与架构策略
1. **当前评估与变更控制:** 当前评估表明大多数 V-01~V-14 的不同答案可由 API / Service / DTO / Adapter 策略吸收,预计无需修改 Database V2。但若现场确认引入新的持久化业务对象、一期范围变化、新的硬件持久化要求或审计要求,必须重新进入 Database Change Review;
2. **契约弹性保障:** 成绩台账矩阵解耦了及格判定,学员画像解耦了雷达图与具体标签,报到逾期解耦了阻断与放行策略,架构具备良好的适配与应对能力。
+152
View File
@@ -0,0 +1,152 @@
# RailwaySystem Phase 2 Contract Review Round 1 审查报告
> **审查日期:** 2026-09-14
> **审查基线:** PRD V1.0 + Database V2 (21张表) + generator-v2-ready-20260914
> **审查对象:** `docs/api-v2/` 目录全套 API 契约、DTO/VO、Service 边界与架构设计规范
> **最终评审结论:** **Contract Review Round 1 Final = A (完全通过,达到架构冻结准入标准)**
---
## 1. 本轮审查发现的核心问题与偏离项
在初版 API 契约完成设计后,经过技术架构与业务基线严审,识别出以下 5 处需要规范化收敛的问题:
1. **“及格/合格判定”存在需求越界:** 初版将 60 分及格线和 `passStatus` 作为【确认】规则固化在成绩台账及统计中,偏离了 PRD V-07/V-08 明确标注的“是否存在及格线待现场确认”基线;
2. **学员画像模型过早冻结:** 初版将四维雷达图、固定指标名称与规则标签直接写死,与 PRD V-10“一期存在简单画像,具体指标与标签待现场确认”存在超前假设;
3. **Report Deadline 误定为硬物理 Invariant:** 初版将 `report_time <= report_deadline` 误列为与跨班串签同等级别的硬一致性拓扑不变量,忽略了业务上超期补录或管理员特批放行的生命周期弹性需求;
4. **外部异步调用存在过度设计隐患与语义模糊:** 文字表述中将 `afterCommit` 误表达为本身就是异步,且提到了 Outbox 等重型方案,容易误导后续开发引入不必要的 MQ/事件总线中间件;
5. **91 个 API 缺乏实施优先级分级与 Command 统计口径模糊:** 未明确区分核心验收闭环与辅助运维接口,Command API 与普通更新混淆。
---
## 2. 本轮修订与收敛内容
针对上述问题,本轮评审进行了系统性修订并同步更新了 `docs/api-v2/` 下的全部相关文档:
| 修订项 | 涉及文档 | 调整前 | 调整后 |
|:---|:---|:---|:---|
| **及格判定解耦** | `01`, `05`, `06`, `10`, `11` | `compositeScore` + 硬编码 `passStatus` 判定 | `weightedTotalScore` 作为核心【确认】业务结果;`passStatus` 降级为【默认/建议方案(待现场确认)】;移除固定合格率 |
| **画像模型弹性化** | `05`, `06`, `11` | 固定四维雷达图 + 固定标签枚举 | 【确认】简单画像数据;采用弹性指标集 `metrics` + 标签集 `tags`;具体计算维度与标签标记为待现场确认 |
| **时效规则归位** | `03`, `08`, `10`, `11` | 6 项硬业务不变量 (含 Report Deadline) | **5 项跨表硬业务不变量** + **1 项报到生命周期策略 (Report Lifecycle Policy)**;支持 BLOCK / WARNING / OVERRIDE 三种模式 |
| **外部调用解耦规范** | `04`, `07`, `08`, `09` | 误表达 afterCommit 本身为异步,提及 Outbox/MQ | **严禁引入 MQ / Kafka / Outbox【扩展】**;明确为事务提交后通过 afterCommit 触发后续调用,一期建议经轻量异步执行器或事务外调用,外部失败绝不回滚已提交事务 |
| **全面优先级划分** | `00`, `01`, `02`, `03`, `04`, `05`, `06` | 91 个 API 未分级 | 全面划分:**P0 (49个)**、**P1 (40个)**、**P2 (2个)**;新增 **Phase 1 Minimal Contract** 视图 |
| **业务Command口径统一** | `00`, `01`, `06` | 19个与20个数字模糊混淆 | 明确 **P0 核心 Command API 共 20 个**(包含 19 个核心业务流程 Command + 1 个 P0 计划管理更新 `PUT /train/plans/{planId}`),P1 辅助 Command 4 个 |
| **消除数据库绝对承诺** | `11`, `12` | 100%无需修改数据库 | 修正为客观评估:当前评估预计无需改 DB,采用“预计无/可能有/需重新评估”,若引入新持久化业务对象重新进入 DB Change Review |
---
## 3. PRD 越界项的裁定与处理
1. **及格线与合格结业 (PRD V-07, V-08):**
- 裁定:一期不做及格门槛硬拦截,不设“考核不合格退学/无法结业”状态机。培训班到达结束时间,所有正常参训学员状态一律置为“已完成”,完整保留其各科成绩与加权总分供后续查阅。
2. **学员画像规则引擎 (PRD V-10):**
- 裁定:严禁在一期引入 Drools 或复杂规则引擎;采用极简统计数值与轻量 JSON 存储,前端动态适配渲染。
3. **复杂课程版本/审批/证书:**
- 裁定:彻底剔除,契约中不设立任何审批流和证书接口。
---
## 4. API 优先级分级 (P0 / P1 / P2) 统计裁定
- **P0 API (共 49 个):** 一期核心闭环必须实现,包含:
- Module A 基础主数据核心 CRUD (16个);
- Module B 开班、本期课程、参训名单导入、排课、冲突预检、完整课表、学员报到 (17个);
- Module C 批量宿舍预分配、调房、走读、住宿查看、门禁重试、签到、签退、考勤修正、课次考勤 (9个);
- Module D 成绩配置、打分录入、成绩台账大表、学员成绩单、外部学习记录接入、综合统计概览 (7个)。
- **P1 API (共 40 个):** 一期辅助管理能力(如主档启停用、教学资源独立管理、班牌注册管理、房态看板、门禁流水、人工补录考勤、成绩Excel导入、学习记录流水、学员训练详情、学员画像等)。
- **P2 API (共 2 个):** 依赖现场确认或面向学员端的次要门户接口(`GET /train/student/my-training` 学员端门户、`GET /train/plans/{id}/statistics/score-distribution` 成绩区间下钻分析)。
---
## 5. 核心业务用例 (UC-01 ~ UC-29) 的 P0 主链映射
经核对 PRD V1.0 业务验收场景,一期主流程由以下 17 个 P0 用例构成闭环:
```text
UC-01 创建培训计划 (P0)
UC-02 编辑培训计划 (P0)
UC-03 配置本期课程 (P0)
UC-04 添加参训人员 (P0)
UC-05 Excel批量导入参训人员 (P0)
UC-06 查看本期参训名单 (P0)
UC-07 新增课次 (P0)
UC-08 修改课次 (P0)
UC-10 排课冲突检查 (P0)
UC-11 获取计划完整课表 (P0)
UC-12 批量住宿预分配 (P0)
UC-13 单人住宿调整 (P0)
UC-14 查看住宿安排 (P0)
UC-15 学员报到确认 (P0)
UC-16 创建/同步门禁授权 (P0)
UC-17 课次签到 (P0)
UC-18 课次签退 (P0)
UC-19 人工修正考勤 (P0)
UC-20 配置成绩项目及权重 (P0)
UC-21 录入成绩 (P0)
UC-23 获取学员本期成绩 (P0)
UC-24 接收外部学习记录 (P0)
UC-26 计划统计概览 (P0)
UC-29 结束培训计划 (P0)
```
*(注:UC-09 删除课次、UC-22 Excel导入成绩、UC-25 查询学习记录、UC-27 学员训练详情、UC-28 学员画像划入 P1 辅助能力)*
---
## 6. Report Deadline 时效生命周期策略
- **确认事实:** `report_deadline` 作为时间戳字段保存在 `train_class_plan` 表中;
- **执行规则:** 学员报到时系统检查当前时间;
- **兼容策略:**
- 默认行为:若超时且 `forceOverride = false`,接口返回警告提醒 `REPORT_DEADLINE_WARNING`;若由管理员代办或 `forceOverride = true`,允许完成报到并在报到流水中标记 `is_overdue = true`;
- 完美兼容现场最终确认的严格阻断、提示警告或特批覆盖,预计无需修改数据库字段。
---
## 7. 学员画像 (Student Portrait) 策略
- **确认事实:** 数据持久化保存在 `train_student_portrait` 表;
- **弹性契约:** API 返回结构抽象为 `metrics` (指标数值数组) + `tags` (能力标签字符串数组) + `summary` (简短文字摘要);
- **实现策略:** 后端一期默认基于考勤率、理论成绩、实操成绩与自主学习时长进行四维归一化打分;现场若对指标名称或标签有微调,仅需在 `PortraitAppService` 中微调权重与打标文案,前端自适应渲染,彻底杜绝数据模型硬编码。
---
## 8. 及格判定 (Pass / Qualified) 策略
- **确认事实:** 成绩核心计算依据为 `train_score_item` 中的 `weight` 与 `full_score`;
- **业务输出:** 各科成绩根据权重汇总计算出的加权总分 `weightedTotalScore` 是唯一【确认】输出结果;
- **及格解耦:** API 默认不对学员进行“及格/不及格”判定,全班台账中的 `passStatus` 设为可选参数;若现场需要及格线,通过入参传递阈值(如60)动态判定,不污染数据库与主干业务流。
---
## 9. 外部调用最小实现策略与技术语义修正
- **铁律:** 本地短事务严禁包裹外部第三方 HTTP 长请求;
- **技术语义规范:**
- `afterCommit` 本身不代表异步,其本质是数据库事务提交成功后的回调钩子;
- 一期默认建议:`afterCommit` → 轻量异步执行器 → Adapter;若未引入异步执行器,则为 `afterCommit` → 当前线程事务外调用 Adapter;
- 两者都必须保证:外部调用失败不得回滚已经提交的本地数据库事务,而是保留/更新同步状态并支持手工重试;
- **明确:MQ / Outbox / 分布式事务 = 一期不引入【扩展】。**
---
## 10. V-01 ~ V-14 数据库影响客观评估原则
- **删除绝对承诺:** 严禁使用“100%无需修改数据库”等绝对化承诺;
- **规范表述口径:**
> 当前评估:大多数 V-01~V-14 的不同答案可由 API / Service / DTO / Adapter 策略吸收,预计无需修改 Database V2。但若现场确认引入新的持久化业务对象、一期范围变化、新的硬件持久化要求或审计要求,必须重新进入 Database Change Review。
- 各 V 项评估采用三态分类:`预计无` (如V-01~V-07, V-10~V-13)、`可能有` (如V-08结业审批状态)、`需重新评估` (如V-09证书持久化、V-14外部数据交换审计)。
---
## 11. 最终收口修订记录与评审裁定
经过本轮全面复查与最终收口修订:
1. `afterCommit` 技术语义已全面纠正,杜绝“afterCommit本身是异步”的误读,明确外部失败不回滚已提交本地事务;
2. 彻底删除了“100%不改DB”绝对化承诺,建立了客观科学的三态评估与变更控制原则;
3. 统计口径完全统一并消除歧义:
- API 总数:**91 个** (P0: 49, P1: 40, P2: 2);
- 核心业务 Command API:共 **24 个** (P0 核心 Command API 20 个 [包含 19 个核心业务流程 Command + 1 个 P0 计划管理更新 `PUT /train/plans/{planId}`],P1 辅助 Command 4 个);
- P0 业务编排 Service:**10 个 P0 Application Service**;
4. 领域核心标识 `enrollment_id`, `plan_course_id`, `lesson_id` 稳固落实,5 大跨表硬 Invariant 与 1 项 Lifecycle Policy 界限分明;
5. 未修改任何一行 Java/Vue 代码或数据库表结构。
**评审最终裁定:【Contract Review Round 1 Final = A】(完全通过,允许进入 API Contract 最终冻结)。**