From a869fa5dbfb266c8ca5d13345e99642a9c633794 Mon Sep 17 00:00:00 2001 From: g82tt Date: Tue, 19 May 2026 22:43:32 +0800 Subject: [PATCH] =?UTF-8?q?K3:=20KmsAdapter=E6=A0=B8=E5=BF=83=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=B0=B1=E7=BB=AA(HealthCheck/GetDevices/GetAlarms/Co?= =?UTF-8?q?nfirm)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../KmsAdapter.cs | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 gateway/src/IntegrationGateway.Adapters.Kms/KmsAdapter.cs diff --git a/gateway/src/IntegrationGateway.Adapters.Kms/KmsAdapter.cs b/gateway/src/IntegrationGateway.Adapters.Kms/KmsAdapter.cs new file mode 100644 index 0000000..5cdf835 --- /dev/null +++ b/gateway/src/IntegrationGateway.Adapters.Kms/KmsAdapter.cs @@ -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; + +/// +/// KMS 智能钥匙柜适配器。 +/// 实现: IHasFlatDevices + IHasAlarms。 +/// +/// 设备模型:柜体为父设备(IsParent=是),锁孔为子设备(ParentSourceId=柜体SourceId)。 +/// AdapterCode: "KMS:{InstanceName}"。 +/// 限流:5 QPS。 +/// +/// 按设计文档 §6 KmsAdapter 完整实现。 +/// +public class KmsAdapter : IHasFlatDevices, IHasAlarms +{ + private readonly HttpClient _http; + private readonly KmsAuthHelper _auth; + private readonly RateLimiter _limiter = new(5); + + /// 适配器编码,格式 "KMS:{实例名}" + public string AdapterCode { get; } + + /// 人类可读的适配器名称 + public string DisplayName => $"KMS ({AdapterCode})"; + + /// 适配器能力声明 + public AdapterCapabilities Capabilities => new() { HasFlatDevices = true, HasAlarms = true }; + + /// 创建 KmsAdapter 实例 + /// 适配器编码 + /// HttpClient 实例 + /// KMS 服务地址 + /// KMS 客户端 ID + /// KMS 客户端密钥 + public KmsAdapter(string adapterCode, HttpClient http, string baseUrl, string clientId, string clientSecret) + { + AdapterCode = adapterCode; + _http = http; + _auth = new KmsAuthHelper(http, baseUrl, clientId, clientSecret); + } + + /// 初始化适配器:获取 KMS Token + public async Task InitializeAsync() => await _auth.GetTokenAsync(); + + // ═══════════════════════════════════════════ + // IGatewayAdapter — 健康检查(2.18.1 心跳) + // ═══════════════════════════════════════════ + + /// 调用 KMS 心跳接口确认可达性 + public async Task 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 柜体+钥匙) + // ═══════════════════════════════════════════ + + /// + /// 获取 KMS 所有柜体及其锁孔,映射为 StandardDevice 列表。 + /// 柜体为父设备(IsParent=是),锁孔为子设备(ParentSourceId=柜体SourceId)。 + /// + public async Task> 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()!; + + var devices = new List(); + 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 { Items = devices, Total = devices.Count }; + } + + /// KMS 柜体 → StandardDevice(父设备) + 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 + { + ["lockerCode"] = locker.LockerCode, + ["lockholeCount"] = locker.LockholeList?.Count ?? 0 + } + }; + + /// KMS 锁孔 → StandardDevice(子设备) + 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 + { + ["openerId"] = hole.OpenerId, + ["openerType"] = hole.OpenerType, + ["openerState"] = hole.OpenerState + } + }; + + // ═══════════════════════════════════════════ + // IHasAlarms — 告警(2.18.7 告警列表) + // ═══════════════════════════════════════════ + + /// 分页查询 KMS 告警列表,映射到 StandardAlarm + public async Task> 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()!; + + 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 { Items = alarms, Total = data.Total }; + } + + /// 确认告警(调 KMS 标准告警确认接口) + public async Task ConfirmAlarmAsync(string alarmId) + { + await _limiter.WaitAsync(); + var client = await _auth.GetAuthenticatedClientAsync(); + await client.PostAsync($"/prod-api/kms/warning/confirm/{alarmId}", null); + } + + /// 结束告警(KMS 第三方接口不提供,留空实现) + public Task EndAlarmAsync(string alarmId) + { + // KMS 第三方接口 (2.18.7) 不提供告警结束 API + return Task.CompletedTask; + } +}