15 KiB
15 KiB
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)
// ── 认证 ──
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 设计
/// <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 类声明与核心属性
/// <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 — 健康检查
/// <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 — 设备同步
/// <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 — 告警同步
/// <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 组路由直接调用)
// 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
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
{
"KMS": [
{
"InstanceName": "main",
"BaseUrl": "http://192.168.1.50:8080",
"ClientId": "your_client_id",
"ClientSecret": "your_client_secret"
}
]
}
6.3 Program.cs 注册
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 + Token)100% 设计覆盖;29 个标准管理接口留 Phase 2 按需扩展。