Files
railwaysystem/docs/development/dms-r3/05_adapter_architecture.md
T

210 lines
11 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.
# 05 门禁集成适配器架构与厂商无关接口设计 (Adapter Architecture)
> **设计核心**:**强边界解耦与高内聚设计**。
> 住宿业务层(Accommodation Service)对外部物联网厂商的协议细节、Token、URL、整数请求类型完全无感知。
> 针对门禁控制定义**厂商中立的抽象网关接口 (`DoorAccessGateway`)**,DMS 专用协议转换完全收敛在 `DmsAccessAdapter`。
---
## 1. 系统分层集成架构图 (Mermaid)
```mermaid
flowchart TD
subgraph UI_Layer["前端与接入层 (Web / Controller)"]
AC["TrainAccommodationController\n(住宿分配 / 换房 / 退宿)"]
DC["TrainDoorAccessController\n(门禁状态查询 / 重试 / 受控应急)"]
end
subgraph Business_Layer["核心业务领域层 (Domain Service)"]
AS["TrainAccommodationService\n(维护住宿事实真源 / 状态推进)"]
AGS["DoorAccessOrchestrationService\n(授权编排: 提早下发 / 补偿 / 批量处理)"]
end
subgraph Gateway_Boundary["厂商无关抽象边界 (Vendor-Agnostic Gateway)"]
DAG["<<interface>>\nDoorAccessGateway\n+grantAccess(GrantCommand): OperationResult\n+revokeAccess(RevokeCommand): OperationResult\n+queryOperation(QueryCommand): OperationStatusResult\n+queryLockHealth(HealthCommand): LockHealthResult\n+remoteUnlock(UnlockCommand): OperationResult"]
end
subgraph Integration_DMS["DMS 专用集成适配层 (ruoyi-train/integration/dms)"]
ADAPTER["DmsDoorAccessGateway\n(协议转换 / 参数映射 / 状态归一化)"]
subgraph DMS_Core["DMS 核心支撑组件"]
TM["DmsTokenManager\n(单飞互斥登录 / 缓存 / 401自愈)"]
PROP["DmsProperties\n(受控环境变量配置)"]
POLLER["DmsOperationPoller\n(Spring/Quartz 10秒对账轮询器)"]
end
subgraph Clients["细分领域 HTTP 客户端 (Feign / RestTemplate)"]
PLC["DmsPlaceClient\n(楼栋/楼层/房间查询)"]
DLC["DmsDoorLockClient\n(门锁列表/电量信号)"]
KC["DmsKeyClient\n(在线密码/在线卡片)"]
OPC["DmsOperationClient\n(query-operation-status)"]
end
end
subgraph Remote_DMS["外部物联网云平台 (DMS IoT Platform)"]
CLOUD["DMS REST API\n(https://dms.dmsiot.com)"]
end
AC --> AS
DC --> AGS
AS --> AGS
AGS --> DAG
DAG <|.. ADAPTER : 实现
ADAPTER --> TM
ADAPTER --> PROP
ADAPTER --> PLC
ADAPTER --> DLC
ADAPTER --> KC
ADAPTER --> OPC
POLLER --> DAG
PLC --> CLOUD
DLC --> CLOUD
KC --> CLOUD
OPC --> CLOUD
```
---
## 2. 厂商无关内部标准接口契约 (`Vendor-Agnostic Interface`)
为确保沈阳房建公寓段学员管理系统未来平滑支持其他设备供应商(如海康威视、大华或铁路专用门禁系统),设计中立门禁网关接口 `DoorAccessGateway`:
```java
package com.ruoyi.train.integration.gateway;
import com.ruoyi.train.integration.gateway.dto.*;
/**
* 厂商中立门禁系统服务网关接口
* 严禁在该接口及方法签名中暴露任何具体厂商(如 DMS)的特定字段、类名或数字枚举。
*/
public interface DoorAccessGateway {
/**
* 下发门禁通行授权 (支持在线密码、在线卡片等凭据)
*
* @param command 统一授权下发指令 (包含目标房间、门锁标识、学员信息、时间窗口、凭据要求)
* @return 异步操作受理结果 (包含本地操作ID、外部受理ID、初步状态)
*/
AccessOperationResult grantAccess(GrantAccessCommand command);
/**
* 撤销已生效或下发中的门禁授权 (退宿、退训、换房时调用)
*
* @param command 统一撤销指令 (包含外部钥匙ID、目标硬件标识等)
* @return 异步操作受理结果
*/
AccessOperationResult revokeAccess(RevokeAccessCommand command);
/**
* 统一查询异步操作执行状态 (对账轮询)
*
* @param command 操作状态查询指令 (包含 operationId, hardwareId, 原始业务类型)
* @return 归一化后的设备执行结果 (SUCCESS, UNDONE, FAILED, TIMEOUT)
*/
OperationStatusResult queryOperationStatus(QueryOperationCommand command);
/**
* 查询指定房间/门锁的物理运行健康度 (电量、信号、在线状态)
*
* @param command 查询健康度指令
* @return 归一化设备健康度指标
*/
DeviceHealthResult queryDeviceHealth(QueryHealthCommand command);
/**
* 紧急远程开锁 (受控应急功能,仅限特定权限管理员并记录审计追踪)
*
* @param command 远程开锁指令 (必须携带操作人、两票审核号、操作原因)
* @return 异步操作受理结果
*/
AccessOperationResult remoteUnlock(RemoteUnlockCommand command);
}
```
---
## 3. 推荐工程目录结构规范 (`Package Structure`)
后续 R.3B 实现时,建议在 `ruoyi-train` 模块中创建如下包结构,本阶段仅冻结设计,**禁止创建 Java 文件**:
```
ruoyi-train
└── src/main/java/com/ruoyi/train
├── domain # 领域实体 (TrainAccommodation, TrainDormitoryInfo, etc.)
├── service # 核心业务服务 (ITrainAccommodationService, etc.)
└── integration # 外部集成根包
├── gateway # 【厂商无关抽象层】
│ ├── DoorAccessGateway.java
│ ├── dto # 统一标准入参/出参 DTO
│ │ ├── GrantAccessCommand.java
│ │ ├── RevokeAccessCommand.java
│ │ ├── AccessOperationResult.java
│ │ └── OperationStatusResult.java
│ └── enums # 标准业务枚举 (CredentialType, NormalizedOpStatus)
└── dms # 【DMS 厂商专用集成适配层】
├── config # 配置类 (DmsProperties, DmsConfiguration)
├── auth # 认证与 Token 管理 (DmsTokenManager, DmsTokenResponse)
├── client # 底层 HTTP 交互客户端
│ ├── DmsBaseClient.java
│ ├── DmsPlaceClient.java
│ ├── DmsDoorLockClient.java
│ ├── DmsKeyClient.java
│ └── DmsOperationClient.java
├── dto # DMS 原始报文映射 DTO (只在 integration.dms 内部可见)
│ ├── request # DMS 专用请求实体 (如 DmsOnlinePasswordCreateRequest)
│ └── response # DMS 专用响应实体 (如 DmsApiResponse, DmsKeyData)
├── adapter # 核心适配器实现
│ └── DmsDoorAccessGateway.java (实现 DoorAccessGateway 接口)
├── scheduler # 异步任务与定时对账
│ └── DmsOperationPoller.java (10秒延迟轮询与超时处理)
└── util # 专用编码工具 (CardNumberCodec: 读卡器十进制与16进制小端转换)
```
---
## 4. 适配器边界与职责隔离红线 (`Adapter Boundary Rules`)
1. **DTO 边界完全隔离**:
- DMS 的返回体对象(如 `DmsApiResponse<DmsKeyData>`)的生命周期**绝不允许超出 `com.ruoyi.train.integration.dms` 包范围**。
- `DmsDoorAccessGateway` 必须负责将其翻译为中立的 `AccessOperationResult`,禁止向上层抛出 DMS 内部原始 JSON。
2. **状态与数字编码归一化**:
- DMS 使用数字 `requestType`(1=下发密码, 2=在线卡片, 3=删除密码, 5=远程开锁)。业务 Service **严禁传递数字 1 或 3**,必须传递强类型枚举 `CredentialOperationType.PASSWORD_GRANT`。适配器内部完成枚举到数字 `requestType` 的转换。
- DMS 返回的 `operationStatus` 字符串(`"undone"`, `"success"`, `"failed"`, `"timeout"`)由适配器转换为内部标准领域枚举 `NormalizedOperationStatus`。
3. **第三方异常吞吐隔离**:
- 外部网络超时、401 故障、DMS 内部报错统一由适配器包装为 `IntegrationException` 及其子类,不得泄露第三方平台的底层堆栈至前端。
---
## 5. Wave 1 Implementation Result (Wave 1 implemented)
- **阶段状态**:Wave 1 implemented。
- **持久化层落地**:
- 完成外部资源映射实体 `TrainExternalResourceMapping` 与操作流水实体 `TrainExternalOperation`。
- 完成持久化 Mapper 与 Service (`IExternalResourceMappingService`, `IExternalOperationService`)。
- 落地 `train_external_resource_mapping`(支持一房多锁、Active 唯一性)与 `train_external_operation`(支持 DoorAuthorization 1:N 流水追踪)。
- **零外部耦合红线**:
- 本 Wave 严格保持 `DMS_HTTP_CLIENT_COUNT = 0`,`DMS_TOKEN_MANAGER_COUNT = 0`。
- 零 RestTemplate / WebClient / HttpClient 网络调用,零明文敏感凭据。
- **数据库 Migration**:`008_external_resource_operation.sql` 验证通过,生产库未执行。
---
## 6. Wave 2 Implementation Result (Wave 2 implemented against Mock DMS only)
- **阶段状态**:Wave 2 implemented against Mock DMS only。
- **适配层核心交付清单**:
1. `config/DmsProperties`:受控配置中心,`dms.enabled = false`(安全默认),支持参数化超时、提前刷新窗口、Token Cookie 头名称。
2. `auth/DmsTokenManager`:全应用唯一集中 Token 缓存,基于 ReentrantLock + DCL 的 Single-Flight 并发互斥锁,支持滑动延展 (`markSuccessfulUse`) 与 401 集中失效自愈。
3. `client/DmsHttpClient`:基础底层 HTTP 客户端,统一 Cookie 传输,脱敏日志(密码、Token、Secret 全程保护),写操作读取超时严禁盲目自动重试 (`AMBIGUOUS_MUTATION_AUTO_RETRY = NO`)。
4. `client/DmsLocationClient` 与 `gateway/ExternalLocationGateway`:位置资源查询与 Place 空间定位。
5. `client/DmsDoorLockClient` 与 `gateway/ExternalDoorLockGateway`:门锁适配器,严格支持 0 把锁(返回空列表,非 NPE)、1 把锁与多把锁(完整返回全部锁)。
6. `client/DmsPasswordClient` 与 `gateway/ExternalCredentialGateway`:在线限时密码创建与删除适配器,支持 4~8 位数字,密码明文内存短暂留存,零落库零输出。
7. `client/DmsOperationStatusClient` 与 `gateway/DmsOperationStatusMapper`:异步状态查询与集中映射器(success->SUCCESS, undone->PENDING, failed->FAILED, timeout->TIMEOUT, 未知状态->UNKNOWN 严禁判定为 SUCCESS)。
8. `poller/DmsOperationPoller`:异步对账轮询调度器,仅在 `dms.enabled=true` 启用,单进程并发防重,终态不可篡改保护,错误分类退避持久化。
9. `mock/MockDmsServer`:轻量内嵌测试服务器,精准模拟 DMS 服务端登录互斥、读超时、多锁/零锁与状态流转。
- **环境纪律与迁移标记**:
- `DMS_DEFAULT_ENABLED = FALSE`
- `LIVE_DMS_REQUEST_COUNT = 0`
- `FRONTEND_MODIFIED = 0`
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`