338 lines
10 KiB
Markdown
338 lines
10 KiB
Markdown
# 网关 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 原生批量 |
|