KMS模块检查报告: 逐接口比对 3致命+4严重+4改善

This commit is contained in:
2026-06-04 01:33:53 +08:00
parent 79b8400e6d
commit 85600d0c80

View File

@@ -0,0 +1,223 @@
# 网关 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 个严重问题需联调验证后确认。