Files
SecMPS/doc/设计文档/KMS钥匙柜适配器详细设计文档.md

15 KiB
Raw Blame History

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 + Token100% 设计覆盖29 个标准管理接口留 Phase 2 按需扩展。