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