13 KiB
13 KiB
02 本地住宿、宿舍与门禁模型现状审计 (Current Accommodation Audit)
基准代码版本:
adcf930ea0a880e1faae36ff3d3e6539d0c0b8f9(HEAD) 审计范围:Kingbase 物理数据库、Java 后端代码、MyBatis XML、前端 Vue 代码 审计执行方式:只读安全查询脚本 (DbTableAuditor.java),无任何写操作与数据破坏
1. Kingbase 数据库真实物理表事实 (Physical Table Facts)
通过直接连接正式 Kingbase 数据库 (jdbc:kingbase8://127.0.0.1:4321/training?currentSchema=public,sys_catalog) 进行只读元数据审计,物理表事实如下:
1.1 train_dormitory_info (宿舍物理资源表)
- TABLE_EXISTS:
true - ROW_COUNT:
1 - KEY_COLUMNS (14列):
dorm_id(varchar(32),NOT NULL, 主键 UUID)dorm_building(varchar(50),NOT NULL, 宿舍楼栋)dorm_floor(varchar(20),NOT NULL, 所在楼层)dorm_room(varchar(20),NOT NULL, 房间号)capacity(integer,NOT NULL, 核定床位数, 1~8)gender_type(smallint,NOT NULL, 适用性别: 0不限, 1男, 2女)lock_device_sn(varchar(64),NULLABLE, 智能门锁设备硬件序列号)dorm_status(smallint,NOT NULL, 房间状态: 0禁用, 1可用)del_flag(bpchar(1),NOT NULL, 逻辑删除: 0正常, 2删除)create_by,create_time,update_by,update_time,remark(标准审计列)
- 物理约束: 唯一约束
uq_dorm_building_room (dorm_building, dorm_room)。
1.2 train_accommodation (学员住宿安排事实表)
- TABLE_EXISTS:
true - ROW_COUNT:
0 - KEY_COLUMNS (11列):
accommodation_id(bigint,NOT NULL, 主键 IDENTITY)enrollment_id(bigint,NOT NULL, 参训档案ID, 唯一约束uq_accommodation_enroll)dorm_id(varchar(32),NOT NULL, 分配宿舍ID, 外键fk_accomm_dorm引用train_dormitory_info.dorm_id)bed_no(varchar(20),NULLABLE, 分配床位号)assigned_time(timestamp,NOT NULL, 分配时间, 默认CURRENT_TIMESTAMP)accommodation_status(smallint,NOT NULL, 住宿状态: 0预分配, 1已入住, 2已退宿)create_by,create_time,update_by,update_time,remark(标准审计列)
- 外键约束:
fk_accomm_enrollment: 引用train_enrollment(enrollment_id)fk_accomm_dorm: 引用train_dormitory_info(dorm_id)
1.3 train_dorm_auth (门禁同步与下发表 - 数据库 V2 重建版本)
- TABLE_EXISTS:
true - ROW_COUNT:
0 - KEY_COLUMNS (14列):
auth_id(bigint,NOT NULL, 主键 IDENTITY)accommodation_id(bigint,NOT NULL, 住宿安排ID, 外键fk_dormauth_accomm, 唯一约束uq_dorm_auth_accomm)auth_start(timestamp,NULLABLE, 权限生效开始时间)auth_end(timestamp,NULLABLE, 权限失效结束时间)face_sync_status(smallint,NOT NULL, 默认 0; 0待下发, 1已下发, 2下发失败, 3已注销)retry_count(integer,NOT NULL, 默认 0, 同步失败重试次数)last_error(varchar(500),NULLABLE, 最后一次下发失败错误信息)external_auth_id(varchar(100),NULLABLE, 外部门禁硬件系统下发唯一凭证ID)last_sync_time(timestamp,NULLABLE, 最近一次同步请求时间)create_by,create_time,update_by,update_time,remark(标准审计列)
1.4 其它相关表事实
bak_v1_train_dorm_auth: 存在历史 V1 备份表(行数 0),包含废弃的student_id,plan_id,dorm_id,bed_no。- 数据库中不存在任何以
door_*,lock_*,access_*命名的其他物理表。
2. 真实代码层实现现状审计 (Code Implementation Facts)
| 模块 / 类 | 当前代码存在情况 | 承担职责与现实状态 |
|---|---|---|
TrainDormitoryInfo (Domain/VO/Service/Controller) |
已完整实现 | 负责物理宿舍增删改查。包含 lockDeviceSn 单一字符串字段;通过 SQL 实时统计 train_accommodation 的 usedCount 与计算 remainingBeds。 |
TrainDormAuth (Domain/Mapper/Service/Controller) |
实现脱节 (V1 遗留代码) | 当前 Java 实体类 TrainDormAuth.java、Mapper 与 Controller 依然映射的是 V1 结构(持有 studentId, planId, dormId, bedNo, faceSyncStatus),尚未重构对齐数据库 V2 的 accommodation_id 关联结构! |
TrainAccommodation (Domain/Service/Controller) |
代码层缺失 | 数据库已存在 train_accommodation 物理表,且 TrainDormitoryInfoMapper.xml 和 TrainEnrollmentMapper.xml 已在子查询中引用该表,但 Java 层尚无对应的 TrainAccommodation.java 实体及专用 Service/Controller。 |
| 前端 Vue 页面 | 未实现专用页面 | ruoyi-ui/src/views/ 目录下尚未建立 train/dormitory 或 train/auth 前端业务界面,目前仅有大盘总览。 |
3. 三大核心业务边界辨析 (Current Business Boundary)
当前系统必须在概念模型与后续实现中严格切分以下三个业务对象,严禁混为一谈:
flowchart TB
subgraph LayerA["A. 物理宿舍资源 (train_dormitory_info)"]
direction TB
A1["物理属性:楼栋、楼层、房间号、核定床位容量、适用性别、房间可用/停用状态<br/>生命周期:独立于任何培训班和学员,长期相对稳定"]
end
subgraph LayerB["B. 住宿业务事实 (train_accommodation)"]
direction TB
B1["业务属性:学员参训档案 (enrollment_id) 在本次开班中分配到的房间与床位<br/>状态流转:预分配 (0) -> 已入住 (1) -> 已退宿 (2)<br/>事实归属:RailwaySystem 是本业务的绝对 Source of Truth (事实唯一真源)"]
end
subgraph LayerC["C. 门锁设备授权 (门禁授权模型)"]
direction TB
C1["设备属性:向 DMS 门锁下发的有效时间区间与电子钥匙凭据 (密码/卡片)<br/>状态流转:待下发 -> 平台已受理 -> 设备执行中 -> 设备生效 -> 撤销中 -> 已注销<br/>属性特征:依赖外部网络与硬件状态,可异步重试,失败不推翻住宿业务事实"]
end
LayerB -->|"1 间宿舍可容纳多个住宿安排"| LayerA
LayerC -->|"1 条住宿事实产生门禁授权要求"| LayerB
4. train_dorm_auth 当前角色与演进方案比选 (TRAIN_DORM_AUTH_CURRENT_ROLE)
4.1 当前角色定性
在当前代码与数据库混合状态下:
- Java 代码层:
train_dorm_auth承担了“住宿分配 + 门锁序列号关联 + 人脸同步标记”的混合式职责。 - 数据库 V2 迁移设计:设计意图已将其向“纯门禁授权下发同步表”推进(引入
accommodation_id,retry_count,last_error,external_auth_id),但尚未考虑一个房间多把锁、**多凭据类型(密码/卡片)**以及 DMSoperationId异步轮询生命周期。
4.2 三种演进方案详细比选
| 评估维度 | 方案 A: 沿用并继续扩展 train_dorm_auth |
方案 B: 收敛 train_dorm_auth + 独立 train_external_operation |
方案 C: 新建通用 train_door_authorization + train_external_operation |
|---|---|---|---|
| 设计描述 | 在现有的 train_dorm_auth 上追加 lock_id, operation_id, credential_type, credential_value 等列 |
将 train_dorm_auth 代码升级对齐 V2(作为授权事实主表),另建异步操作流水表记录轮询与对账 |
新建专用的门禁授权表与外部操作流水表,彻底废弃/弃用命名有误导的 train_dorm_auth |
| 职责清晰度 | 差。混合业务授权与设备异步任务,字段臃肿且无法表达多锁与多钥匙 | 良好。主表表达“谁拥有该房间的有效授权”,流水表表达“向设备发送的异步指令状态” | 优秀。命名脱离“人脸/宿舍”局部限制,完全按照通用门禁控制抽象 |
| 与 Accommodation 关系 | 外键引用 accommodation_id,但多锁时难以保持 1:1 唯一约束 |
主表通过 accommodation_id 关联,支持按锁/凭据类型建立多条授权;流水表关联主表 |
清晰明确,一个 Accommodation 可派生多条 Authorization 凭据 |
| DMS Operation 生命周期支持 | 极差。无法保存多次重试、多次轮询的轨迹与错误历史 | 优秀。所有异步 operationId、轮询重试、超时记录在流水表完整归档 |
优秀。支持完整的异步生命周期追踪与审计 |
| 多锁扩展性 | 无法支持(同一个房间若有外门锁与内门锁,会导致主键或关联冲突) | 良好(主表以 (accommodation_id, lock_id, credential_type) 为逻辑单元) |
优秀(原生支持一房多锁与多供应商适配) |
| 迁移与代码改动成本 | 表面成本低,但后期技术债务严重 | 适中。数据库 V2 已就绪 train_dorm_auth,只需调整列定义或补充流水表 |
略高。需要新增两张物理表并做数据迁移 |
| 审计与故障追溯 | 弱(覆写 last_error 导致历史丢失) |
强(流水表不可变追加,完整记录每一次操作) | 强 |
4.3 演进建议结论
- 本轮 R.3A 不执行任何 DDL。
- 架构倾向:推荐采纳 方案 B/C 的分层解耦思想。绝不能将 DMS 的
operationId轮询与重试直接塞在单个授权记录中覆写,必须将**“门禁业务授权事实 (Door Authorization)”与“外部硬件异步操作记录 (External Operation)”**彻底解耦为两层模型。
5. 本地模型漂移事实与前置对齐要求 (LOCAL_DOMAIN_ALIGNMENT_REQUIRED)
5.1 现实漂移事实总结
LOCAL_DOMAIN_ALIGNMENT_REQUIRED = YES- 当前核心矛盾:
train_accommodation:Kingbase 物理表已在 V2 迁移中正式建立,但 Java 代码层尚未建立对应的实体类、Mapper 与业务 Service,未形成闭环业务层。train_dorm_auth:Kingbase 物理表已在 V2 中升级为以accommodation_id为外键的门禁授权结构,但 Java 实体类TrainDormAuth.java与 XML Mapper 依然停留在 V1 旧结构(持有student_id,plan_id,dorm_id,bed_no)。
- 执行原则: 在进入 R.3B 真正编写 DMS Adapter 之前,必须先在 Wave 0 完成本地住宿与门禁授权领域模型对齐!严禁让外部 DMS 集成适配器直接架设在当前存在严重漂移的本地模型之上。
5.2 Wave 0 Implementation Result (本地模型对齐实施完成)
- 完成状态:已全面对齐并消除结构漂移。
- 实体层:
- 新增
TrainAccommodation.java,严格对应 Kingbase V2 生产物理表(accommodation_id,enrollment_id,dorm_id,bed_no,assigned_time,accommodation_status, 审计列)。 - 重构
TrainDormAuth.java,外键核心对齐accommodation_id,持久化字段剔除 V1 遗留字段(student_id,plan_id,dorm_id,bed_no,createUser),保留为非持久化兼容视图字段以保障只读兼容。
- 新增
- Mapper/SQL:
- 新增
TrainAccommodationMapper.java/TrainAccommodationMapper.xml,所有字段显式指定,严禁SELECT *。 - 重构
TrainDormAuthMapper.xml,INSERT/UPDATE 语句彻底清理 V1 废弃字段写入,通过 LEFT JOIN 视图提供兼容字段。
- 新增
- 业务服务:
- 新增
ITrainAccommodationService/TrainAccommodationServiceImpl,落地“同一时刻最多1个有效住宿安排”(Duplicate Active Accommodation Guard)、宿舍核定容量边界检查(Capacity Boundary Guard)与换房原子流转(Transfer)。 - 升级
TrainDormAuthServiceImpl,严格执行“Enrollment -> Accommodation -> Door Authorization”业务单向依赖,禁止绕过住宿事实直接授权。
- 新增
5.3 Wave 0.1 Accommodation History & 006 Migration (基数模型升级与迁移方案)
- 基数语义冻结:
Enrollment 1:N Accommodation History(一个报名记录可有多条历史住宿记录,调房换房产生历史轨迹)Enrollment 1:0..1 Active Accommodation(同一时间一个报名记录最多只能有 1 条有效住宿记录,accommodation_status IN (0, 1))Accommodation 1:0..1 DoorAuthorization(门禁授权强依赖具体的某一次住宿记录)TIME_RANGE_SUPPORT = LIMITED(当前 Kingbase 物理表仅包含assigned_time,无start_time/end_time,时间窗口支持为有限支持)
- 006 迁移脚本方案 (
006_accommodation_history_constraint.sql):- 脚本路径:
RuoYi-Vue/sql/system/kingbase/006_accommodation_history_constraint.sql - 安全编号:
006(001-003 为基线,004-005 已被 R.2 分支占用) - 前置检查:Fail-fast,若发现同一
enrollment_id存在多条 active(accommodation_status IN (0, 1))则RAISE EXCEPTION阻止迁移 - 安全 DROP:仅在原约束
uq_accommodation_enroll确认为UNIQUE (enrollment_id)时安全移除 - 局部唯一索引:创建
uq_accommodation_enroll_active并在WHERE accommodation_status IN (0, 1)生效 - 幂等性防护:重复执行不会报错或破坏表结构
- 生产执行纪律:本迁移脚本仅完成工程资产化与隔离测试,严禁在生产数据库执行
- 脚本路径:
- 自动化测试:
AccommodationHistoryMigrationContractTest(契约测试:SQL 语法、前置检查、条件 DROP、局部索引及幂等性)AccommodationServiceTest(10 项测试,包含调房历史与并发唯一性)DormAuthAlignmentTest(7 项测试,包含依赖住宿与换房级联)- 全量测试套件 174 项全部 PASS(0 failures, 0 errors)。