11 KiB
11 KiB
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()中:- 查询在线 MC4 网关 (
gateway_nodes WHERE IsOnline=在线 AND AdapterTypes LIKE '%MC4%') - 查对应设备列表 (
base_device WHERE DeviceGroup='IoT设备' AND IsOnline=在线) - 对每个设备调
GatewayClient.GetRealtimeAsync(gwBaseUrl, adapterCode, sourceId) - 结果写入
iot_devicedata表(INSERT 新记录)
- 查询在线 MC4 网关 (
dotnet build→ 0 错误
F1.2 [P0-2] 网关 A1 自注册
- 编辑
gateway/src/IntegrationGateway.Host/Program.cs - 在
registry.InitializeAllAsync()后加入: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()之后添加中间件: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 路由:
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为: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.jsonNodeToken→null, 加注释 "生产环境由 SECMPS_GATEWAY_TOKEN 环境变量注入"- Owl
Password→"", 加注释 - KMS
ClientSecret→"", 加注释
- 编辑
gateway/src/IntegrationGateway.Host/Program.csgwCfg["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.vueconst GW = 'http://localhost:5100'→const GW = window.apiConfig.gatewayUrl || 'http://localhost:5100'
- 编辑
warehouse/src/api/gateway.tsconst GW_BASE = 'http://localhost:5100'→ 读取window.apiConfig.gatewayUrl
- 编辑
warehouse/index.html的window.apiConfig→ 加gatewayUrl
F2.5 [P1-5] 规则引擎增加 DeviceId 映射
- 在规则引擎实现方案中增加
BuildDeviceMappingAsync方法: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:
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查询(JOINbase_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表新增字段:ALTER TABLE warehouse_rulecondition ADD RecoveryThreshold_Numeric DECIMAL(18,2) NULL, -- 恢复阈值(下界) RecoveryThreshold_Switch NVARCHAR(50) NULL; -- 恢复开关状态 RuleEngineService.EvaluateCondition中加逻辑: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) NULLRuleEngineService.EvaluateCondition中:- 如果
DateTime.Now - cond.LastTriggered < rule.CooldownSec→ 跳过此条件 - 触发时更新
LastTriggered和LastTriggerValue
- 如果
F3.4 [P2-4] 生产环境移除 console.log
- 编辑
warehouse/vite.config.tsbuild: { 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中: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: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