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, IAcceptsControl, IHasBusinessLogs, IAcceptsDataSync
{
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;
}
// ═══════════════════════════════════════════
// 扩展方法 — 2.18 第三方接口全覆盖
// ═══════════════════════════════════════════
/// 2.18.6 查询借还记录列表
public async Task> GetBorrowRecordsAsync(DateTime? from = null, DateTime? to = null)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = "{}"; // 联调时加入时间范围参数
var resp = await client.PostAsync("/prod-api/getRecordList",
new StringContent(body, Encoding.UTF8, "application/json"));
resp.EnsureSuccessStatusCode();
var data = await resp.Content.ReadFromJsonAsync()!;
return new PagedResult { Items = data.Rows ?? new(), Total = data.Total };
}
/// 2.18.5 查询授权记录列表
public async Task> GetPermissionListAsync(DateTime? from = null, DateTime? to = null)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = "{}"; // 联调时加入时间范围
var resp = await client.PostAsync("/prod-api/getPermissionList",
new StringContent(body, Encoding.UTF8, "application/json"));
resp.EnsureSuccessStatusCode();
var data = await resp.Content.ReadFromJsonAsync()!;
return new PagedResult { Items = data.Rows ?? new(), Total = data.Total };
}
/// 2.18.3 从 Vol.Pro 向 KMS 批量同步员工
public async Task BatchSyncStaffAsync(List staffList)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var resp = await client.PostAsJsonAsync("/prod-api/batchSyncStaff", new { staff = staffList });
resp.EnsureSuccessStatusCode();
}
/// 2.18.2 从 Vol.Pro 向 KMS 批量删除员工
public async Task BatchDeleteStaffAsync(List staffUuids)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var resp = await client.PostAsJsonAsync("/prod-api/batchDeleteStaff", staffUuids);
resp.EnsureSuccessStatusCode();
}
/// 2.4.3 远程授权开门
public async Task RemoteAuthorizeAsync(KmsRemotePermissionRequest request)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var resp = await client.PostAsJsonAsync("/prod-api/kms/permission/remote", request);
resp.EnsureSuccessStatusCode();
}
/// 2.18.8 代理 KMS 第三方登录/事件记录
public async Task ThirdPlatLoginAsync(string username)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var resp = await client.PostAsync($"/thirdPlatlogin?username={Uri.EscapeDataString(username)}", null);
if (resp.StatusCode == System.Net.HttpStatusCode.Redirect)
return resp.Headers.Location?.ToString();
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsStringAsync();
}
// ═══════════════════════════════════════════
// IAcceptsControl — 设备控制(远程开门)
// ═══════════════════════════════════════════
/// 向设备下发控制指令(如远程开门)
public async Task SendControlAsync(string sourceDeviceId, string command, Dictionary parameters)
{
await _limiter.WaitAsync();
try
{
if (command == "open" || command == "authorize")
{
var req = new KmsRemotePermissionRequest
{
StaffIds = parameters.TryGetValue("staffIds", out var s) && s is List sl ? sl : null,
OpenerIds = parameters.TryGetValue("lockholeSort", out var lh) ? new List { (int)(long)lh! } : null,
Type = command == "authorize" ? 2 : 1
};
await RemoteAuthorizeAsync(req);
}
return new ControlResult { Success = true };
}
catch (Exception ex)
{
return new ControlResult { Success = false, Message = ex.Message };
}
}
// ═══════════════════════════════════════════
// IHasBusinessLogs — 业务记录查询
// ═══════════════════════════════════════════
/// 按类型查询业务记录
public async Task> GetBusinessLogsAsync(
string logType, DateTime? from, DateTime? to, int page, int size, Dictionary? filters = null)
{
if (logType == "borrow" || logType == "handover")
{
var records = await GetBorrowRecordsAsync(from, to);
return new PagedResult
{
Items = records.Items.Select(r => new BusinessLogEntry
{
LogId = r.Uuid ?? "", LogType = logType,
DeviceSourceId = $"lockhole_{r.LockerName}_{r.LockholeSort}",
StaffName = r.StaffName, Description = r.OpenerName,
CreatedAt = DateTime.TryParse(r.BorrowTime, out var bt) ? bt : null
}).ToList(),
Total = records.Total
};
}
if (logType == "permission")
{
var perms = await GetPermissionListAsync(from, to);
return new PagedResult
{
Items = perms.Items.Select(p => new BusinessLogEntry
{
LogId = p.Uuid ?? "", LogType = "permission",
StaffName = p.LendStaffName, Description = p.OpenerCnName,
CreatedAt = DateTime.TryParse(p.ApplyTime, out var at) ? at : null
}).ToList(),
Total = perms.Total
};
}
return new PagedResult { Items = new(), Total = 0 };
}
// ═══════════════════════════════════════════
// IAcceptsDataSync — 数据同步写入
// ═══════════════════════════════════════════
/// 向 KMS 批量同步数据
public async Task SyncDataAsync(string dataType, List