修复: 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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user