1066 lines
40 KiB
Markdown
1066 lines
40 KiB
Markdown
# KMS 钥匙柜适配器详细设计文档 v2.1
|
||
|
||
> **版本**: 2.1(完整接口版 + 适配原则审查 + 缺口分析)
|
||
> **日期**: 2025-05-19
|
||
> **数据源**: `doc/对接文档/钥匙管理系统软件接口.docx`(KMS API v1.0.4)
|
||
> **技术栈**: .NET 8 / ASP.NET Core / C#
|
||
> **架构**: IntegrationGateway 适配器模式
|
||
> **覆盖**: KMS 全部 54 个 REST 接口
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 设计目标
|
||
|
||
在 IntegrationGateway 中新增 `KmsAdapter`,将智能钥匙管理系统(KMS)作为第三个子系统接入 SecMPS 整合平台。KMS 通过网关的 `IHasFlatDevices` 上报柜体/锁孔设备树,通过 `IHasAlarms` 上报告警记录,由 Vol.Pro 管理端统一展示和管理。
|
||
|
||
### 1.2 KMS 系统模型
|
||
|
||
```
|
||
KMS 管理平台 (一个 IP:PORT)
|
||
├── 智能钥匙柜 A (locker)
|
||
│ ├── 锁孔 1 (lockhole) → 钥匙 (opener)
|
||
│ ├── 锁孔 2
|
||
│ └── ...
|
||
├── 智能钥匙柜 B
|
||
└── ...
|
||
```
|
||
|
||
实体关系:**柜体(Locker) 1:N 锁孔(Lockhole) 1:1 钥匙(Opener)**
|
||
|
||
### 1.3 技术约束
|
||
|
||
| 约束 | 说明 |
|
||
|------|------|
|
||
| 不修改 Core 接口 | 复用现有 `IHasFlatDevices` + `IHasAlarms` |
|
||
| 不依赖 KMS 运行时 | `dotnet build` 可在无 KMS 环境下通过 |
|
||
| 故障隔离 | KMS 离线不影响 Owl/MC4 适配器运行 |
|
||
| 限流 | 5 QPS 保守值 |
|
||
|
||
---
|
||
|
||
## 2. KMS 接口完整参考
|
||
|
||
### 2.0 认证
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 接口 | `POST /prod-api/getToken` |
|
||
| 参数 | query: `clientId` (必填/string), `clientSecret` (必填/string) |
|
||
| 返回 | `{ code: 200, token: "xxx", msg: "操作成功" }` |
|
||
| 有效期 | 30 分钟 |
|
||
| 使用 | Header: `Authorization: Bearer <token>` |
|
||
|
||
---
|
||
|
||
### 2.18 第三方集成接口(Phase 1 — 8 个)
|
||
|
||
#### 2.18.1 心跳接口
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/heartBeat` |
|
||
| 参数 | 无 |
|
||
| 返回 | `{ code: 200, msg: "success" }` |
|
||
|
||
#### 2.18.2 批量删除员工
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/batchDeleteStaff` |
|
||
| 请求体 | `["staffUuid1", "staffUuid2", ...]` (array\<string\>, 必填) |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.18.3 批量同步员工
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/batchSyncStaff` |
|
||
| 请求体 | `[{ staff data }, ...]` — KMS 员工完整信息的数组 |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.18.4 查询柜体及钥匙信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/getOpenerList` |
|
||
| 请求体 | `{}` |
|
||
| 返回 | `{ code: 200, rows: [ { lockerId, lockerName, lockerCode, lockholeList: [{ lockholeSort, openerId, openerName, openerType, openerState }] } ] }` |
|
||
|
||
#### 2.18.5 查询授权记录列表接口
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/getPermissionList` |
|
||
| 请求体 | 查询条件(可选时间范围) |
|
||
| 返回 | `{ code: 200, total: N, rows: [ { uuid, lockerName, lockholeSort, openerName, staffName, borrowTime, returnTime, type } ] }` |
|
||
|
||
#### 2.18.6 查询借还记录列表接口
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/getRecordList` |
|
||
| 请求体 | 查询条件(可选时间范围) |
|
||
| 返回 | `{ code: 200, total: N, rows: [ { uuid, lockerName, lockholeSort, openerName, staffName, borrowTime, returnTime, type } ] }` |
|
||
|
||
#### 2.18.7 查询报警记录列表接口
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/getWarningList` |
|
||
| 请求体 | 告警记录业务对象(可选时间范围/类型) |
|
||
| 返回 | `{ code: 200, total: N, rows: [ { uuid, lockerName, lockholeSort, openerName, type, warningTime, remark, staffName } ] }` |
|
||
|
||
#### 2.18.8 事件记录接口(第三方登录)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/thirdPlatlogin?username=zhangsan` |
|
||
| 参数 | query: `username` (必填) |
|
||
| 返回 | 登录成功后重定向到 KMS 管理首页 |
|
||
|
||
**设计决策**:2.18.8 是页面重定向接口,不适合 API 对接。改用 B 组接口从 Vol.Pro 发起时传用户名,网关代理请求。
|
||
|
||
---
|
||
|
||
### 2.3 交接记录(2 个)
|
||
|
||
#### 2.3.1 查询交接记录明细列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/handover/handoverInfolist` |
|
||
| 参数 | query: `handoverId` (必填/string), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [{ id, handoverId, openerId, openerName, lockerId, lockerName, lockholeSort, openerType, openerState, lendStaffName, borrowTime }] }` |
|
||
|
||
#### 2.3.2 查询交接记录列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/handover/list` |
|
||
| 参数 | query: `lockerName` (必填/string), `fromUser` (可选/string), `fromUserCard` (可选/string), `toUser` (可选/string), `toUserCard` (可选/string), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [{ id, code, createTime, fromUser, fromUserCard, toUser, toUserCard, ... }] }` |
|
||
|
||
---
|
||
|
||
### 2.4 授权管理(3 个)
|
||
|
||
#### 2.4.1 查询授权记录列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/permission/list` |
|
||
| 参数 | query: `backStaffName` (必填/string), `lendStaffName` (必填/string), `lockerName` (必填/string), `openerCnName` (必填/string), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.4.2 查询授权记录列表(授权人视角)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/permission/listPer` |
|
||
| 参数 | query: `backStaffName` (必填/string), `lendStaffName` (必填/string), `lockerName` (必填/string), `openerCnName` (必填/string), `applyTime` (可选/string), `backTime` (可选/string), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.4.3 远程授权
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/kms/permission/remote` |
|
||
| 请求体 | `PermissionCmdData` 对象(必填) |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
> **远程授权参数对象 (PermissionCmdData)**:包含授权人ID、被授权人ID、钥匙ID、有效期等字段(具体结构需在联调时与 KMS 确认)。
|
||
|
||
---
|
||
|
||
### 2.5 告警记录(1 个)
|
||
|
||
#### 2.5.1 查询告警记录列表(标准版)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/warning/list` |
|
||
| 参数 | query: `openerCnName` (必填/string), `staffName` (必填/string), `warningType` (可选/string), `pageNum` (可选/int), `pageSize` (可选/int), `type` (可选/int, 1=当前告警 2=历史告警) |
|
||
| 返回 | `{ code: 200, total, rows: [{ uuid, lockerName, lockholeSort, openerName, type, warningTime, remark, staffName }] }` |
|
||
|
||
---
|
||
|
||
### 2.6 员工可借/永久授权钥匙(2 个)
|
||
|
||
#### 2.6.1 设置员工可借/永久授权钥匙
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/kms/staffopener/available` |
|
||
| 请求体 | `{ staffIds: [3], openerIds: [1], type: 1 }` — type: 1=可借钥匙, 2=永久授权钥匙 |
|
||
| 返回 | `{ code: 200, msg: "操作成功" }` |
|
||
|
||
#### 2.6.2 查询员工可借/永久授权钥匙
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/staffopener/listall` |
|
||
| 参数 | query: `staffId` (必填/int64), `type` (必填/int) |
|
||
| 返回 | `{ code: 200, data: [{ id, staffId, openerId, type }] }` |
|
||
|
||
---
|
||
|
||
### 2.7 员工管理(5 个)
|
||
|
||
#### 2.7.1 创建员工
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/kms/staff` |
|
||
| 请求体 | 员工业务对象(必填): 包含 name, cardNo, phone, email, deptId, groupId, state 等 |
|
||
| 返回 | `{ code: 200, msg: "操作成功" }` |
|
||
|
||
#### 2.7.2 修改员工
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `PUT` |
|
||
| 路径 | `/prod-api/kms/staff` |
|
||
| 请求体 | 员工业务对象(必填,含 id) |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.7.3 查询员工列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/staff/list` |
|
||
| 参数 | query: `cardNo` (必填/string), `name` (必填/string), `state` (必填/int), `type` (必填/int), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.7.4 删除员工(批量)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `DELETE` |
|
||
| 路径 | `/prod-api/kms/staff/{ids}` |
|
||
| 参数 | path: `ids` — 逗号分隔的员工ID列表 |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.7.5 获取员工详细信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/staff/{id}` |
|
||
| 参数 | path: `id` (员工ID) |
|
||
| 返回 | `{ code: 200, data: { 员工完整信息 } }` |
|
||
|
||
---
|
||
|
||
### 2.9 Token(1 个)
|
||
|
||
#### 2.9.1 获取 Token
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/getToken` |
|
||
| 参数 | body: `{ clientId, clientSecret }` |
|
||
| 返回 | `{ code: 200, token: "xxx", msg: "操作成功" }` |
|
||
|
||
---
|
||
|
||
### 2.11 部门管理(1 个)
|
||
|
||
#### 2.11.1 根据用户 ID 获取部门信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/system/dept/root/{userId}` |
|
||
| 参数 | path: `userId` (int64) |
|
||
| 返回 | `{ code: 200, data: { 部门树 } }` |
|
||
|
||
---
|
||
|
||
### 2.12 钥匙柜授权信息(1 个)
|
||
|
||
#### 2.12.1 获取钥匙柜授权信息详细信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/permissioninfo/getByPermissionId/{uuid}` |
|
||
| 参数 | path: `uuid` (授权记录 UUID) |
|
||
| 返回 | `{ code: 200, data: { 授权详细信息 } }` |
|
||
|
||
---
|
||
|
||
### 2.14 钥匙管理(6 个)
|
||
|
||
#### 2.14.1 创建钥匙
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/kms/opener` |
|
||
| 请求体 | 钥匙业务对象(必填): 包含 cnName, number, type, state, lockerId 等 |
|
||
| 返回 | `{ code: 200, msg: "操作成功" }` |
|
||
|
||
#### 2.14.2 修改钥匙
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `PUT` |
|
||
| 路径 | `/prod-api/kms/opener` |
|
||
| 请求体 | 钥匙业务对象(必填,含 id) |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.14.4 查询钥匙列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/opener/list` |
|
||
| 参数 | query: `cnName` (必填/string), `lockerId` (必填/int), `number` (必填/string), `state` (必填/int), `type` (必填/int), `pageNum/pageSize` (可选) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.14.5 查询可借钥匙
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/opener/selectCanBorrow` |
|
||
| 参数 | query: `userId` (必填/int64), `pageNum` (必填/int), `pageSize` (必填/int), `openerName` (可选/string) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.14.6 查询可借钥匙员工列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/opener/staff` |
|
||
| 参数 | query: `id` (可选/int, 钥匙ID) |
|
||
| 返回 | `{ code: 200, data: [{ staffId, staffName }] }` |
|
||
|
||
#### 2.14.7 删除钥匙(批量)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `DELETE` |
|
||
| 路径 | `/prod-api/kms/opener/{ids}` |
|
||
| 参数 | path: `ids` — 逗号分隔的钥匙ID列表 |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.14.8 获取钥匙详细信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/opener/{id}` |
|
||
| 参数 | path: `id` (int64, 钥匙ID) |
|
||
| 返回 | `{ code: 200, data: { 钥匙完整信息 } }` |
|
||
|
||
---
|
||
|
||
### 2.16 柜体管理(6 个)
|
||
|
||
#### 2.16.1 创建柜体
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `POST` |
|
||
| 路径 | `/prod-api/kms/locker` |
|
||
| 请求体 | 柜体业务对象(必填): 包含 name, code, state, deptId 等 |
|
||
| 返回 | `{ code: 200, msg: "操作成功" }` |
|
||
|
||
#### 2.16.2 修改柜体
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `PUT` |
|
||
| 路径 | `/prod-api/kms/locker` |
|
||
| 请求体 | 柜体业务对象(必填,含 id) |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.16.3 查询柜体列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/locker/list` |
|
||
| 参数 | query: `name` (必填/string), `state` (必填/int), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.16.4 删除柜体(批量)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `DELETE` |
|
||
| 路径 | `/prod-api/kms/locker/{ids}` |
|
||
| 参数 | path: `ids` — 逗号分隔的柜体ID列表 |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.16.5 获取柜体详细信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/locker/{id}` |
|
||
| 参数 | path: `id` (柜体ID) |
|
||
| 返回 | `{ code: 200, data: { 柜体完整信息(含锁孔列表) } }` |
|
||
|
||
#### 2.16.6 首页统计图表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/locker/statistics` |
|
||
| 参数 | 无 |
|
||
| 返回 | 统计图表数据 |
|
||
|
||
---
|
||
|
||
### 2.17 锁孔管理(4 个)
|
||
|
||
#### 2.17.1 修改锁孔
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `PUT` |
|
||
| 路径 | `/prod-api/kms/lockhole` |
|
||
| 请求体 | 锁孔业务对象(必填) |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.17.2 查询锁孔列表
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/lockhole/list` |
|
||
| 参数 | query: `state` (必填/int), `pageNum` (可选/int), `pageSize` (可选/int) |
|
||
| 返回 | `{ code: 200, total, rows: [...] }` |
|
||
|
||
#### 2.17.3 删除锁孔(批量)
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `DELETE` |
|
||
| 路径 | `/prod-api/kms/lockhole/{ids}` |
|
||
| 参数 | path: `ids` — 逗号分隔的锁孔ID列表 |
|
||
| 返回 | 统一响应状态 |
|
||
|
||
#### 2.17.4 获取锁孔详细信息
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 方法 | `GET` |
|
||
| 路径 | `/prod-api/kms/lockhole/{id}` |
|
||
| 参数 | path: `id` (锁孔ID) |
|
||
| 返回 | `{ code: 200, data: { 锁孔完整信息 } }` |
|
||
|
||
---
|
||
|
||
## 3. 统一响应状态码
|
||
|
||
KMS 统一响应格式:
|
||
|
||
```json
|
||
{
|
||
"code": 200, // 200=成功, 0/其他=失败
|
||
"msg": "操作成功", // 消息文本
|
||
"total": 100, // 分页总记录数(列表接口)
|
||
"rows": [...], // 数据列表(列表接口)
|
||
"data": {...} // 单个对象(详情接口)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. KmsModels 完整设计
|
||
|
||
### 4.1 响应模型
|
||
|
||
```csharp
|
||
namespace IntegrationGateway.Adapters.Kms;
|
||
|
||
// ═══ 认证 ═══
|
||
public class KmsTokenResponse { public int Code { get; set; } public string Token { get; set; } = ""; public string? Msg { get; set; } }
|
||
|
||
// ═══ 第三方接口 DTO ═══
|
||
public class KmsOpenerListResponse { public int Code { get; set; } public string? Msg { get; set; } public List<KmsLocker>? Rows { get; set; } }
|
||
public class KmsLocker { public int LockerId { get; set; } public string? LockerName { get; set; } public string? LockerCode { get; set; } public List<KmsLockhole>? LockholeList { get; set; } }
|
||
public class KmsLockhole { public int LockholeSort { get; set; } public int OpenerId { get; set; } public string? OpenerName { get; set; } public string? OpenerType { get; set; } public string? OpenerState { get; set; } }
|
||
|
||
public class KmsWarningListResponse { public int Code { get; set; } public string? Msg { get; set; } public int Total { get; set; } public List<KmsWarning>? Rows { get; set; } }
|
||
public class KmsWarning { public string? Uuid { get; set; } public string? LockerName { get; set; } public int LockholeSort { get; set; } public string? OpenerName { get; set; } public int Type { get; set; } public string? WarningTime { get; set; } public string? Remark { get; set; } public string? StaffName { get; set; } }
|
||
|
||
public class KmsRecordListResponse { public int Code { get; set; } public string? Msg { get; set; } public int Total { get; set; } public List<KmsRecord>? Rows { get; set; } }
|
||
public class KmsRecord { public string? Uuid { get; set; } public string? LockerName { get; set; } public int LockholeSort { get; set; } public string? OpenerName { get; set; } public string? StaffName { get; set; } public string? BorrowTime { get; set; } public string? ReturnTime { get; set; } public string? Type { get; set; } }
|
||
|
||
// ═══ 标准接口 DTO ═══
|
||
public class KmsHandoverInfo { public string? Id { get; set; } public string? HandoverId { get; set; } public int OpenerId { get; set; } public string? OpenerName { get; set; } public int LockerId { get; set; } public string? LockerName { get; set; } public int LockholeSort { get; set; } public string? OpenerType { get; set; } public string? OpenerState { get; set; } public string? LendStaffName { get; set; } public string? BorrowTime { get; set; } }
|
||
|
||
public class KmsPermissionListResponse { public int Code { get; set; } public int Total { get; set; } public List<KmsPermission>? Rows { get; set; } }
|
||
public class KmsPermission { public string? Uuid { get; set; } public string? LockerName { get; set; } public string? OpenerCnName { get; set; } public string? LendStaffName { get; set; } public string? BackStaffName { get; set; } public string? ApplyTime { get; set; } public string? BackTime { get; set; } }
|
||
|
||
public class KmsStaffListResponse { public int Code { get; set; } public int Total { get; set; } public List<KmsStaff>? Rows { get; set; } }
|
||
public class KmsStaff { public string? Uuid { get; set; } public string? Name { get; set; } public string? CardNo { get; set; } public string? Phone { get; set; } public string? Email { get; set; } public int? DeptId { get; set; } public int? GroupId { get; set; } public int State { get; set; } public int Type { get; set; } }
|
||
|
||
public class KmsLockerListResponse { public int Code { get; set; } public int Total { get; set; } public List<KmsLockerInfo>? Rows { get; set; } }
|
||
public class KmsLockerInfo { public int Id { get; set; } public string? Name { get; set; } public string? Code { get; set; } public int State { get; set; } public int? DeptId { get; set; } public List<KmsLockhole>? LockholeList { get; set; } }
|
||
|
||
public class KmsLockholeListResponse { public int Code { get; set; } public int Total { get; set; } public List<KmsLockholeInfo>? Rows { get; set; } }
|
||
public class KmsLockholeInfo { public int Id { get; set; } public int LockerId { get; set; } public int LockholeSort { get; set; } public int State { get; set; } public int? OpenerId { get; set; } }
|
||
|
||
public class KmsOpenerListResponse2 { public int Code { get; set; } public int Total { get; set; } public List<KmsOpenerInfo>? Rows { get; set; } }
|
||
public class KmsOpenerInfo { public int Id { get; set; } public string? CnName { get; set; } public string? Number { get; set; } public int Type { get; set; } public int State { get; set; } public int? LockerId { get; set; } }
|
||
|
||
public class KmsStaffOpenerListResponse { public int Code { get; set; } public List<KmsStaffOpener>? Data { get; set; } }
|
||
public class KmsStaffOpener { public int Id { get; set; } public int StaffId { get; set; } public int OpenerId { get; set; } public int Type { get; set; } }
|
||
|
||
public class KmsRemotePermissionRequest { /* 远程授权参数 — 联调时与KMS确认字段 */ }
|
||
public class KmsBatchSyncStaffRequest { public List<KmsStaff> Staff { get; set; } = new(); }
|
||
|
||
// 通用包装
|
||
public class KmsApiResponse<T> { public int Code { get; set; } public string? Msg { get; set; } public int Total { get; set; } public List<T>? Rows { get; set; } public T? Data { get; set; } }
|
||
```
|
||
|
||
---
|
||
|
||
## 5. KmsAuthHelper 完整设计
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// KMS Bearer Token 认证辅助。
|
||
/// 流程: POST /prod-api/getToken?clientId=x&clientSecret=y → { code:200, token:"xxx" }
|
||
/// Token 缓存 25 分钟(KMS 有效期 30 分钟,留 5 分钟余量)。
|
||
/// </summary>
|
||
public class KmsAuthHelper
|
||
{
|
||
private readonly HttpClient _http;
|
||
private readonly string _baseUrl, _clientId, _clientSecret;
|
||
private string? _token;
|
||
private DateTime _tokenExpiry = DateTime.MinValue;
|
||
|
||
public KmsAuthHelper(HttpClient http, string baseUrl, string clientId, string clientSecret)
|
||
{
|
||
_http = http; _baseUrl = baseUrl.TrimEnd('/');
|
||
_clientId = clientId; _clientSecret = clientSecret;
|
||
}
|
||
|
||
public async Task<string> GetTokenAsync()
|
||
{
|
||
if (!string.IsNullOrEmpty(_token) && DateTime.UtcNow < _tokenExpiry) return _token;
|
||
var resp = await _http.PostAsync(
|
||
$"{_baseUrl}/prod-api/getToken?clientId={Uri.EscapeDataString(_clientId)}&clientSecret={Uri.EscapeDataString(_clientSecret)}", null);
|
||
resp.EnsureSuccessStatusCode();
|
||
var result = await resp.Content.ReadFromJsonAsync<KmsTokenResponse>()
|
||
?? throw new Exception("KMS Token 响应为空");
|
||
if (result.Code != 200) throw new Exception($"KMS 认证失败: code={result.Code}");
|
||
_token = result.Token; _tokenExpiry = DateTime.UtcNow.AddMinutes(25);
|
||
return _token;
|
||
}
|
||
|
||
public async Task<HttpClient> GetAuthenticatedClientAsync()
|
||
{
|
||
var token = await GetTokenAsync();
|
||
var client = new HttpClient { BaseAddress = new Uri(_baseUrl) };
|
||
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
|
||
return client;
|
||
}
|
||
|
||
public void Invalidate() => _token = null;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. KmsAdapter 完整设计
|
||
|
||
### 6.1 类定义与构造函数
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// KMS 智能钥匙柜适配器。
|
||
/// 实现: IHasFlatDevices + IHasAlarms。
|
||
///
|
||
/// 设备模型: 柜体为父设备(IsParent=是),锁孔为子设备(ParentSourceId=柜体SourceId)。
|
||
/// AdapterCode: "KMS:{InstanceName}"。
|
||
/// 限流: 5 QPS。
|
||
/// </summary>
|
||
public class KmsAdapter : IHasFlatDevices, IHasAlarms
|
||
{
|
||
private readonly HttpClient _http;
|
||
private readonly KmsAuthHelper _auth;
|
||
private readonly RateLimiter _limiter = new(5);
|
||
|
||
public string AdapterCode { get; }
|
||
public string DisplayName => $"KMS ({AdapterCode})";
|
||
public AdapterCapabilities Capabilities => new() { HasFlatDevices = true, HasAlarms = true };
|
||
|
||
public KmsAdapter(string adapterCode, HttpClient http, string baseUrl, string clientId, string clientSecret)
|
||
{
|
||
AdapterCode = adapterCode; _http = http;
|
||
_auth = new KmsAuthHelper(http, baseUrl, clientId, clientSecret);
|
||
}
|
||
|
||
public async Task InitializeAsync() => await _auth.GetTokenAsync();
|
||
```
|
||
|
||
### 6.2 健康检查(2.18.1)
|
||
|
||
```csharp
|
||
public async Task<bool> HealthCheckAsync()
|
||
{
|
||
try
|
||
{
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
var resp = await client.GetAsync("/prod-api/heartBeat");
|
||
return resp.IsSuccessStatusCode;
|
||
}
|
||
catch { return false; }
|
||
}
|
||
```
|
||
|
||
### 6.3 设备列表(2.18.4)
|
||
|
||
```csharp
|
||
public async Task<PagedResult<StandardDevice>> GetDevicesAsync(int page, int size, string? keyword = null)
|
||
{
|
||
await _limiter.WaitAsync();
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
var resp = await client.PostAsync("/prod-api/getOpenerList",
|
||
new StringContent("{}", Encoding.UTF8, "application/json"));
|
||
resp.EnsureSuccessStatusCode();
|
||
var data = await resp.Content.ReadFromJsonAsync<KmsOpenerListResponse>()!;
|
||
|
||
var devices = new List<StandardDevice>();
|
||
foreach (var locker in data.Rows ?? new())
|
||
{
|
||
devices.Add(MapLockerToDevice(locker));
|
||
if (locker.LockholeList != null)
|
||
devices.AddRange(locker.LockholeList.Select(h => MapLockholeToDevice(h, locker.LockerId)));
|
||
}
|
||
return new PagedResult<StandardDevice> { Items = devices, Total = devices.Count };
|
||
}
|
||
|
||
private static StandardDevice MapLockerToDevice(KmsLocker locker) => new()
|
||
{
|
||
SourceId = $"locker_{locker.LockerId}", Name = locker.LockerName ?? $"柜体{locker.LockerId}",
|
||
Category = "智能钥匙柜", Group = "门禁设备", IsParent = true, IsOnline = true,
|
||
Extra = new Dictionary<string, object?> { ["lockerCode"] = locker.LockerCode, ["lockholeCount"] = locker.LockholeList?.Count ?? 0 }
|
||
};
|
||
|
||
private static StandardDevice MapLockholeToDevice(KmsLockhole hole, int lockerId) => new()
|
||
{
|
||
SourceId = $"lockhole_{lockerId}_{hole.LockholeSort}", Name = hole.OpenerName ?? $"锁孔{hole.LockholeSort}",
|
||
Category = "钥匙位", Group = "门禁设备", IsParent = false, IsOnline = hole.OpenerState == "在位",
|
||
ParentSourceId = $"locker_{lockerId}",
|
||
Extra = new Dictionary<string, object?> { ["openerId"] = hole.OpenerId, ["openerType"] = hole.OpenerType, ["openerState"] = hole.OpenerState }
|
||
};
|
||
```
|
||
|
||
### 6.4 告警列表(2.18.7)
|
||
|
||
```csharp
|
||
public async Task<PagedResult<StandardAlarm>> GetAlarmsAsync(
|
||
int page, int size, DateTime from, DateTime to, string? level = null, string? state = null)
|
||
{
|
||
await _limiter.WaitAsync();
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
var resp = await client.PostAsync("/prod-api/getWarningList",
|
||
new StringContent("{}", Encoding.UTF8, "application/json"));
|
||
resp.EnsureSuccessStatusCode();
|
||
var data = await resp.Content.ReadFromJsonAsync<KmsWarningListResponse>()!;
|
||
|
||
var alarms = (data.Rows ?? new()).Select(w => new StandardAlarm
|
||
{
|
||
AlarmId = w.Uuid ?? "", AdapterCode = AdapterCode, Level = "普通",
|
||
Title = $"{w.LockerName} 锁孔{w.LockholeSort}: {w.OpenerName}",
|
||
Content = w.Remark, OccurTime = DateTime.TryParse(w.WarningTime, out var t) ? t : DateTime.MinValue,
|
||
Status = w.Type == 1 ? "未确认" : "已结束"
|
||
}).ToList();
|
||
return new PagedResult<StandardAlarm> { Items = alarms, Total = data.Total };
|
||
}
|
||
|
||
public async Task ConfirmAlarmAsync(string alarmId)
|
||
{
|
||
await _limiter.WaitAsync();
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
await client.PostAsync($"/prod-api/kms/warning/confirm/{alarmId}", null);
|
||
}
|
||
|
||
public async Task EndAlarmAsync(string alarmId)
|
||
{
|
||
// KMS 第三方接口不提供告警结束 API,标准接口联调时补充
|
||
}
|
||
```
|
||
|
||
### 6.5 借还记录(2.18.6)
|
||
|
||
```csharp
|
||
/// <summary>查询借还记录(Phase 2 — 可作为额外 B 接口暴露)</summary>
|
||
public async Task<PagedResult<KmsRecord>> GetBorrowRecordsAsync(DateTime? from = null, DateTime? to = null)
|
||
{
|
||
await _limiter.WaitAsync();
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
var body = "{}"; // 联调时加时间范围参数
|
||
var resp = await client.PostAsync("/prod-api/getRecordList",
|
||
new StringContent(body, Encoding.UTF8, "application/json"));
|
||
resp.EnsureSuccessStatusCode();
|
||
var data = await resp.Content.ReadFromJsonAsync<KmsRecordListResponse>()!;
|
||
return new PagedResult<KmsRecord> { Items = data.Rows ?? new(), Total = data.Total };
|
||
}
|
||
```
|
||
|
||
### 6.6 员工同步(2.18.3 — Phase 2)
|
||
|
||
```csharp
|
||
/// <summary>从 Vol.Pro 向 KMS 批量同步员工</summary>
|
||
public async Task BatchSyncStaffAsync(List<KmsStaff> staffList)
|
||
{
|
||
await _limiter.WaitAsync();
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
var resp = await client.PostAsJsonAsync("/prod-api/batchSyncStaff", new { staff = staffList });
|
||
resp.EnsureSuccessStatusCode();
|
||
}
|
||
```
|
||
|
||
### 6.7 远程授权(2.4.3 — Phase 2)
|
||
|
||
```csharp
|
||
/// <summary>远程授权开门</summary>
|
||
public async Task RemoteAuthorizeAsync(KmsRemotePermissionRequest request)
|
||
{
|
||
await _limiter.WaitAsync();
|
||
var client = await _auth.GetAuthenticatedClientAsync();
|
||
var resp = await client.PostAsJsonAsync("/prod-api/kms/permission/remote", request);
|
||
resp.EnsureSuccessStatusCode();
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 设备映射逻辑
|
||
|
||
```
|
||
POST /prod-api/getOpenerList
|
||
│
|
||
▼
|
||
遍历 Lockers [ { lockerId, lockerName, lockholeList: [...] } ]
|
||
├── 父设备: SourceId="locker_{lockerId}", IsParent=true, Category="智能钥匙柜"
|
||
└── 子设备foreach: SourceId="lockhole_{lockerId}_{lockholeSort}", ParentSourceId="locker_{lockerId}"
|
||
Category="钥匙位", IsOnline = (openerState=="在位")
|
||
```
|
||
|
||
**parentSourceId 解析**(A3 同步时由 `gateway_nodesService.SyncDevicesAsync` 处理):
|
||
```
|
||
"locker_{lockerId}" → 查 base_device WHERE SourceId='locker_{lockerId}' → 得到 DeviceId → 填入子设备 ParentDeviceId
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 告警映射逻辑
|
||
|
||
| KMS 字段 (2.18.7) | StandardAlarm 字段 | 映射规则 |
|
||
|------|------|------|
|
||
| uuid | AlarmId | 直接映射 |
|
||
| type (1/2) | Status | 1→"未确认", 2→"已结束" |
|
||
| warningTime | OccurTime | DateTime.Parse |
|
||
| lockerName + lockholeSort | Title | 拼接: "{lockerName} 锁孔{sort}: {openerName}" |
|
||
| openerName | — | 用于 Title 拼接 |
|
||
| remark | Content | 直接映射 |
|
||
| staffName | — | Extra 扩展字段 |
|
||
| — | Level | 固定 "普通"(KMS 不区分等级) |
|
||
|
||
---
|
||
|
||
## 9. 配置
|
||
|
||
### 9.1 appsettings.json
|
||
|
||
```json
|
||
{
|
||
"KMS": [
|
||
{
|
||
"InstanceName": "main",
|
||
"BaseUrl": "http://192.168.1.50:8080",
|
||
"ClientId": "your_client_id",
|
||
"ClientSecret": "your_client_secret"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 9.2 KmsConfig POCO
|
||
|
||
```csharp
|
||
public class KmsConfig
|
||
{
|
||
public string? InstanceName { get; set; }
|
||
public string BaseUrl { get; set; } = "";
|
||
public string ClientId { get; set; } = "";
|
||
public string ClientSecret { get; set; } = "";
|
||
}
|
||
```
|
||
|
||
### 9.3 Program.cs 注册
|
||
|
||
```csharp
|
||
var kmsList = app.Configuration.GetSection("KMS").Get<List<KmsConfig>>() ?? new();
|
||
foreach (var k in kmsList)
|
||
{
|
||
var code = $"KMS:{k.InstanceName ?? "default"}";
|
||
var a = new KmsAdapter(code,
|
||
app.Services.GetRequiredService<IHttpClientFactory>().CreateClient("VolPro"),
|
||
k.BaseUrl, k.ClientId, k.ClientSecret);
|
||
registry.Register(a);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 与 Vol.Pro 管理端的交互
|
||
|
||
### 10.1 数据流
|
||
|
||
```
|
||
KMS 硬件柜 ──→ KmsAdapter.GetDevices ──→ A3 Sync → base_device (AdapterCode="KMS:main")
|
||
KMS 告警 ──→ KmsAdapter.GetAlarms ──→ A4 Sync → iot_alarm (SourceAlarmId=uuid)
|
||
管理端操作 ←── B-interface ←── KmsAdapter.RemoteAuthorize (Phase 2)
|
||
```
|
||
|
||
### 10.2 管理端改动
|
||
|
||
| 项 | 改动 |
|
||
|------|------|
|
||
| 数据库 | 无 — base_device / iot_alarm 已兼容 |
|
||
| 字典 | 新增 "智能钥匙柜" / "钥匙位" 字典项 |
|
||
| 后端代码 | 无 — A1-A4 逻辑通用 |
|
||
| 前端列表 | 自动显示 KMS 设备(AdapterCode 列区分来源) |
|
||
| 前端按钮 | Phase 2: KeyDeviceActions.vue |
|
||
|
||
### 10.3 钥匙状态展示
|
||
|
||
设备列表中每个锁孔(钥匙位)的 `IsOnline` 反映钥匙在位状态,`Extra.openerState` 存储详细状态字符串。管理端可通过 `Extra` 列的 JSON 展示查看钥匙类型和状态。
|
||
|
||
---
|
||
|
||
> **接口覆盖**: 54 个 REST 端点全部记录,其中 Phase 1 实现 4 个核心接口,Phase 2 实现 12 个扩展接口,其余 38 个为标准 KMS 管理接口按需对接。
|
||
> **版本历史**: v1.0 (初版) → v2.0 (完整接口版)
|
||
|
||
|
||
---
|
||
|
||
## 附录A: 接口全覆盖比对
|
||
|
||
### A.1 KMS 38 个接口 vs 设计覆盖度
|
||
|
||
| # | KMS 接口 | 方法 | 适配器方法 | 覆盖 |
|
||
|---|------|:---:|------|:--:|
|
||
| 1 | `/prod-api/getToken` | POST | KmsAuthHelper.GetTokenAsync | ✅ |
|
||
| 2 | `/prod-api/heartBeat` | GET | HealthCheckAsync | ✅ |
|
||
| 3 | `/prod-api/batchDeleteStaff` | POST | BatchDeleteStaffAsync | ✅ |
|
||
| 4 | `/prod-api/batchSyncStaff` | POST | BatchSyncStaffAsync | ✅ |
|
||
| 5 | `/prod-api/getOpenerList` | POST | GetDevicesAsync | ✅ |
|
||
| 6 | `/prod-api/getPermissionList` | POST | GetPermissionListAsync | ✅ |
|
||
| 7 | `/prod-api/getRecordList` | POST | GetBorrowRecordsAsync | ✅ |
|
||
| 8 | `/prod-api/getWarningList` | POST | GetAlarmsAsync | ✅ |
|
||
| 9 | `/thirdPlatlogin` | POST | ThirdPlatLoginAsync | ✅ |
|
||
| 10 | `/kms/handover/handoverInfolist` | GET | ⏭️ Phase2 | 📋 |
|
||
| 11 | `/kms/handover/list` | GET | ⏭️ Phase2 | 📋 |
|
||
| 12 | `/kms/permission/list` | GET | ⏭️ Phase2 | 📋 |
|
||
| 13 | `/kms/permission/listPer` | GET | ⏭️ Phase2 | 📋 |
|
||
| 14 | `/kms/permission/remote` | POST | RemoteAuthorizeAsync | ✅ |
|
||
| 15 | `/kms/warning/list` | GET | ⏭️ Phase2 (已有 2.18.7) | 📋 |
|
||
| 16 | `/kms/staffopener/available` | POST | ⏭️ Phase2 | 📋 |
|
||
| 17 | `/kms/staffopener/listall` | GET | ⏭️ Phase2 | 📋 |
|
||
| 18 | `/kms/staff` (create) | POST | ⏭️ Phase2 | 📋 |
|
||
| 19 | `/kms/staff` (update) | PUT | ⏭️ Phase2 | 📋 |
|
||
| 20 | `/kms/staff/list` | GET | ⏭️ Phase2 | 📋 |
|
||
| 21 | `/kms/staff/{ids}` (delete) | DELETE | ⏭️ Phase2 | 📋 |
|
||
| 22 | `/kms/staff/{id}` (detail) | GET | ⏭️ Phase2 | 📋 |
|
||
| 23 | `/system/dept/root/{userId}` | GET | ⏭️ Phase2 | 📋 |
|
||
| 24 | `/kms/permissioninfo/getByPermissionId/{uuid}` | GET | ⏭️ Phase2 | 📋 |
|
||
| 25 | `/kms/opener` (create) | POST | ⏭️ Phase2 | 📋 |
|
||
| 26 | `/kms/opener` (update) | PUT | ⏭️ Phase2 | 📋 |
|
||
| 27 | `/kms/opener/list` | GET | ⏭️ Phase2 | 📋 |
|
||
| 28 | `/kms/opener/selectCanBorrow` | GET | ⏭️ Phase2 | 📋 |
|
||
| 29 | `/kms/opener/staff` | GET | ⏭️ Phase2 | 📋 |
|
||
| 30 | `/kms/opener/{ids}` (delete) | DELETE | ⏭️ Phase2 | 📋 |
|
||
| 31 | `/kms/opener/{id}` (detail) | GET | ⏭️ Phase2 | 📋 |
|
||
| 32 | `/kms/locker` (create) | POST | ⏭️ Phase2 | 📋 |
|
||
| 33 | `/kms/locker` (update) | PUT | ⏭️ Phase2 | 📋 |
|
||
| 34 | `/kms/locker/list` | GET | ⏭️ Phase2 | 📋 |
|
||
| 35 | `/kms/locker/{ids}` (delete) | DELETE | ⏭️ Phase2 | 📋 |
|
||
| 36 | `/kms/locker/{id}` (detail) | GET | ⏭️ Phase2 | 📋 |
|
||
| 37 | `/kms/locker/statistics` | GET | ⏭️ Phase2 | 📋 |
|
||
| 38 | `/kms/lockhole/*` (4接口) | CRUD | ⏭️ Phase2 | 📋 |
|
||
|
||
> ✅ = 已设计 📋 = 标准 KMS 管理接口(非第三方集成接口),KMS 自身管理端即可操作,无需网关代理
|
||
|
||
---
|
||
|
||
## 附录B: 适配器设计原则适配性审查
|
||
|
||
### B.1 遵守的设计原则
|
||
|
||
| 原则 | KMS 适配器 | 合规 |
|
||
|------|------|:--:|
|
||
| 显式优于隐式 | 通过 IHasFlatDevices + IHasAlarms 显式声明能力 | ✅ |
|
||
| 异步优先 | 全部方法返回 Task/Task<T> | ✅ |
|
||
| 统一分页 | GetDevices/GetAlarms 返回 PagedResult<T> | ✅ |
|
||
| 弹性 Extra | 锁孔状态/类型/ID 存 Extra 字典 | ✅ |
|
||
| 故障隔离 | KMS 离线不影响 Owl/MC4 | ✅ |
|
||
| 编译独立性 | 零外部依赖,只引用 Core | ✅ |
|
||
| 热插拔 | 新增 KMS 不改 Core/Controller 签名 | ✅ |
|
||
|
||
### B.2 现有接口不能满足的 KMS 能力
|
||
|
||
以下 KMS 功能**超出了**当前 7 个网关能力接口的范围,需要新增 Core 接口或通过 B 组路由扩展:
|
||
|
||
| KMS 功能 | 缺口 | 影响 |
|
||
|------|------|------|
|
||
| **远程授权开门** (2.4.3/2.18.5) | 无可下发控制指令的通用接口 | 需新增 `IAcceptsControl` 或专用接口 |
|
||
| **借还记录查询** (2.18.6) | 无通用事件/记录查询接口 | 需新增接口或 B 路由 |
|
||
| **员工批量同步** (2.18.3) | 无外部数据写入适配器的接口 | 需新增接口 |
|
||
| **第三方登录代理** (2.18.8) | 无页面代理/SSO 接口 | B 路由直接代理 |
|
||
| **标准 CRUD 透传** | 适配器不代理子系统的管理接口 | 可走 KMS 自身管理端 |
|
||
|
||
### B.3 对接网关设计原则 3.4 要求的新增接口建议
|
||
|
||
按照"接口扩展规则"第 2 条:**如果现有接口不覆盖 → Core 中新增接口**。
|
||
|
||
以下是为 KMS(以及未来的门禁、道闸等子系统)拟新增的能力接口,写入 Core,不改已有接口签名:
|
||
|
||
```csharp
|
||
namespace IntegrationGateway.Core.Abstractions;
|
||
|
||
/// <summary>
|
||
/// 设备反向控制接口。适用于支持远程下发指令的子系统(如 KMS 远程开门、门禁远程开闸、道闸抬杆)。
|
||
/// 控制指令为通用键值对字典,适配器内部转换。
|
||
/// </summary>
|
||
public interface IAcceptsControl : IGatewayAdapter
|
||
{
|
||
/// <summary>向设备下发控制指令</summary>
|
||
/// <param name="sourceDeviceId">子系统设备原始 ID</param>
|
||
/// <param name="command">指令名,如 "open"/"close"/"authorize"</param>
|
||
/// <param name="parameters">指令参数键值对</param>
|
||
Task<ControlResult> SendControlAsync(string sourceDeviceId, string command, Dictionary<string, object?> parameters);
|
||
}
|
||
|
||
/// <summary>控制结果</summary>
|
||
public class ControlResult { public bool Success { get; set; } public string? Message { get; set; } }
|
||
```
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 业务记录查询接口。适用于具有借还、交接、授权等业务日志的子系统。
|
||
/// 不走 StandardAlarm 通道,独立分页查询。
|
||
/// </summary>
|
||
public interface IHasBusinessLogs : IGatewayAdapter
|
||
{
|
||
/// <summary>分页查询业务记录</summary>
|
||
/// <param name="logType">记录类型: "borrow"/"handover"/"permission"/"event"</param>
|
||
/// <param name="from">开始时间</param>
|
||
/// <param name="to">结束时间</param>
|
||
/// <param name="page">页码</param>
|
||
/// <param name="size">每页条数</param>
|
||
/// <param name="filters">额外过滤条件</param>
|
||
Task<PagedResult<BusinessLogEntry>> GetBusinessLogsAsync(
|
||
string logType, DateTime? from, DateTime? to,
|
||
int page, int size, Dictionary<string, string>? filters = null);
|
||
}
|
||
|
||
/// <summary>业务记录条目</summary>
|
||
public class BusinessLogEntry
|
||
{
|
||
public string LogId { get; set; } = "";
|
||
public string LogType { get; set; } = ""; // borrow/handover/permission
|
||
public string? DeviceSourceId { get; set; }
|
||
public string? StaffName { get; set; }
|
||
public string? Description { get; set; }
|
||
public DateTime? CreatedAt { get; set; }
|
||
public Dictionary<string, object?>? Extra { get; set; }
|
||
}
|
||
```
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 外部数据同步写入接口。适用于需要从 Vol.Pro 向子系统推送数据的场景(如员工同步)。
|
||
/// </summary>
|
||
public interface IAcceptsDataSync : IGatewayAdapter
|
||
{
|
||
/// <summary>批量写入数据到子系统</summary>
|
||
/// <param name="dataType">数据类型: "staff"/"department"</param>
|
||
/// <param name="items">待同步的数据对象列表(JSON 兼容)</param>
|
||
Task<SyncResult> SyncDataAsync(string dataType, List<object> items);
|
||
|
||
/// <summary>批量删除</summary>
|
||
Task<SyncResult> DeleteDataAsync(string dataType, List<string> ids);
|
||
}
|
||
|
||
public class SyncResult { public int SuccessCount { get; set; } public int FailCount { get; set; } public string? Message { get; set; } }
|
||
```
|
||
|
||
### B.4 KMS 适配器利用新增接口后的能力矩阵
|
||
|
||
```
|
||
旧接口 新接口
|
||
IGatewayAdapter ✅ (已有) —
|
||
IHasFlatDevices ✅ (已有) —
|
||
IHasAlarms ✅ (已有) —
|
||
IAcceptsControl — ✅ 远程授权/开门
|
||
IHasBusinessLogs — ✅ 借还/交接/授权记录查询
|
||
IAcceptsDataSync — ✅ 员工批量同步/删除
|
||
```
|
||
|
||
### B.5 需同步修改的网关组件
|
||
|
||
如果上述新接口被采用,以下文件需要增加对应路由:
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `Core/Abstractions/IAcceptsControl.cs` | 新增接口 + ControlResult |
|
||
| `Core/Abstractions/IHasBusinessLogs.cs` | 新增接口 + BusinessLogEntry |
|
||
| `Core/Abstractions/IAcceptsDataSync.cs` | 新增接口 + SyncResult |
|
||
| `Host/Program.cs` | 新增 3 条 B 组路由 |
|
||
| `KmsAdapter.cs` | 实现新接口(3 个方法) |
|
||
|
||
**或采用更轻量的方案**(不新增接口,直接在 KmsAdapter 上增加非接口方法 + Host 加专用路由):
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `Host/Program.cs` | 加 `/api/gateway/kms/authorize`、`/kms/records`、`/kms/sync-staff` |
|
||
| `KmsAdapter.cs` | 加对应 public 方法,通过 `FindByCode` 查适配器调用 |
|
||
|
||
**推荐**: 新增接口方案(符合设计原则 §3.4),因为 KMS 的远程控制/记录查询/数据同步能力具备通用性,门禁、道闸等未来子系统均可复用。
|
||
|
||
### B.6 Vol.Pro 端需新增的配套改动
|
||
|
||
| 改动项 | 说明 |
|
||
|------|------|
|
||
| KMS 操作按钮 (KeyDeviceActions.vue) | 显示钥匙状态 + "远程开门"/"远程授权" 按钮 |
|
||
| 员工同步入口 | 管理端员工管理页增加"同步到KMS"按钮 |
|
||
| 借还记录页 | 管理端新增 KMS 借还/交接记录查询页面 |
|
||
| 字典 | 新增 "智能钥匙柜" / "钥匙位" 到设备种类字典 |
|
||
|
||
---
|
||
|
||
> **比对结论**: 38 个 KMS 接口全部有对应设计,其中 9 个第三方接口 100% 完成方法设计。KMS 特有的远程控制/记录查询/数据同步能力超出了现有 7 个能力接口的范围,按设计原则 §3.4 需新增 3 个 Core 接口(IAcceptsControl / IHasBusinessLogs / IAcceptsDataSync)。
|