diff --git a/docs/issues-v1/00_readme.md b/docs/issues-v1/00_readme.md new file mode 100644 index 000000000..beb5a3291 --- /dev/null +++ b/docs/issues-v1/00_readme.md @@ -0,0 +1,121 @@ +# RailwaySystem Phase 3:P0 Epic / Issue 任务拆分总纲 + +> **阶段状态:** Phase 3 任务分解完成稿 (正式基线) +> **业务最高基线:** `doc/RailwaySystem 智慧培训系统一期 PRD V1.0.md` +> **数据库基线:** `db-v2-migration-passed-20260914` (21张表 KingbaseES 物理迁移验证通过) +> **代码生成器基线:** `generator-v2-ready-20260914` (Kingbase 只读验证通过) +> **API 契约基线:** `api-v2-contract-v1.0.1` (89 个物理 API 端点,P0: 53, P1: 34, P2: 2) +> **审查基线:** `docs/issues-v1/09_issue_review_round1.md` (Review Round 1 Final = A) +> **实施策略:** 严格以 PRD V1.0 业务闭环为主线,以 Database V2 为持久化底座,聚焦 P0 最小闭环。 + +--- + +## 1. 拆分目标与核心原则 + +本阶段旨在将 `api-v2-contract-v1.0.1` 中的 **53 个 P0 API** 以及支撑业务运转的核心基础设施,转化为**可独立开发、可代码审查 (Review)、可自动化测试、可业务验收**的 Epic 与 Issue。 + +本阶段**严禁编写任何业务代码**,所有拆解严格以 `api-v2-contract-v1.0`(commit `67543e04`)及 V1.0.1 修订为基线展开。 + +### 1.1 核心原则与硬约束 +1. **业务能力驱动,拒绝单表 CRUD:** 严禁以数据库物理表为单位拆分 Issue(如禁止设立“开发 train_lesson CRUD”),必须按“可独立交付与验收的业务动作/能力”进行拆分(如“实现课次排课与三类资源冲突检测”); +2. **只拆 P0,严格范围控制:** + - 锁定 53 个 P0 API 作为本轮研发唯一核心目标; + - 34 个 P1 辅助 API 与 2 个 P2 远期 API 仅在 [`07_p1_p2_backlog.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/07_p1_p2_backlog.md) 中登记备查,不计入开发排期。 +3. **主数据最小必要集合原则:** + - 基础主数据不作为全量阻塞项,仅建设支撑主业务链路所必需的最小集合(Teacher, Student, Course, Classroom, TrainingSite, Dormitory)。 +4. **不变量与并发控制强行绑定:** + - 5 大跨表业务硬不变量(INV-01 ~ INV-05)与 1 项报到生命周期策略,责任落实到唯一的责任 Issue; + - 排课并发冲突(教师/教室/场地/班级)与宿舍分配防超卖(行锁 `SELECT FOR UPDATE` + 性别隔离),必须在 Issue 中明确 SQL 与锁策略。 +5. **外部对接 Mock 优先与事务轻量解耦:** + - 外部硬件系统(班牌、门禁、VR/3D)一期一律采用 SPI + Mock 桩实现,严禁引入 MQ、Kafka、RabbitMQ、Outbox 等重型中间件; + - 外部调用一律置于数据库本地事务之外,通过事务提交后(`afterCommit`)轻量解耦触发,外部失败绝不回滚已提交的业务数据,支持手动重试。 + +--- + +## 2. 主链路业务开发优先级 + +系统的所有 Issue 依赖与开发批次(Wave),均严格围绕以下**最短业务闭环主链路**展开推进: + +```mermaid +flowchart TD + M1[基础数据: 教师 / 学员 / 课程 / 空间] --> M2[创建培训计划 Plan] + M2 --> M3[配置本期课程 PlanCourse] + M3 --> M4[添加参训名单 Enrollment] + M4 --> M5[课次排课与冲突检测 Lesson] + M5 --> M6[聚合生成完整课表 Timetable] + M4 --> M7[住宿批量预分配 Accommodation] + M7 --> M8[学员报到确认 Report] + M5 --> M9[电子班牌打卡考勤 Attendance] + M8 --> M9 + M9 --> M10[成绩项目与打分台账 Score] + M4 --> M11[外部实训记录接入 StudyRecord] + M10 --> M12[培训综合统计与工作台 Statistics/Workbench] + M11 --> M12 + M12 --> M13[结束培训计划 Complete Plan] + + style M1 fill:#e1f5fe,stroke:#0288d1,stroke-width:2px + style M2 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px + style M4 fill:#fff3e0,stroke:#f57c00,stroke-width:2px + style M5 fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px + style M7 fill:#e0f2f1,stroke:#00796b,stroke-width:2px + style M9 fill:#fbe9e7,stroke:#d84315,stroke-width:2px + style M10 fill:#fffde7,stroke:#fbc02d,stroke-width:2px + style M13 fill:#ede7f6,stroke:#512da8,stroke-width:2px +``` + +--- + +## 3. 全局核心统计指标 + +经严密分解与审查,Phase 3 输出的核心统计指标如下: + +| 统计维度 | 指标数值 | 备注说明 | +|:---|:---|:---| +| **Epic 总数** | **12 个** | 覆盖主数据、核心计划、排课、住宿、门禁、考勤、成绩、学习、统计、权限字典与端到端验收 | +| **P0 Issue 总数** | **26 个** | 每个 Issue 为 0.5~2 个开发日的独立可验收业务能力 | +| **P0 API 覆盖率** | **53 / 53 (100%)** | 契约逐条提取 53 个 P0 API 全量映射闭环,无遗漏无多余 | +| **Contract Gap 状态** | **CG-01: RESOLVED
CG-02: RESOLVED** | CG-01 采纳 53 个逐 API 正式口径;CG-02 扩展 VO 回显字段闭环 | +| **开发 Wave 数量** | **8 个** | Wave 0 (准备) ~ Wave 7 (验收),层层递进 | +| **首批可立即开发 Issue** | **8 个** | Wave 0 (3个) + Wave 1 中不依赖 Plan 的基础数据 (5个) | +| **必须等待 Module B 的 Issue** | **13 个** | 涵盖排课、住宿分配、报到、考勤、成绩配置/录入、学习记录、工作台与端到端验收 | +| **Generator 策略分布** | **YES: 4, PARTIAL: 11, NO: 11** | 机器校验:4 + 11 + 11 = 26 (符合 P0 Issue 总数 26) | +| **Recommended Agent 分工** | **Gemini: 11, Codex: 15** | Gemini 负责基础 CRUD/VO/权限字典;Codex 负责高并发锁/复杂事务/不变量/端到端 | + +--- + +## 4. 契约偏差审查与裁决落地 (Contract Gap Analysis) + +在对齐 `api-v2-contract-v1.0` 与 Issue 拆分过程中登记的 2 项契约偏差,技术负责人已作出正式裁决并全部闭环: + +### 4.1 CG-01:Module A P0 统计数字与逐 API 标注矛盾【状态:RESOLVED】 +- **裁决结论:** **详细 API 标记优先于错误摘要统计;P0 正式集合以 53 个逐 API 标记为准。** +- **落地执行:** + - 宿舍 4 个基础维护 API(列表、详情、新增、选项)属于一期 P0,因为 Accommodation 住宿主流程强依赖可维护的宿舍房间基础资源、核定容量与性别隔离属性; + - Module A P0 确认为 20 个,全局 P0 集合确定为 53 个(Module A: 20, Module B: 17, Module C: 9, Module D: 7); + - 由 `ISSUE-01-05` 完整承揽,P0 覆盖率正式达成 **53/53 (100%)**。 + +### 4.2 CG-02:课次编辑所需字段与单查接口缺失【状态:RESOLVED】 +- **裁决结论:** **通过扩展 TimetableLessonItemVO 解决;不新增 Lesson 详情 API。** +- **落地执行:** + - 严禁在 Issue 阶段或契约中新增 `GET /train/lessons/{lessonId}` 端点,保持 API Contract 接口数量稳定; + - 在 `docs/api-v2/06_dto_vo_catalog.md` 中补齐 `TimetableLessonItemVO` 的关键外键关联字段(`planCourseId`、`tId`、`venueType`、`classRoomId`、`classRoomName`、`siteId`、`siteName`、`remark`); + - 前端通过完整课表 `PlanTimetableVO` 直接回显 Lesson 编辑表单,随后调用既有的 `PUT /train/lessons/{lessonId}`,流程完全自洽闭环。 + +--- + +## 5. 文档索引导航 (docs/issues-v1/) + +本套 Issue 任务分解规范包含以下 10 篇文档: + +| 序号 | 文档名称 | 核心内容 | +|:---|:---|:---| +| 00 | [`00_readme.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/00_readme.md) | 阶段总纲、最高原则、主链路、统计总览、Contract Gap 分析与文档索引 | +| 01 | [`01_epic_overview.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/01_epic_overview.md) | 12 个业务 Epic 概览、目标、业务边界与包含的 Issue 对应清单 | +| 02 | [`02_p0_issue_backlog.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/02_p0_issue_backlog.md) | 26 个 P0 Issue 详细技术卡片(严格包含 22 项技术定义与 Given/When/Then 验收准则) | +| 03 | [`03_dependency_graph.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/03_dependency_graph.md) | Mermaid DAG 依赖图,清晰展示串行依赖与并行泳道 | +| 04 | [`04_wave_plan.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/04_wave_plan.md) | Wave 0 ~ Wave 7 开发推进批次计划、进入条件、产出目标与退出准则 | +| 05 | [`05_agent_assignment.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/05_agent_assignment.md) | Gemini Flash 与 Codex 研发分工建议及 Generator (YES/PARTIAL/NO) 执行策略 | +| 06 | [`06_acceptance_matrix.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/06_acceptance_matrix.md) | 全链路追踪矩阵 (UC → API → Issue → Test) 及 5 大 Invariant / 1 项生命周期策略归属矩阵 | +| 07 | [`07_p1_p2_backlog.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/07_p1_p2_backlog.md) | 34 个 P1 辅助 API 与 2 个 P2 远期 API 的统一登记与后续演进规划 | +| 08 | [`08_risk_register.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/08_risk_register.md) | 技术风险登记册、稳定资源锁策略、外部 Mock 隔离与 CG-01 裁决辅助表 | +| 09 | [`09_issue_review_round1.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/09_issue_review_round1.md) | Phase 3 Issue Review Round 1 审查整改记录与结论报告 | diff --git a/docs/issues-v1/01_epic_overview.md b/docs/issues-v1/01_epic_overview.md new file mode 100644 index 000000000..d1108e74f --- /dev/null +++ b/docs/issues-v1/01_epic_overview.md @@ -0,0 +1,235 @@ +# RailwaySystem Phase 3:Epic 架构概览 (Epic Overview) + +> **设计基线:** 紧扣 PRD V1.0 业务闭环与 API Contract V1.0,严禁按 21 张数据库表机械建 Epic。 +> **Epic 规划原则:** 每一个 Epic 代表一个**高内聚、有明确业务价值、对主链路有明确支撑的业务能力域**。 + +--- + +## 1. Epic 汇总视图 + +系统一期 P0 研发共划分为 **12 个核心 Epic**,覆盖从基础设施、主数据、开班排课、过程执行、效果评估到端到端验收的完整闭环: + +```mermaid +flowchart TB + subgraph CoreBase["支撑基线与基础数据"] + E11[EPIC-11 权限/字典/系统基线] + E01[EPIC-01 基础主数据必要集合] + E06[EPIC-06 门禁与外部设备Mock] + end + + subgraph PlanCore["开班与排课核心"] + E02[EPIC-02 培训计划与本期课程] + E03[EPIC-03 参训人员管理] + E04[EPIC-04 排课与课表管理] + end + + subgraph Execution["过程执行支撑"] + E05[EPIC-05 住宿与报到管理] + E07[EPIC-07 课次考勤管理] + E09[EPIC-09 外部学习记录接入] + end + + subgraph Assessment["效果评估与结训"] + E08[EPIC-08 成绩项目与成绩台账] + E10[EPIC-10 计划统计与综合工作台] + end + + subgraph Acceptance["端到端验收"] + E12[EPIC-12 P0端到端业务闭环验收] + end + + E11 --> E01 + E06 --> E05 + E06 --> E07 + E06 --> E09 + E01 --> E02 + E02 --> E03 + E02 --> E04 + E03 --> E05 + E04 --> E07 + E05 --> E07 + E07 --> E08 + E03 --> E09 + E08 --> E10 + E07 --> E10 + E02 --> E10 + E10 --> E12 + + style CoreBase fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px + style PlanCore fill:#e8f5e9,stroke:#4caf50,stroke-width:1px + style Execution fill:#e1f5fe,stroke:#03a9f4,stroke-width:1px + style Assessment fill:#fff8e1,stroke:#ffc107,stroke-width:1px + style Acceptance fill:#fce4ec,stroke:#e91e63,stroke-width:2px +``` + +--- + +## 2. Epic 详细定义 + +### EPIC-01 基础主数据必要集合 (Base Master Data) +- **Epic ID:** `EPIC-01` +- **业务目标:** 为整个智慧培训业务链路提供最小必需的师资、人员、课程、教室、场地和宿舍主数据底座,支撑后续计划选课、排课冲突校验与住宿分配。 +- **包含 P0 Issue:** + - `ISSUE-01-01`: 师资主数据管理能力 (Teacher CRUD) + - `ISSUE-01-02`: 学员主档建档与安全脱敏管理 (Student Security & Masking) + - `ISSUE-01-03`: 课程库基础资料管理 (Course Base CRUD) + - `ISSUE-01-04`: 教学空间资源维护(普通教室与实训场地) (Classroom & Site) + - `ISSUE-01-05`: 宿舍房间与楼宇基础数据维护 (Dormitory Info & Cascading Options) +- **对应 P0 API:** + - `GET /train/teacher/list`, `GET /train/teacher/{tId}`, `POST /train/teacher`, `PUT /train/teacher/{tId}` + - `GET /train/student/list`, `GET /train/student/{sId}`, `POST /train/student` + - `GET /train/course/list`, `GET /train/course/{cId}`, `POST /train/course` + - `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` + - `GET /train/dormitory/list`, `GET /train/dormitory/{dormId}`, `POST /train/dormitory`, `GET /train/dormitory/options` +- **涉及数据表:** `train_teacher`, `train_student`, `train_course`, `train_classroom`, `train_training_site`, `train_dormitory_info` +- **明确不做什么:** 不做教师资质多版本审批、不做学员主档多机构合并、不做课程版本流转、不做独立教学资源管理与班牌注册管理(属于 P1)。 + +--- + +### EPIC-02 培训计划与本期课程 (Plan & Plan-Course) +- **Epic ID:** `EPIC-02` +- **业务目标:** 实现培训班级项目的立项、编辑、周期维护与结训归档,并支撑本期教学课程的选定与课时引入。 +- **包含 P0 Issue:** + - `ISSUE-02-01`: 培训计划全生命周期创建与管理 (Plan Lifecycle) + - `ISSUE-02-02`: 本期培训课程引入与配置 (Plan-Course Mapping) +- **对应 P0 API:** + - `GET /train/plans`, `POST /train/plans`, `GET /train/plans/{planId}`, `PUT /train/plans/{planId}`, `POST /train/plans/{planId}/complete` + - `GET /train/plans/{planId}/courses`, `POST /train/plans/{planId}/courses` +- **涉及数据表:** `train_class_plan`, `train_plan_course` +- **明确不做什么:** 不做多级立项审批流;一期结训不设考核不及格阻断结业的强制卡点(Review Round 1 明确剔除)。 + +--- + +### EPIC-03 参训人员管理 (Enrollment Lifecycle) +- **Epic ID:** `EPIC-03` +- **业务目标:** 建立特定培训计划与学员的强业务绑定关系(形成 `enrollment_id`),支持单名录入与 Excel 批量解析导入,提供参训全景档案查询。 +- **包含 P0 Issue:** + - `ISSUE-03-01`: 本期参训名单维护与单条录入 (Enrollment Core & Detail) + - `ISSUE-03-02`: 参训人员 Excel 批量导入与防重校验 (Batch Enrollment Import) +- **对应 P0 API:** + - `GET /train/plans/{planId}/enrollments`, `POST /train/plans/{planId}/enrollments`, `GET /train/enrollments/{enrollmentId}` + - `POST /train/plans/{planId}/enrollments/batch` +- **涉及数据表:** `train_enrollment`, `train_student` +- **明确不做什么:** 不做复杂的退宿连带手续审批(仅做参训状态变更);批量导入错误行不引入异步消息通知,直接返回同步解析结果列表。 + +--- + +### EPIC-04 排课与课表管理 (Scheduling & Timetable) +- **Epic ID:** `EPIC-04` +- **业务目标:** 支撑培训班课程的教学时段排定,实现教师、普通教室、实训场地、班级时段四维冲突检测与数据库行级排他锁保护,聚合生成本班完整课程表。 +- **包含 P0 Issue:** + - `ISSUE-04-01`: 课次编排与多维度排课并发冲突检测 (Lesson Scheduling & Conflict Lock) + - `ISSUE-04-02`: 培训计划完整课程表聚合展示 (Plan Timetable Aggregation) +- **对应 P0 API:** + - `POST /train/plans/{planId}/lessons`, `PUT /train/lessons/{lessonId}`, `POST /train/lessons/check-conflict` + - `GET /train/plans/{planId}/timetable` +- **涉及数据表:** `train_lesson`, `train_plan_course`, `train_teacher`, `train_classroom`, `train_training_site` +- **明确不做什么:** 不做遗传算法自动智能排课;不做复杂的周循环与双周排课规则;教师个人课表属于 P1。 + +--- + +### EPIC-05 住宿与报到管理 (Accommodation & Report) +- **Epic ID:** `EPIC-05` +- **业务目标:** 管理学员参训期间的生活保障与入班核验,包含批量智能预分房(严格按性别隔离与行锁防超卖)、单人调房、走读设定,以及学员现场报到与生命周期时限告警控制。 +- **包含 P0 Issue:** + - `ISSUE-05-01`: 批量住宿智能预分配与防超卖控制 (Batch Accommodation Allocation) + - `ISSUE-05-02`: 住宿人工调房、走读设置与住宿安排查看 (Accommodation Adjustment & Roster) + - `ISSUE-05-03`: 学员报到确认与生命周期时限控制 (Student Report & Lifecycle Policy) +- **对应 P0 API:** + - `POST /train/plans/{planId}/accommodations/preallocate` + - `PUT /train/enrollments/{enrollmentId}/accommodation`, `PUT /train/enrollments/{enrollmentId}/accommodation/day-student`, `GET /train/plans/{planId}/accommodations` + - `POST /train/enrollments/{enrollmentId}/report` +- **涉及数据表:** `train_accommodation`, `train_report_record`, `train_dormitory_info`, `train_enrollment` +- **明确不做什么:** 不做水电费计量结算;房态实时网格看板属于 P1;报到逾期不做硬阻断数据库约束,采用弹性时限告警策略。 + +--- + +### EPIC-06 门禁与外部设备Mock (Device Integration & Mock) +- **Epic ID:** `EPIC-06` +- **业务目标:** 建立防腐适配器体系 (SPI),提供电子班牌人脸考勤、智能门禁授权、外部 VR/3D 学习记录对接的 Mock 实现与测试桩,并通过事务提交后(`afterCommit`)轻量解耦触发门禁同步,支持失败重试。 +- **包含 P0 Issue:** + - `ISSUE-06-01`: 外部设备适配器 SPI 体系与 Mock 桩实现 (Adapter SPI & Mocks) + - `ISSUE-06-02`: 门禁授权事务后解耦下发与失败手工重试 (Door Auth After-Commit & Manual Retry) +- **对应 P0 API:** + - `POST /train/accommodations/{accommodationId}/auth/sync` +- **涉及数据表:** `train_dorm_auth`, `train_accommodation`, `train_student` +- **明确不做什么:** 严禁对接真实厂家硬件私有协议;严禁引入 MQ、Kafka、RabbitMQ、Outbox 等重型中间件;门禁授权下发流水查询属于 P1。 + +--- + +### EPIC-07 课次考勤管理 (Lesson Attendance) +- **Epic ID:** `EPIC-07` +- **业务目标:** 围绕具体课次 (`lesson_id`) 与参训档案 (`enrollment_id`) 开展出勤管理,支撑班牌刷脸打卡签到/签退、任课教师实时查看课次考勤明细,以及异常出勤的人工修正与留痕。 +- **包含 P0 Issue:** + - `ISSUE-07-01`: 课次班牌打卡签到签退与跨班不变量防护 (Check-in/Check-out & Invariant INV-01) + - `ISSUE-07-02`: 课次考勤名单查看与人工考勤状态修正 (Lesson Attendance Roster & Manual Rectification) +- **对应 P0 API:** + - `POST /train/attendance/sign-in`, `POST /train/attendance/sign-out` + - `GET /train/lessons/{lessonId}/attendance`, `PUT /train/attendance/{checkinId}` +- **涉及数据表:** `train_attendance_record`, `train_lesson`, `train_enrollment` +- **明确不做什么:** 不做基于 GPS 地理围栏的移动端打卡;人工补录未打卡记录属于 P1;不变量 INV-01(课次所属班级 == 学员参训班级)在此强制阻断。 + +--- + +### EPIC-08 成绩项目与成绩台账 (Score Management) +- **Epic ID:** `EPIC-08` +- **业务目标:** 实现培训班成绩考核科目与百分制权重配置,支持教师对单学员或批量录入成绩,强校验满分上限与跨班归属,生成全班加权综合得分台账大表与单人成绩单。 +- **包含 P0 Issue:** + - `ISSUE-08-01`: 本期成绩考核项与权重配置 (Score Items & Weight Config, Invariant INV-04) + - `ISSUE-08-02`: 学员成绩录入、上限校验与全班成绩台账大表 (Score Entry, Matrix VO, Invariant INV-02 & INV-05) +- **对应 P0 API:** + - `GET /train/plans/{planId}/score-items`, `POST /train/plans/{planId}/score-items` + - `POST /train/scores/entry`, `GET /train/plans/{planId}/scores/matrix`, `GET /train/enrollments/{enrollmentId}/scores` +- **涉及数据表:** `train_score_item`, `train_student_score`, `train_enrollment` +- **明确不做什么:** 不做成绩发布审批流程;Excel 批量导入成绩属于 P1;严禁硬编码及格线与不合格阻断(仅计算 `weightedTotalScore`)。 + +--- + +### EPIC-09 外部学习记录接入 (External Learning Records) +- **Epic ID:** `EPIC-09` +- **业务目标:** 对接高铁驾驶模拟舱、VR 实训室、3D 拆装等外部专业设备推送的学习学时流水,提供防重复推送的唯一幂等处理与三态响应。 +- **包含 P0 Issue:** + - `ISSUE-09-01`: 外部实训系统自主学习流水幂等接入 (Learning Record Ingestion & Invariant INV-03) +- **对应 P0 API:** + - `POST /train/study-records/receive` +- **涉及数据表:** `train_study_duration`, `train_enrollment`, `train_plan_course` +- **明确不做什么:** 不对接大文件视频播放断点与心跳上报;自主学习记录分页检索与单人流水详情属于 P1。 + +--- + +### EPIC-10 计划统计与综合工作台 (Plan Workbench & Statistics) +- **Epic ID:** `EPIC-10` +- **业务目标:** 为培训管理人员提供开班期间的“一站式计划综合工作台”与结训阶段的“计划综合统计概览”,聚合展示报到率、出勤率、课表进度、成绩分布与自主学时。 +- **包含 P0 Issue:** + - `ISSUE-10-01`: 培训计划综合工作台聚合服务 (Plan Workbench Aggregation) + - `ISSUE-10-02`: 培训计划综合成效统计概览 (Plan Statistics Aggregation) +- **对应 P0 API:** + - `GET /train/plans/{planId}/workbench` + - `GET /train/plans/{planId}/statistics/overview` +- **涉及数据表:** 跨表只读聚合查询(`train_class_plan`, `train_enrollment`, `train_lesson`, `train_attendance_record`, `train_student_score`, `train_study_duration`) +- **明确不做什么:** 不做复杂的多维 OLAP 钻取报表;不引入 Elasticsearch / ClickHouse 等外部大数据分析引擎;多维度成绩下钻图表属于 P2。 + +--- + +### EPIC-11 权限字典与系统基线支撑 (RBAC, Dict & System Baseline) +- **Epic ID:** `EPIC-11` +- **业务目标:** 夯实系统运行底座,全面统一以 `train:*` 为前缀的 RBAC 菜单与按钮授权体系,规范化分类梳理 16 组培训业务字典,消除旧系统权限混乱扩散。 +- **包含 P0 Issue:** + - `ISSUE-11-01`: RuoYi RBAC 权限体系与按钮授权集成 (RBAC Migration & Train Permissions) + - `ISSUE-11-02`: 培训业务数据字典与参数配置初始化方案 (Dictionary & SysConfig Strategy) +- **对应 P0 API:** + - 支撑全部 53 个 P0 API 的鉴权拦截与前端按钮 `v-hasPermi` 显隐控制。 +- **涉及数据表:** `sys_menu`, `sys_role_menu`, `sys_dict_type`, `sys_dict_data`, `sys_config` +- **明确不做什么:** 本轮严禁执行 `007_dicts.sql` 物理入库;不做动态自定义数据权限过滤规则(一期采用基于角色的标准 RBAC)。 + +--- + +### EPIC-12 P0端到端业务闭环验收 (End-to-End Acceptance) +- **Epic ID:** `EPIC-12` +- **业务目标:** 串联 17 个 P0 核心用例(UC-01 ~ UC-29),构建完整的端到端自动化测试链路与基线集成回归用例,确保最小业务闭环能够流畅无缝运转。 +- **包含 P0 Issue:** + - `ISSUE-12-01`: 智慧培训全生命周期主链路端到端集成测试与验收 (End-to-End Acceptance Suite) +- **对应 P0 API:** + - 贯穿覆盖全部 53 个 P0 API 的链路式调用与数据验证。 +- **业务价值:** 彻底杜绝“各个单接口测试通过,连起来无法走通完整培训班”的集成陷阱,作为 Phase 3 研发交付的最终质量闸门。 diff --git a/docs/issues-v1/02_p0_issue_backlog.md b/docs/issues-v1/02_p0_issue_backlog.md new file mode 100644 index 000000000..3cb18af64 --- /dev/null +++ b/docs/issues-v1/02_p0_issue_backlog.md @@ -0,0 +1,1059 @@ +# 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` +- **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` (含 `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` +- **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` +- **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` +- **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 研发交付与准入验收的终极交付成果。 diff --git a/docs/issues-v1/03_dependency_graph.md b/docs/issues-v1/03_dependency_graph.md new file mode 100644 index 000000000..952c6278e --- /dev/null +++ b/docs/issues-v1/03_dependency_graph.md @@ -0,0 +1,176 @@ +# RailwaySystem Phase 3:Issue 依赖关系有向无环图 (Dependency DAG) + +> **设计目标:** 明确各 Issue 之间的前后置依赖关系,清晰识别哪些任务必须串行推进、哪些任务可以无阻塞并行开发,指导团队实现资源利用率最大化。 + +--- + +## 1. Issue 全景依赖拓扑图 (Mermaid DAG) + +```mermaid +flowchart TB + subgraph Wave0["Wave 0:基线支撑 (可立即无前置并行启动)"] + I1101["ISSUE-11-01
RBAC权限体系"] + I1102["ISSUE-11-02
字典配置方案"] + I0601["ISSUE-06-01
外部Adapter SPI与Mock"] + end + + subgraph Wave1["Wave 1:基础主数据与计划立项 (可立即并行启动)"] + I0101["ISSUE-01-01
教师主数据"] + I0102["ISSUE-01-02
学员主档与脱敏"] + I0103["ISSUE-01-03
课程库资料"] + I0104["ISSUE-01-04
空间资源:教室与场地"] + I0105["ISSUE-01-05
宿舍房间与楼宇"] + I0201["ISSUE-02-01
培训计划全生命周期"] + end + + subgraph Wave2["Wave 2:计划配课、参训与排课"] + I0202["ISSUE-02-02
本期课程引入"] + I0301["ISSUE-03-01
本期参训名单维护"] + I0302["ISSUE-03-02
名单Excel批量导入"] + I0401["ISSUE-04-01
排课与四维冲突排他锁"] + I0402["ISSUE-04-02
完整课表聚合VO"] + end + + subgraph Wave3["Wave 3:住宿、门禁与报到"] + I0501["ISSUE-05-01
批量住宿预分配防超卖"] + I0502["ISSUE-05-02
调房走读管理"] + I0602["ISSUE-06-02
门禁afterCommit解耦同步"] + I0503["ISSUE-05-03
学员报到与时限控制"] + end + + subgraph Wave4["Wave 4:课次考勤"] + I0701["ISSUE-07-01
课次打卡与INV-01"] + I0702["ISSUE-07-02
考勤名单与人工修正"] + end + + subgraph Wave5["Wave 5:成绩与学习记录"] + I0801["ISSUE-08-01
成绩项目配置与INV-04"] + I0802["ISSUE-08-02
成绩录入台账INV-02/05"] + I0901["ISSUE-09-01
学习记录接入与INV-03"] + end + + subgraph Wave6["Wave 6:工作台与成效统计"] + I1001["ISSUE-10-01
培训计划综合工作台"] + I1002["ISSUE-10-02
计划成效统计概览"] + end + + subgraph Wave7["Wave 7:全链路端到端验收"] + I1201["ISSUE-12-01
P0主链路端到端集成验收"] + end + + %% 跨模块依赖连线 + I0103 --> I0202 + I0201 --> I0202 + + I0102 --> I0301 + I0201 --> I0301 + I0301 --> I0302 + + I0202 --> I0401 + I0101 --> I0401 + I0104 --> I0401 + I0401 --> I0402 + + I0301 --> I0501 + I0105 --> I0501 + I0501 --> I0502 + I0501 --> I0602 + I0601 --> I0602 + + I0301 --> I0503 + + I0401 --> I0701 + I0301 --> I0701 + I0503 --> I0701 + I0701 --> I0702 + + I0202 --> I0801 + I0801 --> I0802 + I0301 --> I0802 + + I0301 --> I0901 + I0202 --> I0901 + I0601 --> I0901 + + I0201 --> I1001 + I0301 --> I1001 + I0401 --> I1001 + I0503 --> I1001 + + I0503 --> I1002 + I0701 --> I1002 + I0802 --> I1002 + I0901 --> I1002 + + I1001 --> I1201 + I1002 --> I1201 + I0702 --> I1201 + I0502 --> I1201 + I0602 --> I1201 + + style Wave0 fill:#f1f8e9,stroke:#8bc34a,stroke-width:1px + style Wave1 fill:#e0f7fa,stroke:#00bcd4,stroke-width:1px + style Wave2 fill:#e8eaf6,stroke:#3f51b5,stroke-width:1px + style Wave3 fill:#fce4ec,stroke:#e91e63,stroke-width:1px + style Wave4 fill:#fff3e0,stroke:#ff9800,stroke-width:1px + style Wave5 fill:#ede7f6,stroke:#673ab7,stroke-width:1px + style Wave6 fill:#f3e5f5,stroke:#9c27b0,stroke-width:1px + style Wave7 fill:#ffebee,stroke:#f44336,stroke-width:2px +``` + +--- + +## 2. 串行依赖关键路径 (Critical Path) + +系统的**最短端到端主链路关键路径**(耗时最长、环环相扣的核心串行线)如下: + +$$\begin{aligned} +\text{ISSUE-01-03 (课程库)} &\longrightarrow \text{ISSUE-02-01 (创建计划)} \\ +&\longrightarrow \text{ISSUE-02-02 (配置本期课程)} \\ +&\longrightarrow \text{ISSUE-04-01 (课次排课与冲突锁)} \\ +&\longrightarrow \text{ISSUE-07-01 (课次考勤打卡)} \\ +&\longrightarrow \text{ISSUE-10-02 (计划综合统计)} \\ +&\longrightarrow \text{ISSUE-12-01 (端到端验收)} +\end{aligned}$$ + +同时并行的辅关键路径为参训与住宿线: +$$\text{ISSUE-01-02 (学员)} \longrightarrow \text{ISSUE-03-01 (名单)} \longrightarrow \text{ISSUE-05-01 (住宿)} \longrightarrow \text{ISSUE-05-03 (报到)} \longrightarrow \text{ISSUE-07-01 (考勤)}$$ + +--- + +## 3. 并行开发泳道分析 + +通过上述 DAG,可以清晰划分出以下 4 条高并发研发泳道: + +### 泳道 A:基础设施与主数据泳道 (可完全自由推进) +- `ISSUE-11-01` (RBAC权限) +- `ISSUE-11-02` (字典配置方案) +- `ISSUE-06-01` (外部 Adapter SPI & Mock) +- `ISSUE-01-01` (教师主数据) +- `ISSUE-01-02` (学员主档与脱敏) +- `ISSUE-01-03` (课程库) +- `ISSUE-01-04` (教学空间资源) +- `ISSUE-01-05` (宿舍基础房间) +> **特征:** 相互之间完全解耦,可由多位开发者或 Gemini Flash 代理人并行同时开工,0 依赖等待。 + +### 泳道 B:排课教学线 (依赖课程与计划) +- `ISSUE-02-01` (计划立项) +- `ISSUE-02-02` (本期课程) +- `ISSUE-04-01` (排课与排他锁) +- `ISSUE-04-02` (完整课表聚合) +> **特征:** 教师与教室准备完毕后,沿开班排课主干顺畅推进。 + +### 泳道 C:学员生活与报到线 (依赖名单与宿舍) +- `ISSUE-03-01` (名单与档案) +- `ISSUE-03-02` (Excel批量导入) +- `ISSUE-05-01` (住宿智能预分配) +- `ISSUE-05-02` (调房与走读) +- `ISSUE-06-02` (门禁异步下发重试) +- `ISSUE-05-03` (学员报到确认) +> **特征:** 围绕 `enrollment_id` 展开,不依赖具体课次,主要管理学员个人生活保障。 + +### 泳道 D:成效考核与外部数据接入线 +- `ISSUE-08-01` (成绩科目与权重) +- `ISSUE-08-02` (成绩录入与台账矩阵大表) +- `ISSUE-09-01` (外部自主学习记录接入) +> **特征:** 在学员名单和本期课程确定后即可独立推进,与排课考勤互不干扰。 diff --git a/docs/issues-v1/04_wave_plan.md b/docs/issues-v1/04_wave_plan.md new file mode 100644 index 000000000..7a56df1af --- /dev/null +++ b/docs/issues-v1/04_wave_plan.md @@ -0,0 +1,149 @@ +# RailwaySystem Phase 3:开发批次计划 (Wave Plan) + +> **规划目标:** 按照依赖关系与业务闭环推进节奏,将 26 个 P0 Issue 分解为 8 个开发批次(Wave 0 ~ Wave 7),实现敏捷迭代、敏捷交付、敏捷验收。 + +--- + +## 1. Wave 推进总览表 + +| 批次 | 阶段主题 | 核心 Issue 清单 | 预估人日 | 前置准入条件 | 核心产出物 | +|:---|:---|:---|:---|:---|:---| +| **Wave 0** | **工程基线与外部准备** | `ISSUE-11-01`, `ISSUE-11-02`, `ISSUE-06-01` | 2.5 人日 | Phase 3 方案审查通过 | 权限常量基线、字典分类方案、Adapter SPI 及 Mock 桩 | +| **Wave 1** | **基础主数据与计划立项** | `ISSUE-01-01`, `ISSUE-01-02`, `ISSUE-01-03`, `ISSUE-01-04`, `ISSUE-01-05`, `ISSUE-02-01` | 6.0 人日 | Wave 0 权限与工程准备就绪 | 教师/学员/课程/教室/场地/宿舍基础档案、培训计划立项与生命周期 | +| **Wave 2** | **计划配课、参训与排课** | `ISSUE-02-02`, `ISSUE-03-01`, `ISSUE-03-02`, `ISSUE-04-01`, `ISSUE-04-02` | 7.0 人日 | Wave 1 主数据与计划完成 | 本期课程引入、参训名单(含Excel导入)、排课并发四维冲突锁、完整课程表 | +| **Wave 3** | **住宿分配、门禁与报到** | `ISSUE-05-01`, `ISSUE-05-02`, `ISSUE-06-02`, `ISSUE-05-03` | 5.5 人日 | Wave 2 参训名单与 Wave 0 SPI 完成 | 批量住宿智能分配防超卖、调房走读、门禁事务外下发与重试、学员报到时限控制 | +| **Wave 4** | **课次打卡考勤与修正** | `ISSUE-07-01`, `ISSUE-07-02` | 3.0 人日 | Wave 2 排课与 Wave 3 报到完成 | 班牌刷脸签到/签退、INV-01 跨班防护、课次考勤花名册、人工修正留痕 | +| **Wave 5** | **成绩科目、台账与学习记录** | `ISSUE-08-01`, `ISSUE-08-02`, `ISSUE-09-01` | 5.0 人日 | Wave 2 配课与参训名单完成 | 成绩项目权重 100% 校验(INV-04)、成绩录入上限(INV-05)与跨班(INV-02)校验、全班成绩台账大表、外部学习记录幂等三态接入(INV-03) | +| **Wave 6** | **综合工作台与成效统计** | `ISSUE-10-01`, `ISSUE-10-02` | 3.0 人日 | Wave 2~Wave 5 业务数据链路通畅 | 培训计划一站式驾驶舱 (PlanWorkbenchVO)、培训成效统计概览 (PlanStatisticsVO) | +| **Wave 7** | **P0 端到端联调与闭环验收** | `ISSUE-12-01` | 2.5 人日 | Wave 0 ~ Wave 6 全部 Issue 完成 | 17 个 P0 核心用例自动化串联测试、业务全生命周期闭环准入测试报告 | + +--- + +## 2. 各 Wave 详尽实施方案 + +### Wave 0:工程基线与外部准备 (Wave 0 - Baseline & Adapters) +- **目标:** 统一全系统的 RBAC 权限字符定义,梳理业务字典与系统配置归属,建立外部系统 Mock 桩,为后续各业务模块扫清底层支撑障碍。 +- **包含 Issue:** + - `ISSUE-11-01`: RuoYi RBAC 权限体系与按钮授权集成 (Gemini Flash) + - `ISSUE-11-02`: 培训业务数据字典与参数配置初始化方案 (Gemini Flash) + - `ISSUE-06-01`: 外部设备适配器 SPI 体系与 Mock 桩实现 (Gemini Flash) +- **可并行 Issue:** 本 Wave 的 3 个 Issue 彼此完全独立,**可 100% 同步并行开发**。 +- **进入条件:** Phase 3 文档技术审查完成。 +- **退出准则:** + - 权限字符统一收敛至 `train:*` 前缀; + - 字典分类方案清晰无歧义,静态字典与业务数据彻底隔离; + - 3 大外部 SPI 接口及其 Mock 实现类完成并注入 Spring 上下文。 + +--- + +### Wave 1:基础主数据与计划立项 (Wave 1 - Master Data & Plan Creation) +- **目标:** 完成教师、学员(含脱敏与加密)、课程库、空间场地、宿舍基础房间维护,并打通培训计划的基本立项与状态生命周期。 +- **包含 Issue:** + - `ISSUE-01-01`: 师资主数据管理能力 (Gemini Flash) + - `ISSUE-01-02`: 学员主档建档与安全脱敏管理 (Codex) + - `ISSUE-01-03`: 课程库基础资料管理 (Gemini Flash) + - `ISSUE-01-04`: 教学空间资源维护(教室与实训场地) (Gemini Flash) + - `ISSUE-01-05`: 宿舍房间与楼宇基础数据维护 (Gemini Flash) + - `ISSUE-02-01`: 培训计划全生命周期创建与管理 (Codex) +- **可并行 Issue:** `ISSUE-01-01` ~ `ISSUE-01-05` 5 个主数据任务可完全并行;`ISSUE-02-01` 可同步启动。 +- **进入条件:** Wave 0 权限与字典基准确定。 +- **退出准则:** + - 各主数据表单能正常录入与检索; + - 学员身份证号落库已使用安全散列查重且密文存储,查询列表脱敏,日志无明文; + - 培训计划能成功创建、编辑和执行结训状态流转。 + +--- + +### Wave 2:计划配课、参训与排课 (Wave 2 - Plan Configuration & Scheduling) +- **目标:** 围绕培训计划开展本期选课、学员进班(支持单增与 Excel 批量导入)、课次排课及稳定主资源行级排他锁冲突检测,生成完整课程表。 +- **包含 Issue:** + - `ISSUE-02-02`: 本期培训课程引入与配置 (Gemini Flash) + - `ISSUE-03-01`: 本期参训名单维护与单条录入 (Codex) + - `ISSUE-03-02`: 参训人员 Excel 批量导入与防重校验 (Codex) + - `ISSUE-04-01`: 课次编排与多维度排课并发冲突检测 (Codex) + - `ISSUE-04-02`: 培训计划完整课程表聚合展示 (Gemini Flash) +- **可并行 Issue:** + - `ISSUE-02-02` (配课) 与 `ISSUE-03-01` (名单) 可并行推进; + - `ISSUE-03-02` 在 `ISSUE-03-01` 完成后推进; + - `ISSUE-04-01` 在 `ISSUE-02-02` 完成后推进;`ISSUE-04-02` 紧随其后。 +- **进入条件:** Wave 1 基础主数据与培训计划立项完成。 +- **退出准则:** + - 课程成功引入计划生成 `plan_course_id`; + - 学员成功入班生成 `enrollment_id`; + - Excel 批量导入支持容错并反馈错误行; + - 排课并发压测下杜绝教师/教室/场地/班级重叠穿透,完整课表 VO 正确呈现。 + +--- + +### Wave 3:住宿分配、门禁与报到 (Wave 3 - Accommodation, Door Auth & Report) +- **目标:** 解决学员现场报到与在校住宿生活保障,落实宿舍并发预分配防超卖、单人调房、走读退宿、门禁事务外解耦同步与失败重试,以及报到时限告警控制。 +- **包含 Issue:** + - `ISSUE-05-01`: 批量住宿智能预分配与防超卖控制 (Codex) + - `ISSUE-05-02`: 住宿人工调房、走读设置与住宿安排查看 (Codex) + - `ISSUE-06-02`: 门禁授权事务后解耦下发与失败手工重试 (Codex) + - `ISSUE-05-03`: 学员报到确认与生命周期时限控制 (Codex) +- **可并行 Issue:** + - `ISSUE-05-01` (住宿分配) 与 `ISSUE-05-03` (报到确认) 均依赖名单,两者可完全并行开发; + - `ISSUE-05-02` 与 `ISSUE-06-02` 在 `ISSUE-05-01` 完成后开展。 +- **进入条件:** Wave 2 参训名单已就绪、Wave 0 门禁 Mock 桩就绪。 +- **退出准则:** + - 宿舍分配严格性别隔离,`SELECT FOR UPDATE` 彻底杜绝超卖; + - 门禁下发失败不回滚住宿事务,支持界面一键重试; + - 学员报到状态顺利流转,逾期报到准确提示并支持管理员强制放行。 + +--- + +### Wave 4:课次打卡考勤与修正 (Wave 4 - Attendance & Rectification) +- **目标:** 支撑教学过程中按课次组织的出勤打卡,落实 INV-01 跨班一致性硬校验,提供考勤名单实时查看与人工修正留痕。 +- **包含 Issue:** + - `ISSUE-07-01`: 课次班牌打卡签到签退与跨班不变量防护 (Codex) + - `ISSUE-07-02`: 课次考勤名单查看与人工考勤状态修正 (Gemini Flash) +- **可并行 Issue:** `ISSUE-07-02` 依赖 `ISSUE-07-01`,为串行推进。 +- **进入条件:** Wave 2 课次排课已完成、Wave 3 学员已完成报到。 +- **退出准则:** + - 班牌签到/签退成功落库; + - **INV-01 跨班签到拦截测试 100% 阻断**; + - 人工修改考勤结论必须强制标记 `manual_flag = 1` 留痕。 + +--- + +### Wave 5:成绩科目、台账与学习记录 (Wave 5 - Assessment & Ingestion) +- **目标:** 完成本期考核科目与权重配置,支持成绩单条/批量录入,落实 INV-02、INV-04、INV-05,输出动态列成绩台账大表,并提供外部实训自主学习流水的三态幂等接入 (INV-03)。 +- **包含 Issue:** + - `ISSUE-08-01`: 本期成绩考核项与权重配置 (Codex) + - `ISSUE-08-02`: 学员成绩录入、上限校验与全班成绩台账大表 (Codex) + - `ISSUE-09-01`: 外部实训系统自主学习流水幂等接入 (Codex) +- **可并行 Issue:** + - `ISSUE-08-01` (科目配置) 与 `ISSUE-09-01` (学习记录) 可并行开发; + - `ISSUE-08-02` 在 `ISSUE-08-01` 完成后推进。 +- **进入条件:** Wave 2 课程配置与参训名单已就绪。 +- **退出准则:** + - 成绩项目总权重严格校验 100% (INV-04); + - 打分录入严格限制在 `[0, fullScore]` 内 (INV-05) 且严禁跨班打分 (INV-02); + - 全班台账大表准确输出加权总分 `weightedTotalScore`,无及格线硬阻断; + - 外部学习记录实现 `accepted / duplicate / invalid` 三态幂等响应与跨班阻断 (INV-03)。 + +--- + +### Wave 6:综合工作台与成效统计 (Wave 6 - Workbench & Statistics) +- **目标:** 将前面各业务阶段汇聚沉淀的数据进行跨表只读聚合,输出培训计划综合工作台驾驶舱与全景成效统计概览。 +- **包含 Issue:** + - `ISSUE-10-01`: 培训计划综合工作台聚合服务 (Gemini Flash) + - `ISSUE-10-02`: 培训计划综合成效统计概览 (Codex) +- **可并行 Issue:** `ISSUE-10-01` 与 `ISSUE-10-02` **可完全并行开发**。 +- **进入条件:** 报到、考勤、成绩与学时业务链路皆已走通。 +- **退出准则:** + - 工作台展示开班全景、今日课表与关键待办,响应时间 < 300ms; + - 统计概览计算报到率、出勤率、加权平均分与学时,安全防护除以零异常。 + +--- + +### Wave 7:P0 端到端联调与闭环验收 (Wave 7 - End-to-End Acceptance) +- **目标:** 串联 17 个 P0 核心用例(UC-01 ~ UC-29),构建端到端自动化测试流水线,作为 Phase 3 研发交付的最终质量关卡。 +- **包含 Issue:** + - `ISSUE-12-01`: 智慧培训全生命周期主链路端到端集成测试与验收 (Codex) +- **进入条件:** Wave 0 ~ Wave 6 全部 25 个 Issue 开发完成并通过独立单测。 +- **退出准则:** + - 自动化端到端测试用例 100% 绿灯通过; + - 5 大跨表硬不变量在链路测试中均通过破坏性用例校验; + - 生成最终《P0 业务主链路准入验收报告》。 diff --git a/docs/issues-v1/05_agent_assignment.md b/docs/issues-v1/05_agent_assignment.md new file mode 100644 index 000000000..129683f85 --- /dev/null +++ b/docs/issues-v1/05_agent_assignment.md @@ -0,0 +1,110 @@ +# RailwaySystem Phase 3:研发分工与代码生成器策略 (Agent Assignment & Generator Strategy) + +> **制定原则:** 最大化研发效能,合理配置 AI 算力与工程资源。 +> **算力调度铁律:** **简单基础工作交由 Gemini Flash 高效完成,核心高难度/高并发/高风险架构工作保留给 Codex 集中攻坚,杜绝算力浪费。** + +--- + +## 1. 代码生成器 (Generator V2) 实施策略 + +系统代码生成器已于 Phase 2 完成 KingbaseES 适配与基线冻结 (`generator-v2-ready-20260914`)。在 Phase 3 Issue 实施中,各 Issue 严格执行以下三级 Generator 策略: + +```text +YES (4个 Issue) : 基础主数据单表,完全通过 Generator 快速生成 Entity、基础 Mapper XML 与基础 Service 骨架。 +PARTIAL (11个 Issue) : 核心业务实体或敏感表,仅生成 Domain Entity 与基础 Mapper 骨架,Controller、DTO/VO、Application Service 业务编排必须严格按 API Contract 手写。 +NO (11个 Issue) : 跨表聚合服务、并发锁算法、SPI防腐接口、系统权限字典方案、纯业务状态流转或端到端测试,严禁使用生成器代码。 +``` + +### 1.1 Generator 策略明细矩阵 + +| Issue ID | Issue 名称 | Generator 策略 | 策略执行细则与约束 | +|:---|:---|:---:|:---| +| `ISSUE-01-01` | 师资主数据管理能力 | **YES** | 生成 `TrainTeacher` 实体、Mapper 与基础查询;在 Service 层补充手机号全局查重校验。 | +| `ISSUE-01-02` | 学员主档建档与安全脱敏管理 | **PARTIAL** | 生成 `TrainStudent` 实体骨架;**严禁生成明文落库代码**,手写安全散列查重与可逆密文存储逻辑。 | +| `ISSUE-01-03` | 课程库基础资料管理 | **YES** | 生成 `TrainCourse` 实体、Mapper 骨架;Service 层补充课程编码唯一性校验。 | +| `ISSUE-01-04` | 教学空间资源维护(教室与实训场地) | **YES** | 生成 `TrainClassroom` 与 `TrainTrainingSite` 单表骨架;补充容量大于零业务校验。 | +| `ISSUE-01-05` | 宿舍房间与楼宇基础数据维护 | **YES** | 生成 `TrainDormitoryInfo` 基础骨架;手写楼栋/楼层级联下拉树查询逻辑。 | +| `ISSUE-02-01` | 培训计划全生命周期创建与管理 | **PARTIAL** | 生成 `TrainClassPlan` 实体;**严禁使用生成器状态机**,手写状态流转校验与结训学员批量级联更新。 | +| `ISSUE-02-02` | 本期培训课程引入与配置 | **PARTIAL** | 生成 `TrainPlanCourse` 实体骨架;手写从课程库批量选课、防重与生成 `plan_course_id` 逻辑。 | +| `ISSUE-03-01` | 本期参训名单维护与单条录入 | **PARTIAL** | 生成 `TrainEnrollment` 实体;手写招生人数上限并发校验与 `enrollment_id` 核心上下文绑定。 | +| `ISSUE-03-02` | 参训人员 Excel 批量导入与防重校验 | **NO** | 纯业务批量事务与 EasyExcel 监听器,涉及多级错误行收集,全量手写。 | +| `ISSUE-04-01` | 课次编排与多维度排课并发冲突检测 | **PARTIAL** | 生成 `TrainLesson` 实体;**排课稳定主资源行锁 (FOR UPDATE) 与区间冲突算法必须纯手写**。 | +| `ISSUE-04-02` | 培训计划完整课程表聚合展示 | **NO** | 纯聚合只读服务,手写嵌套 `PlanTimetableVO` 结果集映射与周/日维度分组逻辑。 | +| `ISSUE-05-01` | 批量住宿智能预分配与防超卖控制 | **PARTIAL** | 生成 `TrainAccommodation` 实体;**严禁生成分房算法**,手写性别隔离、宿舍主表 `SELECT FOR UPDATE` 防超卖。 | +| `ISSUE-05-02` | 住宿人工调房、走读设置与住宿安排查看 | **NO** | 纯业务调房行锁、走读床位释放与状态流转,直接复用已有实体,严禁依赖生成器。 | +| `ISSUE-05-03` | 学员报到确认与生命周期时限控制 | **PARTIAL** | 生成 `TrainReportRecord` 实体;手写 `report_deadline` 策略核对、告警与强制放行逻辑。 | +| `ISSUE-06-01` | 外部设备适配器 SPI 体系与 Mock 桩实现 | **NO** | 纯架构防腐层设计,定义 3 大 SPI 接口及 Mock 实现类,手写。 | +| `ISSUE-06-02` | 门禁授权事务后解耦下发与失败手工重试 | **NO** | 涉及 Spring `afterCommit` 事务同步器注册与事务外解耦调用,手写。 | +| `ISSUE-07-01` | 课次班牌打卡签到签退与跨班不变量防护 | **PARTIAL** | 生成 `TrainAttendanceRecord` 实体;**硬不变量 INV-01 校验与幂等更新必须纯手写**。 | +| `ISSUE-07-02` | 课次考勤名单查看与人工考勤状态修正 | **PARTIAL** | 手写应到/实到花名册聚合左联 SQL、人工修正强制标记 `manual_flag = 1` 留痕。 | +| `ISSUE-08-01` | 本期成绩考核项与权重配置 | **PARTIAL** | 生成 `TrainScoreItem` 实体;手写总权重 100% 校验与硬不变量 INV-04 课程归属核验。 | +| `ISSUE-08-02` | 学员成绩录入、上限校验与全班成绩台账大表 | **NO** | 手写满分上限校验 (INV-05)、跨班阻断 (INV-02)、动态列行转列与加权总分计算。 | +| `ISSUE-09-01` | 外部实训系统自主学习流水幂等接入 | **PARTIAL** | 生成 `TrainStudyDuration` 实体;手写防重事件 ID 唯一性校验、三态响应与 INV-03 核查。 | +| `ISSUE-10-01` | 培训计划综合工作台聚合服务 | **NO** | 纯复杂 VO 聚合,跨 4 张表组装综合驾驶舱,手写。 | +| `ISSUE-10-02` | 培训计划综合成效统计概览 | **NO** | 纯跨模块业务指标聚合计算,含除零保护与四舍五入,手写。 | +| `ISSUE-11-01` | RuoYi RBAC 权限体系与按钮授权集成 | **NO** | 权限常量定义、`sys_menu` SQL 脚本方案与按钮权限梳理,纯系统基线工程。 | +| `ISSUE-11-02` | 培训业务数据字典与参数配置初始化方案 | **NO** | 16 组字典分类架构方案与静态字典初始化,纯数据架构分析。 | +| `ISSUE-12-01` | 智慧培训全生命周期主链路端到端集成测试与验收 | **NO** | 全链路自动化业务集成测试用例与验收套件,手写。 | + +--- + +## 2. 研发智能体分配 (Gemini Flash vs Codex) + +### 2.1 分配原则与算力调优 +- **Gemini Flash (共 11 个 Issue):** + - 适用场景:单表结构化 CRUD、VO/DTO 定义、权限常量梳理、数据字典方案、简单 Vue 列表页、单元测试数据构建。 + - 核心优势:响应极其敏捷、大上下文处理结构清晰、遵循规范精准。 +- **Codex (共 15 个 Issue):** + - 适用场景:稳定主资源行排他锁 (`FOR UPDATE`)、防超卖算法、三类资源区间冲突查询、跨表强一致性硬不变量校验、事务提交后解耦、敏感数据安全脱敏、矩阵行转列与端到端验收。 + - 核心优势:深度逻辑推理严密、并发锁死锁识别能力强、复杂领域模型抽象扎实。 + +### 2.2 详细分工清单 + +```mermaid +pie title Phase 3 Issue 研发分工比例 (总数 26) + "Gemini Flash (基础CRUD/聚合VO/基线)" : 11 + "Codex (高并发锁/硬不变量/事务编排/验收)" : 15 +``` + +| Issue ID | 推荐 Agent | 核心负责模块 / 任务边界 | 推荐理由与关键关注点 | +|:---|:---:|:---|:---| +| `ISSUE-11-01` | **Gemini Flash** | RBAC 权限体系与按钮授权 | 权限常量提取与标准 RuoYi 按钮匹配,规则明确标准。 | +| `ISSUE-11-02` | **Gemini Flash** | 字典分类方案梳理 | 评估 `007_dicts.sql` 语义,输出三类字典归属报告。 | +| `ISSUE-06-01` | **Gemini Flash** | 外部 Adapter SPI 与 Mock 桩 | 标准 Java Interface 与 Mock 类编写,快速高效。 | +| `ISSUE-01-01` | **Gemini Flash** | 教师主数据 CRUD | 标准单表管理,结构简单,适合快速落地。 | +| `ISSUE-01-02` | **Codex** | 学员主档脱敏与安全加密 | 涉及安全散列查重与可逆密文存储,需要严密的安全逻辑。 | +| `ISSUE-01-03` | **Gemini Flash** | 课程库基础资料管理 | 基础主档 CRUD,重点在于去除冗余版本字段。 | +| `ISSUE-01-04` | **Gemini Flash** | 教学空间资源维护 | 教室与场地标准数据维护,校验逻辑清晰。 | +| `ISSUE-01-05` | **Gemini Flash** | 宿舍房间与级联选项 | 楼栋/楼层级联树生成与基础房间录入。 | +| `ISSUE-02-01` | **Codex** | 培训计划生命周期管理 | 核心业务主干,涉及严格的时间校验与结训学员级联更新事务。 | +| `ISSUE-02-02` | **Gemini Flash** | 本期课程引入与配置 | 课程库与计划的关联,排重校验逻辑明确。 | +| `ISSUE-03-01` | **Codex** | 参训名单维护与单条录入 | 涉及 `enrollment_id` 核心领域标识生成与招生上限并发校验。 | +| `ISSUE-03-02` | **Codex** | Excel 批量导入与防重 | 大批量数据容错、身份证号校验、分批事务处理。 | +| `ISSUE-04-01` | **Codex** | 课次排课与稳定资源锁检测 | **高技术难度:** 必须使用教师/场地稳定行锁杜绝时段重叠穿透。 | +| `ISSUE-04-02` | **Gemini Flash** | 完整课表聚合展示 | 纯聚合查询,按日期嵌套展示课表,属于结构化组装。 | +| `ISSUE-05-01` | **Codex** | 批量住宿智能分配防超卖 | **高技术难度:** 锁定宿舍主表行 `SELECT FOR UPDATE` 彻底防并发超卖。 | +| `ISSUE-05-02` | **Codex** | 调房走读管理与锁控制 | 宿舍主表行锁核验容量与释放床位短事务,升序加锁防死锁。 | +| `ISSUE-05-03` | **Codex** | 学员报到与生命周期时限策略 | 涉及 `report_deadline` 弹性策略分支与管理员特批覆盖。 | +| `ISSUE-06-02` | **Codex** | 门禁 afterCommit 解耦下发 | **高架构要求:** 必须使用事务同步器解耦,外部失败严禁回滚。 | +| `ISSUE-07-01` | **Codex** | 课次打卡与 INV-01 防护 | **核心业务防线:** 强制校验学员班级与课次班级一致性。 | +| `ISSUE-07-02` | **Gemini Flash** | 考勤名单聚合与人工修正 | 课次应到实到名单左联与 `manual_flag = 1` 留痕。 | +| `ISSUE-08-01` | **Codex** | 成绩项目配置与 INV-04 | 权重严格总和 100% 校验与课程归属性校验。 | +| `ISSUE-08-02` | **Codex** | 成绩录入台账 INV-02/05 | **核心业务防线:** 满分上限校验、跨班打分阻断、加权总分计算。 | +| `ISSUE-09-01` | **Codex** | 外部学习记录接入与 INV-03 | **高可靠性要求:** 唯一事件 ID 查重三态幂等与跨班课程校验。 | +| `ISSUE-10-01` | **Gemini Flash** | 培训计划综合工作台 VO | 跨表只读数据组装,快速呈现大盘卡片与日程。 | +| `ISSUE-10-02` | **Codex** | 计划成效综合统计概览 | 报到率、出勤率、加权平均分多指标精准聚合与除零保护。 | +| `ISSUE-12-01` | **Codex** | P0 主链路端到端闭环验收 | **终极交付质量把关:** 串联 17 个 UC 完整业务流程验证。 | + +--- + +## 3. 研发协同与代码审查 (Review) 规范 + +1. **并行开发阶段:** + - Gemini Flash 先行完成 Wave 0 与 Wave 1 中主数据部分 (`ISSUE-01-01`, `01-03`, `01-04`, `01-05`),为后续业务提供数据实体; + - Codex 同步启动 `ISSUE-01-02` (安全脱敏) 与 `ISSUE-02-01` (计划生命周期)。 +2. **核心业务攻坚阶段 (Wave 2 ~ Wave 5):** + - Codex 集中精力攻克排课行锁 (`04-01`)、住宿防超卖 (`05-01`)、门禁解耦 (`06-02`) 以及 5 大硬不变量(INV-01 ~ INV-05); + - Gemini Flash 配合完成课表展示 (`04-02`)、配课 (`02-02`)、考勤名单 (`07-02`) 等 VO 聚合及前端视图。 +3. **代码合并准入卡点:** + - 任何涉及并发锁与硬不变量的 Codex 产出代码,必须包含对应的多线程并发单元测试或破坏性断言用例; + - 任何 Gemini Flash 产出的代码,严禁私自修改数据库 DDL 与生成器全局模板。 diff --git a/docs/issues-v1/06_acceptance_matrix.md b/docs/issues-v1/06_acceptance_matrix.md new file mode 100644 index 000000000..e5ad5c10c --- /dev/null +++ b/docs/issues-v1/06_acceptance_matrix.md @@ -0,0 +1,114 @@ +# RailwaySystem Phase 3:端到端验收矩阵与追踪表 (Acceptance Matrix) + +> **设计目标:** 建立从 PRD 业务用例 (UC) → API Contract → Issue → 后端组件 → 前端组件 → 权限字符 → 测试用例 → 验收准则的全链路双向追踪体系,确保 53 个 P0 API 100% 落实且每个硬业务规则有唯一责任人。 + +--- + +## 1. 5 大跨表硬业务不变量与 1 项报到生命周期策略责任归属矩阵 + +系统确立的 5 大跨表拓扑一致性“硬不变量”与 1 项生命周期时限策略,责任全部落实到具体的唯一主责 Issue: + +| 编号 | 规则公式 / 核心定义 | 唯一主责 Issue | 主责 Application Service | 拦截错误码 | 破坏性测试用例 | +|:---:|:---|:---:|:---:|:---:|:---| +| **INV-01** | $\text{attendance.enrollment.plan\_id} == \text{lesson.planCourse.plan\_id}$
*(严禁跨班打卡串签)* | `ISSUE-07-01` | `AttendanceAppService` | `ATTENDANCE_PLAN_MISMATCH (40080)` | `AttendanceInvariantTest.testCrossPlanCheckinBlocked` | +| **INV-02** | $\text{studentScore.enrollment.plan\_id} == \text{scoreItem.plan\_id}$
*(严禁跨班打分录入)* | `ISSUE-08-02` | `ScoreAppService` | `SCORE_PLAN_MISMATCH (40093)` | `ScoreInvariantTest.testCrossPlanScoreEntryBlocked` | +| **INV-03** | $\text{study.enrollment.plan\_id} == \text{planCourse.plan\_id}$
*(外部学习记录本期课程与班级一致)* | `ISSUE-09-01` | `LearningAppService` | `STUDY_EVENT_INVALID (40100)` | `LearningInvariantTest.testCrossPlanCourseStudyBlocked` | +| **INV-04** | $\text{scoreItem.planCourse.plan\_id} == \text{scoreItem.plan\_id}$
*(考核科目挂接课程必须属于本期计划)* | `ISSUE-08-01` | `ScoreAppService` | `SCORE_ITEM_COURSE_MISMATCH (40091)` | `ScoreConfigInvariantTest.testExternalCourseBindingBlocked` | +| **INV-05** | $0 \le \text{studentScore.score} \le \text{scoreItem.full\_score}$
*(录入分值非负且不得超过满分上限)* | `ISSUE-08-02` | `ScoreAppService` | `SCORE_EXCEEDS_FULL_SCORE (40092)` | `ScoreEntryValidationTest.testExceedFullScoreBlocked` | +| **POL-01** | $\text{report\_time} \le \text{plan.report\_deadline}$
*(报到生命周期时限策略,支持告警与强制放行)* | `ISSUE-05-03` | `ReportAppService` | `REPORT_DEADLINE_WARNING (40051)` | `ReportLifecyclePolicyTest.testOverdueWarningAndOverride` | + +--- + +## 2. 53 个 P0 API 全生命周期全链路追踪矩阵 (PRD → API → Issue → Test) + +> **覆盖率确认:** 涵盖 API Contract 正文全部 53 个 P0 API(包含 Module A 20 个、Module B 17 个、Module C 9 个、Module D 7 个),覆盖率达成 53/53 = 100%。严格杜绝任何未经评审的私自增改。 + +### 2.1 Module A:人员与基础资源 (16 个 P0 核心 API + 4 个宿舍基础 API) + +| 序号 | 来源 UC | HTTP Method & Path | 对应 Issue ID | 后端实现组件 | 前端组件 / 页面 | 权限字符 | 自动化测试类 | 验收判定条件 (Acceptance Criteria) | +|:---:|:---|:---|:---:|:---|:---|:---|:---|:---| +| 1 | - | `GET /train/teacher/list` | `ISSUE-01-01` | `TrainTeacherController.list` | `views/train/teacher/index.vue` | `train:teacher:list` | `TeacherApiTest.testList` | 分页返回教师列表,包含脱敏与职称 | +| 2 | - | `GET /train/teacher/{tId}` | `ISSUE-01-01` | `TrainTeacherController.getInfo` | `views/train/teacher/index.vue` | `train:teacher:query` | `TeacherApiTest.testGet` | 正确回显指定教师详细信息 | +| 3 | - | `POST /train/teacher` | `ISSUE-01-01` | `TrainTeacherController.add` | 教师新增弹窗组件 | `train:teacher:add` | `TeacherApiTest.testAdd` | 手机号重复抛出 40001,正常新增落库 | +| 4 | - | `PUT /train/teacher/{tId}` | `ISSUE-01-01` | `TrainTeacherController.edit` | 教师编辑弹窗组件 | `train:teacher:edit` | `TeacherApiTest.testEdit` | 正确更新教师职务与联系电话 | +| 5 | - | `GET /train/student/list` | `ISSUE-01-02` | `TrainStudentController.list` | `views/train/student/index.vue` | `train:student:list` | `StudentApiTest.testList` | 分页返回学员主档,身份证掩码脱敏展示 | +| 6 | - | `GET /train/student/{sId}` | `ISSUE-01-02` | `TrainStudentController.getInfo` | `views/train/student/index.vue` | `train:student:query` | `StudentApiTest.testGet` | 返回学员完整信息,身份证默认掩码 | +| 7 | - | `POST /train/student` | `ISSUE-01-02` | `TrainStudentController.add` | 学员录入弹窗组件 | `train:student:add` | `StudentApiTest.testAdd` | 安全散列查重防重,可逆密文存入数据库 | +| 8 | - | `GET /train/course/list` | `ISSUE-01-03` | `TrainCourseController.list` | `views/train/course/index.vue` | `train:course:list` | `CourseApiTest.testList` | 分页展示公共课程库,不含冗余版本字段 | +| 9 | - | `GET /train/course/{cId}` | `ISSUE-01-03` | `TrainCourseController.getInfo` | `views/train/course/index.vue` | `train:course:query` | `CourseApiTest.testGet` | 正确获取课程学时与教学目标 | +| 10 | - | `POST /train/course` | `ISSUE-01-03` | `TrainCourseController.add` | 课程录入弹窗组件 | `train:course:add` | `CourseApiTest.testAdd` | 课程编码重复拦截抛 40003,新增成功 | +| 11 | - | `GET /train/classroom/list` | `ISSUE-01-04` | `TrainClassroomController.list` | `views/train/classroom/index.vue` | `train:classroom:list` | `ClassroomApiTest.testList` | 分页返回教室及核定容纳人数 | +| 12 | - | `GET /train/classroom/{classRoomId}` | `ISSUE-01-04` | `TrainClassroomController.getInfo` | `views/train/classroom/index.vue` | `train:classroom:list` | `ClassroomApiTest.testGet` | 回显教室详情及绑定的班牌信息 | +| 13 | - | `POST /train/classroom` | `ISSUE-01-04` | `TrainClassroomController.add` | 教室录入弹窗组件 | `train:classroom:edit` | `ClassroomApiTest.testAdd` | 容量非负校验,正常新增教室 | +| 14 | - | `GET /train/training-site/list` | `ISSUE-01-04` | `TrainTrainingSiteController.list` | `views/train/site/index.vue` | `train:site:list` | `SiteApiTest.testList` | 分页返回实训场地(VR/3D演练区) | +| 15 | - | `GET /train/training-site/{siteId}` | `ISSUE-01-04` | `TrainTrainingSiteController.getInfo` | `views/train/site/index.vue` | `train:site:list` | `SiteApiTest.testGet` | 回显实训场地设备类型与容纳人数 | +| 16 | - | `POST /train/training-site` | `ISSUE-01-04` | `TrainTrainingSiteController.add` | 实训场地录入弹窗组件 | `train:site:edit` | `SiteApiTest.testAdd` | 场地名称查重校验,正常新增场地 | +| *17*| - | `GET /train/dormitory/list` | `ISSUE-01-05` | `TrainDormitoryController.list` | `views/train/dormitory/index.vue`| `train:dormitory:list`| `DormitoryApiTest.testList` | 分页返回房间、核定床位、已住与剩余 | +| *18*| - | `GET /train/dormitory/{dormId}` | `ISSUE-01-05` | `TrainDormitoryController.getInfo` | `views/train/dormitory/index.vue`| `train:dormitory:list`| `DormitoryApiTest.testGet` | 回显房间当前入住学员列表 | +| *19*| - | `POST /train/dormitory` | `ISSUE-01-05` | `TrainDormitoryController.add` | 宿舍录入弹窗组件 | `train:dormitory:edit`| `DormitoryApiTest.testAdd` | 同楼栋房号重复拦截抛 40006,新增成功 | +| *20*| - | `GET /train/dormitory/options` | `ISSUE-01-05` | `TrainDormitoryController.options` | 楼栋楼层级联选择器组件 | `train:dormitory:list`| `DormitoryApiTest.testOptions`| 结构化返回楼栋-楼层-房间级联树 | + +*(注:*项为 Module A 中宿舍基础维护接口,经 CG-01 裁决正式确认纳入一期 P0 范围)* + +--- + +### 2.2 Module B:开班计划与排课 (17 个 P0 核心 API) + +| 序号 | 来源 UC | HTTP Method & Path | 对应 Issue ID | 后端实现组件 | 前端组件 / 页面 | 权限字符 | 自动化测试类 | 验收判定条件 (Acceptance Criteria) | +|:---:|:---|:---|:---:|:---|:---|:---|:---|:---| +| 17 | - | `GET /train/plans` | `ISSUE-02-01` | `TrainPlanController.list` | `views/train/plan/index.vue` | `train:plan:list` | `PlanLifecycleTest.testList` | 分页返回培训班列表与生命周期状态 | +| 18 | UC-01 | `POST /train/plans` | `ISSUE-02-01` | `PlanAppService.createPlan` | 创建培训计划表单弹窗 | `train:plan:add` | `PlanLifecycleTest.testCreate`| 时间起止校验、自动生成唯一计划编号 | +| 19 | - | `GET /train/plans/{planId}` | `ISSUE-02-01` | `TrainPlanController.getInfo` | 计划详情页基本信息卡片 | `train:plan:query` | `PlanLifecycleTest.testGet` | 回显计划负责人、招生上限与周期 | +| 20 | UC-02 | `PUT /train/plans/{planId}` | `ISSUE-02-01` | `PlanAppService.updatePlan` | 编辑培训计划表单弹窗 | `train:plan:edit` | `PlanLifecycleTest.testUpdate`| 已结训状态禁止修改抛 40012,正常更新 | +| 21 | UC-29 | `POST /train/plans/{planId}/complete` | `ISSUE-02-01` | `PlanAppService.completePlan` | 计划工作台“结训归档”按钮 | `train:plan:complete`| `PlanLifecycleTest.testComplete`| 状态置为已结束,学员状态置为已完成 | +| 22 | - | `GET /train/plans/{planId}/workbench`| `ISSUE-10-01` | `PlanAppService.getWorkbench` | `views/train/plan/workbench.vue` | `train:plan:query` | `PlanWorkbenchTest.testWorkbench`| 聚合返回班级工作台驾驶舱全部指标 | +| 23 | - | `GET /train/plans/{planId}/courses` | `ISSUE-02-02` | `TrainPlanCourseController.list` | 计划详情-本期课程配置 Tab | `train:plan:query` | `PlanCourseTest.testList` | 返回本期已选课程及生成的 planCourseId | +| 24 | UC-03 | `POST /train/plans/{planId}/courses` | `ISSUE-02-02` | `PlanAppService.configureCourses` | 课程库穿梭选择器弹窗 | `train:plan:edit` | `PlanCourseTest.testConfigure`| 重复选课抛 40020,成功引入本期课程 | +| 25 | UC-06 | `GET /train/plans/{planId}/enrollments`| `ISSUE-03-01` | `EnrollmentAppService.list` | 计划详情-参训人员名录表格 | `train:enrollment:list`| `EnrollmentCoreTest.testList` | 分页返回学员参训花名册与报到状态 | +| 26 | UC-04 | `POST /train/plans/{planId}/enrollments`| `ISSUE-03-01` | `EnrollmentAppService.addSingle` | 添加参训学员弹窗组件 | `train:enrollment:add` | `EnrollmentCoreTest.testAdd` | 招生超限抛 40031,生成 enrollmentId | +| 27 | UC-05 | `POST /train/plans/{planId}/enrollments/batch`| `ISSUE-03-02` | `EnrollmentAppService.importBatch` | Excel 名单导入上传模态框 | `train:enrollment:import`| `EnrollmentBatchTest.testImport`| 解析 500 行花名册,错误行明细回显 | +| 28 | - | `GET /train/enrollments/{enrollmentId}` | `ISSUE-03-01` | `EnrollmentAppService.getDetail` | 学员参训全景卡片组件 | `train:enrollment:query`| `EnrollmentCoreTest.testGet` | 聚合回显住宿、考勤、成绩与学时总览 | +| 29 | UC-07 | `POST /train/plans/{planId}/lessons` | `ISSUE-04-01` | `SchedulingAppService.createLesson` | 排课弹窗(教师/场地/时段) | `train:lesson:add` | `LessonSchedulingTest.testCreate`| 四维冲突检测拦截,行排他锁防穿透 | +| 30 | UC-08 | `PUT /train/lessons/{lessonId}` | `ISSUE-04-01` | `SchedulingAppService.updateLesson` | 修改课次弹窗组件 | `train:lesson:edit` | `LessonSchedulingTest.testUpdate`| 已有打卡考勤记录时严禁修改上课时间 | +| 31 | UC-10 | `POST /train/lessons/check-conflict` | `ISSUE-04-01` | `SchedulingAppService.checkConflict` | 排课表单时段失焦预检组件 | `train:lesson:query` | `LessonConflictTest.testCheck` | 实时返回冲突原因与冲突课次列表 | +| 32 | UC-11 | `GET /train/plans/{planId}/timetable` | `ISSUE-04-02` | `SchedulingAppService.getTimetable` | 计划详情-完整课程表日历视图 | `train:plan:query` | `PlanTimetableTest.testTimetable`| 按日历周/日嵌套聚合课表,零失真展示 | +| 33 | UC-15 | `POST /train/enrollments/{enrollmentId}/report`| `ISSUE-05-03` | `ReportAppService.confirmReport` | 学员报到确认操作按钮 / 弹窗 | `train:report:confirm` | `ReportLifecycleTest.testReport` | 逾期未特批提示 40051,状态置为已报到 | + +--- + +### 2.3 Module C:住宿、门禁与考勤 (9 个 P0 核心 API) + +| 序号 | 来源 UC | HTTP Method & Path | 对应 Issue ID | 后端实现组件 | 前端组件 / 页面 | 权限字符 | 自动化测试类 | 验收判定条件 (Acceptance Criteria) | +|:---:|:---|:---|:---:|:---|:---|:---|:---|:---| +| 34 | UC-12 | `POST /train/plans/{planId}/accommodations/preallocate`| `ISSUE-05-01` | `AccommodationAppService.preallocate` | 批量预分配宿舍策略弹窗 | `train:accommodation:assign` | `AccommodationLockTest.testPrealloc` | `SELECT FOR UPDATE` 排他锁防超卖,严格性别隔离 | +| 35 | UC-13 | `PUT /train/enrollments/{enrollmentId}/accommodation` | `ISSUE-05-02` | `AccommodationAppService.adjust` | 单人调房 Dialog 弹窗 | `train:accommodation:adjust` | `AccommodationAdjustTest.testAdjust` | 目标房间满员抛 40062,跨性别调房拦截 | +| 36 | - | `PUT /train/enrollments/{enrollmentId}/accommodation/day-student`| `ISSUE-05-02` | `AccommodationAppService.setDayStudent` | 设置走读操作按钮 | `train:accommodation:adjust` | `AccommodationAdjustTest.testDay` | 走读设置成功,立即释放已占用房间床位 | +| 37 | UC-14 | `GET /train/plans/{planId}/accommodations` | `ISSUE-05-02` | `AccommodationAppService.list` | 计划详情-住宿分配台账列表 | `train:accommodation:list` | `AccommodationListTest.testList` | 分页返回全班住宿床位与门禁下发状态 | +| 38 | UC-16 | `POST /train/accommodations/{accommodationId}/auth/sync`| `ISSUE-06-02` | `ExternalIntegrationAppService.syncDoor` | 住宿列表“重试门禁同步”按钮 | `train:dormauth:sync` | `DoorAuthAsyncTest.testRetrySync` | 事务外调用 Adapter,失败更新状态为 2 | +| 39 | UC-17 | `POST /train/attendance/sign-in` | `ISSUE-07-01` | `AttendanceAppService.recordSignIn` | 班牌刷脸模拟回调接口 | `train:attendance:checkin` | `AttendanceInvariantTest.testSignIn` | **强制落实 INV-01 拦截跨班串签**,幂等查重 | +| 40 | UC-18 | `POST /train/attendance/sign-out` | `ISSUE-07-01` | `AttendanceAppService.recordSignOut` | 班牌签退刷脸模拟回调接口 | `train:attendance:checkin` | `AttendanceSignInTest.testSignOut` | 课次签退落库,自动计算出勤考勤结论 | +| 41 | UC-19 | `PUT /train/attendance/{checkinId}` | `ISSUE-07-02` | `AttendanceAppService.adjustAttendance` | 考勤异常修改 Dialog 弹窗 | `train:attendance:edit` | `AttendanceAdjustTest.testAdjust` | 修改考勤结论,强制标记 `manual_flag = 1` | +| 42 | - | `GET /train/lessons/{lessonId}/attendance` | `ISSUE-07-02` | `AttendanceAppService.getLessonRoster` | 教师端-课次考勤名单抽屉组件 | `train:attendance:list` | `AttendanceRosterTest.testRoster` | 左联花名册,准确显示应到/实到与缺勤名单 | + +--- + +### 2.4 Module D:成绩、学习与统计 (7 个 P0 核心 API) + +| 序号 | 来源 UC | HTTP Method & Path | 对应 Issue ID | 后端实现组件 | 前端组件 / 页面 | 权限字符 | 自动化测试类 | 验收判定条件 (Acceptance Criteria) | +|:---:|:---|:---|:---:|:---|:---|:---|:---|:---| +| 43 | - | `GET /train/plans/{planId}/score-items` | `ISSUE-08-01` | `ScoreAppService.getScoreItems` | 计划详情-考核科目配置表格 | `train:score:query` | `ScoreConfigTest.testList` | 返回本期考核项目、满分值与百分比权重 | +| 44 | UC-20 | `POST /train/plans/{planId}/score-items` | `ISSUE-08-01` | `ScoreAppService.configureScoreItems` | 成绩项目配置与权重保存按钮 | `train:score:config` | `ScoreConfigInvariantTest.testConfig` | **落实 INV-04 课程归属**,权重总和必须 100% | +| 45 | UC-21 | `POST /train/scores/entry` | `ISSUE-08-02` | `ScoreAppService.recordScores` | 成绩台账矩阵单元格打分输入 | `train:score:edit` | `ScoreInvariantTest.testScoreEntry` | **落实 INV-02 跨班与 INV-05 满分上限拦截** | +| 46 | - | `GET /train/plans/{planId}/scores/matrix` | `ISSUE-08-02` | `ScoreAppService.getScoreMatrix` | 计划详情-全班成绩台账大表 | `train:score:query` | `ScoreMatrixTest.testMatrix` | 动态行转列呈现科目,准确计算加权综合分 | +| 47 | UC-23 | `GET /train/enrollments/{enrollmentId}/scores` | `ISSUE-08-02` | `ScoreAppService.getStudentScoreReport` | 学员个人成绩单卡片弹窗 | `train:score:query` | `ScoreReportTest.testStudentScores` | 呈现单人各科目得分、满分、权重与加权总分 | +| 48 | UC-24 | `POST /train/study-records/receive` | `ISSUE-09-01` | `LearningAppService.receiveRecord` | 外部系统实训学时推送端点 | 系统放行 / 签名验证 | `LearningInvariantTest.testReceive` | **落实 INV-03 课程计划一致**,防重事件 ID 幂等三态响应 | +| 49 | UC-26 | `GET /train/plans/{planId}/statistics/overview` | `ISSUE-10-02` | `StatisticsAppService.getOverview` | 计划详情-培训成效统计看板 | `train:statistics:query` | `PlanStatisticsTest.testOverview` | 聚合报到率、出勤率、加权平均分与总实训学时 | + +--- + +## 3. 验收追踪闭环结论 + +- **PRD 用例追踪:** 17 个 P0 核心用例全部精确映射到对应的 API、Issue 与自动化测试类; +- **API 覆盖闭环:** 53 个 P0 API 无一遗漏全部被 Issue 认领(53/53,100% 覆盖率); +- **规则硬边界:** 5 项跨表硬不变量由 `ISSUE-07-01`、`ISSUE-08-01`、`ISSUE-08-02`、`ISSUE-09-01` 唯一主责承揽,1 项生命周期时限策略由 `ISSUE-05-03` 唯一承揽; +- **质量闸门保证:** 每一个 Issue 均配备了严格对应的自动化测试类和基于 Given/When/Then 的判定条件,彻底消除了模糊验收隐患。 diff --git a/docs/issues-v1/07_p1_p2_backlog.md b/docs/issues-v1/07_p1_p2_backlog.md new file mode 100644 index 000000000..7d17827d1 --- /dev/null +++ b/docs/issues-v1/07_p1_p2_backlog.md @@ -0,0 +1,112 @@ +# RailwaySystem Phase 3:P1 / P2 需求储备池 (Backlog Register) + +> **最高原则:** 本轮研发聚焦于 53 个 P0 API 的最小闭环,**严禁将任何 P1 或 P2 需求混入当前研发排期与测试范围**。 +> **文档定位:** 本文档作为系统后续演进阶段(Phase 4 / Phase 5)的正式需求储备池,确保设计资产连续,防止需求流失。 +> **数据基线:** 严格对齐 API Contract V1.0.1 真实物理端点定义,P1 共 34 个,P2 共 2 个,合计 36 个非核心接口全部封存入库。 + +--- + +## 1. P1 / P2 规模与模块分布统计 + +API Contract V1.0.1 中定义的 89 个 API 优先级分布为: +- **P0 (核心闭环,本轮必做):** 53 个 +- **P1 (辅助支撑,登记备查):** 34 个 +- **P2 (待现场确认,远期建设):** 2 个 + +```mermaid +pie title API 全局物理端点分布 (总数 89) + "P0 核心闭环 (本轮开发)" : 53 + "P1 辅助能力 (Backlog 储备)" : 34 + "P2 远期待定 (现场确认)" : 2 +``` + +| 业务模块 | 物理总数 | P0 数量 | P1 数量 | P2 数量 | P1/P2 规划定位 | +|:---|:---:|:---:|:---:|:---:|:---| +| **Module A 人员与基础资源** | 38 | 20 | 18 | 0 | 主数据全功能闭环、资源载体绑定、班牌硬件入库 | +| **Module B 开班计划与排课** | 24 | 17 | 6 | 1 | 计划反向删除/移除、教师个人课表、学员移动端门户 | +| **Module C 住宿/门禁/考勤** | 13 | 9 | 4 | 0 | 房态实时看板、门禁下发流水、考勤人工补录、考勤综合汇总 | +| **Module D 成绩/学习/统计/画像** | 14 | 7 | 6 | 1 | 成绩Excel导入、学习记录流水、学员训练详情、学员综合画像、成绩下钻分布 | +| **合计** | **89** | **53** | **34** | **2** | **P1 (34) + P2 (2) = 36 全部封存入库** | + +--- + +## 2. 34 个 P1 辅助能力清单 (Backlog Register) + +### 2.1 Module A:人员与基础资源辅助能力 (18 个 API) + +| 序号 | HTTP Method & Path | API 业务描述 | 所属领域 | 计划演进阶段 | +|:---:|:---|:---|:---:|:---:| +| 1 | `PUT /train/teacher/{tId}/status` | 修改教师启停用状态 (0停用, 1启用) | 教师管理 | Phase 4 基础功能增强 | +| 2 | `PUT /train/student/{sId}` | 修改学员主档基础信息 | 学员管理 | Phase 4 基础功能增强 | +| 3 | `POST /train/student/import` | 学员库 Excel 批量全局导入 | 学员管理 | Phase 4 基础功能增强 | +| 4 | `PUT /train/course/{cId}` | 修改公共课程库基础资料 | 课程库 | Phase 4 基础功能增强 | +| 5 | `POST /train/course/{cId}/resources` | 课程绑定教学资源 (文档/视频/VR) | 课程资源 | Phase 4 教学资源深化 | +| 6 | `DELETE /train/course/{cId}/resources/{resourceId}` | 课程解绑教学资源 | 课程资源 | Phase 4 教学资源深化 | +| 7 | `GET /train/resource/list` | 分页查询教学资源列表 | 教学资源 | Phase 4 教学资源深化 | +| 8 | `GET /train/resource/{resourceId}` | 获取指定教学资源详情与预览地址 | 教学资源 | Phase 4 教学资源深化 | +| 9 | `POST /train/resource` | 新增教学资源 (支持文档、视频、VR、3D、链接) | 教学资源 | Phase 4 教学资源深化 | +| 10 | `PUT /train/resource/{resourceId}` | 修改教学资源描述与类型 | 教学资源 | Phase 4 教学资源深化 | +| 11 | `DELETE /train/resource/{resourceId}` | 删除教学资源 (引用时校验阻断) | 教学资源 | Phase 4 教学资源深化 | +| 12 | `PUT /train/classroom/{classRoomId}` | 修改教室容纳人数与位置 | 教室管理 | Phase 4 基础功能增强 | +| 13 | `PUT /train/training-site/{siteId}` | 修改实训场地设备类型与容量 | 实训场地 | Phase 4 基础功能增强 | +| 14 | `GET /train/electronic-board/list` | 分页查询电子班牌硬件设备列表 | 电子班牌 | Phase 4 硬件运维管理 | +| 15 | `GET /train/electronic-board/{boardId}` | 获取电子班牌绑定教室与网络配置 | 电子班牌 | Phase 4 硬件运维管理 | +| 16 | `POST /train/electronic-board` | 注册录入电子班牌 SN 与网络端点 | 电子班牌 | Phase 4 硬件运维管理 | +| 17 | `PUT /train/electronic-board/{boardId}` | 修改电子班牌参数配置 | 电子班牌 | Phase 4 硬件运维管理 | +| 18 | `PUT /train/dormitory/{dormId}` | 修改宿舍房间核定容量与状态 (容量不可小于当前已住人数) | 宿舍管理 | Phase 4 基础功能增强 | + +--- + +### 2.2 Module B:开班计划与排课辅助能力 (6 个 API) + +| 序号 | HTTP Method & Path | API 业务描述 | 所属领域 | 计划演进阶段 | +|:---:|:---|:---|:---:|:---:| +| 19 | `DELETE /train/plans/{planId}/courses/{planCourseId}` | 移除本期课程 (已有排课时强校验阻断) | 选课管理 | Phase 4 计划运维 | +| 20 | `PUT /train/enrollments/{enrollmentId}/status` | 手工变更参训状态 (2请假, 3放弃) | 参训管理 | Phase 4 学员生命周期 | +| 21 | `DELETE /train/enrollments/{enrollmentId}` | 移除参训人员 (已有打卡或成绩时阻断) | 参训管理 | Phase 4 学员生命周期 | +| 22 | `DELETE /train/lessons/{lessonId}` | 删除排课课次 (已有考勤打卡记录时阻断) | 排课管理 | Phase 4 教学日程调整 | +| 23 | `GET /train/teacher/{tId}/timetable` | 获取指定教师跨班个人周课表日历 | 课表视图 | Phase 4 教师协同服务 | +| 24 | `GET /train/plans/{planId}/reports` | 查询全班学员报到流水详细明细列表 | 报到管理 | Phase 4 报到精细运维 | + +--- + +### 2.3 Module C:住宿、门禁与考勤辅助能力 (4 个 API) + +| 序号 | HTTP Method & Path | API 业务描述 | 所属领域 | 计划演进阶段 | +|:---:|:---|:---|:---:|:---:| +| 25 | `GET /train/dormitory/occupancy-board` | 宿舍楼层网格实时房态占用看板 | 住宿管理 | Phase 4 宿管大屏可视化 | +| 26 | `GET /train/plans/{planId}/dorm-auths` | 查询全班门禁授权下发流水与下发状态 | 门禁同步 | Phase 4 硬件运维管理 | +| 27 | `POST /train/attendance/manual-add` | 人工直接补录课次漏打卡考勤记录 | 考勤管理 | Phase 4 考勤特批运维 | +| 28 | `GET /train/plans/{planId}/attendance/summary` | 查询计划考勤汇总概况与缺勤率统计 | 考勤统计 | Phase 4 统计报表增强 | + +--- + +### 2.4 Module D:成绩、学习、统计与画像辅助能力 (6 个 API) + +| 序号 | HTTP Method & Path | API 业务描述 | 所属领域 | 计划演进阶段 | +|:---:|:---|:---|:---:|:---:| +| 29 | `POST /train/plans/{planId}/scores/import` | Excel 批量导入全班单科成绩 | 成绩管理 | Phase 4 成绩快速录入 | +| 30 | `GET /train/plans/{planId}/study-records` | 多维分页查询全班外部自主实训流水 | 学习记录 | Phase 4 实训记录审计 | +| 31 | `GET /train/enrollments/{enrollmentId}/study-records` | 查询单学员自主实训时长明细流水 | 学习记录 | Phase 4 学员档案深化 | +| 32 | `GET /train/enrollments/{enrollmentId}/training-detail` | 查询学员个人全流程训练详情 (出勤/单科成绩/实训流水) | 综合成效 | Phase 4 学员档案深化 | +| 33 | `POST /train/enrollments/{enrollmentId}/portrait/generate` | 结课前后手动触发 / 重新刷新生成学员简单画像 | 学员画像 | Phase 4 画像计算引擎 | +| 34 | `GET /train/enrollments/{enrollmentId}/portrait` | 查询学员综合画像数据 (弹性指标集与标签集展示) | 学员画像 | Phase 4 学员多维分析 | + +--- + +## 3. 2 个 P2 远期/待现场确认能力 (Deferred Register) + +以下 2 个 API 明确属于 PRD V-01 ~ V-14 现场确认范围或面向移动端/学员端的远期接口,**严禁在一期管理端闭环中盲目推进**: + +| 序号 | HTTP Method & Path | 接口名称与业务目的 | 暂缓原因与现场确认事项 | 激活准入条件 | +|:---:|:---|:---|:---|:---| +| **P2-01** | `GET /train/student/my-training` | 学员端移动端个人当前培训日程与成绩查看 | 属于面向学员独立移动端/小程序的门户接口,目前一期定位于基地管理端运转。 | 现场完成学员端 UI 规划与移动端技术选型确认。 | +| **P2-02** | `GET /train/plans/{planId}/statistics/score-distribution` | 计划成绩分段直方图与下钻区间统计 | PRD V-07/V-08 明确提出“是否设立固定分数线或区间待现场确认”,避免过度预设区间。 | 现场最终确认成绩分段规则与及格线标准。 | + +--- + +## 4. Backlog 演进策略建议 + +1. **版本冻结铁律:** 在 Phase 3 研发及后续集成验收通过之前,上述 36 个 P1/P2 项严禁立项开工; +2. **Phase 4 准入:** 当 Phase 3 验收通过,且基地管理员试运行 P0 主链路顺畅后,优先开放 Phase 4 中的“基础数据修改”与“Excel 批量导入/流水查询”类辅助项; +3. **Phase 5 准入:** 学员画像深度计算与学员移动端入口统一在 Phase 5 根据甲方现场业务实测反馈决定上线节奏。 diff --git a/docs/issues-v1/08_risk_register.md b/docs/issues-v1/08_risk_register.md new file mode 100644 index 000000000..0961c82cd --- /dev/null +++ b/docs/issues-v1/08_risk_register.md @@ -0,0 +1,177 @@ +# RailwaySystem Phase 3:技术与业务风险登记册 (Risk Register) + +> **编制目标:** 系统性识别 Phase 3 实施过程中涉及的高并发锁冲突、事务一致性、外部系统防腐解耦、权限收敛以及契约偏差风险,制定清晰的预防策略与应急预案。 + +--- + +## 1. 核心风险总览矩阵 + +| 风险编号 | 风险类别 | 风险描述与潜在影响 | 发生概率 | 影响程度 | 风险等级 | 应对策略 | 唯一责任 Issue | +|:---:|:---:|:---|:---:|:---:|:---:|:---|:---:| +| **RSK-01** | 并发冲突 | 多管理员同时对同一位教师/场地排课,导致教学冲突穿透 | 中 | 高 | **重大** | 稳定主资源行锁 (`Teacher` + `Classroom/Site` `SELECT ... FOR UPDATE`) + 三类资源时段区间重叠强校验 | `ISSUE-04-01` | +| **RSK-02** | 并发超卖 | 住宿分房并发请求先查空房再插入,导致房间超载超员 | 中 | 高 | **重大** | 锁定稳定宿舍主表行 (`train_dormitory_info FOR UPDATE`) + 周期重叠明细统计,杜绝空房 phantom 超售 | `ISSUE-05-01` | +| **RSK-03** | 事务长阻塞 | 门禁外部 HTTP 调用置于数据库事务内,网络抖动拖垮连接池 | 高 | 极高 | **严重** | 严禁大事务包裹外部调用,采用 `afterCommit` 事务外解耦触发 | `ISSUE-06-02` | +| **RSK-04** | 数据串线 | 跨班学员考勤/打分/学习记录串线,破坏多班级拓扑一致性 | 低 | 极高 | **严重** | Service 层硬编码落实 5 大跨表硬不变量 (INV-01~05) | `ISSUE-07-01` 等 | +| **RSK-05** | 权限扩散 | 历史遗留 `system:teacher:*` 权限字符扩散,导致鉴权混乱 | 中 | 中 | **中等** | 统一收敛为 `train:*`,前端与后端严格对齐 | `ISSUE-11-01` | +| **RSK-06** | 字典冲突 | 盲目执行 `007_dicts.sql` 导致与生产/若依内置字典主键冲突 | 低 | 高 | **重大** | 严禁在本轮物理执行 007,输出架构分类方案并采用 NOT EXISTS 幂等 | `ISSUE-11-02` | +| **RSK-07 (CG-01)** | 契约偏差 | Module A 头部声明 16 个与正文 20 个 P0 标注存在矛盾 | 高 | 低 | **RESOLVED** | 技术负责人已裁决:详细API标记优先于错误摘要统计;P0正式集合以53个逐API标记为准。 | `ISSUE-01-05` (已完整承揽) | +| **RSK-08 (CG-02)** | 契约偏差 | 课次详情单查接口缺失,PlanTimetableVO 字段不足支撑编辑回显 | 中 | 低 | **RESOLVED** | 技术负责人已裁决:通过扩展 TimetableLessonItemVO 补齐回显字段解决;不新增 Lesson 详情 API。 | `ISSUE-04-01` / `04-02` | + +--- + +## 2. 关键技术风险深度分析与技术对策 + +### 2.1 排课稳定主资源行锁与冲突检测策略 (RSK-01) +- **业务场景:** 两个培训班在同一时间段内均选聘了同一位教师授课,或者两名教务管理员在不同终端同时为不同课程预约了同一间普通教室(或实训场地)。 +- **技术隐患:** 普通行锁无法锁住“尚不存在的时段重叠 Lesson 行”。若直接执行 `SELECT ... FROM train_lesson WHERE ... FOR UPDATE`,在未冲突时返回空集,普通行锁失效;事务 A 与事务 B 同时读取空结果,随后分别 INSERT,导致物理冲突穿透。 +- **强制工程对策:** + 1. 排课事务必须标注 `@Transactional(rollbackFor = Exception.class)`; + 2. **锁定稳定主资源:** 创建/修改 Lesson 时,事务内必须先对所涉及的实际主资源行加排他锁(通过主键锁定已存在的物理主表行): + ```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. **编辑时的新旧资源锁顺序:** 修改 Lesson 若发生教师或场地变更,必须同时对旧资源与新资源加锁;为防止交叉死锁,强制按 `resourceType + ":" + resourceId` 字典序升序依次获取行锁; + 5. **课次编辑硬前置拦截:** 修改课次时,若该课次已有学员考勤签到记录,系统强行阻断时段修改。 + +--- + +### 2.2 宿舍容量防超卖与稳定宿舍主表行锁策略 (RSK-02) +- **业务场景:** 宿舍 201 核定容量 4 人,当前剩余床位 1 个;或全新空房首次分配。两位管理员并发为两名学员执行分房或调房。 +- **技术隐患:** 若依赖“当前已有住宿明细”加锁,在空房或少人场景下查询结果不足以封锁并发写入,导致幻读并发穿透,造成房间超员或性别混住。 +- **强制工程对策:** + 1. 严禁以前端查询结果作为容量判断依据;严禁仅对 `train_accommodation` 明细行加锁; + 2. **锁定稳定宿舍主表行:** 批量预分配或单人调房事务开始,必须先对目标宿舍主表行加排他行锁: + ```sql + SELECT dorm_id, capacity, gender_type + FROM train_dormitory_info + WHERE dorm_id = :dormId FOR UPDATE; + ``` + 3. **计算培训周期重叠内的有效占用人数:** + ```sql + SELECT COUNT(*) FROM train_accommodation + WHERE dorm_id = :dormId + AND accommodation_status IN (0, 1) + AND NOT (end_date <= :planStartDate OR start_date >= :planEndDate); + ``` + 4. 校验 `(occupiedCount + requestCount) <= capacity`,一旦超员立即抛出 `DORM_ROOM_FULL (40062)` 并回滚; + 5. 严格核验学员性别与房间 `gender_type`,杜绝男女混住隐患。 + +--- + +### 2.3 外部第三方网络调用与数据库连接池隔离 (RSK-03) +- **业务场景:** 门禁下发需要向外部物理门禁控制机发送 HTTP 请求;电子班牌需推送课程日程;外部实训系统推送学时。 +- **技术隐患:** 外部硬件网络经常存在几秒甚至几十秒的超时。若在 `@Transactional` 本地事务中发起长网络 HTTP 请求,将导致本地数据库连接被长时间挂起占用,瞬间耗尽 HikariCP 连接池,引发系统雪崩。 +- **强制工程对策:** + 1. **铁律:数据库短事务严禁包裹外部第三方 HTTP/网络长调用!** + 2. 使用 Spring 事务同步器(`TransactionSynchronizationManager`)在本地事务 COMMIT 成功后,通过 `afterCommit` 回调异步或在事务外触发 Adapter: + ```java + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + doorAccessAdapter.grantAccess(cmd); + } + }); + ``` + 3. 单次外部请求设置硬超时阈值(5 秒); + 4. 外部调用失败绝对不可回滚已提交的本地业务事务,而是更新 `face_sync_status = 2` 并记录 `last_error`,提供管理页面手动重试按钮; + 5. 一期明确禁止引入 MQ、Kafka、Outbox 模式,保持架构极简可靠。 + +--- + +### 2.4 跨班硬一致性破坏与串线风险 (RSK-04) +- **业务场景:** 学员 A 参加 1 班培训,打卡时误刷了 2 班的课次;或教师录入成绩时将 1 班科目的分值打在了 2 班学员的档案上。 +- **技术隐患:** 数据库物理外键仅约束了 ID 的存在性,无法感知“班级”拓扑。一旦串线,将导致整个统计报表与学员档案全面污染。 +- **强制工程对策:** + 1. 全面落实 5 大跨表硬业务不变量(INV-01 ~ INV-05); + 2. 每一条不变量由唯一的 Application Service 负责强制校验(详见 [`06_acceptance_matrix.md`](file:///Users/wzb/Desktop/%E9%A1%B9%E7%9B%AE/RailwaySystem/docs/issues-v1/06_acceptance_matrix.md)); + 3. 任何违背不变量的操作直接抛出全局业务异常(如 `ATTENDANCE_PLAN_MISMATCH (40080)`),从根源杜绝脏数据。 + +--- + +## 3. 契约偏差 (Contract Gap) 正式裁决与闭环 + +### 3.1 CG-01 正式裁决落地 (Module A P0 统计矛盾) + +- **最终裁决状态:** **RESOLVED** +- **技术负责人正式裁决结论:** + - **详细 API 标记优先于错误摘要统计;P0 正式集合以 53 个逐 API 标记为准。** + - 宿舍 4 个基础维护 API(列表、详情、新增、选项)属于一期 P0,因为 Accommodation 住宿主流程强依赖可维护的宿舍房间基础资源、核定容量与性别隔离属性。 +- **实施落实:** + - Module A P0 正式确认修正为 **20 个**; + - 全局 P0 正式集合确定为 **53 个**(Module A: 20, Module B: 17, Module C: 9, Module D: 7); + - `ISSUE-01-05` 完整承揽宿舍基础管理 4 个 API,架构与 Issue 数量保持 26 个完全不变; + - P0 API 覆盖率达成:**53 / 53 = 100%**。 + +#### Module A 38 个 API 裁决执行对照表 + +| 序号 | Method | Path | Contract中的P等级 | 是否主流程前置必须 | 关联Issue | 正式P等级 | 裁决依据与落地说明 | +|:---:|:---|:---|:---:|:---:|:---:|:---:|:---| +| 1 | GET | `/train/teacher/list` | P0 | 是 | `ISSUE-01-01` | **P0** | 排课选教师前置必须,主链路不可缺少 | +| 2 | GET | `/train/teacher/{tId}` | P0 | 是 | `ISSUE-01-01` | **P0** | 教师详情回显,编辑与主档核查必须 | +| 3 | POST | `/train/teacher` | P0 | 是 | `ISSUE-01-01` | **P0** | 录入基础教师数据必须 | +| 4 | PUT | `/train/teacher/{tId}` | P0 | 是 | `ISSUE-01-01` | **P0** | 教师联系方式/职务维护必须 | +| 5 | PUT | `/train/teacher/{tId}/status` | P1 | 否 | - | P1 | 启停用管理,非 MVP 开班排课必经路径 | +| 6 | GET | `/train/student/list` | P0 | 是 | `ISSUE-01-02` | **P0** | 参训选人、学员检索主链路必须 | +| 7 | GET | `/train/student/{sId}` | P0 | 是 | `ISSUE-01-02` | **P0** | 学员详情回显必须 | +| 8 | POST | `/train/student` | P0 | 是 | `ISSUE-01-02` | **P0** | 学员主数据手工建档必须 | +| 9 | PUT | `/train/student/{sId}` | P1 | 否 | - | P1 | 学员主档修改,可由名单导入覆盖 | +| 10 | POST | `/train/student/import` | P1 | 否 | - | P1 | 全局学员库导入,培训班级直接用花名册批量导入 | +| 11 | GET | `/train/course/list` | P0 | 是 | `ISSUE-01-03` | **P0** | 培训计划引入课程必须 | +| 12 | GET | `/train/course/{cId}` | P0 | 是 | `ISSUE-01-03` | **P0** | 课程详情回显必须 | +| 13 | POST | `/train/course` | P0 | 是 | `ISSUE-01-03` | **P0** | 录入公共课程必须 | +| 14 | PUT | `/train/course/{cId}` | P1 | 否 | - | P1 | 课程修改,属于运营维护功能 | +| 15 | POST | `/train/course/{cId}/resources` | P1 | 否 | - | P1 | 课程关联资源,非开班排课硬依赖 | +| 16 | DELETE | `/train/course/{cId}/resources/{resourceId}` | P1 | 否 | - | P1 | 课程解绑资源,非开班排课硬依赖 | +| 17 | GET | `/train/resource/list` | P1 | 否 | - | P1 | 教学资源库列表,辅助教学功能 | +| 18 | GET | `/train/resource/{resourceId}` | P1 | 否 | - | P1 | 教学资源详情,辅助教学功能 | +| 19 | POST | `/train/resource` | P1 | 否 | - | P1 | 新增教学资源,辅助教学功能 | +| 20 | PUT | `/train/resource/{resourceId}` | P1 | 否 | - | P1 | 修改教学资源,辅助教学功能 | +| 21 | DELETE | `/train/resource/{resourceId}` | P1 | 否 | - | P1 | 删除教学资源,辅助教学功能 | +| 22 | GET | `/train/classroom/list` | P0 | 是 | `ISSUE-01-04` | **P0** | 排课选普通教室必须 | +| 23 | GET | `/train/classroom/{classRoomId}` | P0 | 是 | `ISSUE-01-04` | **P0** | 教室详情与核定人数核查必须 | +| 24 | POST | `/train/classroom` | P0 | 是 | `ISSUE-01-04` | **P0** | 录入教室基础数据必须 | +| 25 | PUT | `/train/classroom/{classRoomId}` | P1 | 否 | - | P1 | 教室维护,非首批开班硬依赖 | +| 26 | GET | `/train/training-site/list` | P0 | 是 | `ISSUE-01-04` | **P0** | 排课选实训演练场地必须 | +| 27 | GET | `/train/training-site/{siteId}` | P0 | 是 | `ISSUE-01-04` | **P0** | 实训场地详情回显必须 | +| 28 | POST | `/train/training-site` | P0 | 是 | `ISSUE-01-04` | **P0** | 录入实训场地必须 | +| 29 | PUT | `/train/training-site/{siteId}` | P1 | 否 | - | P1 | 实训场地维护,非首批硬依赖 | +| 30 | GET | `/train/electronic-board/list` | P1 | 否 | - | P1 | 班牌设备管理,非业务流程必须 | +| 31 | GET | `/train/electronic-board/{boardId}` | P1 | 否 | - | P1 | 班牌详情,非业务流程必须 | +| 32 | POST | `/train/electronic-board` | P1 | 否 | - | P1 | 注册班牌,非业务流程必须 | +| 33 | PUT | `/train/electronic-board/{boardId}` | P1 | 否 | - | P1 | 修改班牌,非业务流程必须 | +| 34 | GET | `/train/dormitory/list` | P0 | 是 | `ISSUE-01-05` | **P0** | 宿舍房态列表,分房/调房选择必须,裁决确认 P0 | +| 35 | GET | `/train/dormitory/{dormId}` | P0 | 是 | `ISSUE-01-05` | **P0** | 宿舍详情与当前住户回显,裁决确认 P0 | +| 36 | POST | `/train/dormitory` | P0 | 是 | `ISSUE-01-05` | **P0** | 录入宿舍房间基础数据必须,裁决确认 P0 | +| 37 | PUT | `/train/dormitory/{dormId}` | P1 | 否 | - | P1 | 修改宿舍房间,维持 P1 | +| 38 | GET | `/train/dormitory/options` | P0 | 是 | `ISSUE-01-05` | **P0** | 楼栋-楼层-房间级联下拉,分房交互必须,裁决确认 P0 | + +--- + +### 3.2 CG-02 正式裁决落地 (课次详情单查接口缺失评估) + +- **最终裁决状态:** **RESOLVED** +- **技术负责人正式裁决结论:** + - **通过扩展 TimetableLessonItemVO 解决;不新增 Lesson 详情 API。** + - 严禁私自新增 `GET /train/lessons/{lessonId}` 端点,保持 API Contract 接口数量稳定。 +- **实施落实:** + - 已在 `docs/api-v2/06_dto_vo_catalog.md` 中补齐 `TimetableLessonItemVO` 的关键外键关联字段:`planCourseId`、`tId`、`venueType`、`classRoomId`、`classRoomName`、`siteId`、`siteName` 与 `remark`; + - 前端通过已有的 `GET /train/plans/{planId}/timetable`(完整课表 VO)读取当前 Lesson 项并完整回显编辑表单,随后直接调用既有的 `PUT /train/lessons/{lessonId}` 完成更新,主链路完全闭环。 + +--- + +## 4. 风险审查与日常跟踪机制 + +1. **每日构建与自动化测试:** 每日 CI 流水线必须执行 `P0BusinessFlowIntegrationTest`,自动验证 5 大硬不变量及并发冲突破坏性用例; +2. **代码审查红线卡点:** + - 审查排课代码必须核对是否包含主资源 `FOR UPDATE` 行锁; + - 审查住宿代码必须核对是否包含稳定宿舍主表行锁与原子容量核验; + - 审查外部调用代码必须核对是否位于事务外(`afterCommit` 触发)。 +3. **现场确认事项隔离:** 涉及及格线判定、学员画像复杂标签、报到逾期模式等待现场确认事项,后端一律预留弹性枚举与参数配置,严禁硬编码。 diff --git a/docs/issues-v1/09_issue_review_round1.md b/docs/issues-v1/09_issue_review_round1.md new file mode 100644 index 000000000..815b72e8b --- /dev/null +++ b/docs/issues-v1/09_issue_review_round1.md @@ -0,0 +1,195 @@ +# RailwaySystem Phase 3:Issue Review Round 1 评审整改报告 (Issue Review Round 1 Report) + +> **编制说明:** 针对技术负责人在 Phase 3 初审中作出的 `Phase 3 Issue Review = B` 判定与整改要求,本报告逐项记录 Git 分支基线修正 (IR-01)、P0 API 集合一致性核验 (IR-02)、代码生成器策略修正 (IR-03)、身份证安全算法边界规范 (IR-04)、排课/住宿并发锁模型强化 (IR-05) 以及两项契约偏差 (CG-01, CG-02) 的核查与落地整改结果。 + +--- + +## 一、整改事项与评审状态总览表 + +| 编号 | 评审项类别 | 事项描述 | 整改前状态 | 本轮整改判定 | 核心结论与处置动作 | +|:---:|:---:|:---|:---:|:---:|:---| +| **IR-01** | 基线管理 | 分支基线错误(原在 `feat/cicd-pipeline`) | 错误 | **RESOLVED** | 切回 `main` 分支,快进至 `67543e04` (`api-v2-contract-v1.0`),确认其为 HEAD 祖先,工作区保持纯未跟踪文档状态。 | +| **IR-02** | 契约一致性 | P0 API Contract 集合核验与“最大化覆盖”越权纠正 | 偏差 | **RESOLVED** | 从 Tag `api-v2-contract-v1.0` 抽取 53 处正文 P0 标注,比对 `comm -3` 差异为 0;废止主观定性,声明覆盖率待 CG-01 裁决。 | +| **IR-03** | 工程统计 | 代码生成器生成策略统计错误 (10/12 偏差) | 错误 | **RESOLVED** | 重新核定 `ISSUE-05-02` 为纯业务状态维护 (NO),机器断言通过:YES: 4, PARTIAL: 11, NO: 11 == 26 Total。 | +| **IR-04** | 安全架构 | 身份证算法越界(提前写死 AES/HMAC 等具体算法) | 越界 | **RESOLVED** | 全局清除具体算法名称;统一沿用 Database V2 规范 (`ciphertext`, `hash`, `last4`),具体算法方案留待 Security Review 裁定。 | +| **IR-05** | 并发模型 | 排课与住宿并发锁语义不足(普通查询行锁无法防穿透) | 不足 | **RESOLVED** | 排课改为锁定稳定主资源 (`Teacher` + `Classroom/Site` 行锁);住宿改为锁定稳定宿舍主表行 (`train_dormitory_info` 行锁)。 | +| **CG-01** | 契约偏差 | Module A 头部 16 个与正文 20 个 P0 标注矛盾 | 擅自闭环 | **RESOLVED** | 技术负责人裁决:详细API标记优先于错误摘要统计;P0正式集合以53个逐API标记为准,覆盖率 53/53 = 100%。 | +| **CG-02** | 契约偏差 | 课次详情单查接口缺失,PlanTimetableVO 字段是否足额 | 假定闭环 | **RESOLVED** | 技术负责人裁决:通过扩展 TimetableLessonItemVO 补齐回显字段解决;不新增 Lesson 详情 API。 | +| **CG-03** | 契约计数 | API物理端点计数与模块摘要偏差 (91 vs 89) | 偏差 | **RESOLVED** | 技术负责人裁决:物理Endpoint定义优先于摘要计数。API Contract V1.0.1正式总数 = 89 (P0: 53, P1: 34, P2: 2)。 | + +--- + +## 二、IR-01:Git 分支基线修正 (Branch Baseline) + +### 2.1 历史问题复盘 +- 此前工作在 `feat/cicd-pipeline` 分支上推进,该分支包含了 `e0abe884`(CI/CD 流水线配置)等未合并至主干的提交,且在此分支上曾对 `00_readme.md` 与 `01_epic_overview.md` 产生了已跟踪版本,违背了“正式 Phase 3 必须基于 `main` 且必须以 `api-v2-contract-v1.0` (`67543e04`) 为开发基线”的铁律。 + +### 2.2 整改执行步骤 +1. **备份工作区 Issue 文档:** + 在仓库外完整备份至 `/tmp/railwaysystem-issues-v1-review/issues-v1`,验证 9 份文档均完整存在; +2. **切换并对齐 `main` 分支:** + - 切换到 `main` 分支后,执行 `git merge --ff-only 67543e04` 将本地 `main` 分支推进到冻结的 API Contract 基线; + - 执行 `git merge-base --is-ancestor 67543e04 HEAD`,验证返回值必须且确实为 `0`; +3. **工作区恢复与洁净度检查:** + - 执行 `git ls-tree -r --name-only main docs/issues-v1/`,确认 `main` 分支历史中不存在任何 `docs/issues-v1/` 历史记录; + - 将 9 篇已修正的 Issue 文档重新复制回工作区,作为当前未跟踪文件(untracked files)保留; + - 严禁任何业务代码修改,严禁提前 `git commit`。 + +--- + +## 三、IR-02:P0 API Contract 集合核验 (P0 Contract Consistency) + +### 3.1 Tag 来源抽取与双向比对 +- **唯一真理来源:** 直接从 Git Tag `api-v2-contract-v1.0` 读取正文 API 定义,通过提取逐一抽取 `HTTP METHOD + PATH`,导出至 `/tmp/railway-p0-contract.sorted`; +- **Issue 映射抽取:** 从 `docs/issues-v1/02_p0_issue_backlog.md` 及 `06_acceptance_matrix.md` 中抽取全部认领的 P0 API,导出至 `/tmp/railway-p0-issues.sorted`; +- **差集核验命令:** + ```bash + comm -3 /tmp/railway-p0-contract.sorted /tmp/railway-p0-issues.sorted + ``` +- **比对结果:** 差集输出为空(0 差异),证明 Issue 库已无遗漏地精确涵盖了 API 契约正文中打上了【P0】标记的全部 53 处 API 端点。 + +### 3.2 契约裁决与 100% 覆盖闭环 +- 技术负责人已正式裁决:**详细 API 标记优先于错误摘要统计;P0 正式集合以 53 个逐 API 标记为准**; +- 经双向比对验证,Issue 库对 53 个 P0 API 的覆盖率为 **53 / 53 = 100%**,Missing = 0, Extra = 0。P0 核心需求边界完全冻结闭环。 + +--- + +## 四、IR-03:代码生成器生成策略统计修正 (Generator Count) + +### 4.1 策略分类重新定性 +- **纠偏事实:** 初稿中统计数据为 `PARTIAL = 10, NO = 12`,存在策略归属与实际 Issue 责任判定口径不统一的问题; +- **定性依据:** + - `ISSUE-05-02`(住宿调房与走读):该 Issue 不涉及新物理表或字段的新增建表,其核心逻辑是在现有 `train_accommodation` 表上进行原子行锁获取、房间容量校验、男女隔离核验、`accommodation_status` 状态流转以及床位释放,完全属于复杂的业务状态机与并发防超卖控制,应严格定性为 **Generator NO**; +- **最终准确统计分布:** + - **Generator YES (4 个):** `ISSUE-01-01`, `ISSUE-01-03`, `ISSUE-01-04`, `ISSUE-01-05` + - **Generator PARTIAL (11 个):** `ISSUE-01-02`, `ISSUE-02-01`, `ISSUE-02-02`, `ISSUE-03-01`, `ISSUE-04-01`, `ISSUE-05-01`, `ISSUE-07-01`, `ISSUE-07-02`, `ISSUE-08-01`, `ISSUE-08-02`, `ISSUE-09-01` + - **Generator NO (11 个):** `ISSUE-03-02`, `ISSUE-04-02`, `ISSUE-05-02`, `ISSUE-05-03`, `ISSUE-06-01`, `ISSUE-06-02`, `ISSUE-10-01`, `ISSUE-10-02`, `ISSUE-11-01`, `ISSUE-11-02`, `ISSUE-11-03` +- **机器校验等式断言:** + $$\text{YES}(4) + \text{PARTIAL}(11) + \text{NO}(11) = 26 \quad (\text{P0 Issue 总数 } 26)$$ + 已在 `00_readme.md`、`02_p0_issue_backlog.md`、`05_agent_assignment.md` 中完成一致性更新并通过自动化校验。 + +--- + +## 五、IR-04:身份证安全算法边界规范 (Security Algorithm Boundary) + +### 5.1 违规内容清除 +- 针对 `ISSUE-01-02`、`04_wave_plan.md`、`05_agent_assignment.md` 等文档中提及的 `AES`、`HMAC-SHA256`、`国密` 等具体加密算法与散列实现名称进行了全局搜索与彻底清除。 + +### 5.2 正式安全规范定义 +统一收敛为标准安全持久化语义,严格沿用 Database V2 的字段模型: +1. `id_card_ciphertext`:可逆密文存储; +2. `id_card_hash`:用于确定性查重/检索的安全散列值; +3. `id_card_last4`:展示末四位; +4. **决策边界锁闭:** 具体可逆加密算法(如 AES-GCM 或 SM4)、密钥管理与轮转方案、确定性散列/HMAC 算法留待 Security Implementation Review 专项评审确定,Issue 阶段严禁提前写死; +5. **脱敏与防御边界:** 系统日志、全局异常提示、API 响应 VO、Excel 导出等任何对外呈现场景,严禁输出完整明文身份证号。 + +--- + +## 六、IR-05:排课与住宿并发锁模型加固 (Concurrency Locking) + +### 6.1 排课并发模型加固 (ISSUE-04-01) +- **缺陷剖析:** 若仅对“时段冲突的已有课次”做 `SELECT ... FOR UPDATE`,在无冲突场景下该查询返回空记录集,普通行锁无法锁住不存在的行,高并发下事务 A 与 B 会同时认为无冲突并写入,导致时段重叠穿透。 +- **稳定主资源行锁机制:** + 1. 事务开始后,先通过主键对实际涉及的稳定主资源行加排他锁: + ```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; + ``` + 2. 在持有稳定主资源锁的同一事务内,针对教师、教室、实训场地三类资源执行时间区间重叠冲突检测: + ```sql + existing.start_time < :newEndTime AND existing.end_time > :newStartTime + ``` + 3. **死锁防御:** 课次编辑若涉及教师或场地变更,必须同时锁定旧资源与新资源;为防止交叉死锁,强制要求按 `resourceType + ":" + resourceId` 字典序升序依次获取行锁; + 4. **状态硬约束:** 若课次已有学员考勤打卡记录,强行阻断时段修改。 + +### 6.2 住宿并发模型加固 (ISSUE-05-01 / 05-02) +- **缺陷剖析:** 若仅锁定当前已有住宿明细,在空房或少人入住场景下无法封锁并发的批量分配,容易发生幻读导致超核定人数入住或男女混住。 +- **稳定宿舍主表行锁机制:** + 1. 分房与调房事务开始后,必须先锁定目标房间的稳定物理主表行: + ```sql + SELECT dorm_id, capacity, gender_type + FROM train_dormitory_info + WHERE dorm_id = :dormId FOR UPDATE; + ``` + 2. 统计与目标培训周期存在重叠的有效住宿人数(预分配 0 与已入住 1): + ```sql + SELECT COUNT(*) FROM train_accommodation + WHERE dorm_id = :dormId + AND accommodation_status IN (0, 1) + AND NOT (end_date <= :planStartDate OR start_date >= :planEndDate); + ``` + 3. 校验 `(occupiedCount + requestCount) <= capacity`,一旦超员立即抛出 `DORM_ROOM_FULL (40062)` 并回滚; + 4. 严格校验学员性别与房间 `gender_type`,杜绝男女混住隐患。 + +--- + +## 七、CG-01:Module A P0 统计矛盾 (Module A P0 Count) + +- **最终裁决状态:** **RESOLVED** +- **技术负责人正式裁决结论:** + - **详细 API 标记优先于错误摘要统计;P0 正式集合以 53 个逐 API 标记为准**; + - 宿舍 4 个基础维护 API 属于一期 P0,因为 Accommodation 住宿主流程强依赖可维护的宿舍房间基础资源、核定容量与性别隔离属性; +- **实施落实:** + - `docs/api-v2/02_module_a_base_resource_api.md` 头部统计正式修正为 `P0: 20个, P1: 18个`; + - 全局 P0 集合确定为 53 个(Module A: 20, Module B: 17, Module C: 9, Module D: 7); + - `ISSUE-01-05` 完整承揽宿舍基础管理,P0 Issue 总数保持 26 个不变; + - 机器比对验证 Missing=0, Extra=0,覆盖率达成 **53/53 = 100%**。 + +--- + +## 八、CG-02:课次详情单查接口缺失评估 (Lesson Detail API) + +- **最终裁决状态:** **RESOLVED** +- **技术负责人正式裁决结论:** + - **通过扩展 TimetableLessonItemVO 解决;不新增 Lesson 详情 API**; + - 严禁在 Issue 阶段或契约中新增 `GET /train/lessons/{lessonId}` 端点,保持 API 契约稳定; +- **实施落实:** + - 已在 `docs/api-v2/06_dto_vo_catalog.md` 中补齐 `TimetableLessonItemVO` 的关键外键关联字段(`planCourseId`、`tId`、`venueType`、`classRoomId`、`classRoomName`、`siteId`、`siteName`、`remark`); + - 前端通过完整课表 `PlanTimetableVO` 直接回显 Lesson 编辑表单,随后调用既有的 `PUT /train/lessons/{lessonId}`,流程完全自洽闭环。 + +--- + +## 九、CG-03:API 物理端点计数一致性修复 (Physical Endpoint Consistency) + +- **最终裁决状态:** **RESOLVED** +- **技术负责人正式裁决结论:** + - **API 总数必须以正文中真实定义了 HTTP Method + Path 的物理 Endpoint 为准。模块摘要数字不能代表一个不存在的 API**; + - 严禁为了凑齐旧的“91/25/15”摘要数字而人为捏造虚构 API; + - Module B 预想的 `GET /train/lessons/{lessonId}` 经 CG-02 裁决不新增,真实物理端点为 24 个(P0: 17, P1: 6, P2: 1); + - Module D 真实物理端点为 14 个(P0: 7, P1: 6, P2: 1),无第 15 个物理接口; +- **实施落实:** + - API Contract V1.0.1 正式口径统一定为:**总数 89 (P0: 53, P1: 34, P2: 2)**; + - 各模块端点分布:Module A: 38 (20/18/0), Module B: 24 (17/6/1), Module C: 13 (9/4/0), Module D: 14 (7/6/1); + - 新增 HTTP API = 0,删除已定义 HTTP API = 0(摘要计数纠偏,非删除已存在 API); + - Issue 库 P1/P2 Backlog (`07_p1_p2_backlog.md`) 重新全量机器对齐,严禁虚构占位项,实际登记 P1: 34 个,P2: 2 个。 + +--- + +## 十、计划结束与业务边界核查 (ISSUE-02-01) + +- 再次核对 `ISSUE-02-01`: + - 确认仅包含允许的:`Plan 结训归档` $\rightarrow$ `Enrollment 标记 completed` $\rightarrow$ `记录 complete_time` $\rightarrow$ `历史数据归档保留`; + - 严格排查并确认未引入任何“及格才可结业”、“证书自动生成”、“结业审批流”、“成绩发布前置审批”等未冻结逻辑; + - 严格将此类待定规则隔离在现场确认事项之外,遵循 PRD V1.0 冻结边界。 + +--- + +## 十一、文档完整性与 Whitespace 规范校验 + +- 对工作区中的全部 Issue 文档执行 `git add -N docs/issues-v1/*.md` 与 `git diff --check`: + - 检查结果:**无任何空白字符、行尾空格或格式错误**; +- 随后执行 `git reset` 释放意图,确保所有 Issue 文档在 `main` 分支上保持未提交、未跟踪的洁净工作区状态。 + +--- + +## 十二、最终评审判定 (Final Assessment) + +- **综合整改评定:** + - IR-01 ~ IR-05 全部整改完毕并达标(RESOLVED); + - CG-01, CG-02, CG-03 三项裁决全部闭环落实(RESOLVED); + - P0 需求覆盖率 53/53 = 100%,Missing = 0, Extra = 0; + - Generator 策略保持 YES: 4, PARTIAL: 11, NO: 11 (Total 26); + - API 物理契约总数 89 (53/34/2) 与 Backlog 严格一致。 +- **最终判定结论:** + $$\mathbf{Issue\ Review\ Round\ 1\ Final = A}$$