153 lines
8.6 KiB
Markdown
153 lines
8.6 KiB
Markdown
# 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**,等待评审。
|