Files
SecMPS/doc/设计文档/网关KMS模块检查报告20260604.md

224 lines
10 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.
# 网关 KMS 模块检查报告 2026-06-04
> **基准文档**: `doc/对接文档/钥匙管理系统软件接口.docx` (KMS API v1.0.4)
> **检查范围**: `gateway/src/IntegrationGateway.Adapters.Kms/` (KmsAdapter.cs / KmsAuthHelper.cs / KmsModels.cs) + `Program.cs` B10-B13 路由
> **方法**: 逐接口比对文档 → 代码 → 路由
---
## 1. 覆盖率总览
| 模块 | KMS 文档端点数 | Gateway 覆盖 | 覆盖率 |
|------|:---:|:---:|:---:|
| 2.9 Token 获取 | 1 | 1 | 100% |
| 2.18 开放接口 | 8 | 8 | 100% |
| **总计 (Phase 1)** | **9** | **9** | **100%** |
| 2.3-2.17 标准接口 | 38 | 1 (确认告警) | 3% |
---
## 2. 逐接口检查
### 2.18.1 心跳 — `GET /prod-api/heartBeat`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | `client.GetAsync(...)` | GET | ✅ |
| 请求路径 | `/prod-api/heartBeat` | `/prod-api/heartBeat` | ✅ |
| 请求体 | 无 | 无 | ✅ |
| 错误处理 | `catch (Exception ex) { Console.Error.WriteLine; return false; }` | — | ✅ |
### 2.18.2 批量删除员工 — `POST /prod-api/batchDeleteStaff`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | `PostAsJsonAsync(...)` | POST | ✅ |
| 请求路径 | `/prod-api/batchDeleteStaff` | `/prod-api/batchDeleteStaff` | ✅ |
| 请求体 | `List<string>` (staffUuid 数组) | `["uuid1","uuid2",...]` | ✅ |
| 参数类型 | 数组 | 数组 (v1.0.2 修正) | ✅ |
### 2.18.3 批量同步员工 — `POST /prod-api/batchSyncStaff`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | `PostAsJsonAsync(...)` | POST | ✅ |
| 请求路径 | `/prod-api/batchSyncStaff` | `/prod-api/batchSyncStaff` | ✅ |
| 请求体 | `new { staff = staffList }` | staff 数组 | ⚠️ |
| account 字段 | 模型中有 Account? | v1.0.4 新增 account | ⚠️ 待验证 |
**风险**: 文档 v1.0.4 新增了 `account` (登录账号) 字段。`KmsStaff` 模型需确认包含此字段。网关包装为 `{ staff: [...] }` 可能与 KMS 期望的裸数组不一致。
### 2.18.4 查询柜体钥匙 — `POST /prod-api/getOpenerList`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | POST | POST | ✅ |
| 请求路径 | `/prod-api/getOpenerList` | `/prod-api/getOpenerList` | ✅ |
| 请求体 | `"{}"` | 无明确要求 / 空对象 | ✅ |
| 响应 → StandardDevice | 柜体→父设备, 锁孔→子设备 | 树状结构 | ✅ |
### 2.18.5 查询授权记录 — `POST /prod-api/getPermissionList`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | POST | POST | ✅ |
| 请求路径 | `/prod-api/getPermissionList` | `/prod-api/getPermissionList` | ✅ |
| 请求体 | `"{}"` | 授权记录业务对象 (含 lockerName, lendStaffName 等 20+ 字段) | 🔴 |
| 时间范围 | `DateTime? from, DateTime? to` 参数**未传入请求体** | `beginApplyTime`/`endApplyTime` | 🔴 |
| 分页 | `page`/`size` 参数**未传入请求体** | `pageNum`/`pageSize` | 🔴 |
**致命问题**: 网关注入 `from`/`to`/`page`/`size` 参数但**从未传入 KMS 请求体**。代码注释 `// 联调时加入时间范围` 确认这是已知缺口。当前实现等价于无过滤全量查询,无法按时间范围分页。
### 2.18.6 查询借还记录 — `POST /prod-api/getRecordList`
**与 2.18.5 完全相同的致命问题**: `from`/`to`/`page`/`size` 参数未传入 KMS 请求体。此外文档标记 `lockerName``lockholeSort``openerCnName` 为必填字段,但网关传 `"{}"` 无这些字段。
### 2.18.7 查询告警记录 — `POST /prod-api/getWarningList`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | POST | POST | ✅ |
| 请求路径 | `/prod-api/getWarningList` | `/prod-api/getWarningList` | ✅ |
| 请求体 | `"{}"` | 告警业务对象 (含 type, beginWarningTime 等) | 🔴 |
| 时间范围 | 未传 | `beginWarningTime`/`endWarningTime` | 🔴 |
| 告警类型 | 未传 (type=1当前/2历史) | 文档支持过滤 | 🔴 |
| 响应映射 | `Type==1 ? "未确认" : "已结束"` | `type` 1=当前告警, 2=历史告警 | 🔴 |
**状态映射错误**: `type` 字段在告警接口中表示 1=当前告警 / 2=历史告警,**不是** 1=未确认 / 2=已结束。代码将 type=1 映射为 Status="未确认"、type=2 映射为 Status="已结束",语义错误。正确的映射应该是 type=1 → "活跃", type=2 → "历史"。
### 2.18.8 单点登录 — `POST /thirdPlatlogin`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | POST | POST | ✅ |
| 请求路径 | `/thirdPlatlogin?username={x}` | `/thirdPlatlogin?username={x}` | ✅ |
| 重定向处理 | 捕获 302, 返回 Location header | 文档说明"调用成功后直接重定向" | ✅ |
| 超时 | 无显式设置 | — | 🟡 |
### 2.9 Token 获取 — `POST /prod-api/getToken`
| 检查项 | Gateway 实现 | 文档规范 | 状态 |
|------|------|------|:--:|
| 请求方法 | `http.PostAsync(url, null)` | POST | ✅ |
| 参数位置 | query string: `?clientId=&clientSecret=` | query string | ✅ |
| 响应校验 | `Code != 200` → 抛异常 | `code: 200` = 成功 | ✅ |
| 缓存策略 | 25分钟 (30分钟效期-5分钟余量) | 30分钟效期 | ✅ |
> **注意**: 文档 2.9.1 显示 `POST /prod-api/getToken` 参数在 body 中 (`{ clientId, clientSecret }`),但 2.9 节概述描述为 query 参数。两种方式 KMS 可能都支持。当前实现用 query string联调时需确认兼容。
---
## 3. 模型映射核对
### 3.1 KMS 柜体 → StandardDevice
| 文档字段 | 代码映射 | 准确性 |
|------|------|:--:|
| lockerId | `SourceId = $"locker_{lockerId}"` | ✅ |
| lockerName | `Name = lockerName` | ✅ |
| lockerCode | `Extra["lockerCode"]` | ✅ |
| lockholeList | 遍历展开为子设备 | ✅ |
| IsParent | `true` | ✅ |
### 3.2 KMS 锁孔 → StandardDevice
| 文档字段 | 代码映射 | 准确性 |
|------|------|:--:|
| lockholeSort | `SourceId = $"lockhole_{lockerId}_{lockholeSort}"` | ✅ |
| openerName | `Name = openerName` | ✅ |
| openerType | `Extra["openerType"]` (1/2/3 数值) | ✅ |
| openerState | `Extra["openerState"]` + `IsOnline = (openerState=="在位")` | 🔴 |
| ParentSourceId | `$"locker_{lockerId}"` | ✅ |
**openerState 映射错误**: 根据文档数据字典§4`openerState` 是数值编码:
- 1 = 在柜
- 2 = 借出
- 3 = 录入
- 10 = 丢失
代码用 `openerState == "在位"` 做字符串比较,**永远不成立**。需改为 `openerState == "1"` 或解析为 int 后判断。
### 3.3 KMS 借还记录 → BusinessLogEntry
| 文档字段 | 代码映射 | 准确性 |
|------|------|:--:|
| uuid | `LogId` | ✅ |
| lockerName | 拼入 `DeviceSourceId` | ✅ |
| staffName | `StaffName` | ✅ |
| borrowTime | `CreatedAt` | ✅ |
| openerName | `Description` (不充分) | 🟡 |
### 3.4 KMS 告警 → StandardAlarm
| 文档字段 | 代码映射 | 准确性 |
|------|------|:--:|
| uuid | `AlarmId` | ✅ |
| warningTime | `OccurTime` | ✅ |
| remark | `Content` | ✅ |
| type (1/2) | `Status = Type==1 ? "未确认""已结束"` | 🔴 |
| level | 固定 `"普通"` | 🟡 |
**type 语义错误**: 见 2.18.7 说明。文档明确 `type` 表示告警分类(1=当前,2=历史),而非确认状态。
---
## 4. B 路由链路检查
| B 路由 | 对应 KMS 能力 | 适配器方法 | 参数传递 | 状态 |
|------|------|------|:--:|:--:|
| B1 `/health` | 心跳 2.18.1 | `HealthCheckAsync` | ✅ | ✅ |
| B2 `/devices` | 柜体钥匙 2.18.4 | `GetDevicesAsync` | ✅ | ✅ |
| B8 `/alarms` | 告警 2.18.7 | `GetAlarmsAsync` | ✅ | ✅ (映射有误) |
| B9 `/alarms/{id}/confirm` | 确认告警 | `ConfirmAlarmAsync` | ✅ | ⚠️ 端点未确认 |
| B10 `/control` | 远程控制 | `SendControlAsync` | ✅ | ⚠️ |
| B11 `/logs` | 业务记录 | `GetBusinessLogsAsync` | ✅ | ⚠️ |
| B12 `/sync` (POST) | 员工同步 2.18.3 | `SyncDataAsync` | ✅ | ⚠️ |
| B13 `/sync` (DELETE) | 删除员工 2.18.2 | `DeleteDataAsync` | ✅ | ✅ |
---
## 5. 问题汇总
### 🔴 致命问题 (需联调前修复)
| # | 问题 | 影响 |
|:--:|------|------|
| **R1** | 2.18.5/2.18.6/2.18.7 请求体只传 `"{}"`,忽略 `from`/`to`/`page`/`size` 参数 | 无法按时间分页查询,联调时大概率返回全量数据或报错 |
| **R2** | 2.18.7 type 字段语义错误 (1=当前告警被映射为"未确认") | 告警状态全部错误 |
| **R3** | openerState 字符串比较 vs 文档数值编码 | 所有锁孔 IsOnline 永远为 false |
### 🟠 严重问题
| # | 问题 | 影响 |
|:--:|------|------|
| **S1** | 2.18.6 必填字段 (`lockerName`, `lockholeSort`, `openerCnName`) 未传 | 借还记录查询可能被 KMS 拒绝 |
| **S2** | `ConfirmAlarmAsync` 端点 (`/prod-api/kms/warning/confirm/{id}`) 未在文档中确认存在 | 告警确认功能不可用 |
| **S3** | 2.18.5 请求体结构未知 (文档未给完整示例) | 授权记录查询格式需联调验证 |
### 🟡 改善项
| # | 问题 | 建议 |
|:--:|------|------|
| **M1** | KmsModels.cs 包含大量 Phase 2 DTO 但未使用 | 保留Phase 2 可用 |
| **M2** | B10 控制指令 `command == "open"``command == "authorize"` 都调同一方法 | 区分"开门"和"授权"两种指令 |
| **M3** | ThirdPlatLoginAsync 无超时设置 | 加 15s 超时 |
| **M4** | SyncDevicesJob 中 `gwRepo.Update(node)` vs `gwSvc.UpdateAsync(node)` 不一致 | 统一风格 |
---
## 6. 需联调验证项
| # | 验证项 | 说明 |
|:--:|------|------|
| V1 | Token 获取用 query string vs body | 文档 2.9.1 标记为 body 参数2.9 节概述为 query |
| V2 | `batchSyncStaff` body 格式 | `{ staff: [...] }` vs `[...]` |
| V3 | `KmsStaff` 是否需 account 字段 | v1.0.4 新增 |
| V4 | `getOpenerList` 返回的 openerState 是数值还是中文 | 决定映射逻辑 |
| V5 | `getRecordList` 必填字段是否真的必填 | 决定请求体最小字段集 |
| V6 | `getPermissionList` 请求体完整格式 | 文档示例不完整 |
| V7 | `warning/confirm` 端点存在性 | 调标准管理接口 |
---
> **结论**: 9 个 Phase 1 接口全部实现覆盖100%),但 3 个致命问题R1-R3需在联调前修复——核心是请求体格式、type 语义映射、openerState 编码映射。其余 4 个严重问题需联调验证后确认。