Files
railwaysystem/docs/development/dms-r3/14_decision_summary.md
T

239 lines
19 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.
# 14 DMS 门禁集成审计与设计决策一页纸总结 (Decision Summary)
> **面向对象**:项目负责人、架构指导老师、业务决策评审委员会
> **核心宗旨**:以最短篇幅、最清晰视角,讲透“现状、能力、接入边界、推荐架构与实施规划”。
---
## 1. 当前系统有什么?(Current RailwaySystem Assets)
1. **业务与数据基线**:
- 当前生产分支基于 `adcf930e`,已完成 V2 数据库结构迁移。
- 物理表层:`train_dormitory_info`(宿舍楼栋/房间台账)、`train_accommodation`(学员参训住宿分配事实表)、`train_dorm_auth`(门禁授权表,外键绑定 `accommodation_id`)已在 Kingbase 物理库建表。
- 业务逻辑层:已具备完整的培训计划编排、学员报到核验、宿舍入住分配与床位容量实时统计能力。
2. **本地模型漂移与前置对齐要求 (`LOCAL_DOMAIN_ALIGNMENT_REQUIRED = YES`)**:
- **`train_accommodation`**:DB 物理表已建,但 Java 层尚无对应的实体类、Mapper 与业务 Service,未形成完整业务层。
- **`train_dorm_auth`**:DB 已升级为以 `accommodation_id` 为外键的 V2 结构,但 Java 实体类与 XML Mapper 仍残留 V1 字段(`student_id`, `plan_id`, `dorm_id`)。
- **决策红线**:在真正编写外部 DMS Adapter 之前,**必须在 Wave 0 优先完成本地住宿与门禁授权领域模型对齐**,严禁将外部适配器直接架设在漂移模型上。
---
## 2. DMS 平台能提供什么?(DMS IoT Platform Capabilities)
外部 DMS 智能门锁平台(`https://dms.dmsiot.com`)具备完整的 RESTful 接口体系:
1. **楼栋-楼层-房间-门锁全层级资产查询**(可精确匹配房间获取 `placeId`,支持一房多锁)。
2. **多模态电子钥匙下发能力**:
- **在线限时密码 (Online Password)**:系统可生成 4~8 位纯数字密码(Pilot 建议采用 6 位限时密码),指定起止有效时间,支持远程主动注销。
- **在线实体芯片卡 (Online Card)**:支持将学员 IC/M1 卡按小端十六进制编码下发至锁具,支持远程主动注销。
- **二维码/手机钥匙 (QR Key)**:端点已定义 (`POST /rest/doorlock/qrcode/create` 与 `POST /rest/qrcode/delete`),分类为 `DOCUMENTED`,但返回体存在数据污染且无具体二维码载荷,适用性归为 `NEEDS_BUSINESS_CONFIRMATION`。
3. **低功耗异步执行与对账体系**:门锁基于电池无线通信,下发指令后返回 `operationId`,要求 10 秒后通过接口查询终态。
4. **设备运行指标**:提供门锁电池电量、信号强度及门锁开门记录流水拉取。
---
## 3. 一期建议先接什么?暂时不接什么?(Scope Boundary)
```
+-------------------------------------------------------------------------------+
| 一期重点纳入 (P0 / P1) |
| 1. 认证鉴权: Token 内存单飞排他锁缓存 + 滑动刷新 + 401 单次重试自愈 |
| 2. 房源映射: 本地宿舍与 DMS placeId / lockId 的预绑定持久化 (支持一房多锁) |
| 3. 首选凭据: 【在线限时密码】(零硬件与发卡器依赖, 支持4~8位/推荐6位, 随时主动撤销) |
| 4. 异步对账: 后台调度器 10 秒后异步轮询 operationId,消除 HTTP 线程阻塞 |
| 5. 换房补偿: 严格遵循"先发新房权限、确认生效后再撤旧房"原则,避免学员无处可住 |
| 6. 结训撤权: 培训结束主动派发 Revoke 指令,同时以 validUntil 作为有效期边界兜底 |
| 7. 设备巡检: 门锁电量与信号查询 (P1),辅助宿管员发现低电量设备 |
+-------------------------------------------------------------------------------+
▲
【严格划清边界】
▼
+-------------------------------------------------------------------------------+
| 一期明确排除 (P2 / Out of Scope / Needs Confirmation) |
| 1. 取电开关全套接口 (P2): 涉及强电改造,与学员核心学籍住宿无关,暂缓接入 |
| 2. 远程网络开锁 (P2): 属于高危主动开门操作,一期严格封存,仅设计受控两票制应急方案 |
| 3. 二维码钥匙 (Needs Biz Confirm): 接口已文档化但载荷污染,一期不作首选方案 |
| 4. 离线密码与离线发卡 (Out of Scope): 缺乏可靠远程主动撤销机制,增加现场运维负担 |
+-------------------------------------------------------------------------------+
```
---
## 4. 需要向甲方与业务负责人确认的核心问题 (Blocking Decisions)
1. **下发触发时机 (Q-BLK-03)**:
- *推荐意见*:**学员在现场完成《报到核验》后自动触发门禁下发**。确保人已到校才给钥匙,杜绝未报到人员提前进房。
2. **首期钥匙类型 (Q-BLK-02)**:
- *推荐意见*:首选采用**系统派发 6 位在线限时密码**(学员移动端个人中心受控查看);若后续统一下发实体卡,可平滑启用在线卡片模式。
3. **主数据权威归属 (Q-NON-01)**:
- *推荐意见*:**坚决以 RailwaySystem 作为住宿与房间主数据**,DMS 仅作为外部执行端,严禁让 DMS 状态覆盖本地系统。
4. **现场网络连通性与沙箱 (Q-BLK-01 & Q-BLK-05)**:
- 生产环境机房是否允许访问公网 `dms.dmsiot.com`?供应商必须提供包含测试网关与实验台门锁的**独立联调测试沙箱**。
5. **设备离线时 `validUntil` 过期语义 (Q-BLK-06)**:
- 门锁离线断网时,到达 `validUntil` 是否由锁端本地时钟强制失效?需供应商提供硬件保证级别说明。
---
## 5. 推荐技术架构设计
```mermaid
flowchart TD
UI["前端 Accommodation / 个人中心"]
-->|"业务动作触发"| Domain["业务领域层 (绝对 Source of Truth)<br/>住宿分配 / 换房补偿 / 报到联动"]
-->|"强类型 DTO 屏蔽厂商细节"| Gateway["中立网关接口: DoorAccessGateway<br/>(grant / revoke / query)"]
-->|"SPI 适配器实现"| Adapter["DMS 适配器: DmsDoorAccessGateway<br/>- DmsTokenManager (进程级单飞排他锁)<br/>- 细分 HTTP 客户端 (Place / Lock / Key / Op)<br/>- DmsOperationPoller (10秒后台对账调度)"]
-->|"HTTPS / Cookie 传递 Token"| External["外部 DMS 物联网平台<br/>(https://dms.dmsiot.com)"]
```
---
## 6. 后续 R.3B 四阶段推进路线图 (Four Waves, 约 15~16 人天)
1. **Wave 0: Local Domain Alignment (本地领域模型对齐,3天)**:
- 实现 `TrainAccommodation` Java 实体/Mapper/Service;升级并清洗 `TrainDormAuth` 对齐 V2 数据库;确立住宿事实与门禁授权的清晰边界。
2. **Wave 1: External Mapping & Operation Persistence (外部映射与持久化,2天)**:
- 创建 `train_external_resource_mapping` 与 `train_external_operation` 物理表及 Migration 脚本;落地双层状态机模型。
3. **Wave 2: DMS Adapter & Mock Tests (DMS 适配器与离线仿真,5天)**:
- 配置与环境变量绑定;`DmsTokenManager` 单飞排他锁并发控制;细分 HTTP 客户端;在线密码/卡片适配器;10 秒轮询调度器;WireMock 22 项单测全覆盖。
4. **Wave 3: Business Orchestration & UI & Sandbox (业务编排、界面与现场验收,5~6天)**:
- 报到联动授权下发;“先发新房后撤旧房”换房补偿;退训/结训撤权;管理端门禁综合视图;学员端 30 秒倒计时密码受控查看;现场实验台沙箱 E2E 闭环验收。
### 6.1 Wave 0 Implementation Result (本地领域模型闭环)
- **基线与状态**:
- 成功消除数据库 V2 模型与 Java 代码之间的全部结构漂移。
- 严格保持 0 DMS 耦合:无 DmsClient、无 TokenManager、无外部调用、无假设备成功。
- 保持 Kingbase 生产库只读,零 Migration(`MIGRATION_REQUIRED = NO`)。
- 交付本地领域模型:`TrainAccommodation` 与升级后的 `TrainDormAuth`。
### 6.2 Wave 0.1 Accommodation History & Final Checkpoint Decision (基数模型升级与终态)
- **核心基数关系冻结**:
- `Enrollment 1:N Accommodation History`:允许学员产生多次历史住宿记录(调房/换房留痕)。
- `Enrollment 1:0..1 Active Accommodation`:同一时刻最多一条有效住宿(`accommodation_status IN (0, 1)`)。
- `Accommodation 1:0..1 DoorAuthorization`:门禁授权强引用住宿实体。
- `TIME_RANGE_SUPPORT = LIMITED`:当前 Kingbase 物理表仅有 `assigned_time`,无 `start_time` / `end_time` 列,时间窗口支持为有限支持。
- **006 Migration 方案设计**:
- 脚本路径:`RuoYi-Vue/sql/system/kingbase/006_accommodation_history_constraint.sql`。
- 安全序号:`006`(001-003 为基线,004-005 已被 R.2 分支占用)。
- 迁移安全护栏:前置重复 active 检查抛异常、仅在定义匹配时安全 DROP `uq_accommodation_enroll`、创建 `uq_accommodation_enroll_active` 局部唯一索引(`WHERE accommodation_status IN (0, 1)`)、全流程幂等。
- 生产执行纪律:**禁止在生产库执行 006 迁移脚本**,本 Wave 仅作资产与契约验证储备。
- **质量与构建验收**:
- 单元与契约测试总数:174 项,全部 PASS(0 failures, 0 errors)。
- Maven `clean test` 与 `clean package` 均顺利构建成功。
### 6.3 Wave 1 External Resource Mapping & Operation Persistence (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)`。
- 门锁映射基数:`LOCK_MAPPING_CARDINALITY = Dormitory 1:0..N DoorLocks`(支持一房多锁)。
- 外部资源防冲突:`EXTERNAL_RESOURCE_ACTIVE_UNIQUENESS = Vendor + External Resource ID + Active Unique`。
- 操作流水模型:`OPERATION_REFERENCE_MODEL = DOOR_AUTH_ID_FK`,以 `door_auth_id` 外键强关联 `train_dorm_auth`。
- 授权操作基数:`DOOR_AUTH_OPERATION_CARDINALITY = DoorAuthorization 1:N ExternalOperation`。
- 操作类型与状态分离:`OPERATION_TYPE_MODEL = GRANT, REVOKE`;`EXTERNAL_OPERATION_STATUS_MODEL = CREATED, REQUESTED, ACCEPTED, PENDING, RETRY_WAIT, SUCCESS, FAILED, TIMEOUT`(`REVOKED` 属于 `DoorAuthorization` 业务凭据状态,不属于单次操作执行结果,注销操作的执行结果为 `operation_type=REVOKE` 达成 `SUCCESS`)。
- 本地幂等模型:`IDEMPOTENCY_MODEL` 基于 `idempotency_key` 唯一索引防重。
- 轮询检索支持:`PENDING_OPERATION_QUERY` 准备就绪,本 Wave 保持 0 调度器运行。
- **安全红线保持**:
- 敏感字段:`SENSITIVE_OPERATION_FIELD_COUNT = 0`。
- 外部耦合:`DMS_HTTP_CLIENT_COUNT = 0`,`DMS_TOKEN_MANAGER_COUNT = 0`。
- 生产数据库:只读保护,`PRODUCTION_DB_MODIFIED? NO`。
- **Migration 证据与准入准则**:
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`(生产与非隔离库绝不盲跑 DDL)。
- `008_CONTRACT_TEST = PASS`,`008_IDEMPOTENCY_STATIC_VERIFICATION = PASS`。
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`(在正式生产投产前必须在隔离 Kingbase 完成双跑验证)。
- **测试与验证指标**:
- 测试套件全部通过:`TOTAL_TEST_COUNT = 203` (ruoyi-train: 175, ruoyi-admin: 28),Failures = 0, Errors = 0, Skipped = 0。
- Maven 构建:`clean test` 与 `clean package` 均 `SUCCESS`。
- 代码格式校验:`git diff --check = PASS`。
- 阶段评审状态:`R3_WAVE1_REVIEW = PASS`。
- 提交状态:`R3_WAVE1_COMMIT = COMMITTED` (SHA: 22bcd1bacb66826e8e911454e9c01b54c8b64b32)。
### 6.4 Wave 2 DMS Adapter, Token, Poller & Mock Integration (Wave 2 implemented against Mock DMS only)
- **阶段状态**:`R3_WAVE2_STATUS = PASS`。
- **配置与安全默认**:
- `DMS_DEFAULT_ENABLED = FALSE`:默认不开启 DMS 通信与轮询。
- `DMS_PROPERTIES`:完整实现受控配置,零硬编码密钥,参数化超时与刷新窗口。
- **令牌与通信架构**:
- `TOKEN_TRANSPORT_MODEL` = `Cookie: token={token}`(严禁使用未核实的 `Authorization: Bearer`)。
- `TOKEN_CACHE_MODEL` = `SINGLE_PROCESS_IN_MEMORY`(全应用单一集中缓存)。
- `TOKEN_SINGLE_FLIGHT_MODEL` = `REENTRANT_LOCK_DCL`(高并发过期单飞防击穿,实测 20 线程并发登录仅 1 次)。
- `TOKEN_TTL_MODEL` = `7200S_WITH_REFRESH_SKEW`(滑动窗口提前 120 秒平滑单飞刷新)。
- `SUCCESSFUL_API_EXTENDS_LOCAL_TTL` = `YES`(成功调用 DMS API 顺延本地失效时间戳)。
- `AUTH_FAILURE_REFRESH_MODEL` = `INVALIDATE_THEN_SINGLE_FLIGHT`(401 集中失效并在下一次请求单飞获取新 Token,无 login storm)。
- `AMBIGUOUS_MUTATION_AUTO_RETRY` = `NO`(写操作读取超时坚决抑制自动重发,防止重复生成门锁凭据)。
- `DMS_HTTP_CLIENT` = `DmsHttpClient` 统一底层通信与全局脱敏日志(零明文密码/Token/Secret 泄露)。
- **设备与凭据适配能力**:
- `LOCATION_ADAPTER` = `DmsLocationClient` / `ExternalLocationGateway`(Building, Floor, Room 空间定位与 placeId 解析)。
- `DOORLOCK_ADAPTER` = `DmsDoorLockClient` / `ExternalDoorLockGateway`(支持 0 把锁空列表与多把锁完整返回)。
- `ONLINE_PASSWORD_CREATE` = `DmsPasswordClient` / `ExternalCredentialGateway`(支持 4~8 位数字限时密码下发,密码零落库)。
- `ONLINE_PASSWORD_DELETE` = `DmsPasswordClient` / `ExternalCredentialGateway`(注销并返回 operationId)。
- `OPERATION_STATUS_QUERY` = `DmsOperationStatusClient`。
- `VENDOR_STATUS_MAPPING` = `DmsOperationStatusMapper`(集中统一映射,未知状态判定为 UNKNOWN,绝非 SUCCESS)。
- **异步轮询与测试模拟**:
- `POLLER_IMPLEMENTED` = `DmsOperationPoller`(条件化启动,单进程并发防重,终态不可篡改保护,指数退避重试)。
- `POLLER_INITIAL_DELAY` = 10s。
- `MULTI_INSTANCE_POLLER_LOCK` = `FUTURE_GATE`。
- `MOCK_DMS_IMPLEMENTED` = `MockDmsServer`(纯轻量 JDK 原生模拟,登录互斥、短 TTL、多锁/零锁与状态流转)。
- **测试与质量指标**:
- `OLD_TEST_COUNT = 203`
- `NEW_TEST_COUNT = 26`
- `TOTAL_TEST_COUNT = 229` (ruoyi-train: 201, ruoyi-admin: 28),Failures = 0, Errors = 0, Skipped = 0。
- `MAVEN_TEST = SUCCESS`,`MAVEN_PACKAGE = SUCCESS`。
- `git diff --check = PASS`。
- **环境纪律与迁移标记**:
- `LIVE_DMS_REQUEST_COUNT = 0`(全生命周期未对公网真实 DMS 发起任何请求)。
- `FRONTEND_MODIFIED = 0`,`CONTROLLER_COUNT = 0`。
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`。
- `COMMIT? NO`,`PUSH? NO`,`DEPLOY? NO`(严格 HOLD 等待审查)。
### 6.5 Wave 3A Access Authorization Orchestration Dry-Run (Wave 3A implemented against Mock DMS only)
- **阶段状态**:`R3_WAVE3A_STATUS = PASS`。
- **架构与模型扩展**:
- `AUTH_SCHEMA_SUFFICIENT = NO`(基线 `train_dorm_auth` 缺失凭据类型、细粒度状态、关联操作流水ID及版本号)。
- `009_MIGRATION_REQUIRED = YES`。
- `009_MIGRATION_FILE` = `RuoYi-Vue/sql/system/kingbase/009_dorm_authorization_orchestration.sql`(安全编号 009,幂等增量,纯非破坏性,零明文密钥)。
- `AUTHORIZATION_STATUS_MODEL` = `PENDING_GRANT / GRANTING / ACTIVE / PENDING_REVOKE / REVOKING / REVOKED / ERROR`(厂商中立领域授权生命周期)。
- `CREDENTIAL_TYPE_MODEL` = `ONLINE_PASSWORD`(未来可扩展 QR, CARD, MOBILE_KEY)。
- `VALIDITY_WINDOW_SOURCE` = `BUSINESS_POLICY_REQUIRED`(一期从培训计划与住宿业务时间推导,未决策略显式记录)。
- **统一编排与驱动机制**:
- `RECONCILIATION_MODEL` = `AccessAuthorizationOrchestrator.reconcile(enrollmentId)`(状态驱动对账,解耦业务事件发生顺序)。
- `GRANT_PRECONDITIONS` = `Enrollment status=1 (已报到) + Active Accommodation (status 0/1) + Active Room Mapping + Exactly 1 Primary DoorLock Mapping + 有效期可推导`。
- `REPORT_CONFIRM_TRIGGER` = `reconcile(enrollmentId)`(报到核验通过后驱动,异步对账)。
- `ACCOMMODATION_TRIGGER` = `reconcile(enrollmentId)`(分配或激活住宿后驱动)。
- `REPORT_CONFIRM_EXTERNAL_FAILURE_ROLLBACK` = `NO`(外部网络/DMS 失败绝不回滚报到主事务)。
- `LOCK_SELECTION_POLICY` = `PRIMARY_LOCK_ONLY`(Least Privilege 原则,严格锁定唯一定义的主门锁发码,0主锁与多主锁阻断)。
- `RECONCILE_IDEMPOTENT` = `YES`(多事件/重复报到触发多次 reconcile 结果幂等,无重复授权、无重复发码)。
- **状态流转与时序保护**:
- `GRANT_HTTP_SUCCESS_MARKS_ACTIVE` = `NO`(HTTP 受理仅标记为 GRANTING/ACCEPTED,Poller 确认后方推进 ACTIVE)。
- `GRANT_SUCCESS_RESULT` = `ExternalOperation=SUCCESS, DoorAuthorization=ACTIVE`。
- `GRANT_FAILED_RESULT` = `ExternalOperation=FAILED, DoorAuthorization=ERROR (保留上下文待重试)`。
- `GRANT_TIMEOUT_RESULT` = `ExternalOperation=TIMEOUT, DoorAuthorization=ERROR (保留上下文待重试)`。
- `REVOKE_SUCCESS_RESULT` = `ExternalOperation=SUCCESS, DoorAuthorization=REVOKED`。
- `REVOKE_FAILED_RESULT` = `ExternalOperation=FAILED, DoorAuthorization=ERROR/REVOKING`。
- `STALE_OPERATION_RESULT_GUARD` = `LATEST_OPERATION_OWNERSHIP_AND_VERSION_CHECK`(防迟到旧操作结果覆盖已推进的新状态)。
- `LONG_DB_TRANSACTION_AROUND_HTTP` = `NO`(本地短事务写库 -> 外部调用 -> 本地短事务更新,绝不用长事务包裹网络 IO)。
- **换房调宿与高可用**:
- `TRANSFER_POLICY` = `GRANT_NEW_THEN_REVOKE_OLD`(先下发新宿舍门禁,对账确认 ACTIVE 后才撤销旧宿舍)。
- `TRANSFER_FAILURE_PRESERVES_OLD_ACCESS` = `YES`(新宿舍发码失败严格保留旧宿舍门禁,保障学员可用性)。
- `TRANSFER_DUAL_ACCESS_WINDOW` = `ACKNOWLEDGED_FOR_AVAILABILITY`(可用性优先,审计留痕)。
- **凭据安全与零泄露**:
- `PASSWORD_GENERATOR` = `SecurePasswordGenerator`(`SecureRandom` 强随机、6 位纯数字、杜绝弱密码)。
- `PLAINTEXT_PASSWORD_PERSISTENCE_COUNT` = `0`(数据库、流水表、日志、异常栈、toString 全链路零明文留存)。
- **未决业务与环境门禁**:
- `CREDENTIAL_DELIVERY_POLICY` = `OPEN_QUESTION`。
- `LEAVE_AUTO_REVOKE_POLICY` = `OPEN_QUESTION`。
- `TOKEN_COOKIE_NAME` = `VENDOR_CONFIRMATION_REQUIRED`。
- `LIVE_DMS_REQUEST_COUNT` = `0`(100% Mock DMS 验证)。
- `FRONTEND_MODIFIED` = `0`。
- **全量测试指标**:
- `OLD_TEST_COUNT = 233`
- `NEW_TEST_COUNT = 18`
- `TOTAL_TEST_COUNT = 251` (ruoyi-train: 219, ruoyi-admin: 32)
- `FAILURES = 0`,`ERRORS = 0`,`SKIPPED = 0`。
- `MAVEN_TEST = SUCCESS`,`MAVEN_PACKAGE = SUCCESS`。
- `git diff --check = PASS`。
- **环境纪律与迁移标记**:
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `009_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`009_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`。
- `COMMIT? NO`,`PUSH? NO`,`DEPLOY? NO`(严格 HOLD 等待审查)。