From 4427ca9fb98d7363798b51e68d83edcda4926198 Mon Sep 17 00:00:00 2001 From: g82tt Date: Wed, 3 Jun 2026 17:26:52 +0800 Subject: [PATCH] =?UTF-8?q?SecMPS=E7=BB=9F=E4=B8=80=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E6=B8=85=E5=8D=95+=E4=BF=AE=E5=A4=8D=E6=96=B9=E6=A1=88:=2018?= =?UTF-8?q?=E9=A1=B9=E9=97=AE=E9=A2=98=204=E9=98=B6=E6=AE=B5F1-F4=2025?= =?UTF-8?q?=E6=96=87=E4=BB=B6~9h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/设计文档/SecMPS统一问题清单20260603.md | 56 ++++ .../SecMPS统一问题清单20260603修复方案.md | 280 ++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 doc/设计文档/SecMPS统一问题清单20260603.md create mode 100644 doc/设计文档/SecMPS统一问题清单20260603修复方案.md diff --git a/doc/设计文档/SecMPS统一问题清单20260603.md b/doc/设计文档/SecMPS统一问题清单20260603.md new file mode 100644 index 0000000..8157224 --- /dev/null +++ b/doc/设计文档/SecMPS统一问题清单20260603.md @@ -0,0 +1,56 @@ +# SecMPS 统一问题清单 2026-06-03 + +> **版本**: 1.0 +> **日期**: 2026-06-03 +> **范围**: gateway / VolPro (api_sqlsugar) / web.vite / warehouse / owl_zlmediakit +> **来源**: 项目深度审计 + 规则引擎方案审查 + +--- + +## P0 — 阻塞性(影响功能完整性,必须修复) + +| 编号 | 类别 | 问题 | 影响 | 方案 | +|:---:|:---:|------|------|------| +| P0-1 | 规则引擎 | RealtimePollJob 空壳 — IoT 实时值从未持久化,规则引擎无历史数据源 | 规则无法追溯历史趋势 | 在此 Job 实现轮询→写入 iot_devicedata,或合并到 RuleEngineJob | +| P0-2 | 网关 | A1 自注册未调用 — `GatewayClientFactory.RegisterAsync` 已定义但 Program.cs 从未执行 | 网关启动后不向 Vol.Pro 注册 | `InitializeAllAsync()` 后遍历适配器调 A1 | +| P0-3 | 安全 | B 组路由零认证 — 14+ 条路由无任何认证 | 内网未授权客户端可操控设备、查视频流 | 生产环境绑定 `127.0.0.1`,或加 `X-Gateway-Key` 中间件 | + +--- + +## P1 — 重要(影响性能、安全、可靠性) + +| 编号 | 类别 | 问题 | 影响 | 方案 | +|:---:|:---:|------|------|------| +| P1-1 | 性能 | 逐设备 B4 调用 — 规则引擎按设备逐个调 B4 | 规则引擎 90% 时间耗在网络往返 | 新增 `POST /realtime/{adapter}/batch` 批量接口 | +| P1-2 | 性能 | 级联离线标记逐条 UPDATE — HeartbeatMonitorJob 对每台设备单独更新 | 设备多时慢且无事务 | 一条 SQL: `UPDATE base_device SET IsOnline='离线' WHERE GatewayNodeId=@id` | +| P1-3 | 安全 | Token/密码明文存储 — appsettings.json 明文且被复制到 bin/ | 源码泄露 = 凭据泄露 | 环境变量覆盖 + `.gitignore bin` | +| P1-4 | 可维护 | 前端硬编码网关地址 — `const GW = 'http://localhost:5100'` | 部署时需逐文件修改 | 统一用 `window.apiConfig.gatewayUrl` | +| P1-5 | 规则引擎 | DeviceId→(AdapterCode, SourceId) 解析缺失 | 规则引擎无法直接调网关 B4 | 批量查 base_device 建映射表 | +| P1-6 | 规则引擎 | ValueId 语义模糊 — 字典绑定但无对应实体表 | "变量"选的是什么不明确 | 新建 `warehouse_variable` 表 | + +--- + +## P2 — 改善(影响排错效率、维护成本) + +| 编号 | 类别 | 问题 | 影响 | 方案 | +|:---:|:---:|------|------|------| +| P2-1 | 代码质量 | 静默异常吞噬 — 适配器 `catch { return false; }` | 离线不知道原因 | `catch(Exception ex)` + STDERR 输出 | +| P2-2 | 规则引擎 | 阈值抖动 — 温度反复跳变时规则频繁触发→恢复 | 空调反复开关,告警洪水 | hysteresis 滞后窗 | +| P2-3 | 规则引擎 | 冷却期粒度 — Cooldown 在规则级,OR 组合不该整体冷却 | 冷却期过宽 | 冷却期下沉到条件表或基于"上次触发值"去重 | +| P2-4 | 可维护 | warehouse 端 console.log 残留 — 30+ 处开发日志 | 生产环境噪声 | vite.config.ts 移除非生产日志 | +| P2-5 | 可维护 | 双端 gateway API 重复封装 | 维护两份 | 统一到 web.vite/src/api/gateway.js | + +--- + +## P3 — 优化(影响开发体验、仓库整洁) + +| 编号 | 类别 | 问题 | 影响 | 方案 | +|:---:|:---:|------|------|------| +| P3-1 | 规则引擎 | 动作执行阻塞 — `ExecuteActionsAsync` 串行等待 B5 响应 | 一条规则卡住全部阻塞 | `Task.WhenAll` + 5s 超时 | +| P3-2 | 文档 | bin 目录残留配置 | 仓库体积 + 凭据泄露 | `.gitignore` 加 `**/bin/` | +| P3-3 | 开发 | 网关无 Swagger | 调试需手动 curl | `AddEndpointsApiExplorer` + `MapSwagger` | +| P3-4 | 文档 | 设计文档与代码路由数不一致 | 架构文档过时 | 每次 Phase 同步更新 | + +--- + +> **总计**: 18 项 — P0: 3 / P1: 6 / P2: 5 / P3: 4 diff --git a/doc/设计文档/SecMPS统一问题清单20260603修复方案.md b/doc/设计文档/SecMPS统一问题清单20260603修复方案.md new file mode 100644 index 0000000..71f5db4 --- /dev/null +++ b/doc/设计文档/SecMPS统一问题清单20260603修复方案.md @@ -0,0 +1,280 @@ +# 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(adapter); + if (a == null) return Results.NotFound(); + var results = new Dictionary>(); + foreach (var deviceId in req.DeviceIds ?? new()) + results[deviceId] = await a.GetRealtimeValuesAsync(deviceId); + return Results.Ok(results); + }); + ``` +- [ ] 新增 `record BatchRealtimeRequest(List? 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