T完成: TaskController创建+3个IJob构造函数改造(IServiceProvider注入)+RuleEngineJob标记迁移

This commit is contained in:
2026-06-04 00:43:48 +08:00
parent bb56c229f8
commit 79b8400e6d
9 changed files with 1415 additions and 1067 deletions

View File

@@ -0,0 +1,148 @@
# 网关 ↔ Vol.Pro 自动注册机制检查报告 2026-06-03
> **日期**: 2026-06-03
> **检查范围**: `gateway/src/IntegrationGateway.Host/Program.cs` A1 注册段 + `VolPro gateway_nodesController.cs` A1-A4 + 相关 Service
> **方法**: 逐行比对网关发送体 ↔ Vol.Pro 接收体 ↔ 数据库 Schema
---
## 1. 数据流追踪
```
网关 Program.cs (line 82-105)
├─ InitializeAllAsync() ← 适配器初始化
├─ RegisterAsync(registerReq) ← A1: POST /api/gateway/register
└─ (无后续调用) ← A2/A3 未触发
Vol.Pro gateway_nodesController
├─ [POST] /api/gateway/register ← RegisterGateway → RegisterNodeAsync
├─ [POST] /api/gateway/heartbeat ← GatewayHeartbeat → UpdateHeartbeatAsync
├─ [POST] /api/gateway/sync/devices ← SyncDevices → SyncDevicesAsync
└─ [POST] /api/gateway/sync/alarms ← SyncAlarms → UpsertAlarmAsync
```
---
## 2. 发现的问题
### 2.1 🔴 网关不调用 A3 设备同步 — 注册后设备列表为空
**现状**: 网关 Program.cs 在 A1 注册后**不调用** `clientFactory.SyncDevicesAsync()`。Vol.Pro 的 `RegisterGateway` 返回设备列表时调用 `GetDevicesByGatewayNodeAsync(node.NodeId)`,查询 `WHERE NodeId=xxx AND ParentDeviceId IS NULL`
**后果**: 首次注册返回的设备列表永远为空(数据库尚无此网关的设备记录)。设备必须等 Vol.Pro 的 Quartz `SyncDevicesJob`(每 5 分钟触发一次)才有机会同步。
**修复**: A1 注册后立即遍历适配器同步设备,整体流程改为:
```csharp
// A1 注册
var registerResult = await clientFactory.RegisterAsync(registerReq);
// A3 同步设备Owl → GetDevicesAsync, MC4 → GetObjectTreeAsync
foreach (var adapter in registry.All)
{
var devices = adapter switch
{
IHasFlatDevices f => (await f.GetDevicesAsync(1, 1000)).Items,
IHasOwnDeviceTree t => FlattenTree(await t.GetObjectTreeAsync()),
_ => new()
};
if (devices.Any())
await clientFactory.SyncDevicesAsync(nodeCode, nodeToken, devices.Select(d => new { ... }).ToList());
}
```
### 2.2 🔴 网关 A1 BaseUrl 硬编码 `localhost`
**现状**:
```csharp
BaseUrl = $"http://localhost:{app.Urls.FirstOrDefault()?.Split(':').LastOrDefault() ?? "5100"}"
```
**后果**: 网关部署在 `192.168.1.100`Vol.Pro 存的 BaseUrl 仍是 `http://localhost:5100`。Vol.Pro 端的 GatewayClient 和 Quartz Job 用此地址回调网关时全部失败。
**修复**: 改为读取配置或使用 `app.Configuration["Gateway:SelfUrl"]`,不填时降级为 `localhost`
```csharp
BaseUrl = gwCfg["SelfUrl"] ?? $"http://localhost:{port}"
```
### 2.3 🟠 网关不调用 A2 心跳 — 无持续在线状态更新
**现状**: 网关只在 A1 注册时上报一次在线状态,之后不再调 A2 心跳。
**后果**: Vol.Pro 的 `gateway_nodes.LastHeartbeat` 停留在注册时刻,`HeartbeatMonitorJob`(每 15s会在注册后 30s 将网关标记离线。
**修复**: 注册完成后启动后台心跳循环:
```csharp
_ = Task.Run(async () => {
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(15));
try { await clientFactory.HeartbeatAsync(new GatewayHeartbeatRequest { NodeCode = nodeCode, Token = nodeToken }); }
catch { Console.Error.WriteLine("[Gateway] 心跳失败"); }
}
});
```
### 2.4 🟠 Vol.Pro — RegisterNodeAsync 使用旧 Queryable 语法
**现状**:
```csharp
var existing = _repository.DbContext.Queryable<gateway_nodes>()
.First(x => x.NodeCode == nodeCode);
```
`DbContext.Queryable` 是 SqlSugar 原始方式,项目中其他 Service 使用 `FindAsIQueryable`Vol.Pro 封装)。
**后果**: 不影响功能但风格不一致。且 `.First()` 可能抛异常(找不到记录时),而 `.FirstOrDefault()` + null 检查更安全。
**修复**: 改为 `FindAsIQueryable(x => x.NodeCode == nodeCode).FirstOrDefaultAsync()`
### 2.5 🟡 A1 返回结果未被网关使用
**现状**:
```csharp
var registerResult = await clientFactory.RegisterAsync(registerReq);
// registerResult 未使用
```
`RegisterGateway` 返回 `{ nodeId, devices: [...] }`,网关不读取也不使用。
**后果**: 网关不知道自己的 NodeId后续 A2/A3 需要 nodeCode + token 而非 nodeId。GatewayClientFactory 的 A2/A3 方法也用的是 nodeCode + token所以不依赖 nodeId。
**评估**: **不需要修复** — 当前设计合理nodeCode 是天然业务主键)。
### 2.6 🟡 base_deviceService 与 gateway_nodesService 重复实现设备同步
**现状**:
- `gateway_nodesService.SyncDevicesAsync` — 完整的设备同步(新增+更新)
- `base_deviceService.UpsertDeviceAsync` — 单设备 Upsert被 Controller 调用但实际未被使用)
`gateway_nodesController.SyncDevices` 调的是 `gateway_nodesService.SyncDevicesAsync` 而非 `base_deviceService.UpsertDeviceAsync`
**后果**: `base_deviceService.UpsertDeviceAsync` 是死代码。
**修复**: 保留 `gateway_nodesService.SyncDevicesAsync`(批量处理更高效),移除或标记 `base_deviceService.UpsertDeviceAsync``[Obsolete]`
---
## 3. 调用链完整性矩阵
| 接口 | Vol.Pro 端 | 网关调用 | 状态 |
|------|:---:|:---:|:--:|
| A1 /api/gateway/register | ✅ RegisterGateway | ✅ 已调用 | ⚠️ BaseUrl=localhost / 不返设备 |
| A2 /api/gateway/heartbeat | ✅ GatewayHeartbeat | ❌ 未调用 | 🔴 30秒后被标记离线 |
| A3 /api/gateway/sync/devices | ✅ SyncDevices | ❌ 未调用 | 🔴 首次注册设备列表为空 |
| A4 /api/gateway/sync/alarms | ✅ SyncAlarms | ❌ 未调用 | ⚪ 告警由 Vol.Pro Quartz 拉取 |
---
## 4. 修复优先级
| 编号 | 问题 | 严重度 | 预计 |
|:---:|------|:---:|:---:|
| 1 | A3 设备同步未触发 | 🔴 | 30min |
| 2 | A1 BaseUrl=localhost | 🔴 | 10min |
| 3 | A2 心跳未循环 | 🟠 | 15min |
| 4 | RegisterNodeAsync 语法不一致 | 🟠 | 5min |
| 5 | 重复的设备 Upsert 逻辑 | 🟡 | 10min |