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

338 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 网关 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 问题
```csharp
// 当前: 调 /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
```csharp
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 构造函数签名变更
```csharp
// 旧: public Mc4AuthHelper(HttpClient http, string baseUrl)
// 新: public Mc4AuthHelper(HttpClient http, string baseUrl, string account, string password)
```
### 2.5 Mc4Adapter 构造函数变更
```csharp
// 旧:
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 注册变更
```csharp
// 旧: new Mc4Adapter(code, http, m.BaseUrl)
// 新: new Mc4Adapter(code, http, m.BaseUrl,
// m.Username ?? "admin", m.Password ?? "admin")
```
### 2.7 Mc4Config 增加字段
```csharp
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 更新
```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 新增方法
```csharp
/// <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
```csharp
/// <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 新增方法
```csharp
/// <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 原生批量接口:
```csharp
// 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 原生批量 |