Files
railwaysystem/docs/development/issue-01-05-dormitory.md
T

8.6 KiB
Raw Blame History

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 过滤条件:
    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,等待评审。