Files
SecMPS/doc/设计文档/KMS钥匙柜适配器详细设计文档.md

365 lines
15 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 钥匙柜适配器详细设计文档
> **版本**: 1.0
> **日期**: 2025-05-19
> **基准**: `doc/整合方案/KMS钥匙柜整合方案_v2.0.md`
> **接口文档**: `doc/对接文档/钥匙管理系统软件接口.docx` (KMS API v1.0.4)
> **技术栈**: .NET 8 / ASP.NET Core / C#
---
## 1. 接口覆盖设计
### 1.1 完整 KMS API 概览
KMS 系统共有 **38 个 REST 端点**,分 9 大类。按设计原则,适配器只代理 **第三方集成接口**(第 2.18 节),不代理 KMS 自身管理接口。
### 1.2 第三方集成接口Phase 1 — 核心实现)
以下 8 个接口是 KMS 专为第三方对接设计的扁平化 API
| # | 方法 | 路径 | 用途 | 适配器方法 |
|---|:---:|------|------|------|
| 2.18.1 | GET | `/prod-api/heartBeat` | 心跳检测 | `HealthCheckAsync()` |
| 2.18.2 | POST | `/prod-api/batchDeleteStaff` | 批量删除员工 | `BatchDeleteStaffAsync()` |
| 2.18.3 | POST | `/prod-api/batchSyncStaff` | 批量同步员工 | `BatchSyncStaffAsync()` |
| 2.18.4 | POST | `/prod-api/getOpenerList` | 查询柜体+钥匙信息 | `GetDevicesAsync()` |
| 2.18.5 | POST | `/prod-api/getPermissionList` | 查询授权记录 | `GetPermissionListAsync()` |
| 2.18.6 | POST | `/prod-api/getRecordList` | 查询借还记录 | `GetBorrowRecordsAsync()` |
| 2.18.7 | POST | `/prod-api/getWarningList` | 查询告警记录 | `GetAlarmsAsync()` |
| 2.18.8 | POST | `/thirdPlatlogin` | 第三方登录/事件 | `ThirdPlatLoginAsync()` |
加上认证接口 `POST /prod-api/getToken`,适配器共需对接 **9 个 KMS 接口**
### 1.3 标准 KMS 管理接口Phase 2 可选)
以下 29 个接口属于 KMS 自身管理功能(员工 CRUD、柜体 CRUD、钥匙 CRUD 等KMS 自带 Web 管理端即可操作。Vol.Pro 如需代理这些接口,可在 Phase 2 扩展,但设计文档不列入必需实现。
| 模块 | 接口数 | 备注 |
|------|:---:|------|
| 交接记录 | 2 | GET `/kms/handover/*` |
| 授权管理 | 3 | GET `/kms/permission/*` + POST remote |
| 告警记录(标准) | 1 | GET `/kms/warning/list` |
| 员工可借钥匙 | 2 | POST/GET `/kms/staffopener/*` |
| 员工管理 | 5 | CRUD `/kms/staff/*` |
| 员工组管理 | 6 | CRUD `/kms/staffGroup/*` |
| 物品类别 | 6 | CRUD `/kms/openerType/*` |
| 柜体管理 | 5 | CRUD `/kms/locker/*` + statistics |
| 锁孔管理 | 4 | CRUD `/kms/lockhole/*` |
| 钥匙管理 | 7 | CRUD `/kms/opener/*` + selectCanBorrow |
| 钥匙组 | 7 | CRUD `/kms/openerGroup/*` |
| 部门 | 1 | GET `/system/dept/root/{userId}` |
| 授权详情 | 1 | GET `/kms/permissioninfo/getByPermissionId/{uuid}` |
| **合计** | **29** | Phase 2 按需实现 |
---
## 2. 项目结构
```
gateway/src/IntegrationGateway.Adapters.Kms/
├── IntegrationGateway.Adapters.Kms.csproj # 类库, net8.0
├── KmsAdapter.cs # 适配器主体 (IHasFlatDevices + IHasAlarms)
├── KmsAuthHelper.cs # Bearer Token 认证
└── KmsModels.cs # 请求/响应 DTO
```
### 2.1 依赖关系
```
Host → Adapters.Kms → Core
Host → Core
```
适配器只引用 Core零外部 NuGet 依赖。
---
## 3. 数据模型设计
### 3.1 KMS 响应模型(全部 9 个第三方接口的 DTO
```csharp
// ── 认证 ──
public class KmsTokenResponse { public int Code { get; set; } public string Token { get; set; } = ""; public string? Msg { get; set; } }
// ── 2.18.4 柜体+钥匙 ──
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; } }
// ── 2.18.7 告警 ──
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; } }
// ── 2.18.6 借还记录 ──
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; } }
// ── 2.18.5 授权记录 ──
public class KmsPermissionListResponse { public int Code { get; set; } public string? Msg { 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 int LockholeSort { get; set; } public string? OpenerName { get; set; } public string? StaffName { get; set; } public string? ApplyTime { get; set; } public string? BackTime { get; set; } }
// ── 2.18.3 员工同步 ──
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 KmsApiResponse { public int Code { get; set; } public string? Msg { get; set; } }
```
### 3.2 实体映射关系
```
KMS 物理拓扑 base_device 映射
─────── ──────────────────
智能钥匙柜A (lockerId=25) SourceId="locker_25", IsParent=是
├── 锁孔1 "仓库大门" SourceId="lockhole_25_1", ParentSourceId="locker_25"
├── 锁孔2 "机房钥匙" SourceId="lockhole_25_2", ParentSourceId="locker_25"
└── 锁孔N ...
```
---
## 4. KmsAuthHelper 设计
```csharp
/// <summary>
/// KMS Bearer Token 认证辅助。
/// Token 通过 POST /prod-api/getToken 获取,参数 clientId + clientSecret。
/// 缓存 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;
}
/// <summary>获取或刷新 Token自动缓存</summary>
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;
}
/// <summary>创建一个已认证的 HttpClient</summary>
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;
}
```
---
## 5. KmsAdapter 设计
### 5.1 类声明与核心属性
```csharp
/// <summary>
/// KMS 智能钥匙柜适配器。
/// 实现 IHasFlatDevices + IHasAlarms。
/// 通过 8 个第三方接口2.18.X对接 KMS 子系统。
/// 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();
```
### 5.2 IGatewayAdapter — 健康检查
```csharp
/// <summary>2.18.1: 心跳检测 — GET /prod-api/heartBeat</summary>
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; }
}
```
### 5.3 IHasFlatDevices — 设备同步
```csharp
/// <summary>2.18.4: 柜体+钥匙信息 — POST /prod-api/getOpenerList</summary>
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(new StandardDevice
{
SourceId = $"locker_{locker.LockerId}", Name = locker.LockerName ?? $"柜体{locker.LockerId}",
Category = "智能钥匙柜", Group = "门禁设备", IsParent = true, IsOnline = true,
Extra = new() { ["lockerCode"] = locker.LockerCode, ["lockholeCount"] = locker.LockholeList?.Count ?? 0 }
});
// 子设备: 锁孔(钥匙位)
foreach (var hole in locker.LockholeList ?? new())
{
devices.Add(new StandardDevice
{
SourceId = $"lockhole_{locker.LockerId}_{hole.LockholeSort}",
Name = hole.OpenerName ?? $"锁孔{hole.LockholeSort}",
Category = "钥匙位", Group = "门禁设备", IsParent = false,
IsOnline = hole.OpenerState == "在位", ParentSourceId = $"locker_{locker.LockerId}",
Extra = new() { ["openerId"] = hole.OpenerId, ["openerType"] = hole.OpenerType, ["openerState"] = hole.OpenerState }
});
}
}
return new PagedResult<StandardDevice> { Items = devices, Total = devices.Count };
}
```
### 5.4 IHasAlarms — 告警同步
```csharp
/// <summary>2.18.7: 告警记录 — POST /prod-api/getWarningList</summary>
public async Task<PagedResult<StandardAlarm>> GetAlarmsAsync(...)
{
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 };
}
```
### 5.5 扩展方法(非接口方法,供 B 组路由直接调用)
```csharp
// 2.18.6 借还记录
public async Task<PagedResult<KmsRecord>> GetBorrowRecordsAsync(DateTime? from = null, DateTime? to = null) { ... }
// 2.18.5 授权记录
public async Task<PagedResult<KmsPermission>> GetPermissionListAsync(DateTime? from = null, DateTime? to = null) { ... }
// 2.18.3 批量同步员工
public async Task BatchSyncStaffAsync(List<KmsStaff> staffList) { ... }
// 2.18.2 批量删除员工
public async Task BatchDeleteStaffAsync(List<string> staffUuids) { ... }
// 2.4.3 远程授权开门
public async Task RemoteAuthorizeAsync(KmsRemotePermissionRequest request) { ... }
// 2.18.8 第三方登录代理
public async Task<string?> ThirdPlatLoginAsync(string username) { ... }
```
---
## 6. 配置
### 6.1 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; } = "";
}
```
### 6.2 appsettings.json
```json
{
"KMS": [
{
"InstanceName": "main",
"BaseUrl": "http://192.168.1.50:8080",
"ClientId": "your_client_id",
"ClientSecret": "your_client_secret"
}
]
}
```
### 6.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);
}
```
---
## 7. Vol.Pro 端配套
| 项 | 改动 | 说明 |
|------|:---:|------|
| 数据库 | 无 | base_device / iot_alarm 已兼容 |
| 后端 | 无 | A1-A4 同步逻辑通用 |
| 字典 | 新增 2 项 | "智能钥匙柜" / "钥匙位" |
| 前端列表 | 无 | 自动显示 KMS 设备 |
| 前端操作 | Phase 2 | KeyDeviceActions.vue开门/授权按钮)|
---
> **接口覆盖**: 9 个第三方接口2.18.X + Token100% 设计覆盖29 个标准管理接口留 Phase 2 按需扩展。