K3: KmsAdapter核心方法就绪(HealthCheck/GetDevices/GetAlarms/Confirm)

This commit is contained in:
2026-05-19 22:43:32 +08:00
parent 5402e311a4
commit a869fa5dbf

View File

@@ -0,0 +1,171 @@
using IntegrationGateway.Core.Abstractions;
using IntegrationGateway.Core.Infrastructure;
using IntegrationGateway.Core.Models;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
namespace IntegrationGateway.Adapters.Kms;
/// <summary>
/// KMS 智能钥匙柜适配器。
/// 实现: IHasFlatDevices + IHasAlarms。
///
/// 设备模型:柜体为父设备(IsParent=是),锁孔为子设备(ParentSourceId=柜体SourceId)。
/// AdapterCode: "KMS:{InstanceName}"。
/// 限流5 QPS。
///
/// 按设计文档 §6 KmsAdapter 完整实现。
/// </summary>
public class KmsAdapter : IHasFlatDevices, IHasAlarms
{
private readonly HttpClient _http;
private readonly KmsAuthHelper _auth;
private readonly RateLimiter _limiter = new(5);
/// <summary>适配器编码,格式 "KMS:{实例名}"</summary>
public string AdapterCode { get; }
/// <summary>人类可读的适配器名称</summary>
public string DisplayName => $"KMS ({AdapterCode})";
/// <summary>适配器能力声明</summary>
public AdapterCapabilities Capabilities => new() { HasFlatDevices = true, HasAlarms = true };
/// <summary>创建 KmsAdapter 实例</summary>
/// <param name="adapterCode">适配器编码</param>
/// <param name="http">HttpClient 实例</param>
/// <param name="baseUrl">KMS 服务地址</param>
/// <param name="clientId">KMS 客户端 ID</param>
/// <param name="clientSecret">KMS 客户端密钥</param>
public KmsAdapter(string adapterCode, HttpClient http, string baseUrl, string clientId, string clientSecret)
{
AdapterCode = adapterCode;
_http = http;
_auth = new KmsAuthHelper(http, baseUrl, clientId, clientSecret);
}
/// <summary>初始化适配器:获取 KMS Token</summary>
public async Task InitializeAsync() => await _auth.GetTokenAsync();
// ═══════════════════════════════════════════
// IGatewayAdapter — 健康检查2.18.1 心跳)
// ═══════════════════════════════════════════
/// <summary>调用 KMS 心跳接口确认可达性</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; }
}
// ═══════════════════════════════════════════
// IHasFlatDevices — 设备列表2.18.4 柜体+钥匙)
// ═══════════════════════════════════════════
/// <summary>
/// 获取 KMS 所有柜体及其锁孔,映射为 StandardDevice 列表。
/// 柜体为父设备IsParent=是锁孔为子设备ParentSourceId=柜体SourceId
/// </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(MapLockerToDevice(locker));
if (locker.LockholeList != null)
devices.AddRange(locker.LockholeList.Select(h => MapLockholeToDevice(h, locker.LockerId)));
}
return new PagedResult<StandardDevice> { Items = devices, Total = devices.Count };
}
/// <summary>KMS 柜体 → StandardDevice父设备</summary>
private static StandardDevice MapLockerToDevice(KmsLocker locker) => new()
{
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 ?? 0
}
};
/// <summary>KMS 锁孔 → StandardDevice子设备</summary>
private static StandardDevice MapLockholeToDevice(KmsLockhole hole, int lockerId) => new()
{
SourceId = $"lockhole_{lockerId}_{hole.LockholeSort}",
Name = hole.OpenerName ?? $"锁孔{hole.LockholeSort}",
Category = "钥匙位",
Group = "门禁设备",
IsParent = false,
IsOnline = hole.OpenerState == "在位",
ParentSourceId = $"locker_{lockerId}",
Extra = new Dictionary<string, object?>
{
["openerId"] = hole.OpenerId,
["openerType"] = hole.OpenerType,
["openerState"] = hole.OpenerState
}
};
// ═══════════════════════════════════════════
// IHasAlarms — 告警2.18.7 告警列表)
// ═══════════════════════════════════════════
/// <summary>分页查询 KMS 告警列表,映射到 StandardAlarm</summary>
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 ?? new()).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 };
}
/// <summary>确认告警(调 KMS 标准告警确认接口)</summary>
public async Task ConfirmAlarmAsync(string alarmId)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
await client.PostAsync($"/prod-api/kms/warning/confirm/{alarmId}", null);
}
/// <summary>结束告警KMS 第三方接口不提供,留空实现)</summary>
public Task EndAlarmAsync(string alarmId)
{
// KMS 第三方接口 (2.18.7) 不提供告警结束 API
return Task.CompletedTask;
}
}