Files
SecMPS/doc/设计文档/SecMPS统一问题清单20260603修复方案.md

281 lines
11 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.
# SecMPS 统一问题清单 2026-06-03 修复方案
> **版本**: 1.0
> **日期**: 2026-06-03
> **基准**: `SecMPS统一问题清单20260603.md`
> **原则**: 按优先级逐项修复,每项修复后编译验证;涉及网关/Vol.Pro 改动的放一组批量提交
---
## 修复总览
| 阶段 | 优先级 | 涉及项目 | 文件数 | 预计 |
|:---:|:---:|------|:---:|:---:|
| F1 | P0-1 ~ P0-3 | gateway + Vol.Pro | 5 | 2h |
| F2 | P1-1 ~ P1-6 | gateway + Vol.Pro + 库表 + 前端 | 8 | 4h |
| F3 | P2-1 ~ P2-5 | gateway + warehouse | 8 | 2h |
| F4 | P3-1 ~ P3-4 | gateway + 文档 | 4 | 1h |
| **合计** | — | 4 项目 | **25** | **~9h** |
---
## 阶段 F1: P0 阻塞项修复(预计 2h
#### F1.1 [P0-1] RealtimePollJob 填充实现
- [ ] 编辑 `api_sqlsugar/Warehouse/Services/RealtimePollJob.cs`
- [ ] 注入 `GatewayClient` + `Ibase_deviceRepository`
- [ ] `Execute()` 中:
1. 查询在线 MC4 网关 (`gateway_nodes WHERE IsOnline=在线 AND AdapterTypes LIKE '%MC4%'`)
2. 查对应设备列表 (`base_device WHERE DeviceGroup='IoT设备' AND IsOnline=在线`)
3. 对每个设备调 `GatewayClient.GetRealtimeAsync(gwBaseUrl, adapterCode, sourceId)`
4. 结果写入 `iot_devicedata`INSERT 新记录)
- [ ] `dotnet build` → 0 错误
#### F1.2 [P0-2] 网关 A1 自注册
- [ ] 编辑 `gateway/src/IntegrationGateway.Host/Program.cs`
- [ ]`registry.InitializeAllAsync()` 后加入:
```csharp
var nodeCode = gwCfg["NodeCode"] ?? "gw-default";
var nodeToken = gwCfg["NodeToken"] ?? "";
var adapterTypes = string.Join(",", registry.All.Select(a => a.AdapterCode));
await clientFactory.RegisterAsync(nodeCode, nodeToken, adapterTypes, volProUrl);
```
- [ ] `dotnet build` → 0 错误
#### F1.3 [P0-3] B 组路由认证
- [ ] 编辑 `gateway/src/IntegrationGateway.Host/Program.cs`
- [ ] 在 `app.UseCors()` 之后添加中间件:
```csharp
var gatewayKey = gwCfg["GatewayKey"];
if (!string.IsNullOrEmpty(gatewayKey))
{
app.Use(async (context, next) => {
var key = context.Request.Headers["X-Gateway-Key"].FirstOrDefault();
if (key == gatewayKey || context.Request.Path == "/") { await next(); }
else { context.Response.StatusCode = 401; }
});
}
```
- [ ] appsettings.json Gateway 段新增 `"GatewayKey": null`
- [ ] Vol.Pro 端 `GatewayClient` 所有 HTTP 请求头自动附加 `X-Gateway-Key`
- [ ] `dotnet build` → 0 错误
> **F1 提交点**: `Fix-P0: RealtimePollJob+A1自注册+B组认证`
---
## 阶段 F2: P1 重要项修复(预计 4h
#### F2.1 [P1-1] 网关新增批量实时值接口
- [ ] 编辑 `gateway/src/IntegrationGateway.Host/Program.cs`
- [ ] 新增 B4-batch 路由:
```csharp
app.MapPost("/api/gateway/realtime/{adapter}/batch", async (string adapter, BatchRealtimeRequest req) =>
{
var a = registry.FindByCode<IHasPoints>(adapter);
if (a == null) return Results.NotFound();
var results = new Dictionary<string, List<PointValue>>();
foreach (var deviceId in req.DeviceIds ?? new())
results[deviceId] = await a.GetRealtimeValuesAsync(deviceId);
return Results.Ok(results);
});
```
- [ ] 新增 `record BatchRealtimeRequest(List<string>? DeviceIds);`
- [ ] `dotnet build` → 0 错误
#### F2.2 [P1-2] 批量级联离线标记
- [ ] 编辑 `api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs`
- [ ] 替换逐条 `UpdateAsync` 为:
```csharp
context.Repository.DbContext.Db.Ado.ExecuteCommand(
"UPDATE base_device SET IsOnline='离线' WHERE GatewayNodeId=@id AND IsOnline='在线'",
new { id = node.GatewayNodeId });
```
- [ ] `dotnet build` → 0 错误
#### F2.3 [P1-3] 凭据安全化
- [ ] 编辑 `gateway/src/IntegrationGateway.Host/appsettings.json`
- `NodeToken` → `null`, 加注释 "生产环境由 SECMPS_GATEWAY_TOKEN 环境变量注入"
- Owl `Password` → `""`, 加注释
- KMS `ClientSecret` → `""`, 加注释
- [ ] 编辑 `gateway/src/IntegrationGateway.Host/Program.cs`
- `gwCfg["NodeToken"]` → `Environment.GetEnvironmentVariable("SECMPS_GATEWAY_TOKEN") ?? gwCfg["NodeToken"]`
- [ ] 编辑 `.gitignore` → 加 `**/bin/`、`**/obj/`
- [ ] `git rm -r --cached gateway/src/IntegrationGateway.Host/bin/`
#### F2.4 [P1-4] 前端网关地址统一化
- [ ] 编辑 `web.vite/public/index.html` 的 `window.apiConfig` → 加 `gatewayUrl: 'http://localhost:5100'`
- [ ] 编辑 `web.vite/src/views/warehouse/device_manager/base_device.vue`
- `const GW = 'http://localhost:5100'` → `const GW = window.apiConfig.gatewayUrl || 'http://localhost:5100'`
- [ ] 编辑 `warehouse/src/api/gateway.ts`
- `const GW_BASE = 'http://localhost:5100'` → 读取 `window.apiConfig.gatewayUrl`
- [ ] 编辑 `warehouse/index.html` 的 `window.apiConfig` → 加 `gatewayUrl`
#### F2.5 [P1-5] 规则引擎增加 DeviceId 映射
- [ ] 在规则引擎实现方案中增加 `BuildDeviceMappingAsync` 方法:
```csharp
var deviceIds = rules.SelectMany(r => r.Conditions).Select(c => c.DeviceId).Distinct();
var devices = await _deviceRepo.FindAsync(d => deviceIds.Contains(d.DeviceId));
var map = devices.ToDictionary(d => d.DeviceId, d => (d.AdapterCode, d.SourceId));
```
- [ ] 后续调网关时用 `map[cond.DeviceId]` 拼装 URL
#### F2.6 [P1-6] 新建 warehouse_variable 表
- [ ] 执行 SQL:
```sql
CREATE TABLE warehouse_variable (
VariableId INT IDENTITY PRIMARY KEY,
DeviceId INT NOT NULL,
VariableName NVARCHAR(255), -- 温度/湿度/人数
PointIndex INT DEFAULT 0, -- MC4 pointIndex
Unit NVARCHAR(50), -- ℃/%/人
SortOrder INT DEFAULT 0
);
```
- [ ] 在 Vol.Pro 代码生成器选择 `warehouse_variable`,生成全套 CRUD 代码
- [ ] 管理端字典 "变量列表" 绑定到 `warehouse_variable.VariableName`
- [ ] 规则条件/动作的 `ValueId` 下拉框改为从 `warehouse_variable` 查询JOIN `base_device.DeviceId`
> **F2 提交点**: `Fix-P1: B4-batch+批量离线+凭据安全+前端地址+DeviceId映射+变量表`
---
## 阶段 F3: P2 改善项修复(预计 2h
#### F3.1 [P2-1] 适配器异常日志
- [ ] 编辑 `gateway/src/IntegrationGateway.Adapters.Owl/OwlAdapter.cs`
- [ ] 编辑 `gateway/src/IntegrationGateway.Adapters.MC4/Mc4Adapter.cs`
- [ ] 编辑 `gateway/src/IntegrationGateway.Adapters.Kms/KmsAdapter.cs`
- [ ] 所有 `catch { return false; }` → `catch (Exception ex) { Console.Error.WriteLine($"[{AdapterCode}] HealthCheck: {ex.Message}"); return false; }`
- [ ] `dotnet build` → 0 错误
#### F3.2 [P2-2] 规则引擎滞后窗
- [ ] 在 `warehouse_rulecondition` 表新增字段:
```sql
ALTER TABLE warehouse_rulecondition ADD
RecoveryThreshold_Numeric DECIMAL(18,2) NULL, -- 恢复阈值(下界)
RecoveryThreshold_Switch NVARCHAR(50) NULL; -- 恢复开关状态
```
- [ ] `RuleEngineService.EvaluateCondition` 中加逻辑:
```csharp
bool wasTriggered = cond.LastTriggered.HasValue;
if (wasTriggered)
return Compare(actualValue, "大于等于", cond.RecoveryThreshold_Numeric);
else
return Compare(actualValue, cond.CompareOperator, cond.TargetValue_Number);
```
#### F3.3 [P2-3] 条件级冷却
- [ ] `warehouse_rulecondition` 表新增 `LastTriggered DATETIME NULL`、`LastTriggerValue DECIMAL(18,2) NULL`
- [ ] `RuleEngineService.EvaluateCondition` 中:
- 如果 `DateTime.Now - cond.LastTriggered < rule.CooldownSec` → 跳过此条件
- 触发时更新 `LastTriggered` 和 `LastTriggerValue`
#### F3.4 [P2-4] 生产环境移除 console.log
- [ ] 编辑 `warehouse/vite.config.ts`
```typescript
build: {
terserOptions: { compress: { drop_console: true } }
}
```
- [ ] 开发环境保留 `console.log`(仅 build 时移除)
- [ ] `npm run build` → 确认无 console.log 残留
#### F3.5 [P2-5] 统一 gateway API 封装
- [ ] 复制 `warehouse/src/api/gateway.ts` → `web.vite/src/api/gateway.js`
- 修改 `GW_BASE` 为 `window.apiConfig.gatewayUrl || 'http://localhost:5100'`
- [ ] `web.vite/src/views/warehouse/device_manager/base_device.vue`
- 删除内联 `const GW =` + `fetch()` → 改为 `import { gwGet, gwPost } from '@/api/gateway.js'`
- 所有 `fetch(\`\${GW}/api/gateway/...\`)` → `gwGet(...)` / `gwPost(...)`
> **F3 提交点**: `Fix-P2: 异常日志+滞后窗+条件冷却+console清理+API统一`
---
## 阶段 F4: P3 优化项(预计 1h
#### F4.1 [P3-1] 规则引擎并发动作执行
- [ ] 在规则引擎实现方案的 `ExecuteActionsAsync` 中:
```csharp
var tasks = actions.Select(a => ExecuteSingleActionAsync(a, rule));
await Task.WhenAll(tasks);
async Task ExecuteSingleActionAsync(Action a, Rule r) {
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try { await DoAction(a, r, cts.Token); }
catch (OperationCanceledException) { Log($"[RuleEngine] 动作超时: {a.id}"); }
}
```
#### F4.2 [P3-2] 清理 bin/obj + .gitignore
- [ ] `.gitignore` 追加规则(如未在 F2.3 中完成):
```
**/bin/
**/obj/
gateway/src/IntegrationGateway.Host/bin/
api_sqlsugar/**/bin/
```
- [ ] `git rm -r --cached` 所有 bin/obj 目录
#### F4.3 [P3-3] 网关 Swagger
- [ ] 编辑 `gateway/src/IntegrationGateway.Host/Program.cs`:
```csharp
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// ...
app.UseSwagger();
app.UseSwaggerUI();
```
- [ ] 浏览器访问 `http://localhost:5100/swagger` 验证
#### F4.4 [P3-4] 同步设计文档路由数
- [ ] 编辑 `doc/设计文档/对接网关设计文档.md` → 路由表从 14 条更新为当前实际数
- [ ] 确认以下设计文档一致: 对接网关设计文档、规则引擎方案、KMS 设计文档
> **F4 提交点**: `Fix-P3: 并发动作+清理bin+Swagger+文档同步`
---
## 任务总览
| 编号 | 问题 | 涉及文件 | 预计 |
|:---:|------|------|:---:|
| P0-1 | RealtimePollJob 空壳 | RealtimePollJob.cs | 1h |
| P0-2 | A1 自注册 | Program.cs | 30min |
| P0-3 | B 组认证 | Program.cs + appsettings | 30min |
| P1-1 | B4-batch | Program.cs | 30min |
| P1-2 | 批量离线 | HeartbeatMonitorJob.cs | 20min |
| P1-3 | 凭据安全 | appsettings + .gitignore + bin | 20min |
| P1-4 | 前端地址 | base_device.vue + gateway.ts | 20min |
| P1-5 | DeviceId 映射 | RuleEngineService | 30min |
| P1-6 | 变量表 | SQL + 代码生成 + 前端 | 1h |
| P2-1 | 异常日志 | OwlAdapter + MC4Adapter + KmsAdapter | 20min |
| P2-2 | 滞后窗 | SQL + RuleEngineService | 30min |
| P2-3 | 条件冷却 | SQL + RuleEngineService | 20min |
| P2-4 | console 清理 | vite.config.ts | 10min |
| P2-5 | API 统一 | gateway.js + base_device.vue | 30min |
| P3-1 | 并发动作 | RuleEngineService | 15min |
| P3-2 | bin 清理 | .gitignore + git rm | 5min |
| P3-3 | Swagger | Program.cs | 10min |
| P3-4 | 文档同步 | 设计文档 | 15min |
> **总计**: 18 项 / 25 文件 / ~9h