Files
SecMPS/doc/设计文档/网关MC4模块整改方案_v1.0.md

10 KiB
Raw Blame History

网关 MC4 模块整改方案 v1.0

版本: 1.0 日期: 2026-06-03 基准: doc/设计文档/网关MC4模块检查报告20260603.md


1. 整改总览

步骤 优先级 内容 文件 预计
M1 🔴 P0 Mc4AuthHelper 认证修复 Mc4AuthHelper.cs + appsettings 1h
M2 🟠 P1 批量点位查询 Mc4Adapter.cs 30min
M3 🟡 P2 历史告警查询 Mc4Adapter.cs 30min
M4 🟡 P2 B4-batch 路由改用 native batch Program.cs 15min
M5 验证 编译 + 联调 30min
合计 4 文件 ~3h

2. 步骤 M1: Mc4AuthHelper 认证修复(预计 1h

2.1 问题

// 当前: 调 /conf/get (返回 { "encrypt": true }),误读为 Token
var resp = await _http.PostAsync($"{_baseUrl}/api/central/auth/conf/get", null);
var result = JsonSerializer.Deserialize<Mc4AuthResponse>(json);
_token = result?.Token ?? "";  // Token 始终为 null

2.2 MC4.0 实际认证流程

1. POST /api/central/auth/conf/get  → { "encrypt": true/false }
2. 若 encrypt=true → 密码 MD5(原始密码)
3. POST /api/central/auth/login {
     "account": "admin",
     "password": "md5或原始密码"
   }
   → { "token": "xxx", "id": 0, "account": "admin", "name": "管理员" }

2.3 修改后的 Mc4AuthHelper

public class Mc4AuthHelper
{
    private readonly HttpClient _http;
    private readonly string _baseUrl;
    private readonly string _account;
    private readonly string _password;
    private string? _token;
    private DateTime _tokenExpiry = DateTime.MinValue;
    private bool? _needMd5;

    public Mc4AuthHelper(HttpClient http, string baseUrl, string account, string password)
    {
        _http = http;
        _baseUrl = baseUrl.TrimEnd('/');
        _account = account;
        _password = password;
    }

    public async Task<string> GetTokenAsync()
    {
        if (!string.IsNullOrEmpty(_token) && DateTime.UtcNow < _tokenExpiry)
            return _token;

        // 1. 获取加密配置
        if (!_needMd5.HasValue)
        {
            var confResp = await _http.PostAsync($"{_baseUrl}/api/central/auth/conf/get", null);
            if (confResp.IsSuccessStatusCode)
            {
                var confJson = await confResp.Content.ReadAsStringAsync();
                var conf = JsonSerializer.Deserialize<Mc4ConfResponse>(confJson);
                _needMd5 = conf?.Encrypt ?? false;
            }
            else
            {
                _needMd5 = false; // 失败时假定不需要加密
            }
        }

        // 2. 登录获取 Token
        var pwd = _needMd5 == true ? ComputeMd5(_password) : _password;
        var loginBody = JsonSerializer.Serialize(new { account = _account, password = pwd });
        var resp = await _http.PostAsync($"{_baseUrl}/api/central/auth/login",
            new StringContent(loginBody, Encoding.UTF8, "application/json"));
        resp.EnsureSuccessStatusCode();
        var json = await resp.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<Mc4LoginResponse>(json)
            ?? throw new Exception("MC4 登录失败");
        _token = result.Token ?? "";
        _tokenExpiry = DateTime.UtcNow.AddHours(7); // 保守估计 8h
        return _token;
    }

    public async Task<HttpClient> GetAuthenticatedClientAsync()
    {
        var token = await GetTokenAsync();
        var client = new HttpClient { BaseAddress = new Uri(_baseUrl) };
        if (!string.IsNullOrEmpty(token))
            client.DefaultRequestHeaders.Add("token", token);
        return client;
    }

    public void Invalidate() => _token = null;

    private static string ComputeMd5(string input) { /* MD5 实现 or use System.Security.Cryptography */ }

    private class Mc4ConfResponse { public bool? Encrypt { get; set; } }
    private class Mc4LoginResponse { public string? Token { get; set; } public int Id { get; set; } public string? Account { get; set; } }
}

2.4 构造函数签名变更

// 旧: public Mc4AuthHelper(HttpClient http, string baseUrl)
// 新: public Mc4AuthHelper(HttpClient http, string baseUrl, string account, string password)

2.5 Mc4Adapter 构造函数变更

// 旧:
public Mc4Adapter(string adapterCode, HttpClient http, string baseUrl)
{
    _auth = new Mc4AuthHelper(http, baseUrl);
}

// 新:
public Mc4Adapter(string adapterCode, HttpClient http, string baseUrl,
    string account = "admin", string password = "admin")
{
    _auth = new Mc4AuthHelper(http, baseUrl, account, password);
}

2.6 Program.cs 注册变更

// 旧: new Mc4Adapter(code, http, m.BaseUrl)
// 新: new Mc4Adapter(code, http, m.BaseUrl,
//         m.Username ?? "admin", m.Password ?? "admin")

2.7 Mc4Config 增加字段

public class Mc4Config
{
    public string? InstanceName { get; set; }
    public string BaseUrl { get; set; } = "";
    public string Username { get; set; } = "admin";   // 新增
    public string Password { get; set; } = "admin";   // 新增
}

2.8 appsettings.json 更新

"MC4": [
    { "InstanceName": "31ku", "BaseUrl": "http://localhost:3000",
      "Username": "admin", "Password": "your_mc4_password" }
]

2.9 编译验证

dotnet build gateway/IntegrationGateway.slnx → 0 错误。

M1 提交点: Fix-M1: Mc4AuthHelper 认证修复 conf/get→login + account/password支持


3. 步骤 M2: 批量点位查询(预计 30min

3.1 文件

gateway/src/IntegrationGateway.Adapters.MC4/Mc4Adapter.cs

3.2 新增方法

/// <summary>批量获取多个设备的实时点位值MC4.0 原生 multi/value/get</summary>
public async Task<Dictionary<int, List<Mc4PointValue>>> GetMultiRealtimeValuesAsync(List<int> deviceIds)
{
    await _limiter.WaitAsync();
    var client = await _auth.GetAuthenticatedClientAsync();
    var body = JsonSerializer.Serialize(new { ids = deviceIds });
    var resp = await client.PostAsync("/api/central/point/multi/value/get",
        new StringContent(body, Encoding.UTF8, "application/json"));
    resp.EnsureSuccessStatusCode();
    var json = await resp.Content.ReadAsStringAsync();
    var result = JsonSerializer.Deserialize<Dictionary<int, List<Mc4PointValue>>>(json)!;
    return result;
}

3.3 编译验证

dotnet build → 0 错误。

M2 提交点: Fix-M2: MC4 批量点位查询 GetMultiRealtimeValuesAsync


4. 步骤 M3: 历史告警查询(预计 30min

4.1 新增 DTO

/// <summary>MC4.0 历史告警查询请求</summary>
public class Mc4HisAlarmQuery
{
    public string From { get; set; } = "";
    public string To { get; set; } = "";
    public int Skip { get; set; }
    public int Limit { get; set; }
    public int Sort { get; set; } = 1;
}

4.2 新增方法

/// <summary>查询 MC4.0 历史告警(已恢复的告警)</summary>
public async Task<PagedResult<StandardAlarm>> GetHisAlarmsAsync(int page, int size, DateTime from, DateTime to)
{
    await _limiter.WaitAsync();
    var client = await _auth.GetAuthenticatedClientAsync();
    var body = JsonSerializer.Serialize(new Mc4HisAlarmQuery
    {
        From = from.ToString("yyyy-MM-dd HH:mm:ss"),
        To = to.ToString("yyyy-MM-dd HH:mm:ss"),
        Skip = (page - 1) * size,
        Limit = size,
        Sort = 1
    });
    var resp = await client.PostAsync("/api/central/his_alarm/query",
        new StringContent(body, Encoding.UTF8, "application/json"));
    resp.EnsureSuccessStatusCode();
    var json = await resp.Content.ReadAsStringAsync();
    var result = JsonSerializer.Deserialize<Mc4AlarmQueryResult>(json)!;
    return new PagedResult<StandardAlarm>
    {
        Items = (result.List ?? new()).Select(MapAlarmItem).ToList(),
        Total = result.Total
    };
}

private StandardAlarm MapAlarmItem(Mc4AlarmItem a) => new()
{
    AlarmId = a.Id ?? "",
    AdapterCode = AdapterCode,
    Level = MapAlarmLevel(a.Level),
    Title = a.Desc ?? "",
    OccurTime = DateTime.TryParse(a.Stime, out var st) ? st : DateTime.MinValue,
    Status = MapAlarmState(a.State),
    ActualValue = a.Soption?.Value,
    ThresholdValue = a.Eoption?.Value
};

4.3 编译验证

dotnet build → 0 错误。

M3 提交点: Fix-M3: MC4 历史告警查询 GetHisAlarmsAsync


5. 步骤 M4: B4-batch 路由优化(预计 15min

5.1 修改

gateway/src/IntegrationGateway.Host/Program.cs B4-batch 路由改用 MC4 原生批量接口:

// B4-batch 改用 MC4 原生 multi/value/get
app.MapPost("/api/gateway/realtime/{adapter}/batch", async (string adapter, BatchRealtimeRequest req) =>
{
    var a = registry.FindByCode<IHasPoints>(adapter);
    if (a == null) return Results.NotFound();

    if (a is Mc4Adapter mc4 && req.DeviceIds?.Count > 0)
    {
        // MC4.0 原生批量接口
        var intIds = req.DeviceIds.Select(int.Parse).ToList();
        var multi = await mc4.GetMultiRealtimeValuesAsync(intIds);
        return Results.Ok(multi);
    }

    // 其他适配器 fallback
    var results = new Dictionary<string, List<PointValue>>();
    foreach (var id in req.DeviceIds ?? new())
        try { results[id] = await a.GetRealtimeValuesAsync(id); } catch { }
    return Results.Ok(results);
});

5.2 编译验证

dotnet build → 0 错误。

M4 提交点: Fix-M4: B4-batch 优化 MC4原生批量接口


6. 步骤 M5: 编译验证 + 联调

  • dotnet build gateway/IntegrationGateway.slnx → 0 错误 0 警告
  • MC4 appsettings.json 填入真实 Username/Password
  • 网关启动 → A1 注册 → A3 同步 MC4 设备树
  • B4-batch 调 multi/value/get 返回批量值
  • 告警查询 /alarms/MC4:31ku 有数据
  • Mc4AuthHelper Token 非空 → 登录流程正常

M5 提交点: Fix-M5: MC4整改全量编译验证通过


7. 改动文件汇总

步骤 文件 改动
M1 Mc4AuthHelper.cs 重写认证流程: conf/get → login
M1 Mc4Adapter.cs 构造函数加 account/password
M1 Program.cs Mc4Adapter 构造传 Username/Password
M1 appsettings.json MC4 数组加 Username/Password
M2 Mc4Adapter.cs 新增 GetMultiRealtimeValuesAsync
M3 Mc4Adapter.cs 新增 GetHisAlarmsAsync + DTO
M4 Program.cs B4-batch 优化 MC4 原生批量