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

104 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 完成三表关联映射。