新增培训:实现宿舍房间P0能力

This commit is contained in:
wangzhibiao committed 2026-09-14 12:49:12 +08:00
1 parent 4bd2f54856
commit 7a5878fe27
15 files changed
+2130 -328

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
# ISSUE-01-05 宿舍房间与楼宇基础数据维护开发验收文档
## 1. 任务概述与环境基线
- **任务 ID:** `ISSUE-01-05`
- **任务名称:** 宿舍房间与楼宇基础数据维护
- **所属 Epic:** `EPIC-01 基础主数据必要集合`
- **优先级:** P0
- **工作区目录:** `/Users/wzb/Desktop/项目/RailwaySystem-worktrees/issue-01-05-dormitory`
- **当前分支:** `feat/issue-01-05-dormitory`
- **正确基线 Commit:** `4682df03c566776562b693ed5cb48fd98cda2472` (`integration/wave1a-foundation`)
- 基线祖先校验状态:`PASS`(当前分支直接派生自该正式基线)
- **API 契约基准:** `docs/api-v2/02_module_a_base_resource_api.md` 4.4 节、`docs/api-v2/06_dto_vo_catalog.md`、`docs/api-v2/10_permission_and_error_codes.md`
- **数据库基线:** Database V2 (`train_dormitory_info`)
- **Generator 冻结策略:** `YES (单表结构)`(依据 `docs/issues-v1/02_p0_issue_backlog.md` 与 `docs/issues-v1/05_agent_assignment.md`)
- **本轮 Generator 实际执行:** `Generator actually executed = NO`(骨架已在此前生成,本次按 V2 契约与手写逻辑完成重构与对齐,未重新执行 Generator)
- **核心业务目标:** 建立宿舍物理房间主数据台账(楼栋、楼层、房间号、核定容量、适用性别、门锁SN),提供同楼栋房号查重与防重、实时在住人数与剩余床位只读动态聚合、当前入住学员列表回显以及楼栋-楼层-房间三级结构化级联下拉选项。
---
## 2. HTTP Endpoint 最终收口与物理映射
严格依照冻结契约收口 Controller 公开接口,**非冻结接口全部从 Controller 移除,不再对外公开**:
- 彻底移除历史 `/train/dorminfo` 路由及 Alias;
- 彻底移除 `PUT /train/dormitory`、`DELETE /train/dormitory/{dormIds}`、`POST /train/dormitory/export` 等非 P0 接口。
### 2.1 最终公开物理 Endpoint 完整列表(共且仅有 4 个正式 P0)
| 优先级 | HTTP Method | URI 路径 | Controller 方法 | 权限字符 | 适用角色 | 功能说明与契约对齐 |
|:---|:---|:---|:---|:---|:---|:---|
| **P0** | `GET` | `/train/dormitory/list` | `list(TrainDormitoryInfo)` | `train:dormitory:list` | 管理员, 宿管 | 分页查询宿舍列表,附带 capacity、usedCount、remainingBeds |
| **P0** | `GET` | `/train/dormitory/{dormId}` | `getInfo(@PathVariable)` | `train:dormitory:list` | 管理员, 宿管 | 获取指定房间详情及当前入住人员列表 (occupants) |
| **P0** | `POST` | `/train/dormitory` | `add(@Validated @RequestBody)` | `train:dormitory:edit` | 管理员, 宿管 | 新增房间(前置查重与底层唯一约束冲突均转换为 40006) |
| **P0** | `GET` | `/train/dormitory/options` | `options()` | `train:dormitory:list` | 管理员, 宿管 | 获取结构化的楼栋 -> 楼层 -> 房间层级树(分房弹窗必需) |
- **Contract-extra Endpoint 数量:** 0 个。
---
## 3. DTO 与 VO 设计规范
### 3.1 `DormitoryCreateDTO`
- `dormBuilding` (String, `@NotBlank`, `@Size(max = 50)`): 宿舍楼栋
- `dormFloor` (String, `@NotBlank`, `@Size(max = 20)`): 所在楼层
- `dormRoom` (String, `@NotBlank`, `@Size(max = 30)`): 房间号
- `capacity` (Integer, `@NotNull`, `@Min(1)`, `@Max(8)`): 核定床位容量 (1~8)
- `genderType` (Integer, `@NotNull`): 适用性别 (0:不限, 1:男, 2:女)
- `lockDeviceSn` (String, `@Size(max = 64)`): 智能门锁设备序列号 (可选)
- `remark` (String, `@Size(max = 500)`): 备注 (可选)
### 3.2 `DormitoryItemVO`
- `dormId` (String): 宿舍ID主键
- `dormBuilding` (String): 宿舍楼栋
- `dormFloor` (String): 所在楼层
- `dormRoom` (String): 房间号
- `capacity` (Integer): 核定床位容量
- `usedCount` (Integer): 已住人数 (动态聚合自 `train_accommodation`)
- `remainingBeds` (Integer): 剩余床位 (`capacity - usedCount`)
- `genderType` (Integer): 适用性别 (0:不限, 1:男, 2:女)
- `lockDeviceSn` (String): 智能门锁SN
- `dormStatus` (Integer): 状态 (0:禁用, 1:可用)
- `remark` (String): 备注
- `createTime` (Date): 创建时间
### 3.3 `DormitoryOccupantVO`
- `enrollmentId` (Long): 参训档案ID
- `studentId` (String): 学员主档ID
- `studentName` (String): 学员姓名
- `gender` (String): 性别
- `orgName` (String): 所属单位
- `bedNo` (String): 分配床位号 (可选)
- `assignedTime` (Date): 分配入住时间
- `accommodationStatus` (Integer): 住宿状态 (0:预分配, 1:已入住)
### 3.4 `DormitoryDetailVO`
- 包含房间基础信息与动态统计 (`dormId`, `dormBuilding`, `dormFloor`, `dormRoom`, `capacity`, `usedCount`, `remainingBeds`, `genderType`, `lockDeviceSn`, `dormStatus`, `remark`, `createTime`);
- `occupants` (`List<DormitoryOccupantVO>`): 当前入住学员列表(空房时返回 `[]`,严禁返回 `null`)。
### 3.5 `DormOptionsVO`
- 楼栋 -> 楼层 -> 房间三级树节点,包含 `value`, `label`, `type` ("building"/"floor"/"room"), `children`;
- 叶子节点(房间)挂载 `capacity`, `usedCount`, `remainingBeds`, `genderType`, `lockDeviceSn` 元数据,方便前端分房弹窗直接消费。
---
## 4. 业务规则与边界约束
### 4.1 容量与性别规则
- **容量范围:** 严格限定在 $1 \le \text{capacity} \le 8$ 之间,非正数或大于 8 抛出业务校验异常。
- **适用性别:** 必须为合法枚举值(0: 不限, 1: 男, 2: 女)。
### 4.2 动态房态聚合口径
- **依据:** `RuoYi-Vue/sql/train/v2/007_dicts.sql` 中 `train_accommodation_status` 字典定义:
- `0`: 预分配 (报到前系统预先分配)
- `1`: 已入住 (报到确认并已入住)
- `2`: 已退宿 (培训结束已离宿)
- **实际占用定义:** 仅处于“预分配”与“已入住”状态的记录占用床位,退宿后释放。
- **实际 SQL 过滤条件:**
```sql
SELECT dorm_id, COUNT(1) AS used_count
FROM train_accommodation
WHERE accommodation_status IN (0, 1)
GROUP BY dorm_id
```
`remaining_beds = (capacity - COALESCE(acc.used_count, 0))`。
### 4.3 UNIQUE 并发语义保证
- **数据库约束:** `uq_dorm_building_room UNIQUE (dorm_building, dorm_room)`。
- **并发保护机制:**
1. 应用层前置查重:查询 `train_dormitory_info` 是否存在同楼栋同房号未删除记录,存在即抛出 `ServiceException("同一楼栋下该宿舍房号已存在", 40006)`;
2. 底层并发冲突兜底:高并发场景下两请求同时绕过前置查重时,底层 INSERT 触发唯一约束冲突抛出 `DuplicateKeyException`,Service 层显式捕获并转换为 40006 业务异常,杜绝 500 裸异常泄漏。
### 4.4 业务边界铁律
- **Dormitory 仅代表物理房间基础资源:** 负责房间基础资产的维护与只读聚合。
- **无独立 DormBed 实体:** Phase 1 阶段严禁引入 `train_dorm_bed` 表;`bed_no` 仅为 `train_accommodation` 的可空业务字段。
- **禁止 Accommodation 写操作:** 本 Issue 严禁分房、调房、退宿、门禁下发与同步、报到入住办理以及级联物理删除。
---
## 5. 验证结果与质量达标记录
1. **单元测试:**
- 执行测试:`DormitoryServiceTest` 包含 6 个测试用例:
- `testAddAndDuplicateInterception`: 前置防重与 40006 拦截
- `testConcurrentInsertDuplicateKeyConversion`: 高并发 `DuplicateKeyException` 转换为 40006
- `testCapacityAndGenderValidation`: 容量 (1~8) 与性别枚举合法性
- `testList`: 动态聚合 `usedCount` 与 `remainingBeds`
- `testGet`: 房间详情与 `occupants` 列表回显
- `testOptions`: 楼栋-楼层-房间三级树结构组装
- 运行结果:`Tests run: 6, Failures: 0, Errors: 0, Skipped: 0` 全部通过。
2. **模块依赖测试:**
- 命令:`mvn test -pl ruoyi-train -am -f RuoYi-Vue/pom.xml`
- 运行结果:`Tests run: 20, Failures: 0, Errors: 0, Skipped: 0` 全部通过。
3. **后端 Reactor 编译:**
- 命令:`mvn compile -DskipTests -f RuoYi-Vue/pom.xml`
- 运行结果:8 个子模块全部 `SUCCESS`。
4. **前端 ESLint 检查:**
- 命令:`./node_modules/.bin/eslint --no-ignore src/api/train/dormitory.js src/views/train/dormitory/index.vue`
- 运行结果:0 errors, 0 warnings。
5. **前端生产打包 (build:prod):**
- 命令:`npm run build:prod`
- 运行结果:`Build complete. The dist directory is ready to be deployed.` 构建成功。构建完成后已彻底清理临时产物与软链接。
6. **格式检查 (git diff --check):**
- 命令:`git diff --check`
- 运行结果:无任何 trailing whitespace,洁净通过。
7. **Commit 状态:**
- 本地暂存区与工作区保留全部修改,**未执行 git commit**,等待评审。