Files
railwaysystem/docs/development/dms-r3/09_database_mapping_proposal.md
T

12 KiB
Raw Blame History

09 数据库持久化模型方案比选与设计基线 (Database Mapping Proposal)

设计红线:本轮仅做架构比选与实体设计,禁止执行任何 DDL 脚本或修改生产数据库。 设计原则:面向未来多锁扩展与多厂商接入,保持本地宿舍台账的纯粹性,业务授权与硬件异步对账分层治理。


1. 外部资源映射模型比选 (RESOURCE_MAPPING_RECOMMENDATION)

1.1 映射关系现实挑战

  1. 一个房间可能安装多把锁:如公寓入户门锁与套内自习室/卧室锁,单一房间可能绑定 0、1 或多把智能门锁。
  2. 外部厂商多变性:DMS 属于第三方 SaaS/IoT 平台,未来房建公寓段若自建门禁或改用国铁集团集采设备,字段名与标识均可能改变。
  3. 资源解绑与重绑需求:房间门锁损坏更换后,仅涉及设备 SN 变更,不应引起本地宿舍主记录的历史数据污染。

1.2 两种设计方案对比

评估维度 方案一:在 train_dormitory_info 直接加列 方案二:独立 train_external_resource_mapping 表 (推荐)
表结构改动 在宿管表直接加 dms_place_id, dms_lock_id, dms_lock_sn 等 物理宿舍表保持零污染;新建独立的外部资源映射关联表
一房多锁支持 无法支持(单行只能记录一把锁的 SN 和 ID) 天然支持(一个 dorm_id 可对应多行锁具映射记录)
多厂商与扩展性 差(换厂商需继续加字段,如 hik_channel_id) 优秀(通过 vendor_code 区分不同厂商,复用通用映射模型)
设备更换生命周期 弱(直接覆写破坏审计历史) 强(可标记旧锁为 DEPRECATED,新增新锁映射)
迁移与实施成本 极低 低(新增一张轻量级关联表)

1.3 外部资源映射推荐方案结论

采纳方案二:建立独立的外部资源映射模型。

  • 概念模型结构设计:
    train_external_resource_mapping
    ├── mapping_id          BIGINT (主键)
    ├── local_resource_type VARCHAR(32) ('DORMITORY_ROOM', 'DORMITORY_LOCK')
    ├── local_resource_id   VARCHAR(32) (关联本地 dorm_id)
    ├── vendor_code         VARCHAR(32) ('DMS_IOT')
    ├── external_project_id BIGINT      (DMS 租户 projectId)
    ├── external_parent_id  BIGINT      (DMS 楼栋/楼层 ID)
    ├── external_place_id   BIGINT      (DMS 房间 placeId)
    ├── external_device_id  BIGINT      (DMS 门锁 lockId)
    ├── hardware_sn         VARCHAR(64) (门锁硬件 SN / hardwareId)
    ├── mapping_status      SMALLINT    (1: 已绑定有效, 0: 待核对/失效)
    ├── last_sync_time      TIMESTAMP   (最近一次从 DMS 同步元数据的时间)
    └── [标准审计列]
    

2. 门禁授权与操作模型三大方案深度比选

2.1 方案架构与评估矩阵

评估维度 方案 A: 改造并继续扩展 train_dorm_auth 方案 B: train_dorm_auth 收敛为业务授权 + 独立 train_external_operation (推荐) 方案 C: 新建 train_door_authorization + train_external_operation
模型设计说明 在现有的 train_dorm_auth 表中强行塞入 lock_id, dms_key_id, operation_id, password 等列 保留并对齐数据库 V2 的 train_dorm_auth 作为门禁授权主表;新建 train_external_operation 承载所有异步轮询与重试流水 彻底废除 train_dorm_auth,重新设计全套中立命名的门禁授权与外部操作表
一期开发复杂度 低(改单表) 适中(改主表结构 + 建流水表) 略高(需处理原有表废弃与实体完全重构)
语义与职责清晰度 严重混乱(业务授权与设备单次请求纠缠,无法表达异步过程) 良好(业务授权状态与设备异步任务彻底解耦) 极优(概念完全脱离人脸/宿舍局限)
一房多锁支持能力 无法支持(accommodation_id 唯一约束导致无法为同一个住宿下发多把锁) 支持(主表唯一约束调整为 (accommodation_id, device_mapping_id)) 支持(原生复合设计)
异步状态与重试审计 丢失历史(每次重试都会覆写 last_error 和 operation_id) 完整保留(流水表每次调用追加一行,多次轮询与超时全量追踪) 完整保留
历史兼容与代码改动 兼容性最高,但长期技术负债巨大 平衡性最佳(沿用 V2 数据库迁移成果,仅修正 Java 实体脱节) 需调整较多已有引用类名

3. 推荐采纳模型设计基线 (DOOR_AUTH_MODEL_RECOMMENDATION)

综合考虑沈阳房建公寓段学员管理系统目前所处的正式 Release 阶段与 V2 数据库已经上线的实际情况,推荐采纳【方案 B】(业务授权主表收敛 + 异步操作流水表解耦)。

3.1 实体一:门禁业务授权主模型 (TrainDormAuth / 业务授权主表)

承担职责:表达某个学员住宿事实在某把门锁上的长期通行权限状态。

  • auth_id: BIGINT, PK
  • accommodation_id: BIGINT, 外键关联 train_accommodation (业务事实真源)
  • mapping_id: BIGINT, 关联 train_external_resource_mapping (目标门锁)
  • credential_type: VARCHAR(20) ('ONLINE_PASSWORD', 'ONLINE_CARD')
  • credential_mask: VARCHAR(50) (脱敏凭据摘要,如 '34' 或 '244B')
  • external_key_id: BIGINT (DMS 返回的钥匙实体 ID,用于注销撤权)
  • auth_start: TIMESTAMP (权限生效起始时间)
  • auth_end: TIMESTAMP (权限失效截止时间)
  • auth_status: SMALLINT (0:待下发, 1:下发中, 2:已生效, 3:下发失败, 4:撤销中, 5:已注销, 6:撤销失败)
  • grant_version: INT (授权版本号,换房或重新下发时自增,保障本地幂等)
  • [标准审计列] (create_by, create_time, update_by, update_time)

3.2 实体二:外部异步操作流水模型 (TrainExternalOperation / 异步操作流水表)

承担职责:跟踪向 DMS 派发的每一次具体 HTTP 异步任务的生命周期与轮询轨迹。

  • operation_local_id: BIGINT, PK
  • auth_id: BIGINT, 外键关联授权主表
  • business_action: VARCHAR(32) ('GRANT_PASSWORD', 'REVOKE_PASSWORD', 'GRANT_CARD', 'REVOKE_CARD')
  • vendor_code: VARCHAR(32) ('DMS_IOT')
  • dms_operation_id: VARCHAR(64) (DMS 返回的 32 位 UUID 操作流水号)
  • dms_request_type: INT (DMS 数字请求类型,如 1=下发密码, 3=删除密码)
  • hardware_id: VARCHAR(64) (门锁硬件 SN)
  • operation_status: VARCHAR(20) ('ACCEPTED', 'UNDONE', 'SUCCESS', 'FAILED', 'TIMEOUT')
  • poll_count: INT (已轮询次数,初始为 0,上限通常为 3)
  • next_poll_time: TIMESTAMP (调度器下次拉取状态的时间,初始为 request_time + 10s)
  • request_time: TIMESTAMP (向 DMS 发起请求的时间)
  • complete_time: TIMESTAMP (DMS 返回终态的时间)
  • error_code & error_message: VARCHAR (错误摘要)
  • sanitized_response: VARCHAR(1000) (脱敏后的响应快照)
  • [标准审计列]

4. 实施阶段演进建议

在后续 R.3B 实施阶段,数据访问层的演进步骤建议为:

  1. 第一步:重构修复 Java 实体类 TrainDormAuth.java,彻底移除已弃用的 student_id, plan_id, dorm_id 字段,使其与已存在的 Kingbase 物理表字段完全对齐。
  2. 第二步:创建 train_external_resource_mapping 与 train_external_operation 迁移脚本(遵循系统标准 Liquibase/Flyway 规范)。
  3. 第三步:通过 Spring Data / MyBatis-Plus 完成三表关联映射。

4.1 Wave 0 Implementation Result (第一步已正式交付)

  • 已完成事项:
    1. 重构并修复 TrainDormAuth.java,彻底废弃了向 train_dorm_auth 写入 student_id, plan_id, dorm_id, bed_no, create_user 的历史假设。
    2. 补齐 TrainAccommodation.java、TrainAccommodationMapper.java、TrainAccommodationMapper.xml、ITrainAccommodationService 以及 TrainAccommodationServiceImpl,完全对齐 V2 生产物理结构。
    3. 严格禁止在 Wave 0 中引入任何 DMS 物理字段(如 lock_id, place_id, operation_id),保持 Vendor-Neutral 纯净业务边界。
    4. 生产 Migration 检查:MIGRATION_REQUIRED = NO,生产数据库零写入、零变更。

4.2 Wave 0.1 Accommodation History Migration (006 迁移方案与基数冻结)

  • 领域基数与物理约束演进:
    • 历史模型:Enrollment 1:N Accommodation History,允许学员由于调房等产生多条住宿历史。
    • 唯一性收敛:Enrollment 1:0..1 Active Accommodation,原全局唯一约束 UNIQUE(enrollment_id) 阻碍了历史留痕,演进为针对有效状态(accommodation_status IN (0, 1))的局部唯一索引。
    • 授权关联:Accommodation 1:0..1 DoorAuthorization,门禁授权强引用住宿实体。
    • 时间范围特性:TIME_RANGE_SUPPORT = LIMITED,当前物理表仅有 assigned_time,未扩展起止时间列。
  • 006 Migration 脚本设计与验证:
    • 文件路径:RuoYi-Vue/sql/system/kingbase/006_accommodation_history_constraint.sql
    • 安全编号:006(避免与 R.2 占用的 004/005 冲突)。
    • 执行安全机制:
      1. 前置重复数据检查:存在并发违规 active 记录时直接 RAISE EXCEPTION 阻断。
      2. 严格条件 DROP:仅当原约束 uq_accommodation_enroll 确认为 UNIQUE (enrollment_id) 时执行 drop。
      3. 创建局部唯一索引:CREATE UNIQUE INDEX IF NOT EXISTS uq_accommodation_enroll_active ON train_accommodation (enrollment_id) WHERE accommodation_status IN (0, 1);
      4. 幂等防护:重复执行幂等安全。
    • 生产执行纪律:本脚本已在单元/契约测试中完成全量静态与逻辑校验,禁止在生产库执行。

4.3 Wave 1 Implementation Result (外部资源映射与操作流水持久化落地 - Wave 1 implemented)

  • 阶段状态:Wave 1 implemented。
  • 物理模型与引用决策:
    • 资源映射表 (train_external_resource_mapping):
      • 引用模型:RESOURCE_MAPPING_REFERENCE_MODEL = DORM_ID_FK,直接建立外键关联 train_dormitory_info(dorm_id),确保一期强完整性。
      • 房间映射基数:ROOM_MAPPING_CARDINALITY = Dormitory 1:0..1 Active Room (Dormitory 1:N History),局部唯一索引 uq_ext_res_map_active_room。
      • 锁映射基数:LOCK_MAPPING_CARDINALITY = Dormitory 1:0..N DoorLocks,原生支持一房多锁,is_primary 标记主门锁。
      • 外部资源唯一性:EXTERNAL_RESOURCE_ACTIVE_UNIQUENESS = Vendor + External Resource ID + Active Unique,局部唯一索引 uq_ext_res_map_active_ext_res。
    • 操作流水表 (train_external_operation):
      • 引用模型:OPERATION_REFERENCE_MODEL = DOOR_AUTH_ID_FK,直接建立外键关联 train_dorm_auth(auth_id)。
      • 授权操作基数:DOOR_AUTH_OPERATION_CARDINALITY = DoorAuthorization 1:N ExternalOperation,每次具体下发与重试均产生新记录。
      • 本地幂等键:IDEMPOTENCY_MODEL,唯一索引 uq_ext_op_idempotency。
      • 待轮询复合索引:idx_ext_op_poll,高效支持状态机轮询。
  • 008 迁移脚本交付 (008_external_resource_operation.sql):
    • 安全编号:008(001-003 基线, 004-005 R.2, 006 Wave 0, 007 保留禁止执行)。
    • 纯增量与幂等设计:无 DROP TABLE,无 TRUNCATE,无 DELETE,无明文密码/Token敏感字段。
    • 生产状态:PRODUCTION_DB_MODIFIED? NO,未在生产数据库执行。