KMS钥匙柜适配器详细设计文档: 完整KmsAdapter+KmsAuthHelper+KmsModels设计
This commit is contained in:
500
doc/设计文档/KMS钥匙柜适配器详细设计文档.md
Normal file
500
doc/设计文档/KMS钥匙柜适配器详细设计文档.md
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
# KMS 钥匙柜适配器详细设计文档
|
||||||
|
|
||||||
|
> **版本**: 1.0
|
||||||
|
> **日期**: 2025-05-19
|
||||||
|
> **基准**: `doc/整合方案/KMS钥匙柜整合方案_v2.0.md`
|
||||||
|
> **技术栈**: .NET 8 / ASP.NET Core / C#
|
||||||
|
> **架构**: IntegrationGateway 适配器模式
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
### 1.1 设计目标
|
||||||
|
|
||||||
|
在 IntegrationGateway 中新增 `KmsAdapter`,将智能钥匙管理系统(KMS)作为第三个子系统接入 SecMPS 整合平台。KMS 通过网关的 `IHasFlatDevices` 上报柜体/锁孔设备树,通过 `IHasAlarms` 上报告警记录,由 Vol.Pro 管理端统一展示和管理。
|
||||||
|
|
||||||
|
### 1.2 技术约束
|
||||||
|
|
||||||
|
| 约束 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 不修改 Core 接口 | 复用现有 `IHasFlatDevices` + `IHasAlarms`,不需新增接口 |
|
||||||
|
| 不依赖 KMS 运行时 | `dotnet build` 可在无 KMS 环境下通过 |
|
||||||
|
| 故障隔离 | KMS 离线不影响 Owl/MC4 适配器运行 |
|
||||||
|
| 限流 | KMS 无明确 QPS 限制,设 5 QPS 保守值 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 系统架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ HTTP (Bearer Token) ┌────────────────────┐
|
||||||
|
│ KMS 服务端 │◄──────────────────────────►│ IntegrationGateway │
|
||||||
|
│ :8080 │ /prod-api/* │ :5100 │
|
||||||
|
│ (Java) │ │ (NET 8) │
|
||||||
|
└──────────────┘ └────────┬───────────┘
|
||||||
|
│ B 组接口
|
||||||
|
▼
|
||||||
|
┌────────────────────┐
|
||||||
|
│ Vol.Pro 管理端 │
|
||||||
|
│ :9100 │
|
||||||
|
│ (NET 8) │
|
||||||
|
└────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
gateway/src/IntegrationGateway.Adapters.Kms/
|
||||||
|
├── IntegrationGateway.Adapters.Kms.csproj # 类库, net8.0
|
||||||
|
├── KmsAuthHelper.cs # Bearer Token 认证
|
||||||
|
├── KmsAdapter.cs # 适配器主体
|
||||||
|
└── KmsModels.cs # 请求/响应 DTO
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1 依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
Host → Adapters.Kms → Core
|
||||||
|
Host → Core
|
||||||
|
```
|
||||||
|
|
||||||
|
适配器只引用 Core,零外部 NuGet 依赖(除 Microsoft.Extensions.Http 已由 Core 引入)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. KMS 接口详细参考
|
||||||
|
|
||||||
|
### 4.1 认证
|
||||||
|
|
||||||
|
**POST** `/prod-api/getToken`
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| clientId | query | 由 KMS 分配的客户端ID |
|
||||||
|
| clientSecret | query | 由 KMS 分配的客户端密钥 |
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{ "code": 200, "token": "eyJ...", "msg": "操作成功" }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 第三方接口(Phase 1 实现 4 个)
|
||||||
|
|
||||||
|
#### 4.2.1 心跳 — `POST /prod-api/heartBeat`
|
||||||
|
|
||||||
|
请求体:`{}` (空 JSON)
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{ "code": 200, "msg": "success" }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2.2 柜体钥匙列表 — `POST /prod-api/getOpenerList`
|
||||||
|
|
||||||
|
请求体:`{}`
|
||||||
|
|
||||||
|
响应(核心字段):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "查询成功",
|
||||||
|
"rows": [{
|
||||||
|
"lockerId": 25,
|
||||||
|
"lockerName": "10位智能公共钥匙柜",
|
||||||
|
"lockerCode": "888",
|
||||||
|
"lockholeList": [{
|
||||||
|
"lockholeSort": 1,
|
||||||
|
"openerId": 2020,
|
||||||
|
"openerName": "仓库大门钥匙",
|
||||||
|
"openerType": "永久授权",
|
||||||
|
"openerState": "在位"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2.3 告警列表 — `POST /prod-api/getWarningList`
|
||||||
|
|
||||||
|
请求体:`{}`(所有告警)或带时间范围
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"total": 5,
|
||||||
|
"rows": [{
|
||||||
|
"uuid": "xxx",
|
||||||
|
"lockerName": "10位公共钥匙柜",
|
||||||
|
"lockholeSort": 3,
|
||||||
|
"openerName": "机房钥匙",
|
||||||
|
"type": 1,
|
||||||
|
"warningTime": "2025-05-19 10:30:00",
|
||||||
|
"remark": "超时未归还",
|
||||||
|
"staffName": "张三"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2.4 借还记录 — `POST /prod-api/getRecordList`(Phase 2 参考)
|
||||||
|
|
||||||
|
请求体:`{}`
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"total": 10,
|
||||||
|
"rows": [{
|
||||||
|
"uuid": "xxx",
|
||||||
|
"lockerName": "10位公共钥匙柜",
|
||||||
|
"lockholeSort": 2,
|
||||||
|
"openerName": "仓库大门钥匙",
|
||||||
|
"staffName": "张三",
|
||||||
|
"borrowTime": "2025-05-19 09:00:00",
|
||||||
|
"returnTime": "2025-05-19 11:00:00",
|
||||||
|
"type": "1"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 标准业务接口(Phase 2 参考,共 50+)
|
||||||
|
|
||||||
|
见 `doc/整合方案/KMS钥匙柜整合方案_v2.0.md` §1.3。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. KmsModels 详细设计
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
namespace IntegrationGateway.Adapters.Kms;
|
||||||
|
|
||||||
|
// ── 认证 ──
|
||||||
|
public class KmsTokenResponse { public int Code { get; set; } public string Token { get; set; } = ""; public string? Msg { get; set; } }
|
||||||
|
|
||||||
|
// ── 第三方接口响应 ──
|
||||||
|
public class KmsOpenerListResponse { public int Code { get; set; } public string? Msg { get; set; } public List<KmsLocker> Rows { get; set; } = new(); }
|
||||||
|
public class KmsLocker { public int LockerId { get; set; } public string? LockerName { get; set; } public string? LockerCode { get; set; } public List<KmsLockhole> LockholeList { get; set; } = new(); }
|
||||||
|
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; } }
|
||||||
|
|
||||||
|
public class KmsWarningListResponse { public int Code { get; set; } public string? Msg { get; set; } public int Total { get; set; } public List<KmsWarning> Rows { get; set; } = new(); }
|
||||||
|
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; } }
|
||||||
|
|
||||||
|
public class KmsRecordListResponse { public int Code { get; set; } public int Total { get; set; } public List<KmsRecord> Rows { get; set; } = new(); }
|
||||||
|
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; } }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. KmsAuthHelper 详细设计
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// KMS Bearer Token 认证辅助。
|
||||||
|
///
|
||||||
|
/// 流程:
|
||||||
|
/// 1. POST /prod-api/getToken?clientId={}&clientSecret={}
|
||||||
|
/// 2. 返回 { code: 200, token: "xxx" }
|
||||||
|
/// 3. Token 缓存 25 分钟 (KMS 有效期 30 分钟,留 5 分钟余量)
|
||||||
|
/// </summary>
|
||||||
|
public class KmsAuthHelper
|
||||||
|
{
|
||||||
|
private readonly HttpClient _http;
|
||||||
|
private readonly string _baseUrl;
|
||||||
|
private readonly string _clientId;
|
||||||
|
private readonly string _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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<JsonElement>();
|
||||||
|
var code = result.GetProperty("code").GetInt32();
|
||||||
|
if (code != 200) throw new Exception($"KMS 认证失败: {code}");
|
||||||
|
_token = result.GetProperty("token").GetString();
|
||||||
|
_tokenExpiry = DateTime.UtcNow.AddMinutes(25); // 30min TTL, 25min 刷新
|
||||||
|
return _token!;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. KmsAdapter 详细设计
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// KMS 智能钥匙柜适配器。实现 IHasFlatDevices + IHasAlarms。
|
||||||
|
///
|
||||||
|
/// 设备模型:柜体为父设备(IsParent=是),锁孔为子设备(ParentSourceId=柜体ID)。
|
||||||
|
/// 限流:5 QPS。
|
||||||
|
/// AdapterCode: "KMS:{InstanceName}"
|
||||||
|
/// </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();
|
||||||
|
|
||||||
|
// ── HealthCheck → 2.18.1 心跳 ──
|
||||||
|
public async Task<bool> HealthCheckAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = await _auth.GetAuthenticatedClientAsync();
|
||||||
|
var resp = await client.PostAsync("/prod-api/heartBeat",
|
||||||
|
new StringContent("{}", Encoding.UTF8, "application/json"));
|
||||||
|
return resp.IsSuccessStatusCode;
|
||||||
|
}
|
||||||
|
catch { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IHasFlatDevices → 2.18.4 柜体钥匙列表 ──
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 父设备: 柜体
|
||||||
|
devices.Add(new StandardDevice
|
||||||
|
{
|
||||||
|
SourceId = $"locker_{locker.LockerId}",
|
||||||
|
Name = locker.LockerName ?? $"柜体{locker.LockerId}",
|
||||||
|
Category = "智能钥匙柜", Group = "门禁设备",
|
||||||
|
IsParent = true, IsOnline = true,
|
||||||
|
Extra = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["lockerCode"] = locker.LockerCode,
|
||||||
|
["lockholeCount"] = locker.LockholeList.Count
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 子设备: 锁孔
|
||||||
|
foreach (var hole in locker.LockholeList)
|
||||||
|
{
|
||||||
|
bool isOnline = hole.OpenerState == "在位";
|
||||||
|
devices.Add(new StandardDevice
|
||||||
|
{
|
||||||
|
SourceId = $"lockhole_{locker.LockerId}_{hole.LockholeSort}",
|
||||||
|
Name = hole.OpenerName ?? $"锁孔{hole.LockholeSort}",
|
||||||
|
Category = "钥匙位", Group = "门禁设备",
|
||||||
|
IsParent = false, IsOnline = isOnline,
|
||||||
|
ParentSourceId = $"locker_{locker.LockerId}",
|
||||||
|
Extra = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["openerId"] = hole.OpenerId,
|
||||||
|
["openerType"] = hole.OpenerType,
|
||||||
|
["openerState"] = hole.OpenerState
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new PagedResult<StandardDevice> { Items = devices, Total = devices.Count };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IHasAlarms → 2.18.7 告警列表 ──
|
||||||
|
public async Task<PagedResult<StandardAlarm>> GetAlarmsAsync(
|
||||||
|
int page, int size, DateTime from, DateTime to, string? level = null, string? state = null)
|
||||||
|
{
|
||||||
|
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.Select(w => new StandardAlarm
|
||||||
|
{
|
||||||
|
AlarmId = w.Uuid ?? "", AdapterCode = AdapterCode,
|
||||||
|
Level = "普通", // KMS 不区分告警等级,统一"普通"
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ConfirmAlarmAsync(string alarmId)
|
||||||
|
{
|
||||||
|
// KMS 2.18 接口不提供告警确认,调用标准接口
|
||||||
|
await _limiter.WaitAsync();
|
||||||
|
var client = await _auth.GetAuthenticatedClientAsync();
|
||||||
|
await client.PostAsync($"/prod-api/kms/warning/confirm/{alarmId}", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EndAlarmAsync(string alarmId)
|
||||||
|
{
|
||||||
|
// KMS 2.18 接口不提供告警结束
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 设备映射逻辑
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /prod-api/getOpenerList 响应
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
遍历每个 KmsLocker
|
||||||
|
├── 生成 1 个父 StandardDevice (SourceId="locker_{lockerId}", IsParent=true)
|
||||||
|
└── 遍历 lockholeList
|
||||||
|
└── 每个 lockhole 生成 1 个子 StandardDevice
|
||||||
|
(SourceId="lockhole_{lockerId}_{lockholeSort}", ParentSourceId="locker_{lockerId}")
|
||||||
|
(IsOnline = OpenerState=="在位" ? true : false)
|
||||||
|
```
|
||||||
|
|
||||||
|
**parentSourceId 解析**(A3 同步时在 gateway_nodesService.SyncDevicesAsync 中处理):
|
||||||
|
```
|
||||||
|
"locker_{lockerId}" → 查询 base_device WHERE SourceId='locker_{lockerId}' → 获取 DeviceId → 填入子设备的 ParentDeviceId
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 告警映射逻辑
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /prod-api/getWarningList 响应
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
遍历每个 KmsWarning
|
||||||
|
├── AlarmId ← uuid
|
||||||
|
├── Title ← "{lockerName} 锁孔{lockholeSort}: {openerName}"
|
||||||
|
├── Content ← remark
|
||||||
|
├── OccurTime ← warningTime
|
||||||
|
├── Status ← Type==1 ? "未确认" : "已结束"
|
||||||
|
└── Level ← "普通" (KMS 不区分告警等级)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 配置
|
||||||
|
|
||||||
|
### 10.1 appsettings.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"KMS": {
|
||||||
|
"InstanceName": "main",
|
||||||
|
"BaseUrl": "http://192.168.1.50:8080",
|
||||||
|
"ClientId": "your_client_id",
|
||||||
|
"ClientSecret": "your_client_secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 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; } = "";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 测试策略
|
||||||
|
|
||||||
|
### 11.1 单元测试(无 KMS 依赖)
|
||||||
|
|
||||||
|
| 测试 | 验证点 |
|
||||||
|
|------|------|
|
||||||
|
| KmsModels 序列化 | JSON 往返正确 |
|
||||||
|
| 设备映射 | locker + lockhole → StandardDevice 正确 |
|
||||||
|
| 告警映射 | KmsWarning → StandardAlarm 正确 |
|
||||||
|
|
||||||
|
### 11.2 集成测试(需 KMS 环境)
|
||||||
|
|
||||||
|
| 场景 | 预期 |
|
||||||
|
|------|------|
|
||||||
|
| 认证成功 | GetToken → 返回有效 token |
|
||||||
|
| 设备同步 | GetOpenerList → 返回柜体+锁孔列表 |
|
||||||
|
| 告警同步 | GetWarningList → 返回告警列表 |
|
||||||
|
| 健康检查 | HeartBeat → 200 OK |
|
||||||
|
| 认证失败 | 错误 clientId → 适配器初始化失败 |
|
||||||
|
| KMS 离线 | HealthCheck → false, 不影响 Owl/MC4 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 实施计划
|
||||||
|
|
||||||
|
| 步骤 | 内容 | 文件 | 预计 |
|
||||||
|
|:---:|------|------|:---:|
|
||||||
|
| 1 | 创建 `Adapters.Kms` 项目 + 引用 | csproj, sln | 10min |
|
||||||
|
| 2 | `KmsModels.cs` | 1 文件 | 20min |
|
||||||
|
| 3 | `KmsAuthHelper.cs` | 1 文件 | 30min |
|
||||||
|
| 4 | `KmsAdapter.cs` (HealthCheck + GetDevices) | 1 文件 | 1h |
|
||||||
|
| 5 | `KmsAdapter.cs` (GetAlarms + Confirm/End) | 1 文件 | 30min |
|
||||||
|
| 6 | appsettings.json + Program.cs 注册 | 2 文件 | 15min |
|
||||||
|
| 7 | 编译验证 `dotnet build` | 网关 | 5min |
|
||||||
|
| 8 | 联调 (需 KMS 环境) | — | 2h |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **版本历史**:
|
||||||
|
> - v1.0 (2025-05-19) — 初版详细设计
|
||||||
Reference in New Issue
Block a user