# 网关 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` (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 个严重问题需联调验证后确认。