From 0a803f103c4b5841dd1e21609240dde414a0f3dc Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Mon, 13 Apr 2026 10:36:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20Mapper=20JOIN=20=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=20+=20/list=20=E6=8E=A5=E5=8F=A3=E4=B8=89=E7=BA=A7?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=90=88=E5=B9=B6=20+=20saveGlobalParams=20?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. AiRoiAlgoBindMapper.queryByDeviceId: LEFT JOIN 改为 INNER JOIN, WHERE 条件已要求 r.device_id 匹配,LEFT JOIN 无意义 2. AiAlgorithmController /list 接口: 设备参数不再直接覆盖 globalParams, 改为三级合并 paramSchema.default < globalParams < bindParams 3. AiAlgorithmServiceImpl.saveGlobalParams: 补充注释说明全量推送的合理性 (全局参数影响所有设备,全量推送开销可接受) --- .../controller/AiAlgorithmController.java | 12 +++-- .../iot/vmp/aiot/dao/AiRoiAlgoBindMapper.java | 2 +- .../vmp/aiot/service/IAiAlgorithmService.java | 6 +++ .../service/impl/AiAlgorithmServiceImpl.java | 50 ++++++++++++++++++- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiAlgorithmController.java b/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiAlgorithmController.java index 3c1365c79..794303ce1 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiAlgorithmController.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiAlgorithmController.java @@ -28,7 +28,7 @@ public class AiAlgorithmController { List algorithms = algorithmService.queryAll(); if (deviceId != null && !deviceId.isEmpty()) { List binds = algorithmService.queryBindsByDevice(deviceId); - // 按 algo_code 分组,取第一个绑定的 params 作为该设备的"实际参数" + // 按 algo_code 分组,取第一个有参数的绑定作为该设备的绑定参数 Map deviceParams = new HashMap<>(); for (AiRoiAlgoBind bind : binds) { if (!deviceParams.containsKey(bind.getAlgoCode()) && bind.getParams() != null) { @@ -36,10 +36,14 @@ public class AiAlgorithmController { } } for (AiAlgorithm algo : algorithms) { - String dp = deviceParams.get(algo.getAlgoCode()); - if (dp != null) { - algo.setGlobalParams(dp); // 用设备实际参数覆盖展示 + String bindParams = deviceParams.get(algo.getAlgoCode()); + if (bindParams != null) { + // 三级合并:paramSchema.default < globalParams < bindParams + String merged = algorithmService.mergeEffectiveParams( + algo.getParamSchema(), algo.getGlobalParams(), bindParams); + algo.setGlobalParams(merged); } + // 如果没有绑定参数,保留原 globalParams(全局默认) } } return algorithms; diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiRoiAlgoBindMapper.java b/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiRoiAlgoBindMapper.java index c77318fe8..101d34d86 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiRoiAlgoBindMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiRoiAlgoBindMapper.java @@ -47,7 +47,7 @@ public interface AiRoiAlgoBindMapper { List queryAll(); @Select("SELECT b.*, r.device_id, r.name AS roi_name FROM wvp_ai_roi_algo_bind b " + - "LEFT JOIN wvp_ai_roi r ON b.roi_id = r.roi_id " + + "INNER JOIN wvp_ai_roi r ON b.roi_id = r.roi_id " + "WHERE r.device_id = #{deviceId} ORDER BY b.priority DESC, b.id") List queryByDeviceId(@Param("deviceId") String deviceId); diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiAlgorithmService.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiAlgorithmService.java index 22a3b09f7..35718de64 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiAlgorithmService.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiAlgorithmService.java @@ -20,4 +20,10 @@ public interface IAiAlgorithmService { List queryBindsByDevice(String deviceId); void updateDeviceAlgoParams(String deviceId, String algoCode, String params); + + /** + * 三级参数合并:paramSchema.default < globalParams < bindParams + * 用于 /list 接口按设备覆盖参数时,保留完整的参数层级 + */ + String mergeEffectiveParams(String paramSchema, String globalParams, String bindParams); } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiAlgorithmServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiAlgorithmServiceImpl.java index 738e4a86d..10eccb42e 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiAlgorithmServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiAlgorithmServiceImpl.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.aiot.service.impl; +import com.alibaba.fastjson2.JSON; import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm; import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind; import com.genersoft.iot.vmp.aiot.config.AiServiceConfig; @@ -177,7 +178,9 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService { algorithmMapper.updateGlobalParams(algoCode, globalParams, now); log.info("[AI算法] 保存全局参数: algoCode={}, globalParams={}", algoCode, globalParams); - // 保存后自动推送配置到所有边缘端 + // 全局参数变更会影响所有使用该算法的设备,因此需要全量推送配置到边缘端 + // 这是合理的全量推送场景:无法预知哪些设备绑定了该算法(需要跨表查询 bind→roi→device), + // 且全局参数变更频率低,全量推送的开销可接受 try { configService.pushAllConfig(); log.info("[AI算法] 全局参数变更已推送到边缘端"); @@ -205,4 +208,49 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService { log.warn("[AI算法] 设备参数推送失败(参数已保存): {}", e.getMessage()); } } + + @Override + @SuppressWarnings("unchecked") + public String mergeEffectiveParams(String paramSchema, String globalParams, String bindParams) { + Map merged = new LinkedHashMap<>(); + + // 1. 从 paramSchema 提取 default 值(最低优先级) + if (paramSchema != null && !paramSchema.isEmpty()) { + try { + Map schema = JSON.parseObject(paramSchema, LinkedHashMap.class); + for (Map.Entry entry : schema.entrySet()) { + if (entry.getValue() instanceof Map) { + Map fieldDef = (Map) entry.getValue(); + if (fieldDef.containsKey("default")) { + merged.put(entry.getKey(), fieldDef.get("default")); + } + } + } + } catch (Exception e) { + log.debug("[AI算法] 解析 paramSchema 失败: {}", e.getMessage()); + } + } + + // 2. 用 globalParams 覆盖 + if (globalParams != null && !globalParams.isEmpty()) { + try { + Map global = JSON.parseObject(globalParams, LinkedHashMap.class); + merged.putAll(global); + } catch (Exception e) { + log.debug("[AI算法] 解析 globalParams 失败: {}", e.getMessage()); + } + } + + // 3. 用 bindParams 覆盖(最高优先级) + if (bindParams != null && !bindParams.isEmpty()) { + try { + Map bind = JSON.parseObject(bindParams, LinkedHashMap.class); + merged.putAll(bind); + } catch (Exception e) { + log.debug("[AI算法] 解析 bindParams 失败: {}", e.getMessage()); + } + } + + return JSON.toJSONString(merged); + } }