210 lines
11 KiB
Markdown
210 lines
11 KiB
Markdown
# 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`
|