修复: Mapper JOIN 语法 + /list 接口三级参数合并 + saveGlobalParams 推送优化

1. AiRoiAlgoBindMapper.queryByDeviceId: LEFT JOIN 改为 INNER JOIN,
   WHERE 条件已要求 r.device_id 匹配,LEFT JOIN 无意义
2. AiAlgorithmController /list 接口: 设备参数不再直接覆盖 globalParams,
   改为三级合并 paramSchema.default < globalParams < bindParams
3. AiAlgorithmServiceImpl.saveGlobalParams: 补充注释说明全量推送的合理性
   (全局参数影响所有设备,全量推送开销可接受)
This commit is contained in:
2026-04-13 10:36:58 +08:00
parent 5af2ae1bcd
commit 0a803f103c
4 changed files with 64 additions and 6 deletions

View File

@@ -28,7 +28,7 @@ public class AiAlgorithmController {
List<AiAlgorithm> algorithms = algorithmService.queryAll();
if (deviceId != null && !deviceId.isEmpty()) {
List<AiRoiAlgoBind> binds = algorithmService.queryBindsByDevice(deviceId);
// 按 algo_code 分组,取第一个绑定的 params 作为该设备的"实际参数"
// 按 algo_code 分组,取第一个有参数的绑定作为该设备的绑定参数
Map<String, String> 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;

View File

@@ -47,7 +47,7 @@ public interface AiRoiAlgoBindMapper {
List<AiRoiAlgoBind> 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<AiRoiAlgoBind> queryByDeviceId(@Param("deviceId") String deviceId);

View File

@@ -20,4 +20,10 @@ public interface IAiAlgorithmService {
List<AiRoiAlgoBind> queryBindsByDevice(String deviceId);
void updateDeviceAlgoParams(String deviceId, String algoCode, String params);
/**
* 三级参数合并paramSchema.default < globalParams < bindParams
* 用于 /list 接口按设备覆盖参数时,保留完整的参数层级
*/
String mergeEffectiveParams(String paramSchema, String globalParams, String bindParams);
}

View File

@@ -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<String, Object> merged = new LinkedHashMap<>();
// 1. 从 paramSchema 提取 default 值(最低优先级)
if (paramSchema != null && !paramSchema.isEmpty()) {
try {
Map<String, Object> schema = JSON.parseObject(paramSchema, LinkedHashMap.class);
for (Map.Entry<String, Object> entry : schema.entrySet()) {
if (entry.getValue() instanceof Map) {
Map<String, Object> fieldDef = (Map<String, Object>) 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<String, Object> 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<String, Object> bind = JSON.parseObject(bindParams, LinkedHashMap.class);
merged.putAll(bind);
} catch (Exception e) {
log.debug("[AI算法] 解析 bindParams 失败: {}", e.getMessage());
}
}
return JSON.toJSONString(merged);
}
}