Files
railwaysystem/docs/development/dms-r3/03_domain_mapping.md
T

199 lines
11 KiB
Markdown
Raw Normal View History

# 03 RailwaySystem 与 DMS 领域模型映射体系 (Domain Mapping)
> **核心原则**:**RailwaySystem 是住宿业务事实的唯一真源 (Source of Truth)**。
> **DMS 平台**仅作为外部门禁与物联网设备的执行端与监控端。
> **严禁**:以 DMS 房间状态 (`status`) 直接替代 RailwaySystem 住宿占用与床位台账。
---
## 1. 核心实体对应关系总览 (Mermaid 架构图)
```mermaid
classDiagram
direction TB
class RailwayTrainingPlan {
+String planId "内部培训班计划ID"
+String planName
+Date trainStart
+Date trainEnd
}
class RailwayEnrollment {
+Long enrollmentId "参训学员档案ID"
+String planId
+String studentId
+Integer reportStatus
}
class RailwayAccommodation {
+Long accommodationId "住宿安排事实ID (Source of Truth)"
+Long enrollmentId
+String dormId
+String bedNo
+Integer accommodationStatus "0预分配, 1已入住, 2已退宿"
}
class RailwayDormitoryInfo {
+String dormId "本地宿舍UUID"
+String dormBuilding "楼栋"
+String dormFloor "楼层"
+String dormRoom "房号"
+Integer capacity "床位容量"
}
class ExternalResourceMapping {
+Long mappingId
+String localDormId "关联本地宿舍"
+Long dmsProjectId "DMS租户/物业站点ID"
+Long dmsPlaceId "DMS房间ID"
+String dmsBuilding
+String dmsFloor
+String dmsRoom
+Integer mappingStatus "1已绑定, 0待校验"
}
class DmsPlaceRoom {
<<DMS Remote>>
+Long id "DMS placeId"
+Long projectId "DMS租户ID (≠ planId)"
+String building
+String floor
+String room
}
class DmsDoorLock {
<<DMS Remote>>
+Long lockId "DMS锁ID"
+String hardwareId "锁硬件SN (如 5C9F539A)"
+Long placeId
+Integer battery
+Integer signal
}
class DoorAuthorization {
+Long authId "本地授权凭证主记录"
+Long accommodationId
+Long dmsLockId
+String dmsHardwareId
+Long dmsKeyId "DMS返回的钥匙ID"
+String credentialType "PASSWORD / CARD"
+Timestamp validStart
+Timestamp validUntil
+Integer authStatus "PENDING / ACTIVE / REVOKED"
}
class ExternalOperationRecord {
+Long operationLocalId
+Long authId
+String dmsOperationId "DMS异步操作流水ID"
+Integer requestType "1下发密码, 3删除密码等"
+String operationStatus "undone / success / failed / timeout"
+Integer pollCount
+Timestamp nextPollTime
}
RailwayTrainingPlan "1" --> "0..*" RailwayEnrollment : 包含
RailwayEnrollment "1" --> "1" RailwayAccommodation : 产生住宿安排
RailwayAccommodation "0..*" --> "1" RailwayDormitoryInfo : 分配房间
RailwayDormitoryInfo "1" --> "0..1" ExternalResourceMapping : 稳定映射
ExternalResourceMapping "1" ..> "1" DmsPlaceRoom : 对应DMS物理房间
DmsPlaceRoom "1" ..> "0..*" DmsDoorLock : 挂载智能门锁 (支持一房多锁)
RailwayAccommodation "1" --> "0..*" DoorAuthorization : 派生门锁授权
DoorAuthorization "1" --> "0..*" ExternalOperationRecord : 追踪异步执行
```
---
## 2. 关键实体边界与重大概念澄清
### 2.1 铁律:`DMS projectId ≠ RailwaySystem Training Plan ID`
- **DMS `projectId`**(示例中为 `319402846`、`3837087560`):代表 DMS 物联网平台的**物业项目/租户/公寓站点 ID**。属于系统级或物理站点级的静态上下文配置,由设备供应商分配。
- **RailwaySystem `plan_id`**(如 `PLAN20260901`):代表铁路局/段内的**具体教学培训开班批次**。生命周期通常为几天到几周。
- **红线**:严禁将 DMS `projectId` 错写成或动态传为学员开班 `planId`!在配置文件中应统一作为 `dms.project-id` 进行站点级静态受控配置。
### 2.2 铁律:本地住宿事实是唯一真源 (`Local Source of Truth`)
- DMS 的 `room.status`(0:空置, 1:已入住, 2:脏房等)仅为其平台或第三方酒店系统的简单标记,**绝对不能用于覆盖或推断 RailwaySystem 的床位与入住状态**。
- 只有 RailwaySystem 自身的 `train_accommodation` 与 `train_enrollment` 能决定学员是否在住、床位是否已满、是否已报到。
- DMS 门锁授权状态仅是硬件执行层面的技术属性(开门凭据是否已在锁具上成功生效)。
---
## 3. 房间与门锁映射策略深度比选 (Mapping Proposals)
### 3.1 房间映射 (Local Dormitory ↔ DMS Place)
DMS 提供了两种查询房间的途径:
- 模糊查询列表:`POST /rest/place/room/list`
- 精确查询:`POST /rest/place/room/get`(入参:`building`, `floor`, `room`)
| 方案 | 实现方式 | 优缺点分析 | 结论 |
| :--- | :--- | :--- | :--- |
| **方案 1:直接在 `train_dormitory_info` 加列** | 增加 `dms_place_id`, `dms_project_id`, `dms_sync_time` | **优点**:查询直观,单表读取。<br>**缺点**:污染本地物理宿舍模型;若未来接入其他门禁厂商(如海康、大华),列定义难以复用。 | 可作为一期过渡备选 |
| **方案 2:独立 `train_external_resource_mapping` 映射表** | 建立独立映射表:`local_resource_type ('DORMITORY')`, `local_id`, `vendor ('DMS')`, `external_id (placeId)`, `metadata_json` | **优点**:模型高度解耦;本地宿舍资源生命周期不受外部影响;天然支持多厂商与资源解绑/重绑。<br>**缺点**:需要多做一次关联查询。 | **推荐采纳** |
**映射规则明确**:
- **严禁**每次下发授权时现场调用 `/rest/place/room/get` 进行文本字符串模糊匹配。网络抖动或楼栋名称微小差异(例如 "1号楼" vs "楼栋1")会导致授权链路阻断。
- **必须**在管理端提供“房源映射绑定/同步”功能,预先在数据库中持久化 `localDormId ↔ dmsPlaceId` 的权威主键映射。
### 3.2 门锁映射:支持“一房多锁”模型 (`One Room to Multi-Locks`)
DMS 接口 `POST /rest/doorlock/list`(入参 `placeId`)返回的是一个门锁列表:`rows: [...]`。
这证实了物理现实:**一个公寓房间可能没有门锁(0把)、有一把门锁(1把)、或者有多个门锁(例如外防盗门锁 + 内卧室锁,多把锁)**。
- **错误做法**:在 `train_dormitory_info` 中仅保留单一的 `lock_device_sn` 字符串,假设一房仅有一锁。
- **推荐设计**:
- 系统必须能够处理一个 `placeId` 对应 `0..N` 把门锁的情况。
- 当为学员下发该房间的开门权限时,集成适配器应查询该房间绑定的**所有有效门锁**,并为每把锁分别申请/下发开门凭据,生成对应的 `DoorAuthorization` 记录。
---
## 4. 详细字段级映射字典 (Field-Level Mapping Dictionary)
### 4.1 宿舍/房间信息映射
| RailwaySystem 本地字段 | DMS 对应字段 | 类型转换 / 映射规则 | 说明 |
| :--- | :--- | :--- | :--- |
| `train_dormitory_info.dorm_id` | `mapping.local_dorm_id` | `VARCHAR(32)` (UUID) | 本地主键 |
| - | `dms.place.id` / `placeId` | `BIGINT` | DMS 房间全局唯一主键 |
| `train_dormitory_info.dorm_building` | `dms.place.building` | 字符串精确比对/绑定 | 宿舍楼栋名 |
| `train_dormitory_info.dorm_floor` | `dms.place.floor` | 字符串精确比对/绑定 | 所在楼层 |
| `train_dormitory_info.dorm_room` | `dms.place.room` | 字符串精确比对/绑定 | 房间号 |
| - | `dms.place.projectId` | `BIGINT` | 系统级受控站点配置 |
### 4.2 门锁设备信息映射
| RailwaySystem 本地概念 | DMS 对应字段 | 类型 / 格式 | 说明 |
| :--- | :--- | :--- | :--- |
| `mapping.external_lock_id` | `doorlock.id` | `BIGINT` | DMS 门锁管理 ID |
| `train_dormitory_info.lock_device_sn` | `doorlock.hardwareId` | `VARCHAR(64)` (如 `5C9F539A`) | 门锁硬件 SN / 通信 ID |
| `device_battery` | `doorlock.battery` | `INT` (0~100, 255为未知) | 门锁电量 |
| `device_signal` | `doorlock.signal` | `INT` (-100~0, -128为未知) | 信号强度 (RSSI) |
### 4.3 门禁授权与钥匙下发映射 (Online Password 场景)
| RailwaySystem 本地字段 | DMS 对应入参 / 字段 | 映射规则与约束 |
| :--- | :--- | :--- |
| `train_accommodation.accommodation_id` | - | 本地业务事实归属 |
| `target_lock_id` | `create.lockId` | 目标门锁的 DMS ID |
| `auth_start` (配合提早 Buffer) | `create.validStart` | 毫秒级时间戳 (`Long`) |
| `auth_end` (配合延后 Buffer) | `create.validUntil` | 毫秒级时间戳 (`Long`) |
| `student_name` | `create.holder` | 学员姓名 (可选,建议填写真实姓名) |
| - | `create.canOpenAntilock` | 固定设为 `false` (不可开反锁,保障私密安全) |
| `phone_country` | `create.phoneCountry` | 默认 `+86` |
| `student_phonenumber` | `create.phoneNumber` | 学员手机号 |
| `door_password` (一次性生成或安全受控) | `create.password` | 系统可生成4~8位纯数字密码,Pilot建议采用6位限时密码 |
| `credential_sub_type` | `create.subType` | 固定为 `4` (限时密码) |
| `external_key_id` | 返回的 `data.id` | DMS 钥匙全局 ID (退宿撤权时作为删除入参) |
| `dms_operation_id` | 返回的 `data.operationId` | DMS 异步下发任务 ID (用于10秒后状态轮询) |
---
## 5. Wave 1 Implementation Result (Wave 1 implemented)
- **阶段状态**:Wave 1 implemented(外部资源映射模型与外部操作流水持久化模型已交付并验证)。
- **资源映射基数与引用模型 (`RESOURCE_MAPPING_REFERENCE_MODEL = DORM_ID_FK`)**:
- 本地采用 `dorm_id` 外键强关联 `train_dormitory_info`,确保一期完整性与零类型歧义。
- **房间映射基数**:`ROOM_MAPPING_CARDINALITY = Dormitory 1:0..1 Active Room (Dormitory 1:N History)`,同一宿舍在同一 Vendor 下最多只能有 1 个处于 ACTIVE 状态的房间映射(通过局部唯一索引 `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`,同一外部资源在同一厂商下严禁跨房间重复 ACTIVE 绑定(通过局部唯一索引 `uq_ext_res_map_active_ext_res` 保障)。
- **操作流水基数与引用模型 (`OPERATION_REFERENCE_MODEL = DOOR_AUTH_ID_FK`)**:
- 本地采用 `door_auth_id` 外键强关联 `train_dorm_auth`,业务类型默认为 `DOOR_AUTH`。
- **操作流水基数**:`DOOR_AUTH_OPERATION_CARDINALITY = DoorAuthorization 1:N ExternalOperation`,每次具体下发、重试、撤销均保留独立流水行,禁止直接覆写。
- **状态机与幂等**:`OPERATION_STATUS_MODEL` 包含从 `CREATED` 到 `SUCCESS/FAILED/TIMEOUT/RETRY_WAIT` 的合法单向流转(`REVOKED` 属于 `DoorAuthorization` 业务状态,单次注销流水为 `operation_type=REVOKE` 达成 `SUCCESS`),终态不可逆;`idempotency_key` 唯一索引全局防重。
- **敏感数据治理**:不存 raw JSON、密码明文、卡号明文、Token、Secret,敏感字段数为 0。
- **数据库迁移安全**:`008_external_resource_operation.sql` 已通过静态契约测试与幂等验证,生产库保持零变更。