# 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 { <> +Long id "DMS placeId" +Long projectId "DMS租户ID (≠ planId)" +String building +String floor +String room } class DmsDoorLock { <> +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` | **优点**:查询直观,单表读取。
**缺点**:污染本地物理宿舍模型;若未来接入其他门禁厂商(如海康、大华),列定义难以复用。 | 可作为一期过渡备选 | | **方案 2:独立 `train_external_resource_mapping` 映射表** | 建立独立映射表:`local_resource_type ('DORMITORY')`, `local_id`, `vendor ('DMS')`, `external_id (placeId)`, `metadata_json` | **优点**:模型高度解耦;本地宿舍资源生命周期不受外部影响;天然支持多厂商与资源解绑/重绑。
**缺点**:需要多做一次关联查询。 | **推荐采纳** | **映射规则明确**: - **严禁**每次下发授权时现场调用 `/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` 已通过静态契约测试与幂等验证,生产库保持零变更。