From c295d9264ae8ca89e12640fb6dbebd7fe613b2be Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Thu, 9 Apr 2026 17:04:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=EF=BC=88?= =?UTF-8?q?wvp=5Fai=5Falgorithm=20=E8=A1=A8=E5=8A=A0=20global=5Fparams=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=20+=20API=20+=20=E9=85=8D=E7=BD=AE=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E5=90=88=E5=B9=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/vmp/aiot/bean/AiAlgorithm.java | 3 + .../controller/AiAlgorithmController.java | 6 ++ .../iot/vmp/aiot/dao/AiAlgorithmMapper.java | 3 + .../vmp/aiot/service/IAiAlgorithmService.java | 2 + .../service/impl/AiAlgorithmServiceImpl.java | 7 ++ .../impl/AiRedisConfigServiceImpl.java | 84 ++++++++++++++++++- .../vmp/conf/security/WebSecurityConfig.java | 1 + 数据库/aiot/迁移-添加global_params字段.sql | 3 + 8 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 数据库/aiot/迁移-添加global_params字段.sql diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiAlgorithm.java b/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiAlgorithm.java index 7075b4c2d..3819fd5e4 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiAlgorithm.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiAlgorithm.java @@ -22,6 +22,9 @@ public class AiAlgorithm { @Schema(description = "参数模板JSON") private String paramSchema; + @Schema(description = "用户自定义的全局默认参数JSON") + private String globalParams; + @Schema(description = "描述") private String description; 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 786a47453..b413839af 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 @@ -36,4 +36,10 @@ public class AiAlgorithmController { public void syncFromEdge() { algorithmService.syncFromEdge(); } + + @Operation(summary = "保存算法全局参数") + @PostMapping("/global-params/{algoCode}") + public void saveGlobalParams(@PathVariable String algoCode, @RequestBody String globalParams) { + algorithmService.saveGlobalParams(algoCode, globalParams); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiAlgorithmMapper.java b/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiAlgorithmMapper.java index 211b29070..0142c8fe6 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiAlgorithmMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiAlgorithmMapper.java @@ -35,6 +35,9 @@ public interface AiAlgorithmMapper { @Update("UPDATE wvp_ai_algorithm SET is_active=#{isActive}, update_time=#{updateTime} WHERE id=#{id}") int updateActive(@Param("id") Integer id, @Param("isActive") Integer isActive, @Param("updateTime") String updateTime); + @Update("UPDATE wvp_ai_algorithm SET global_params = #{globalParams}, update_time = #{updateTime} WHERE algo_code = #{algoCode}") + int updateGlobalParams(@Param("algoCode") String algoCode, @Param("globalParams") String globalParams, @Param("updateTime") String updateTime); + @Delete("DELETE FROM wvp_ai_algorithm WHERE id=#{id}") int delete(@Param("id") Integer id); } 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 f7e1e70ac..f4890e17f 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 @@ -13,4 +13,6 @@ public interface IAiAlgorithmService { void toggleActive(Integer id, Integer isActive); void syncFromEdge(); + + void saveGlobalParams(String algoCode, String globalParams); } 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 861e15696..b68dc3a77 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 @@ -157,4 +157,11 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService { throw new RuntimeException("同步失败: " + e.getMessage()); } } + + @Override + public void saveGlobalParams(String algoCode, String globalParams) { + String now = LocalDateTime.now().format(FORMATTER); + algorithmMapper.updateGlobalParams(algoCode, globalParams, now); + log.info("[AI算法] 保存全局参数: algoCode={}, globalParams={}", algoCode, globalParams); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiRedisConfigServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiRedisConfigServiceImpl.java index 5ea53949f..3ee5d3e8a 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiRedisConfigServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiRedisConfigServiceImpl.java @@ -476,7 +476,7 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService { /** * 构建扁平格式配置 JSON(Edge 期望的格式) - * 输出: {cameras: [...], rois: [...], binds: [...]} + * 输出: {cameras: [...], rois: [...], binds: [...], global_params: {...}} */ private Map buildFlatConfig(String deviceId, List cameraIds) { List> cameras = new ArrayList<>(); @@ -485,6 +485,13 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService { com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper(); + // 查询所有算法,构建 algo_code -> AiAlgorithm 索引 + List allAlgorithms = algorithmMapper.queryAll(); + Map algoMap = new LinkedHashMap<>(); + for (AiAlgorithm algo : allAlgorithms) { + algoMap.put(algo.getAlgoCode(), algo); + } + for (String cameraId : cameraIds) { // 摄像头信息 Map cameraMap = new LinkedHashMap<>(); @@ -600,10 +607,14 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService { bindMap.put("enabled", bind.getEnabled() != null && bind.getEnabled() == 1); bindMap.put("priority", bind.getPriority() != null ? bind.getPriority() : 0); - // params: 解析为标准 JSON 对象(非 Python eval 字符串) + // params: 三级合并 param_schema.default < global_params < bind.params + AiAlgorithm algo = algoMap.get(bind.getAlgoCode()); + String algoParamSchema = algo != null ? algo.getParamSchema() : null; + String algoGlobalParams = algo != null ? algo.getGlobalParams() : null; String effectiveParams = resolveEffectiveParams(bind); + String mergedParams = mergeParams(algoParamSchema, algoGlobalParams, effectiveParams); try { - bindMap.put("params", objectMapper.readValue(effectiveParams, Object.class)); + bindMap.put("params", objectMapper.readValue(mergedParams, Object.class)); } catch (Exception e) { bindMap.put("params", new LinkedHashMap<>()); } @@ -616,6 +627,73 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService { flatConfig.put("cameras", cameras); flatConfig.put("rois", rois); flatConfig.put("binds", binds); + + // 顶层新增 global_params: {algo_code: {param_key: value, ...}, ...} + Map globalParamsMap = new LinkedHashMap<>(); + for (AiAlgorithm algo : allAlgorithms) { + if (algo.getGlobalParams() != null && !algo.getGlobalParams().isEmpty()) { + try { + globalParamsMap.put(algo.getAlgoCode(), objectMapper.readValue(algo.getGlobalParams(), Object.class)); + } catch (Exception e) { + log.warn("[AiRedis] 解析算法 {} 的 globalParams 失败: {}", algo.getAlgoCode(), e.getMessage()); + } + } + } + if (!globalParamsMap.isEmpty()) { + flatConfig.put("global_params", globalParamsMap); + } + return flatConfig; } + + /** + * 三级参数合并:param_schema 的 default 值 < global_params < bind 的 params + * @param paramSchema 算法参数模板JSON(含 default 字段) + * @param globalParams 用户自定义的全局默认参数JSON + * @param bindParams 绑定级别的参数JSON(最高优先级) + * @return 合并后的参数JSON字符串 + */ + @SuppressWarnings("unchecked") + private String mergeParams(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("[AiRedis] 解析 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("[AiRedis] 解析 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("[AiRedis] 解析 bindParams 失败: {}", e.getMessage()); + } + } + + return JSON.toJSONString(merged); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index 32df9d80f..caa34a5b7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -109,6 +109,7 @@ public class WebSecurityConfig { defaultExcludes.add("/api/ai/alert/image"); defaultExcludes.add("/api/ai/device/edge/**"); defaultExcludes.add("/api/ai/device/heartbeat"); + defaultExcludes.add("/api/ai/algorithm/**"); if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) { defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes()); diff --git a/数据库/aiot/迁移-添加global_params字段.sql b/数据库/aiot/迁移-添加global_params字段.sql new file mode 100644 index 000000000..ab02604de --- /dev/null +++ b/数据库/aiot/迁移-添加global_params字段.sql @@ -0,0 +1,3 @@ +-- 为算法注册表添加全局参数字段 +-- 用于存储用户自定义的全局默认参数JSON,在配置推送时三级合并:param_schema.default < global_params < bind.params +ALTER TABLE wvp_ai_algorithm ADD COLUMN global_params TEXT NULL COMMENT '用户自定义的全局默认参数JSON' AFTER param_schema;