Files
railwaysystem/docs/issues-v1/02_p0_issue_backlog.md
T

1060 lines
78 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RailwaySystem Phase 3:P0 Issue 详细清单 (P0 Issue Backlog)
> **文档定位:** 本文档是 RailwaySystem Phase 3 的核心工程实施清单。
> **编制基线:** `api-v2-contract-v1.0` (53个P0 API) + Database V2 (21张表) + PRD V1.0。
> **质量规范:** 每一个 Issue 均是一个具备独立业务价值、可独立开发与测试的业务能力单元,严禁机械拆解为单表 CRUD。
---
## 目录索引
- [EPIC-01 基础主数据必要集合](#epic-01-基础主数据必要集合)
- [ISSUE-01-01 师资主数据管理能力](#issue-01-01-师资主数据管理能力)
- [ISSUE-01-02 学员主档建档与安全脱敏管理](#issue-01-02-学员主档建档与安全脱敏管理)
- [ISSUE-01-03 课程库基础资料管理](#issue-01-03-课程库基础资料管理)
- [ISSUE-01-04 教学空间资源维护(教室与实训场地)](#issue-01-04-教学空间资源维护教室与实训场地)
- [ISSUE-01-05 宿舍房间与楼宇基础数据维护](#issue-01-05-宿舍房间与楼宇基础数据维护)
- [EPIC-02 培训计划与本期课程](#epic-02-培训计划与本期课程)
- [ISSUE-02-01 培训计划全生命周期创建与管理](#issue-02-01-培训计划全生命周期创建与管理)
- [ISSUE-02-02 本期培训课程引入与配置](#issue-02-02-本期培训课程引入与配置)
- [EPIC-03 参训人员管理](#epic-03-参训人员管理)
- [ISSUE-03-01 本期参训名单维护与单条录入](#issue-03-01-本期参训名单维护与单条录入)
- [ISSUE-03-02 参训人员 Excel 批量导入与防重校验](#issue-03-02-参训人员-excel-批量导入与防重校验)
- [EPIC-04 排课与课表管理](#epic-04-排课与课表管理)
- [ISSUE-04-01 课次编排与多维度排课并发冲突检测](#issue-04-01-课次编排与多维度排课并发冲突检测)
- [ISSUE-04-02 培训计划完整课程表聚合展示](#issue-04-02-培训计划完整课程表聚合展示)
- [EPIC-05 住宿与报到管理](#epic-05-住宿与报到管理)
- [ISSUE-05-01 批量住宿智能预分配与防超卖控制](#issue-05-01-批量住宿智能预分配与防超卖控制)
- [ISSUE-05-02 住宿人工调房、走读设置与住宿安排查看](#issue-05-02-住宿人工调房走读设置与住宿安排查看)
- [ISSUE-05-03 学员报到确认与生命周期时限控制](#issue-05-03-学员报到确认与生命周期时限控制)
- [EPIC-06 门禁与外部设备Mock](#epic-06-门禁与外部设备mock)
- [ISSUE-06-01 外部设备适配器 SPI 体系与 Mock 桩实现](#issue-06-01-外部设备适配器-spi-体系与-mock-桩实现)
- [ISSUE-06-02 门禁授权事务后解耦下发与失败手工重试](#issue-06-02-门禁授权事务后解耦下发与失败手工重试)
- [EPIC-07 课次考勤管理](#epic-07-课次考勤管理)
- [ISSUE-07-01 课次班牌打卡签到签退与跨班不变量防护](#issue-07-01-课次班牌打卡签到签退与跨班不变量防护)
- [ISSUE-07-02 课次考勤名单查看与人工考勤状态修正](#issue-07-02-课次考勤名单查看与人工考勤状态修正)
- [EPIC-08 成绩项目与成绩台账](#epic-08-成绩项目与成绩台账)
- [ISSUE-08-01 本期成绩考核项与权重配置](#issue-08-01-本期成绩考核项与权重配置)
- [ISSUE-08-02 学员成绩录入、上限校验与全班成绩台账大表](#issue-08-02-学员成绩录入上限校验与全班成绩台账大表)
- [EPIC-09 外部学习记录接入](#epic-09-外部学习记录接入)
- [ISSUE-09-01 外部实训系统自主学习流水幂等接入](#issue-09-01-外部实训系统自主学习流水幂等接入)
- [EPIC-10 计划统计与综合工作台](#epic-10-计划统计与综合工作台)
- [ISSUE-10-01 培训计划综合工作台聚合服务](#issue-10-01-培训计划综合工作台聚合服务)
- [ISSUE-10-02 培训计划综合成效统计概览](#issue-10-02-培训计划综合成效统计概览)
- [EPIC-11 权限字典与系统基线支撑](#epic-11-权限字典与系统基线支撑)
- [ISSUE-11-01 RuoYi RBAC 权限体系与按钮授权集成](#issue-11-01-ruoyi-rbac-权限体系与按钮授权集成)
- [ISSUE-11-02 培训业务数据字典与参数配置初始化方案](#issue-11-02-培训业务数据字典与参数配置初始化方案)
- [EPIC-12 P0端到端业务闭环验收](#epic-12-p0端到端业务闭环验收)
- [ISSUE-12-01 智慧培训全生命周期主链路端到端集成测试与验收](#issue-12-01-智慧培训全生命周期主链路端到端集成测试与验收)
---
## EPIC-01 基础主数据必要集合
### ISSUE-01-01 师资主数据管理能力
- **Issue ID:** `ISSUE-01-01`
- **Issue 名称:** 师资主数据管理能力
- **所属 Epic:** `EPIC-01 基础主数据必要集合`
- **优先级:** P0
- **Generator 策略:** YES (利用生成器生成 Entity、Mapper 基础骨架)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 为排课选师资、计划主讲教师提供完备的教师档案检索、详情查看、建档与维护能力。
- **对应 API:**
- `GET /train/teacher/list` (分页查询教师列表)
- `GET /train/teacher/{tId}` (获取教师详情)
- `POST /train/teacher` (新增教师)
- `PUT /train/teacher/{tId}` (修改教师)
- **涉及 DTO/VO:**
- DTO: `TeacherCreateDTO`, `TeacherUpdateDTO`
- VO: `TeacherDetailVO`, `TableDataInfo<TeacherItemVO>`
- **Application Service:** `BaseResourceAppService` / `TrainTeacherService`
- **涉及 Domain/Table:** `TrainTeacher` / `train_teacher`
- **前置依赖:** 无 (Wave 1 独立启动)
- **实现范围:** 教师姓名、单位、性别、职称、联系电话、邮箱维护;分页检索;手机号唯一性校验。
- **明确不做什么:** 不做教师资质审批流;不做教师授课课酬自动核算;启停用状态修改属于 P1。
- **核心业务规则:**
- 手机号系统全局查重,同一手机号不可重复建档;
- 教师姓名必填,字符长度不超过 50。
- **事务边界:** 标准短事务,新增/更新单表入库。
- **权限:** `train:teacher:list`, `train:teacher:query`, `train:teacher:add`, `train:teacher:edit`
- **错误码:** `TEACHER_PHONE_DUPLICATED (40001)`, `TEACHER_NOT_FOUND (40401)`
- **后端任务:** 生成/编写 Controller、DTO、校验注解、Service 实现手机号查重逻辑与脱敏规则。
- **前端任务:** 教师名录列表页、添加/编辑教师 Dialog 弹窗、手机号格式表单验证。
- **数据库任务:** None (使用 Database V2 已冻结 `train_teacher` 表及唯一索引)。
- **测试点:** 正常新增、重复手机号拦截、分页查询过滤、修改非主键字段。
- **Acceptance Criteria:**
- *AC-01:* Given 教师手机号 `13800000001` 不存在,When 提交新增教师请求,Then 成功插入 `train_teacher` 并返回 200。
- *AC-02:* Given 教师手机号 `13800000001` 已存在,When 再次提交新增,Then 抛出错误码 40001 (`TEACHER_PHONE_DUPLICATED`),不产生新增记录。
- **Definition of Done:** API 单元测试通过,前端能正常分页检索并成功录入教师,代码通过 Sonar 规范。
---
### ISSUE-01-02 学员主档建档与安全脱敏管理
- **Issue ID:** `ISSUE-01-02`
- **Issue 名称:** 学员主档建档与安全脱敏管理
- **所属 Epic:** `EPIC-01 基础主数据必要集合`
- **优先级:** P0
- **Generator 策略:** PARTIAL (Entity 骨架由生成器产生,身份证加密脱敏与查重严禁生成,必须手写)
- **Recommended Agent:** Codex (涉及数据安全合规与隐私治理)
- **业务目标:** 建立全局学员长期主档,落实敏感数据(身份证号)安全散列查重、密文存储与脱敏掩码展示规范。
- **对应 API:**
- `GET /train/student/list` (分页查询学员主档列表)
- `GET /train/student/{sId}` (获取学员详情)
- `POST /train/student` (新增学员)
- **涉及 DTO/VO:**
- DTO: `StudentCreateDTO`
- VO: `StudentDetailVO`, `TableDataInfo<StudentItemVO>` (含 `idCardMasked`, `idCardLast4`)
- **Application Service:** `BaseResourceAppService` / `TrainStudentService`
- **涉及 Domain/Table:** `TrainStudent` / `train_student`
- **前置依赖:** 无 (Wave 1 独立启动)
- **实现范围:** 学员姓名、单位、职务、电话建档;身份证号格式校验;计算安全散列存入 `id_card_hash` 查重;密文存入 `id_card_ciphertext`;展示末四位存入 `id_card_last4`;列表返回脱敏身份证掩码。
- **明确不做什么:** 不在 P0 开放身份证明文批量导出;Excel 批量导入学员主档与修改主档属于 P1。
- **核心业务规则与安全治理规范:**
- **身份证号严禁明文持久化**;
- 持久化模型严格沿用 Database V2:
- `id_card_ciphertext`:可逆密文
- `id_card_hash`:用于确定性查重/检索的安全散列值
- `id_card_last4`:展示末四位
- **算法边界约束:** 具体可逆加密算法、密钥管理方案、确定性散列算法在后续 Security Implementation Review 中统一确定,不得由当前 Issue 提前写死;
- **防泄漏铁律:** 系统日志、异常堆栈、前端 VO 响应与 Excel 导出中严禁输出完整身份证号明文;
- 查重必须使用 `id_card_hash` 执行 `SELECT 1 FROM train_student WHERE id_card_hash = ?`。
- **事务边界:** 本地短事务。
- **权限:** `train:student:list`, `train:student:query`, `train:student:add`
- **错误码:** `STUDENT_IDCARD_DUPLICATED (40002)`, `STUDENT_NOT_FOUND (40402)`
- **后端任务:** 编写身份证脱敏与安全加密工具组件、实现散列查重入库、Controller 接口。
- **前端任务:** 学员列表页展示脱敏身份证、新增学员表单(身份证格式前端校验)。
- **数据库任务:** None (使用 Database V2 已冻结 `train_student` 表结构及哈希索引)。
- **测试点:** 身份证合规性校验、重复身份证拦截、落库密文与散列核验、查询返回掩码 `110101********1234`、日志无明文泄漏。
- **Acceptance Criteria:**
- *AC-01:* Given 身份证号为 `110101199001011234`,When 提交建档请求,Then 数据库中 `id_card_ciphertext` 为可逆密文且不为明文,`id_card_hash` 存入安全散列值,`id_card_last4` 为 `1234`,日志中无明文打印。
- *AC-02:* Given 同一身份证号再次提交,When 调用新增接口,Then 拦截并返回 40002 (`STUDENT_IDCARD_DUPLICATED`)。
- **Definition of Done:** 脱敏与散列单测 100% 覆盖,API 安全审查通过,查询列表与日志无明文泄漏。
---
### ISSUE-01-03 课程库基础资料管理
- **Issue ID:** `ISSUE-01-03`
- **Issue 名称:** 课程库基础资料管理
- **所属 Epic:** `EPIC-01 基础主数据必要集合`
- **优先级:** P0
- **Generator 策略:** YES (生成基础 Entity/Mapper/Service)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 维护基地标准公共课程库,为培训班引入本期课程提供元数据支持。
- **对应 API:**
- `GET /train/course/list` (分页查询课程库列表)
- `GET /train/course/{cId}` (获取课程详情及绑定资源)
- `POST /train/course` (新增课程)
- **涉及 DTO/VO:**
- DTO: `CourseCreateDTO`
- VO: `CourseDetailVO`, `TableDataInfo<CourseItemVO>`
- **Application Service:** `BaseResourceAppService` / `TrainCourseService`
- **涉及 Domain/Table:** `TrainCourse` / `train_course`
- **前置依赖:** 无 (Wave 1 独立启动)
- **实现范围:** 课程编码、课程名称、课程类型、学时、教学目标、内容摘要录入与分页查询。
- **明确不做什么:** 剔除多版本流转;剔除审批流;课程绑定资源接口与修改课程属于 P1。
- **核心业务规则:**
- `c_code` (课程编码) 全局唯一;
- `c_duration` (学时) 必须为正整数。
- **事务边界:** 本地单表短事务。
- **权限:** `train:course:list`, `train:course:query`, `train:course:add`
- **错误码:** `COURSE_CODE_DUPLICATED (40003)`
- **后端任务:** Controller、Service、DTO 参数校验、编码防重校验。
- **前端任务:** 课程库列表页、新增课程对话框、学时输入校验。
- **数据库任务:** None (使用 Database V2 已冻结表结构)。
- **测试点:** 正常新增课程、重复课程编码拦截、课程列表按名称/分类过滤。
- **Acceptance Criteria:**
- *AC-01:* Given 课程编码 `KC-2026-001` 不存在,When 提交新增课程,Then 成功落库并生成 `c_id`。
- *AC-02:* Given 课程编码 `KC-2026-001` 已存在,When 再次提交相同编码,Then 接口返回 40003 (`COURSE_CODE_DUPLICATED`)。
- **Definition of Done:** CRUD 单测通过,前端联调成功,无冗余多版本字段。
---
### ISSUE-01-04 教学空间资源维护(教室与实训场地)
- **Issue ID:** `ISSUE-01-04`
- **Issue 名称:** 教学空间资源维护(教室与实训场地)
- **所属 Epic:** `EPIC-01 基础主数据必要集合`
- **优先级:** P0
- **Generator 策略:** YES (教室与场地为标准单表资源)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 建立普通教室与专业实训场地的主数据档案,为后续排课冲突检测提供实体空间校验支撑。
- **对应 API:**
- `GET /train/classroom/list` (分页查询教室列表)
- `GET /train/classroom/{classRoomId}` (获取教室详情及绑定班牌)
- `POST /train/classroom` (新增教室)
- `GET /train/training-site/list` (分页查询实训场地列表)
- `GET /train/training-site/{siteId}` (获取实训场地详情)
- `POST /train/training-site` (新增实训场地)
- **涉及 DTO/VO:**
- DTO: `ClassroomCreateDTO`, `SiteCreateDTO`
- VO: `ClassroomDetailVO`, `SiteDetailVO`, `TableDataInfo`
- **Application Service:** `BaseResourceAppService`
- **涉及 Domain/Table:** `TrainClassroom` (`train_classroom`), `TrainTrainingSite` (`train_training_site`)
- **前置依赖:** 无 (Wave 1 独立启动)
- **实现范围:** 教室/场地名称、所在位置、容纳人数、设备描述的新增与分页查询。
- **明确不做什么:** 不做物理空间 GIS 地图绘制;教室修改/场地修改属于 P1。
- **核心业务规则:**
- 容纳人数 (`capacity`) 必须 > 0;
- 空间名称在同一基地内不得重复。
- **事务边界:** 本地短事务。
- **权限:** `train:classroom:list`, `train:classroom:edit`, `train:site:list`, `train:site:edit`
- **错误码:** `CLASSROOM_NAME_DUPLICATED (40008)`, `SITE_NAME_DUPLICATED (40009)`
- **后端任务:** 编写 Controller、Service、DTO 校验及名称查重。
- **前端任务:** 教室管理列表与新增、实训场地列表与新增。
- **数据库任务:** None (使用 Database V2 表结构)。
- **测试点:** 教室/场地的新增成功、容纳人数负数校验、分页检索。
- **Acceptance Criteria:**
- *AC-01:* Given 教室名称 `第一多媒体教室`,容量 `60`,When 提交新增教室,Then 成功插入 `train_classroom`。
- *AC-02:* Given 场地容量填入 `0` 或 `-5`,When 提交新增,Then 前后端拦截并提示校验失败。
- **Definition of Done:** 教室与实训场地列表及新增 API 均通过自动化测试,返回格式符合若依规范。
---
### ISSUE-01-05 宿舍房间与楼宇基础数据维护
- **Issue ID:** `ISSUE-01-05`
- **Issue 名称:** 宿舍房间与楼宇基础数据维护
- **所属 Epic:** `EPIC-01 基础主数据必要集合`
- **优先级:** P0 (处理 Contract Gap 1,确保住宿分配主链路底座齐备)
- **Generator 策略:** YES (单表结构)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 录入宿舍楼栋、楼层、房间号、核定床位容量、适用性别与智能门锁 SN 编号,并提供楼栋楼层级联下拉数据。
- **对应 API:**
- `GET /train/dormitory/list` (分页查询宿舍房间列表)
- `GET /train/dormitory/{dormId}` (获取指定房间详情及当前入住人员列表)
- `POST /train/dormitory` (新增房间)
- `GET /train/dormitory/options` (获取楼栋、楼层级联下拉选项)
- **涉及 DTO/VO:**
- DTO: `DormitoryCreateDTO`
- VO: `DormitoryItemVO`, `DormOptionsVO`
- **Application Service:** `BaseResourceAppService` / `TrainDormitoryService`
- **涉及 Domain/Table:** `TrainDormitoryInfo` / `train_dormitory_info`
- **前置依赖:** 无 (Wave 1 独立启动)
- **实现范围:** 宿舍房间录入、查重、楼宇级联下拉树生成。
- **明确不做什么:** 不做床位电表水表硬件对接;实时房态网格看板属于 P1。
- **核心业务规则:**
- 同一楼栋下房间号必须唯一(联合唯一键 `UNIQUE(dorm_building, dorm_room)`);
- `capacity` 必须在 1~8 之间;
- `gender_type` 必须为有效枚举(0:不限, 1:男, 2:女)。
- **事务边界:** 本地短事务。
- **权限:** `train:dormitory:list`, `train:dormitory:edit`
- **错误码:** `DORM_ROOM_DUPLICATED (40006)`
- **后端任务:** 实体 Mapper、Service 级联树查询 SQL、Controller。
- **前端任务:** 宿舍列表页、新增宿舍弹窗、级联选择器组件。
- **数据库任务:** None (使用 Database V2 表结构)。
- **测试点:** 楼栋房间查重、容量上下限验证、级联数据结构解析。
- **Acceptance Criteria:**
- *AC-01:* Given 宿舍楼栋 `A栋` 已有 `101` 室,When 再次提交 `A栋-101`,Then 抛出错误码 40006 (`DORM_ROOM_DUPLICATED`)。
- *AC-02:* When 请求 `/train/dormitory/options`,Then 返回结构化的楼栋 -> 楼层 -> 房间层级树。
- **Definition of Done:** API 单测通过,前端分房弹窗能正常消费 options 级联数据。
---
## EPIC-02 培训计划与本期课程
### ISSUE-02-01 培训计划全生命周期创建与管理
- **Issue ID:** `ISSUE-02-01`
- **Issue 名称:** 培训计划全生命周期创建与管理
- **所属 Epic:** `EPIC-02 培训计划与本期课程`
- **优先级:** P0
- **Generator 策略:** PARTIAL (Entity 生成,状态机与校验逻辑手写)
- **Recommended Agent:** Codex (核心业务实体与生命周期控制)
- **业务目标:** 实现培训班项目立项、起止时间维护、报到截止时间设定、基本信息修改以及计划结束(结训)。
- **对应 API:**
- `GET /train/plans` (分页查询培训计划列表)
- `POST /train/plans` (创建培训计划 - UC-01)
- `GET /train/plans/{planId}` (获取培训计划基本信息)
- `PUT /train/plans/{planId}` (编辑培训计划 - UC-02)
- `POST /train/plans/{planId}/complete` (结束培训计划 - UC-29)
- **涉及 DTO/VO:**
- DTO: `PlanCreateDTO`, `PlanUpdateDTO`
- VO: `PlanDetailVO`, `TableDataInfo<PlanItemVO>`
- **Application Service:** `PlanAppService`
- **涉及 Domain/Table:** `TrainClassPlan` / `train_class_plan`, `train_enrollment`
- **前置依赖:** `ISSUE-01-01` (指定教师负责人可选用)
- **实现范围:** 计划编号自动生成、班级名称查重、起止时间校验、状态流转(0:未开始 -> 1:进行中 -> 2:已结束)、结训时将参训学员状态统一更新为已完成。
- **明确不做什么:** 不做多级立项审批流;严禁引入及格线结业门槛、不及格无法结业阻断、结业证书自动生成、毕业审批或成绩发布审批等未冻结流程(PRD V-07/V-08 仍未现场冻结,结训仅置学员状态为 4:已完成,保留完整成绩供后续查阅)。
- **核心业务规则:**
- 结束时间必须大于开始时间;
- `report_deadline` 必须在计划开始之前或合理区间内;
- 已结束 (`status=2`) 或已取消 (`status=3`) 的计划禁止编辑修改;
- 结训操作为幂等保护,不可对已结训计划重复操作。
- **事务边界:**
- 创建/修改:单表短事务;
- 结训 (`completePlan`):事务边界 8,更新 `train_class_plan.status=2` 并批量更新本班所有正常参训学员 `train_enrollment.enrollment_status=4`。
- **权限:** `train:plan:list`, `train:plan:query`, `train:plan:add`, `train:plan:edit`, `train:plan:complete`
- **错误码:** `PLAN_TIME_INVALID (40010)`, `PLAN_NAME_DUPLICATED (40011)`, `PLAN_STATUS_LOCKED (40012)`, `PLAN_ALREADY_COMPLETED (40014)`
- **后端任务:** 实现 `PlanAppService` 核心状态流转、日期区间逻辑校验、Controller 与 DTO。
- **前端任务:** 培训计划列表页、创建/编辑班级表单、结训确认二次弹窗。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 逆序日期校验、同名班级拦截、已结束状态下禁止修改、结训学员状态联动。
- **Acceptance Criteria:**
- *AC-01:* Given 计划结束时间 `2026-09-10` 早于开始时间 `2026-09-15`,When 提交创建,Then 返回 40010 (`PLAN_TIME_INVALID`)。
- *AC-02:* Given 计划当前状态为 2 (已结束),When 提交 PUT 请求修改班级信息,Then 返回 40012 (`PLAN_STATUS_LOCKED`)。
- *AC-03:* Given 计划下有 30 名状态为 1 (已报到) 的学员,When 执行结训,Then 计划状态置为 2,30 名学员的 `enrollment_status` 全部置为 4。
- **Definition of Done:** 状态流转单元测试全覆盖,结训事务提交通过验证,前端完整走通创建与结训。
---
### ISSUE-02-02 本期培训课程引入与配置
- **Issue ID:** `ISSUE-02-02`
- **Issue 名称:** 本期培训课程引入与配置
- **所属 Epic:** `EPIC-02 培训计划与本期课程`
- **优先级:** P0
- **Generator 策略:** PARTIAL (Entity 生成,选课与去重逻辑手写)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 将公共课程库中的课程实例引入到指定培训计划中,生成唯一的本期课程标识 `plan_course_id`,为排课与打分考核项建立锚点。
- **对应 API:**
- `GET /train/plans/{planId}/courses` (查询本期已配置课程列表)
- `POST /train/plans/{planId}/courses` (配置本期课程 - UC-03)
- **涉及 DTO/VO:**
- DTO: `PlanCourseBatchDTO` (`courseIds`, `defaultTeacherId`)
- VO: `PlanCourseItemVO`
- **Application Service:** `PlanAppService` / `TrainPlanCourseService`
- **涉及 Domain/Table:** `TrainPlanCourse` / `train_plan_course`, `train_course`, `train_teacher`
- **前置依赖:** `ISSUE-01-03` (课程库), `ISSUE-02-01` (培训计划)
- **实现范围:** 从课程库弹窗勾选课程、批量添加到本班本期课程列表、生成 `plan_course_id`、排重校验。
- **明确不做什么:** 移除本期课程 (`DELETE`) 属于 P1。
- **核心业务规则:**
- 同一课程不可重复加入同一个培训计划(基于 `UNIQUE(plan_id, c_id)` 约束);
- 默认继承公共课程的标准学时,允许指定本期默认授课教师。
- **事务边界:** 本地短事务,批量 `INSERT INTO train_plan_course`。
- **权限:** `train:plan:query`, `train:plan:edit`
- **错误码:** `PLAN_COURSE_DUPLICATED (40020)`
- **后端任务:** 选课批量处理逻辑、查重校验、查询本期课程联表展示课程元信息。
- **前端任务:** 计划详情-课程配置 Tab、课程库穿梭框/弹窗选择器。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 批量选课入库、重复选同一门课拦截、本期课程列表展示。
- **Acceptance Criteria:**
- *AC-01:* Given 计划 `PLAN01` 已配置课程 `C01`,When 再次尝试向 `PLAN01` 添加 `C01`,Then 返回 40020 (`PLAN_COURSE_DUPLICATED`)。
- *AC-02:* Given 计划 `PLAN01` 成功配置课程 `C01` 与 `C02`,When 查询本期课程列表,Then 返回包含对应的 `plan_course_id` 记录。
- **Definition of Done:** API 单测通过,生成的 `plan_course_id` 可作为后续排课的外键依赖。
---
## EPIC-03 参训人员管理
### ISSUE-03-01 本期参训名单维护与单条录入
- **Issue ID:** `ISSUE-03-01`
- **Issue 名称:** 本期参训名单维护与单条录入
- **所属 Epic:** `EPIC-03 参训人员管理`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Codex (参训生命周期核心主键绑定)
- **业务目标:** 实现从学员库向培训班单名添加学员,生成核心参训档案主键 `enrollment_id`,支持按班级查询参训花名册与学员参训详情。
- **对应 API:**
- `GET /train/plans/{planId}/enrollments` (查看本期参训名单 - UC-06)
- `POST /train/plans/{planId}/enrollments` (添加单名参训人员 - UC-04)
- `GET /train/enrollments/{enrollmentId}` (获取参训档案详情)
- **涉及 DTO/VO:**
- DTO: `EnrollmentCreateDTO` (`studentId`, `orgName`, `remark`)
- VO: `EnrollmentItemVO`, `EnrollmentDetailVO`
- **Application Service:** `EnrollmentAppService`
- **涉及 Domain/Table:** `TrainEnrollment` / `train_enrollment`, `train_student`, `train_class_plan`
- **前置依赖:** `ISSUE-01-02` (学员主档), `ISSUE-02-01` (培训计划)
- **实现范围:** 单学员入班建档、唯一性查重、招生人数上限校验、初始状态置为 0 (待报到)、档案全景聚合回显。
- **明确不做什么:** 修改参训状态 (`PUT /status`) 与移除学员 (`DELETE`) 属于 P1。
- **核心业务规则:**
- 同一学员在同一培训班不可重复参训(`UNIQUE(plan_id, student_id)`);
- 当前参训总人数不得超过班级设定的招生上限 (`recruit_limit`)。
- **事务边界:** 事务边界 1 (`addEnrollment`),排他锁定或行数校验后插入。
- **权限:** `train:enrollment:list`, `train:enrollment:add`, `train:enrollment:query`
- **错误码:** `ENROLLMENT_DUPLICATED (40030)`, `RECRUIT_LIMIT_EXCEEDED (40031)`
- **后端任务:** `EnrollmentAppService` 入班业务方法、招生上限并发校验、详情 VO 聚合组装。
- **前端任务:** 计划详情-参训学员花名册列表、选择学员入班 Dialog、参训档案全景卡片。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 正常入班、重复入班拦截、超出招生人数上限拦截。
- **Acceptance Criteria:**
- *AC-01:* Given 计划 `PLAN01` 招生上限为 50 人且已有 50 人,When 尝试添加第 51 名学员,Then 抛出 40031 (`RECRUIT_LIMIT_EXCEEDED`)。
- *AC-02:* Given 学员 `S01` 已在 `PLAN01` 中,When 再次提交入班,Then 抛出 40030 (`ENROLLMENT_DUPLICATED`)。
- **Definition of Done:** 入班核心逻辑单测通过,`enrollment_id` 成功生成并可供下游住宿、报到、考勤模块引用。
---
### ISSUE-03-02 参训人员 Excel 批量导入与防重校验
- **Issue ID:** `ISSUE-03-02`
- **Issue 名称:** 参训人员 Excel 批量导入与防重校验
- **所属 Epic:** `EPIC-03 参训人员管理`
- **优先级:** P0
- **Generator 策略:** NO (纯业务批量事务与 Excel 解析,必须手写)
- **Recommended Agent:** Codex (大批量数据解析、查重与事务一致性)
- **业务目标:** 支持管理员一键上传单位报名花名册 Excel 文件,系统自动匹配或创建学员主档并批量加入本期培训名单。
- **对应 API:**
- `POST /train/plans/{planId}/enrollments/batch` (Excel 批量导入参训人员 - UC-05)
- **涉及 DTO/VO:**
- DTO: `MultipartFile file`, `Boolean autoCreateStudent`
- VO: `BatchImportResultVO` (`totalCount`, `successCount`, `failureCount`, `errorList`)
- **Application Service:** `EnrollmentAppService`
- **涉及 Domain/Table:** `TrainEnrollment`, `TrainStudent`, `TrainClassPlan`
- **前置依赖:** `ISSUE-03-01` (参训名单核心)
- **实现范围:** 基于 EasyExcel/POI 解析表格、身份证号与手机号格式校验、学员主档匹配/自动建档、防重校验、事务控制、失败行明细返回。
- **明确不做什么:** 不引入异步消息中间件通知;单批次上限控制在 500 行以内。
- **核心业务规则:**
- 文件中单行数据格式错误不阻断其余有效数据的导入(逐行校验并汇总错误报告);
- 导入成功的人员统一赋予初始状态 0 (待报到)。
- **事务边界:** 批量分块事务处理。
- **权限:** `train:enrollment:import`
- **错误码:** `EXCEL_PARSE_ERROR (40033)`, `FILE_SIZE_EXCEEDED (40034)`
- **后端任务:** 编写 Excel 监听器、身份证校验与哈希比对、批量入班插入、错误信息收集。
- **前端任务:** 参训花名册页面“Excel导入”按钮、上传模态框、错误行明细弹窗展示。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 标准模板解析、含格式错误行跳过并报错、重复人员跳过并报错。
- **Acceptance Criteria:**
- *AC-01:* Given 导入文件包含 10 条数据,其中 8 条正常、2 条身份证格式错误,When 调用批量导入接口,Then 成功插入 8 条记录,返回 `successCount=8, failureCount=2`,并清晰列出失败行号及错误原因。
- **Definition of Done:** 单元测试覆盖正常与异常数据混杂场景,前端上传交互体验流畅。
---
## EPIC-04 排课与课表管理
### ISSUE-04-01 课次编排与多维度排课并发冲突检测
- **Issue ID:** `ISSUE-04-01`
- **Issue 名称:** 课次编排与多维度排课并发冲突检测
- **所属 Epic:** `EPIC-04 排课与课表管理`
- **优先级:** P0
- **Generator 策略:** PARTIAL (Entity 生成,核心排课锁与冲突算法必须手写)
- **Recommended Agent:** Codex (高并发冲突检测与排他锁设计)
- **业务目标:** 编排本期课程的具体课次时间与场地,实现教师、普通教室、实训场地三类资源的时间区间重叠并发排他锁检测,提供前端实时预检与后端强制拦截。
- **对应 API:**
- `POST /train/plans/{planId}/lessons` (新增课次 - UC-07)
- `PUT /train/lessons/{lessonId}` (修改课次 - UC-08)
- `POST /train/lessons/check-conflict` (排课冲突检查接口 - UC-10)
- **涉及 DTO/VO:**
- DTO: `LessonCreateDTO`, `LessonUpdateDTO`, `LessonConflictCheckDTO`
- VO: `LessonConflictVO` (`hasConflict`, `conflictType`, `conflictDetail`)
- **Application Service:** `SchedulingAppService`
- **涉及 Domain/Table:** `TrainLesson` / `train_lesson`, `train_teacher`, `train_classroom`, `train_training_site`, `train_plan_course`
- **前置依赖:** `ISSUE-01-01` (教师), `ISSUE-01-04` (空间), `ISSUE-02-02` (本期课程)
- **实现范围:** 课次时间区间合法性校验(必须在班级周期内);场地类型二选一(普通教室或实训场地);基于稳定主资源行锁执行三类资源区间冲突查询;修改课次时排除自身重新核验。
- **明确不做什么:** 自动排课算法;删除课次 (`DELETE`) 属于 P1。
- **核心业务规则与排课并发稳定资源锁设计:**
1. **稳定主资源锁铁律:** 普通针对 `train_lesson` 的区间查询无法锁住尚不存在的行(空时段并发下普通行锁失效),因此必须将锁锚定在**实际涉及的稳定物理主资源行**上;
2. **事务加锁步骤 (Create):**
- 开启本地事务 (`@Transactional`);
- 对排课涉及的教师及教室/场地主表记录加行级排他锁:
```sql
SELECT t_id FROM train_teacher WHERE t_id = :teacherId FOR UPDATE;
-- 若为普通教室:
SELECT class_room_id FROM train_classroom WHERE class_room_id = :classroomId FOR UPDATE;
-- 若为实训场地:
SELECT site_id FROM train_training_site WHERE site_id = :siteId FOR UPDATE;
```
3. **三类资源的时间区间冲突检测 SQL:**
- 在已持有稳定主资源排他锁的同一事务内,执行区间重叠判断(`existing.start_time < :newEndTime AND existing.end_time > :newStartTime`):
- 教师时间冲突:检查该教师在时段内是否存在其他有效课次;
- 教室时间冲突:检查该普通教室在时段内是否被其他有效课次占用;
- 实训场地时间冲突:检查该实训场地在时段内是否被其他有效课次占用;
- 若发生重叠,立刻抛出对应错误码并阻断入库;
4. **编辑课次 (Update) 的新旧资源加锁与防死锁设计:**
- 若编辑修改了授课教师或教学场地,事务必须**同时锁定旧资源 (old resource) 与新资源 (new resource)**;
- **防死锁锁顺序策略:** 为杜绝多事务交叉加锁引发的死锁 (Deadlock),所有涉及的资源行必须**严格按照 `resourceType + resourceId` 升序依次获取锁**;
5. 修改课次时,若该课次已有学员签到打卡流水,禁止修改上课时段。
- **事务边界:** 事务边界 2 (`createLesson`),稳定主资源行加锁 -> 区间排查 -> 写入 `train_lesson` -> COMMIT。
- **权限:** `train:lesson:add`, `train:lesson:edit`, `train:lesson:query`
- **错误码:** `LESSON_TEACHER_CONFLICT (40040)`, `LESSON_CLASSROOM_CONFLICT (40041)`, `LESSON_SITE_CONFLICT (40042)`, `LESSON_PLAN_OVERLAP (40043)`, `LESSON_ALREADY_HAS_ATTENDANCE (40044)`
- **后端任务:** 编写稳定主资源加锁逻辑、升序防死锁器、冲突检测 SQL、Service、Controller 接口。
- **前端任务:** 排课表单弹窗、失焦实时预检、冲突醒目高亮提示。
- **数据库任务:** None (使用 Database V2 已冻结表结构与联合索引)。
- **测试点:** 教师并发冲突、教室并发冲突、场地并发冲突、Update 变更资源防死锁压测。
- **Acceptance Criteria:**
- *AC-01:* Given 教师 A 在 `10:00-12:00` 已有课次,When 并发尝试排入教师 A 在 `11:00-13:00` 的新课次,Then 稳定教师行锁生效,后置事务等待并检测到时间冲突,返回 40040 (`LESSON_TEACHER_CONFLICT`),且不新增记录。
- *AC-02:* Given 教室 101 在 `14:00-16:00` 已被占用,When 另一个培训班尝试在 `15:00-17:00` 占用 101 教室,Then 返回 40041 (`LESSON_CLASSROOM_CONFLICT`)。
- *AC-03:* Given 课次已被打卡签到,When 尝试修改该课次起止时间,Then 抛出 40044 (`LESSON_ALREADY_HAS_ATTENDANCE`)。
- **Definition of Done:** 并发压测下无重复时段穿透入库,死锁测试零发生,单元测试覆盖全部冲突分支。
---
### ISSUE-04-02 培训计划完整课程表聚合展示
- **Issue ID:** `ISSUE-04-02`
- **Issue 名称:** 培训计划完整课程表聚合展示
- **所属 Epic:** `EPIC-04 排课与课表管理`
- **优先级:** P0
- **Generator 策略:** NO (纯复杂 VO 聚合查询)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 按照日历周/天维度,聚合返回本培训计划的全周期完整课程表,供管理人员、授课教师与学员直观查看教学日程。
- **对应 API:**
- `GET /train/plans/{planId}/timetable` (获取计划完整课表 - UC-11)
- **涉及 DTO/VO:**
- VO: `PlanTimetableVO` (嵌套 `DailyScheduleVO`, `LessonScheduleVO`)
- **Application Service:** `SchedulingAppService`
- **涉及 Domain/Table:** `TrainLesson`, `TrainPlanCourse`, `TrainCourse`, `TrainTeacher`, `TrainClassroom`, `TrainTrainingSite`
- **前置依赖:** `ISSUE-04-01` (排课核心)
- **实现范围:** 按班级联查所有课次,按日期分组升序排列,带出课程名称、教师、场地位置、起止时段与课次状态。
- **明确不做什么:** 教师个人课表属于 P1。
- **核心业务规则:** 只读高效聚合,针对大规模课次进行按日分组。
- **事务边界:** 只读事务 (`@Transactional(readOnly = true)`)。
- **权限:** `train:plan:query`
- **错误码:** `PLAN_NOT_FOUND (40410)`
- **后端任务:** 编写聚合 SQL/MyBatis XML、组装 `PlanTimetableVO`。
- **前端任务:** 计划详情页-日历/周日程课表展示组件。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 跨周排课分组、空课表处理、场地信息正确展示。
- **Acceptance Criteria:**
- *AC-01:* Given 计划下在 `2026-09-15` 排有 2 节课,`2026-09-16` 排有 1 节课,When 调用课表接口,Then 返回包含两个日期分组且结构完整的课表 VO。
- **Definition of Done:** 聚合单测通过,前端周视图渲染无畸变。
---
## EPIC-05 住宿与报到管理
### ISSUE-05-01 批量住宿智能预分配与防超卖控制
- **Issue ID:** `ISSUE-05-01`
- **Issue 名称:** 批量住宿智能预分配与防超卖控制
- **所属 Epic:** `EPIC-05 住宿与报到管理`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Codex (高并发防超卖行锁与分配算法)
- **业务目标:** 在开班前一键对本班有住宿需求且尚未分配的学员进行自动化预分配,严格按性别隔离,并通过数据库行锁杜绝并发超卖。
- **对应 API:**
- `POST /train/plans/{planId}/accommodations/preallocate` (批量住宿预分配 - UC-12)
- **涉及 DTO/VO:**
- DTO: `AccommodationPreallocateDTO` (`preferredBuilding`, `genderSeparated`, `sameClassAdjacent`)
- VO: `PreallocateResultVO` (`allocatedCount`, `unallocatedCount`, `allocatedDetails`)
- **Application Service:** `AccommodationAppService`
- **涉及 Domain/Table:** `TrainAccommodation` / `train_accommodation`, `TrainDormitoryInfo` / `train_dormitory_info`, `TrainEnrollment`, `TrainStudent`
- **前置依赖:** `ISSUE-01-05` (宿舍), `ISSUE-03-01` (名单)
- **实现范围:** 筛选未分房学员、按性别拆分、锁定候选房间行记录、计算剩余床位容量、批量写入住宿分配记录(初始状态 0:预分配)、床位号顺编。
- **明确不做什么:** 不直接在事务内调外部门禁接口(门禁交由事务提交后异步触发);不分配走读学员。
- **核心业务规则与宿舍并发稳定资源锁防超卖设计:**
1. **稳定主资源锁铁律:** 严禁仅依赖 `train_accommodation` 已有明细行充当锁对象(空房场景下无行可锁,并发请求同时读到 0 人入住将引发致命的 phantom 超售穿透),必须**直接锁定 `train_dormitory_info` 稳定主表记录**;
2. **加锁与分配事务流程 (Preallocate):**
- 开启本地事务 (`@Transactional`);
- 锁定候选目标宿舍主表行:
```sql
SELECT dorm_id, capacity, gender_type FROM train_dormitory_info
WHERE dorm_id IN (:candidateDormIds) ORDER BY dorm_id ASC FOR UPDATE;
```
- 统计目标宿舍在当前培训计划周期内重叠的有效住宿人数(包含预分配 0 与已入住 1):
```sql
SELECT dorm_id, COUNT(*) FROM train_accommodation
WHERE dorm_id = :dormId AND accommodation_status IN (0, 1) GROUP BY dorm_id;
```
- 计算剩余可用床位,强校验 `(currentOccupied + allocateCount) <= capacity`,超出即抛出 `DORM_CAPACITY_EXCEEDED (40060)` 并阻断;
- **严格性别隔离:** 男学员严禁分入女寝,女学员严禁分入男寝,违者抛出 `DORM_GENDER_CONFLICT (40061)`;
- 批量插入住宿记录,初始状态置为 0 (预分配);
- 提交事务并释放宿舍主表行锁。
- **事务边界:** 事务边界 3 (`preallocateAccommodations`)。
- **权限:** `train:accommodation:assign`
- **错误码:** `DORM_CAPACITY_EXCEEDED (40060)`, `DORM_GENDER_CONFLICT (40061)`
- **后端任务:** 实现智能分房算法、宿舍主表行排他锁与容量核查、批量保存。
- **前端任务:** 计划住宿管理-批量预分配按钮与策略配置弹窗。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 纯男学员分房、男女混编分房、床位不足时抛异常阻断、并发分房防超卖。
- **Acceptance Criteria:**
- *AC-01:* Given 宿舍 201 为男生宿舍且仅剩 1 个床位,When 并发尝试将 2 名男学员分配进 201,Then 宿舍主表行排他锁生效,后置事务被阻断并抛出 40060 (`DORM_CAPACITY_EXCEEDED`),事务整体回滚,不发生超员。
- *AC-02:* Given 宿舍 202 标记为女性寝室,When 尝试分配男学员,Then 抛出 40061 (`DORM_GENDER_CONFLICT`)。
- **Definition of Done:** 并发加锁测试无超卖,单测通过,学员性别隔离 100% 验证。
---
### ISSUE-05-02 住宿人工调房、走读设置与住宿安排查看
- **Issue ID:** `ISSUE-05-02`
- **Issue 名称:** 住宿人工调房、走读设置与住宿安排查看
- **所属 Epic:** `EPIC-05 住宿与报到管理`
- **优先级:** P0
- **Generator 策略:** NO (纯业务调房行锁、走读床位释放与状态流转,直接复用已有实体,严禁依赖生成器)
- **Recommended Agent:** Codex (事务更新与锁控制)
- **业务目标:** 满足特殊情况下的单人调房、本地职工申请走读(释放床位),并支持按班级查看全班住宿安排列表。
- **对应 API:**
- `PUT /train/enrollments/{enrollmentId}/accommodation` (单人调房 - UC-13)
- `PUT /train/enrollments/{enrollmentId}/accommodation/day-student` (设置走读)
- `GET /train/plans/{planId}/accommodations` (查看计划住宿安排列表 - UC-14)
- **涉及 DTO/VO:**
- DTO: `AccommodationAdjustDTO` (`targetDormId`, `bedNo`, `reason`), `DayStudentDTO`
- VO: `TableDataInfo<AccommodationItemVO>`
- **Application Service:** `AccommodationAppService`
- **涉及 Domain/Table:** `TrainAccommodation`, `TrainDormitoryInfo`, `TrainEnrollment`
- **前置依赖:** `ISSUE-05-01` (住宿分配核心)
- **实现范围:** 目标房间锁查校验、调房更新;走读标记与物理删除/逻辑退宿释放床位;住宿清单分页列表与多条件检索。
- **明确不做什么:** 房态实时网格看板属于 P1。
- **核心业务规则与调房并发稳定资源锁设计:**
1. 调房事务中,对涉及的原房间与目标房间主表记录执行排他锁:`SELECT dorm_id, capacity, gender_type FROM train_dormitory_info WHERE dorm_id IN (:oldDormId, :targetDormId) ORDER BY dorm_id ASC FOR UPDATE`(按 `dorm_id` 升序锁定,消除双向调房死锁风险);
2. 统计目标房间当前占用数,强校验 `(currentOccupied + 1) <= capacity` 且性别严格匹配;
3. 更新原住宿记录为已退宿或直接迁移更新 `dorm_id`;
4. 设置为走读时,学员已占用的房间床位立即解除释放。
- **事务边界:** 事务边界 4 (`adjustAccommodation`)。
- **权限:** `train:accommodation:adjust`, `train:accommodation:list`
- **错误码:** `DORM_ROOM_FULL (40062)`, `DORM_GENDER_MISMATCH (40063)`
- **后端任务:** 调房稳定宿舍主表行排他锁、升序加锁防死锁、走读逻辑、住宿列表联查及门禁同步状态展示。
- **前端任务:** 住宿列表页、调房 Dialog、走读确认操作按钮。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 满员房间调入拦截、走读释放床位、跨性别调入拦截、双向调房防死锁。
- **Acceptance Criteria:**
- *AC-01:* Given 目标房间 301 床位已满,When 并发提交调房请求到 301,Then 宿舍主表行锁排查生效,抛出 40062 (`DORM_ROOM_FULL`)。
- *AC-02:* Given 学员原住 101 室,When 设置走读成功,Then 该学员住宿记录解除,101 室可用床位增加 1。
- **Definition of Done:** 调房与走读单测通过,列表准确展示住宿与门禁状态。
---
### ISSUE-05-03 学员报到确认与生命周期时限控制
- **Issue ID:** `ISSUE-05-03`
- **Issue 名称:** 学员报到确认与生命周期时限控制
- **所属 Epic:** `EPIC-05 住宿与报到管理`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Codex (业务生命周期时限策略落地)
- **业务目标:** 支撑学员在报到现场完成入班身份核验与状态确认,严格核对计划配置的 `report_deadline` 生命周期时限,支持管理员特批与时限告警。
- **对应 API:**
- `POST /train/enrollments/{enrollmentId}/report` (学员报到确认 - UC-15)
- **涉及 DTO/VO:**
- DTO: `ReportConfirmDTO` (`reportWay`, `reportLocation`, `forceOverride`, `remark`)
- VO: `ReportConfirmResultVO` (`reportId`, `reportTime`, `isOverdue`)
- **Application Service:** `ReportAppService`
- **涉及 Domain/Table:** `TrainReportRecord` / `train_report_record`, `TrainEnrollment` / `train_enrollment`, `TrainClassPlan`
- **前置依赖:** `ISSUE-03-01` (参训名单)
- **实现范围:** 报到凭证校验(人脸/刷卡/人工)、写入 `train_report_record`、更新 `train_enrollment.enrollment_status=1` (已报到)、核验 `report_deadline` 策略。
- **明确不做什么:** 不做人脸比对活体算法(由前端或设备完成,接口接收结果);报到流水全量分页查询属于 P1。
- **核心业务规则与生命周期时限策略:**
1. 重复报到查重,已报到学员不可重复操作;
2. 读取所在计划的 `report_deadline`;
3. 若当前时间超过 `report_deadline` 且 `forceOverride = false`,返回时限提示错误码 `REPORT_DEADLINE_WARNING (40051)`;
4. 若管理员选择强制放行 (`forceOverride = true`),则允许完成报到并在流水中标记 `is_overdue = 1`。
- **事务边界:** 事务边界 5 (`confirmReport`),插入报到流水并更新参训状态。
- **权限:** `train:report:confirm`
- **错误码:** `REPORT_ALREADY_CONFIRMED (40050)`, `REPORT_DEADLINE_WARNING (40051)`
- **后端任务:** `ReportAppService` 报到确认逻辑、时限判断、Controller。
- **前端任务:** 报到管理界面、学员列表快速报到按钮、逾期强制报到确认提示框。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 正常报到、重复报到拦截、逾期报到告警、逾期管理员特批成功标记 `is_overdue`。
- **Acceptance Criteria:**
- *AC-01:* Given 学员当前状态为 1 (已报到),When 再次调用报到接口,Then 返回 40050 (`REPORT_ALREADY_CONFIRMED`)。
- *AC-02:* Given 当前时间已过报到截止时间且 `forceOverride=false`,When 请求报到,Then 返回 40051 (`REPORT_DEADLINE_WARNING`) 且不修改状态。
- *AC-03:* Given 同样逾期场景下传入 `forceOverride=true`,When 请求报到,Then 报到成功,学员状态置为 1,报到记录 `is_overdue=1`。
- **Definition of Done:** 报到与逾期逻辑单测通过,参训状态正确流转,符合 PRD V1.0 业务验收标准。
---
## EPIC-06 门禁与外部设备Mock
### ISSUE-06-01 外部设备适配器 SPI 体系与 Mock 桩实现
- **Issue ID:** `ISSUE-06-01`
- **Issue 名称:** 外部设备适配器 SPI 体系与 Mock 桩实现
- **所属 Epic:** `EPIC-06 门禁与外部设备Mock`
- **优先级:** P0
- **Generator 策略:** NO (防腐层 SPI 纯代码设计)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 搭建系统与外部硬件系统(人脸班牌、智能门禁、自主实训)之间的防腐层 (Anti-Corruption Layer),定义统一 SPI 接口并提供全功能 Mock 桩与失败模拟能力。
- **对应 API:** 基础设施层服务,支撑 Module C 与 Module D 相关业务调用。
- **涉及 DTO/VO:**
- SPI: `FaceAttendanceAdapter`, `DoorAccessAdapter`, `ExternalLearningAdapter`
- Model: `PushScheduleCmd`, `GrantAccessCmd`, `StandardStudyEvent`, `AccessResult`
- **Application Service:** `ExternalIntegrationAppService`
- **涉及 Domain/Table:** 纯内存与日志 Mock,无持久化表依赖。
- **前置依赖:** 无 (Wave 0 启动)
- **实现范围:** 定义三大 Adapter SPI 接口;编写 `MockFaceAttendanceAdapterImpl`、`MockDoorAccessAdapterImpl`、`MockExternalLearningAdapterImpl`;支持单元测试中模拟硬件超时、离线与参数错误。
- **明确不做什么:** 严禁对接真实厂商底层 HTTP/SDK 协议;严禁引入 MQ、Kafka、RabbitMQ 等中间件。
- **核心业务规则:** Mock 实现默认打印详细调用日志并返回成功;配置 Mock 开关控制模拟失败。
- **事务边界:** 无事务。
- **权限:** 系统内部组件,不直接暴露外部未授权端点。
- **错误码:** 无。
- **后端任务:** 建立 `spi` 模块包、编写 3 个接口及 Mock 实现类、编写单元测试桩。
- **前端任务:** 无。
- **数据库任务:** None。
- **测试点:** SPI 注入验证、Mock 默认调用成功、Mock 模拟失败返回指定错误响应。
- **Acceptance Criteria:**
- *AC-01:* When 调用 `DoorAccessAdapter.grantAccess`,Mock 桩在 50ms 内返回 `success=true` 并记录标准格式授权日志。
- *AC-02:* When 开启超时模拟开关,Mock 桩抛出指定超时异常或返回网络失败标识。
- **Definition of Done:** 3 套 SPI 与 Mock 类编写完成并通过基础单测,Spring 依赖注入顺畅。
---
### ISSUE-06-02 门禁授权事务后解耦下发与失败手工重试
- **Issue ID:** `ISSUE-06-02`
- **Issue 名称:** 门禁授权事务后解耦下发与失败手工重试
- **所属 Epic:** `EPIC-06 门禁与外部设备Mock`
- **优先级:** P0
- **Generator 策略:** NO (涉及 Spring 事务同步器与解耦设计)
- **Recommended Agent:** Codex (事务边界与轻量解耦)
- **业务目标:** 在住宿分配或调房事务成功提交后,通过 `afterCommit` 触发门禁适配器下发授权;当外部调用失败时不影响本地住宿数据,记录失败并提供页面手动重试入口。
- **对应 API:**
- `POST /train/accommodations/{accommodationId}/auth/sync` (手动触发/重试门禁授权同步 - UC-16)
- **涉及 DTO/VO:**
- VO: `AuthSyncResultVO` (`faceSyncStatus`, `lastError`, `syncTime`)
- **Application Service:** `AccommodationAppService`, `ExternalIntegrationAppService`
- **涉及 Domain/Table:** `TrainDormAuth` / `train_dorm_auth`, `TrainAccommodation`, `TrainStudent`
- **前置依赖:** `ISSUE-05-01` (住宿核心), `ISSUE-06-01` (Adapter SPI)
- **实现范围:** Spring `TransactionSynchronizationManager.registerSynchronization` 注册 `afterCommit`;事务外调用 `DoorAccessAdapter`;独立短事务更新 `train_dorm_auth.face_sync_status` (0:待同步, 1:已下发, 2:失败);提供手动重试 HTTP 接口。
- **明确不做什么:** 严禁在数据库大事务内包裹外部 HTTP 调用;严禁引入 Outbox 表轮询与 MQ。
- **核心业务规则:**
- 外部调用失败绝对不可回滚已提交的住宿分配事务;
- 重试接口直接调用 Adapter,成功更新状态为 1,失败记录 `last_error`。
- **事务边界:** 本地住宿短事务 COMMIT -> `afterCommit` 触发 Adapter -> 独立短事务更新门禁状态。
- **权限:** `train:dormauth:sync`
- **错误码:** `STUDENT_FACE_DATA_MISSING (40070)`, `DOOR_DEVICE_OFFLINE (40071)`
- **后端任务:** 实现事务同步器注册、异步/事务外执行器、状态独立回写方法、重试 Controller。
- **前端任务:** 住宿列表中的“重试同步”操作按钮、同步失败悬浮展示 `last_error` 提示。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 住宿事务提交后自动触发 Adapter、Adapter 失败时住宿记录完好保留、手动重试成功更新状态。
- **Acceptance Criteria:**
- *AC-01:* Given 模拟门锁设备离线,When 触发调房或重试同步,Then 住宿记录正常变更,`train_dorm_auth.face_sync_status` 更新为 2,记录 `last_error`。
- *AC-02:* Given 设备恢复正常,When 管理员点击重试同步,Then 状态成功更新为 1 (已下发),`last_error` 清空。
- **Definition of Done:** 事务边界与解耦单测通过,日志输出清晰,前端能正常触发重试。
---
## EPIC-07 课次考勤管理
### ISSUE-07-01 课次班牌打卡签到签退与跨班不变量防护
- **Issue ID:** `ISSUE-07-01`
- **Issue 名称:** 课次班牌打卡签到签退与跨班不变量防护
- **所属 Epic:** `EPIC-07 课次考勤管理`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Codex (跨表业务硬不变量落地)
- **业务目标:** 支撑电子班牌回传或人工代刷课次签到与签退,**强制落实并守护系统硬不变量 INV-01,严禁跨班打卡串签**。
- **对应 API:**
- `POST /train/attendance/sign-in` (课次签到 - UC-17)
- `POST /train/attendance/sign-out` (课次签退 - UC-18)
- **涉及 DTO/VO:**
- DTO: `AttendanceSignInDTO` (`enrollmentId`, `lessonId`, `boardId`, `checkinWay`, `signInTime`), `AttendanceSignOutDTO`
- VO: `AttendanceRecordVO`
- **Application Service:** `AttendanceAppService`
- **涉及 Domain/Table:** `TrainAttendanceRecord` / `train_attendance_record`, `TrainLesson`, `TrainEnrollment`, `TrainPlanCourse`
- **前置依赖:** `ISSUE-03-01` (名单), `ISSUE-04-01` (课次)
- **实现范围:** 签到打卡、签退打卡、幂等查重(同一学员在同一课次不重复插入新记录,而是更新签退时间与考勤结论)。
- **明确不做什么:** 不做移动端 GPS 考勤打卡;人工补录考勤属于 P1。
- **核心业务规则与不变量责任:**
- **【强制落实 INV-01】:**
$$\text{attendance.enrollment.plan\_id} == \text{lesson.planCourse.plan\_id}$$
在写入前强制校验学员所属培训计划必须与课次所属课程的培训计划完全相同,一旦不符立即抛出 `ATTENDANCE_PLAN_MISMATCH (40080)`,严禁入库;
- 幂等性:基于 `(enrollment_id, lesson_id)` 查重,已签到则更新或直接返回已有记录;
- 自动根据签到签退时间判定初步状态(正常、迟到、早退)。
- **事务边界:** 本地短事务。
- **权限:** `train:attendance:checkin`
- **错误码:** `ATTENDANCE_PLAN_MISMATCH (40080)`, `LESSON_NOT_FOUND (40081)`
- **后端任务:** 编写 `AttendanceAppService` 打卡入库逻辑、INV-01 跨表校验、幂等更新 SQL。
- **前端任务:** 班牌模拟打卡测试页面(或教师代签界面)。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 跨班学员打卡阻断测试 (INV-01)、正常签到签退、同一课次重复签到幂等。
- **Acceptance Criteria:**
- *AC-01:* Given 学员归属于班级 A,课次归属于班级 B,When 调用 `/train/attendance/sign-in`,Then 强制抛出 40080 (`ATTENDANCE_PLAN_MISMATCH`),不新增任何考勤数据。
- *AC-02:* Given 课次已打签到卡,When 再次提交签到,Then 不产生第二条记录,返回已有考勤记录。
- **Definition of Done:** INV-01 单元测试通过率 100%,签到签退数据正确持久化。
---
### ISSUE-07-02 课次考勤名单查看与人工考勤状态修正
- **Issue ID:** `ISSUE-07-02`
- **Issue 名称:** 课次考勤名单查看与人工考勤状态修正
- **所属 Epic:** `EPIC-07 课次考勤管理`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Gemini Flash
- **业务目标:** 供任课教师在上课前后实时查看本课次的应到、实到、缺勤人员明细,并对公假、设备未识别等特殊情况人工修正考勤状态并留痕。
- **对应 API:**
- `GET /train/lessons/{lessonId}/attendance` (查询指定课次的考勤名单 - P0)
- `PUT /train/attendance/{checkinId}` (人工修正考勤 - UC-19)
- **涉及 DTO/VO:**
- DTO: `AttendanceAdjustDTO` (`attendanceStatus`, `remark`)
- VO: `LessonAttendanceVO` (`lessonId`, `shouldArriveCount`, `actualArriveCount`, `records`)
- **Application Service:** `AttendanceAppService`
- **涉及 Domain/Table:** `TrainAttendanceRecord`, `TrainLesson`, `TrainEnrollment`, `TrainStudent`
- **前置依赖:** `ISSUE-07-01` (打卡核心)
- **实现范围:** 课次考勤名单聚合查询(以班级参训名单为主表左连考勤记录,自动识别缺勤人员);考勤结论变更(0:缺勤, 1:正常, 2:迟到, 3:早退, 4:请假);强制标记 `manual_flag = 1`。
- **明确不做什么:** 考勤多维度综合统计属于 P1。
- **核心业务规则:**
- 人工修改任何考勤记录必须自动标记 `manual_flag = 1` 并保存修改人与原因备注;
- 未打卡学员默认作为“缺勤”在花名册中呈现。
- **事务边界:** 本地短事务。
- **权限:** `train:attendance:list`, `train:attendance:edit`
- **错误码:** `ATTENDANCE_RECORD_NOT_FOUND (40480)`
- **后端任务:** 编写课次应到/实到花名册聚合 SQL、人工修正 Service 方法。
- **前端任务:** 教师端课次考勤台账弹窗、状态下拉修改与原因输入框。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 课次名单应到与实到人数统计准确性、人工修正后 `manual_flag` 状态检查。
- **Acceptance Criteria:**
- *AC-01:* Given 某学员在指定课次未刷脸打卡,When 教师打开课次考勤,Then 该学员出现在名单中且考勤状态显示为“缺勤”。
- *AC-02:* Given 管理员将某条缺勤记录修改为“请假”,When 提交修改,Then 记录状态更新为 4,且 `manual_flag` 字段被强制置为 1。
- **Definition of Done:** 名单聚合与人工修正单测通过,前端操作留痕无遗漏。
---
## EPIC-08 成绩项目与成绩台账
### ISSUE-08-01 本期成绩考核项与权重配置
- **Issue ID:** `ISSUE-08-01`
- **Issue 名称:** 本期成绩考核项与权重配置
- **所属 Epic:** `EPIC-08 成绩项目与成绩台账`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Codex (硬不变量与百分制权重校验)
- **业务目标:** 为培训班配置考核评价项目(如理论考试、实操考核、日常出勤),设定各项百分比权重与满分分值,**强制落实系统硬不变量 INV-04**。
- **对应 API:**
- `GET /train/plans/{planId}/score-items` (查询计划成绩项目配置列表)
- `POST /train/plans/{planId}/score-items` (配置本期成绩项目 - UC-20)
- **涉及 DTO/VO:**
- DTO: `ScoreItemBatchConfigDTO` (包含科目列表: `itemName`, `itemType`, `weight`, `fullScore`, `planCourseId`)
- VO: `ScoreItemVO`
- **Application Service:** `ScoreAppService`
- **涉及 Domain/Table:** `TrainScoreItem` / `train_score_item`, `TrainPlanCourse`
- **前置依赖:** `ISSUE-02-02` (本期课程)
- **实现范围:** 成绩项目增删改、百分制总权重 100% 强校验、满分值有效性校验、关联课程归属性核查。
- **明确不做什么:** 不做多套成绩方案草稿版本;不设强制及格线。
- **核心业务规则与不变量责任:**
- **权重总和校验:** 所有考核科目的 `weight` 之和必须严格等于 `100.0`,允许精度误差在 0.01 范围内,否则抛出 `SCORE_WEIGHT_SUM_INVALID (40090)`;
- **【强制落实 INV-04】:**
$$\text{scoreItem.planCourse.plan\_id} == \text{scoreItem.plan\_id} \quad (\text{当 } planCourseId \neq null)$$
成绩项目所挂接的课程必须已在本培训班本期课程列表内,严禁将外部班级的课程挂接到本科目;
- `full_score` 必须为正数(如 100)。
- **事务边界:** 事务边界 6 (`configureScoreItems`),覆盖式批量更新(先删除旧项或差异对比后保存)。
- **权限:** `train:score:query`, `train:score:config`
- **错误码:** `SCORE_WEIGHT_SUM_INVALID (40090)`, `SCORE_ITEM_COURSE_MISMATCH (40091)`
- **后端任务:** 编写权重求和与课程归属校验算法、批量覆盖事务实现、Controller。
- **前端任务:** 计划详情-成绩科目配置表格、动态添加/删除行、实时计算总权重并校验 100%。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 权重总和 90% 拦截、权重总和 100% 成功、跨班绑定课程拦截 (INV-04)。
- **Acceptance Criteria:**
- *AC-01:* Given 传入 3 个科目权重分别为 40、30、20 (合计 90),When 提交保存,Then 抛出 40090 (`SCORE_WEIGHT_SUM_INVALID`)。
- *AC-02:* Given 科目绑定的 `plan_course_id` 属于班级 B,When 提交保存到班级 A,Then 抛出 40091 (`SCORE_ITEM_COURSE_MISMATCH`)。
- **Definition of Done:** INV-04 与权重求和逻辑单测 100% 覆盖,前端动态行增删交互完整。
---
### ISSUE-08-02 学员成绩录入、上限校验与全班成绩台账大表
- **Issue ID:** `ISSUE-08-02`
- **Issue 名称:** 学员成绩录入、上限校验与全班成绩台账大表
- **所属 Epic:** `EPIC-08 成绩项目与成绩台账`
- **优先级:** P0
- **Generator 策略:** NO (动态列台账大表与加权总分计算,纯业务聚合)
- **Recommended Agent:** Codex (矩阵计算、满分上限校验与硬不变量)
- **业务目标:** 支撑对学员单条或批量打分录入,**强制落实系统硬不变量 INV-02 与 INV-05**,并动态行转列生成全班成绩台账大表(输出加权综合得分 `weightedTotalScore`)及个人成绩单。
- **对应 API:**
- `POST /train/scores/entry` (录入学员单条/批量成绩 - UC-21)
- `GET /train/plans/{planId}/scores/matrix` (获取培训计划全班成绩台账矩阵 - P0)
- `GET /train/enrollments/{enrollmentId}/scores` (获取单学员本期成绩单 - UC-23)
- **涉及 DTO/VO:**
- DTO: `ScoreEntryBatchDTO` (包含批量打分列表: `enrollmentId`, `itemId`, `score`, `remark`)
- VO: `PlanScoreMatrixVO` (`columns`, `rows`, 包含学员基础信息与动态科目分值及 `weightedTotalScore`), `StudentScoreReportVO`
- **Application Service:** `ScoreAppService`
- **涉及 Domain/Table:** `TrainStudentScore` / `train_student_score`, `TrainScoreItem`, `TrainEnrollment`, `TrainStudent`
- **前置依赖:** `ISSUE-03-01` (名单), `ISSUE-08-01` (成绩科目)
- **实现范围:** 打分数据校验入库;动态矩阵行转列组装算法;加权总分实时计算公式:
$$\text{weightedTotalScore} = \sum (\text{score}_i \times \text{weight}_i / 100)$$
- **明确不做什么:** Excel 批量导入成绩属于 P1;严禁硬编码及格线(60分)与及格阻断(Review Round 1 明确剔除)。
- **核心业务规则与不变量责任:**
- **【强制落实 INV-02】:**
$$\text{studentScore.enrollment.plan\_id} == \text{scoreItem.plan\_id}$$
录入成绩时,参训学员所属的培训计划必须与成绩科目所属计划完全一致,严禁跨班打分;
- **【强制落实 INV-05】:**
$$0 \le \text{studentScore.score} \le \text{scoreItem.full\_score}$$
录入分值不得为负数,且绝对不可超出本科目配置的满分上限,违者阻断并抛出 `SCORE_EXCEEDS_FULL_SCORE (40092)`。
- **事务边界:** 事务边界 7 (`recordScores`),批量打分入库校验。
- **权限:** `train:score:edit`, `train:score:query`
- **错误码:** `SCORE_EXCEEDS_FULL_SCORE (40092)`, `SCORE_PLAN_MISMATCH (40093)`
- **后端任务:** 编写打分事务方法、INV-02 与 INV-05 校验逻辑、动态矩阵聚合查询与加权得分计算。
- **前端任务:** 成绩台账动态大表(表头由科目动态生成)、单元格打分输入与实时保存。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 满分上限校验 (INV-05)、跨班打分拦截 (INV-02)、加权综合得分数学计算准确性、动态矩阵组装。
- **Acceptance Criteria:**
- *AC-01:* Given 理论考试满分为 100 分,When 录入分数为 105 分或 -1 分,Then 拦截并抛出 40092 (`SCORE_EXCEEDS_FULL_SCORE`)。
- *AC-02:* Given 学员属于班级 A,科目属于班级 B,When 提交录入,Then 拦截并抛出 40093 (`SCORE_PLAN_MISMATCH`)。
- *AC-03:* Given 科目 A (权重 40%, 得分 80) 与科目 B (权重 60%, 得分 90),When 查询台账大表,Then 该学员 `weightedTotalScore` 精确等于 `86.0`。
- **Definition of Done:** INV-02 与 INV-05 单测通过,动态矩阵在 100 人规模下响应时间 < 200ms。
---
## EPIC-09 外部学习记录接入
### ISSUE-09-01 外部实训系统自主学习流水幂等接入
- **Issue ID:** `ISSUE-09-01`
- **Issue 名称:** 外部实训系统自主学习流水幂等接入
- **所属 Epic:** `EPIC-09 外部学习记录接入`
- **优先级:** P0
- **Generator 策略:** PARTIAL
- **Recommended Agent:** Codex (外部数据接入、硬不变量与高可靠幂等)
- **业务目标:** 对接高铁驾驶模拟舱、VR 演练设备等外部系统推送的学员自主实训学时记录,**强制落实系统硬不变量 INV-03,并通过防重事件 ID 保证幂等三态响应**。
- **对应 API:**
- `POST /train/study-records/receive` (接收外部系统学习记录 - UC-24)
- **涉及 DTO/VO:**
- DTO: `ExternalStudyRecordDTO` (`sourceSystem`, `sourceEventId`, `enrollmentId`, `planCourseId`, `startTime`, `endTime`, `durationSeconds`, `deviceModel`)
- VO: `StudyRecordReceiveResultVO` (`status` ["accepted", "duplicate", "invalid"], `durId`, `message`)
- **Application Service:** `LearningAppService`
- **涉及 Domain/Table:** `TrainStudyDuration` / `train_study_duration`, `TrainEnrollment`, `TrainPlanCourse`
- **前置依赖:** `ISSUE-03-01` (名单), `ISSUE-06-01` (Adapter SPI)
- **实现范围:** 外部请求参数校验、防重查重、INV-03 一致性校验、落库入账、三态 JSON 响应输出。
- **明确不做什么:** 不做视频大文件切片与心跳上报;内部自主学习记录检索属于 P1。
- **核心业务规则与不变量责任:**
- **【强制落实 INV-03】:**
$$\text{study.enrollment.plan\_id} == \text{planCourse.plan\_id} \quad (\text{当 } planCourseId \neq null)$$
若推送的实训记录指定了本期课程,该课程所属计划必须与学员参训计划完全一致;
- **三态幂等设计:**
1. 首次推送:依据 `(source_system, source_event_id)` 查重,无记录则插入,返回 `status = "accepted"`;
2. 重复推送:若已有记录,不再重复累加学时,直接返回 `status = "duplicate"` (HTTP 200);
3. 数据非法:结束时间小于开始时间或学时为负,返回 `status = "invalid"` 并抛出 `STUDY_EVENT_INVALID (40100)`。
- **事务边界:** 本地短事务(含查重与插入)。
- **权限:** 系统专用接入权限或内部放行(API Contract 标注无需 RBAC 拦截或采用签名/专用权限)。
- **错误码:** `STUDY_EVENT_INVALID (40100)`
- **后端任务:** 编写 `LearningAppService` 接入逻辑、INV-03 校验、幂等查重、三态响应包装。
- **前端任务:** 无(外部系统通过 HTTP 调用),可在计划详情页增加“学习记录”只读子列表以供查验。
- **数据库任务:** None (使用 Database V2 已冻结表与唯一约束)。
- **测试点:** 首次接收成功、重复事件重发幂等返回、跨班选课阻断 (INV-03)、逆序时间校验。
- **Acceptance Criteria:**
- *AC-01:* Given `source_system = "VR_SIM"`,`source_event_id = "EVT-1001"` 首次推送,When 调用接口,Then 插入 `train_study_duration`,返回 `status = "accepted"`。
- *AC-02:* Given 同样参数再次提交,When 接口接收,Then 数据库记录数不变,返回 `status = "duplicate"`。
- *AC-03:* Given 推送数据绑定的 `planCourseId` 归属于班级 B,但学员属于班级 A,When 提交接收,Then 拦截并报错,不新增学习流水。
- **Definition of Done:** 幂等与 INV-03 单测通过,符合三态响应标准契约。
---
## EPIC-10 计划统计与综合工作台
### ISSUE-10-01 培训计划综合工作台聚合服务
- **Issue ID:** `ISSUE-10-01`
- **Issue 名称:** 培训计划综合工作台聚合服务
- **所属 Epic:** `EPIC-10 计划统计与综合工作台`
- **优先级:** P0
- **Generator 策略:** NO (多领域聚合 VO 服务)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 为管理员提供培训班级运营的“综合驾驶舱”,一站式聚合呈现班级概况、选课进度、应到人数、报到人数、今日课表与关键指标。
- **对应 API:**
- `GET /train/plans/{planId}/workbench` (获取培训计划综合工作台 - P0)
- **涉及 DTO/VO:**
- VO: `PlanWorkbenchVO` (包含班级详情、核心统计指标、快捷待办卡片、今日日程)
- **Application Service:** `PlanAppService`
- **涉及 Domain/Table:** 跨表只读聚合 (`train_class_plan`, `train_enrollment`, `train_plan_course`, `train_lesson`)
- **前置依赖:** `ISSUE-02-01` (计划), `ISSUE-03-01` (名单), `ISSUE-04-01` (排课), `ISSUE-05-03` (报到)
- **实现范围:** 并行或联合查询班级元数据、统计总人数、已报到人数、总门数、今日课次列表,组装成结构化工作台 VO。
- **明确不做什么:** 不做跨库复杂 ETL 统计。
- **核心业务规则:** 只读快速聚合,容忍微秒级轻微延迟。
- **事务边界:** 只读事务。
- **权限:** `train:plan:query`
- **错误码:** `PLAN_NOT_FOUND (40410)`
- **后端任务:** 编写聚合 SQL、组装 `PlanWorkbenchVO`、编写单元测试。
- **前端任务:** 培训计划核心工作台主界面开发(指标卡片、快捷入口、今日课表)。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 零数据班级工作台回显、正常运营班级工作台各项指标核对。
- **Acceptance Criteria:**
- *AC-01:* Given 某班级招生 40 人、已报到 35 人、已排课 10 门,When 调用工作台接口,Then VO 中 `totalEnrolled=40`, `reportedCount=35`, `todayLessons` 准确包含当天课次。
- **Definition of Done:** 工作台数据准确,前端首屏加载时间 < 300ms。
---
### ISSUE-10-02 培训计划综合成效统计概览
- **Issue ID:** `ISSUE-10-02`
- **Issue 名称:** 培训计划综合成效统计概览
- **所属 Epic:** `EPIC-10 计划统计与综合工作台`
- **优先级:** P0
- **Generator 策略:** NO (跨模块业务统计分析)
- **Recommended Agent:** Codex (复杂指标聚合与数学口径核算)
- **业务目标:** 在培训班进行中或结训后,提供全景成效统计概览,汇总报到率、考勤出勤率、全班成绩平均分/最高分/最低分以及总自主实训学时。
- **对应 API:**
- `GET /train/plans/{planId}/statistics/overview` (培训计划综合统计概览 - UC-26)
- **涉及 DTO/VO:**
- VO: `PlanStatisticsVO` (`reportRate`, `attendanceRate`, `avgScore`, `maxScore`, `minScore`, `totalStudyHours`)
- **Application Service:** `StatisticsAppService`
- **涉及 Domain/Table:** 跨模块统计查询 (`train_enrollment`, `train_attendance_record`, `train_student_score`, `train_study_duration`)
- **前置依赖:** `ISSUE-05-03` (报到), `ISSUE-07-01` (考勤), `ISSUE-08-02` (成绩), `ISSUE-09-01` (实训)
- **实现范围:** 各业务指标聚合计算算法;除以零安全防护;四舍五入保留一位小数。
- **明确不做什么:** 成绩区间分布直方图下钻分析属于 P2(待现场确认)。
- **核心业务规则:**
- 报到率 = `(已报到人数 / 参训有效总人数) * 100%`;
- 出勤率 = `(正常出勤打卡数 / 应出勤打卡总数) * 100%`;
- 平均分基于所有学员的加权综合总分进行平均计算;
- 班级若无数据时,各项指标安全返回 0.0,严禁发生除零异常。
- **事务边界:** 只读事务。
- **权限:** `train:statistics:query`
- **错误码:** `PLAN_NOT_FOUND (40410)`
- **后端任务:** 编写统计聚合 SQL、除零防护、Service、Controller。
- **前端任务:** 统计概览仪表板展示界面(图表或数字看板)。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 无数据安全计算、标准数据统计计算准确性比对。
- **Acceptance Criteria:**
- *AC-01:* Given 全班 10 人,8 人已报到,考勤正常率 90%,加权平均分 85.5,When 调用统计概览接口,Then 返回值 `reportRate=80.0`, `attendanceRate=90.0`, `avgScore=85.5`。
- **Definition of Done:** 统计逻辑单测全部通过,各类边界条件无报错。
---
## EPIC-11 权限字典与系统基线支撑
### ISSUE-11-01 RuoYi RBAC 权限体系与按钮授权集成
- **Issue ID:** `ISSUE-11-01`
- **Issue 名称:** RuoYi RBAC 权限体系与按钮授权集成
- **所属 Epic:** `EPIC-11 权限字典与系统基线支撑`
- **优先级:** P0
- **Generator 策略:** NO (系统权限工程基准)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 统一系统权限字符为 `train:*` 规范,设计 `sys_menu` 按钮权限初始化数据方案,配置管理人员与授课教师的角色授权,消除旧权限系统扩散。
- **对应 API:** 支撑系统全部 53 个 P0 API 的鉴权拦截与前端 `v-hasPermi` 控制。
- **涉及 DTO/VO:** 系统 RBAC 原生实体。
- **Application Service:** 若依系统自带权限服务。
- **涉及 Domain/Table:** `sys_menu`, `sys_role_menu`
- **前置依赖:** 无 (Wave 0 启动)
- **实现范围:** 梳理全量 20+ 个 `train:*` 权限字符、设计按钮级菜单树 SQL、配置默认角色(培训管理员、任课教师、学员)。
- **明确不做什么:** 本轮禁止直接向数据库执行修改;只输出待执行 SQL 方案与映射规范。
- **核心业务规则:**
- 严禁继续扩散旧的 `system:teacher:*` 或 `system:info:*` 前缀;
- 所有 Controller 方法统一使用 `@PreAuthorize("@ss.hasPermi('train:xxx:xxx')")`。
- **事务边界:** 系统基线维护。
- **权限:** 管理员权限。
- **错误码:** 系统原生 401 / 403。
- **后端任务:** 建立权限常量字典接口/类、输出 `sys_menu` 菜单脚本规划文档。
- **前端任务:** 梳理所有前端按钮的 `v-hasPermi="['train:xxx:xxx']"` 对应表。
- **数据库任务:** 输出 SQL 方案,本轮不执行。
- **测试点:** 未授权用户访问 403 拦截、有权限用户正常放行。
- **Acceptance Criteria:**
- *AC-01:* When 后端 Controller 配置 `@PreAuthorize("@ss.hasPermi('train:plan:add')")` 且用户无该权限,Then 请求返回 403 未授权错误。
- **Definition of Done:** 权限字符全集对照表梳理完毕,无历史废弃权限前缀遗留。
---
### ISSUE-11-02 培训业务数据字典与参数配置初始化方案
- **Issue ID:** `ISSUE-11-02`
- **Issue 名称:** 培训业务数据字典与参数配置初始化方案
- **所属 Epic:** `EPIC-11 权限字典与系统基线支撑`
- **优先级:** P0
- **Generator 策略:** NO (数据架构分析与字典归类)
- **Recommended Agent:** Gemini Flash
- **业务目标:** 对照 API Contract 与此前暂未执行的 `007_dicts.sql`,全面梳理培训业务状态与枚举类型,精准归类哪些进入 RuoYi Dict、哪些进入 `sys_config`、哪些属于业务表自身数据,输出无冲突的字典初始化方案。
- **对应 API:** 支撑所有涉及状态下拉与描述转换的业务 API。
- **涉及 DTO/VO:** 系统字典原生模型。
- **Application Service:** 系统字典服务。
- **涉及 Domain/Table:** `sys_dict_type`, `sys_dict_data`, `sys_config`
- **前置依赖:** 无 (Wave 0 启动)
- **实现范围:** 评估 16 组字典类型;区分固定静态字典与业务动态配置;设计避免 ID 冲突的幂等初始化脚本方案。
- **明确不做什么:** 本轮禁止执行 `007_dicts.sql` 物理入库!只做设计与方案输出。
- **核心业务规则:**
1. **进入 RuoYi Dict 的项 (14 组高内聚状态):** `train_common_status`, `train_plan_status`, `train_enrollment_status`, `train_student_source`, `train_resource_type`, `train_room_status`, `train_dorm_status`, `train_gender_type`, `train_accommodation_status`, `train_auth_sync_status`, `train_report_way`, `train_checkin_way`, `train_attendance_status`, `train_score_item_type`;
2. **进入 `sys_config` 的项 (全局策略参数):** `train.report.deadline.policy` (时限模式), `train.face.sync.timeout` (门禁同步超时阈值 5s);
3. **属于业务表自身数据的项:** 成绩项目配置(`train_score_item` 表数据)、培训课程库(`train_course` 表数据),严禁硬编码进系统字典。
- **事务边界:** 系统基线维护。
- **权限:** 管理员。
- **错误码:** 无。
- **后端任务:** 编写 Java 枚举类映射字典值、编写字典方案说明文档。
- **前端任务:** 确定前端字典混入 (`dicts: ['train_plan_status', ...]`) 映射关系。
- **数据库任务:** 审查 `007_dicts.sql`,确认其幂等逻辑无缺陷。
- **测试点:** 枚举值与字典 code 映射一致性核验。
- **Acceptance Criteria:**
- *AC-01:* 输出完整的字典/参数分类方案矩阵,证明 16 组字典中各项属性归属明确且无业务动态数据误入系统字典。
- **Definition of Done:** 方案经过技术审查,Java 枚举与前端字典名称完全统一。
---
## EPIC-12 P0端到端业务闭环验收
### ISSUE-12-01 智慧培训全生命周期主链路端到端集成测试与验收
- **Issue ID:** `ISSUE-12-01`
- **Issue 名称:** 智慧培训全生命周期主链路端到端集成测试与验收
- **所属 Epic:** `EPIC-12 P0端到端业务闭环验收`
- **优先级:** P0
- **Generator 策略:** NO (全链路集成测试)
- **Recommended Agent:** Codex (端到端链路串联与主链路验收)
- **业务目标:** 串联 17 个 P0 核心用例(UC-01 ~ UC-29),构建全流程端到端自动化测试用例,从教师/学员主数据建档一直走通到结训与成效统计,确保系统形成完美业务闭环。
- **对应 API:** 贯穿覆盖全部 53 个 P0 API。
- **涉及 DTO/VO:** 全流程核心 DTO/VO。
- **Application Service:** 全部 10 个 P0 业务 Application Service。
- **涉及 Domain/Table:** 系统核心业务表。
- **前置依赖:** Wave 1 ~ Wave 6 全部 Issue 完工。
- **实现范围:** 编写端到端 SpringBoot 集成测试用例;模拟完整培训班运行流程;校验各环节业务状态流转与数据一致性;验证 5 大硬不变量与 1 项报到生命周期时限策略。
- **明确不做什么:** 不依赖外部真实物理硬件;不跨越未经评审的流程分支。
- **核心业务规则:**
- 业务链路必须完整闭合:主数据 -> 开班 -> 配课 -> 参训 -> 排课 -> 课表 -> 住宿 -> 门禁Mock -> 报到 -> 考勤打卡 -> 成绩考核项 -> 录入成绩 -> 自主实训接入 -> 综合工作台与统计 -> 结训;
- 链路执行后,数据库数据必须处于合法一致状态。
- **事务边界:** 业务流程串联执行。
- **权限:** 端到端测试上下文。
- **错误码:** 验收不通过即中断并报告。
- **后端任务:** 编写 `P0BusinessFlowIntegrationTest` 自动化测试类、准备标准化测试数据集。
- **前端任务:** 按照主流程开展人工端到端点到点联调走查。
- **数据库任务:** None (使用 Database V2 已冻结表)。
- **测试点:** 17 个 UC 串行无缝走通、各节点状态校验、5 大 Invariant 校验、统计指标数学核对。
- **Acceptance Criteria:**
- *AC-01:* When 自动化端到端测试脚本自顶向下运行,Then 17 个 UC 依次顺利完成,产生合法业务数据,最后一条结训断言成功,全套测试绿灯通过,用时不超过 30 秒。
- **Definition of Done:** 集成测试 100% 成功,作为 Phase 3 研发交付与准入验收的终极交付成果。