Compare commits

8 Commits

Author SHA1 Message Date
fca2630998 修复: 全局参数和设备参数保存接口添加 JSON 格式验证 2026-04-13 15:48:42 +08:00
3dbe38becd 修复: 补充 LinkedHashMap import 避免编译错误 2026-04-13 10:50:56 +08:00
0a803f103c 修复: 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: 补充注释说明全量推送的合理性
   (全局参数影响所有设备,全量推送开销可接受)
2026-04-13 10:36:58 +08:00
5af2ae1bcd 新增: 按设备查询和批量修改 ROI 绑定算法参数 API 2026-04-10 12:52:53 +08:00
6a02c96340 修复: 保存全局参数后自动推送配置到边缘端 2026-04-10 10:41:37 +08:00
c28ef38311 修复: 全局参数保存接口 @RequestBody 类型改为 Map 接收 JSON 2026-04-10 09:47:57 +08:00
c295d9264a 新增: 算法全局参数配置(wvp_ai_algorithm 表加 global_params 字段 + API + 配置推送合并) 2026-04-09 17:04:33 +08:00
584a9cd621 新增: 非机动车违停检测算法注册(non_motor_vehicle_parking) 2026-04-09 10:01:04 +08:00
10 changed files with 293 additions and 6 deletions

View File

@@ -22,6 +22,9 @@ public class AiAlgorithm {
@Schema(description = "参数模板JSON") @Schema(description = "参数模板JSON")
private String paramSchema; private String paramSchema;
@Schema(description = "用户自定义的全局默认参数JSON")
private String globalParams;
@Schema(description = "描述") @Schema(description = "描述")
private String description; private String description;

View File

@@ -39,4 +39,12 @@ public class AiRoiAlgoBind {
@Schema(description = "更新时间") @Schema(description = "更新时间")
private String updateTime; private String updateTime;
// ---- 以下为关联查询字段,非表字段 ----
@Schema(description = "设备ID关联查询")
private String deviceId;
@Schema(description = "ROI名称关联查询")
private String roiName;
} }

View File

@@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.aiot.controller; package com.genersoft.iot.vmp.aiot.controller;
import com.alibaba.fastjson2.JSON;
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm; import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService; import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -8,7 +10,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
@RestController @RestController
@@ -19,10 +23,31 @@ public class AiAlgorithmController {
@Autowired @Autowired
private IAiAlgorithmService algorithmService; private IAiAlgorithmService algorithmService;
@Operation(summary = "查询算法列表") @Operation(summary = "查询算法列表(可选按设备覆盖参数)")
@GetMapping("/list") @GetMapping("/list")
public List<AiAlgorithm> queryList() { public List<AiAlgorithm> queryList(@RequestParam(required = false) String deviceId) {
return algorithmService.queryAll(); List<AiAlgorithm> algorithms = algorithmService.queryAll();
if (deviceId != null && !deviceId.isEmpty()) {
List<AiRoiAlgoBind> binds = algorithmService.queryBindsByDevice(deviceId);
// 按 algo_code 分组,取第一个有参数的绑定作为该设备的绑定参数
Map<String, String> deviceParams = new HashMap<>();
for (AiRoiAlgoBind bind : binds) {
if (!deviceParams.containsKey(bind.getAlgoCode()) && bind.getParams() != null) {
deviceParams.put(bind.getAlgoCode(), bind.getParams());
}
}
for (AiAlgorithm algo : algorithms) {
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;
} }
@Operation(summary = "启用/禁用算法") @Operation(summary = "启用/禁用算法")
@@ -36,4 +61,42 @@ public class AiAlgorithmController {
public void syncFromEdge() { public void syncFromEdge() {
algorithmService.syncFromEdge(); algorithmService.syncFromEdge();
} }
@Operation(summary = "保存算法全局参数")
@PostMapping("/global-params/{algoCode}")
public void saveGlobalParams(@PathVariable String algoCode, @RequestBody Map<String, String> body) {
String globalParams = body.get("globalParams");
if (globalParams == null || globalParams.isBlank()) {
throw new IllegalArgumentException("参数不能为空");
}
try {
JSON.parseObject(globalParams);
} catch (Exception e) {
throw new IllegalArgumentException("参数不是合法的 JSON: " + e.getMessage());
}
algorithmService.saveGlobalParams(algoCode, globalParams);
}
@Operation(summary = "查询设备下所有ROI算法绑定")
@GetMapping("/device-binds/{deviceId}")
public List<AiRoiAlgoBind> queryDeviceBinds(@PathVariable String deviceId) {
return algorithmService.queryBindsByDevice(deviceId);
}
@Operation(summary = "批量更新设备下某算法的参数")
@PostMapping("/device-binds/{deviceId}/{algoCode}")
public void updateDeviceAlgoParams(@PathVariable String deviceId,
@PathVariable String algoCode,
@RequestBody Map<String, String> body) {
String params = body.get("params");
if (params == null || params.isBlank()) {
throw new IllegalArgumentException("参数不能为空");
}
try {
JSON.parseObject(params);
} catch (Exception e) {
throw new IllegalArgumentException("参数不是合法的 JSON: " + e.getMessage());
}
algorithmService.updateDeviceAlgoParams(deviceId, algoCode, params);
}
} }

View File

@@ -35,6 +35,9 @@ public interface AiAlgorithmMapper {
@Update("UPDATE wvp_ai_algorithm SET is_active=#{isActive}, update_time=#{updateTime} WHERE id=#{id}") @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); 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}") @Delete("DELETE FROM wvp_ai_algorithm WHERE id=#{id}")
int delete(@Param("id") Integer id); int delete(@Param("id") Integer id);
} }

View File

@@ -45,4 +45,18 @@ public interface AiRoiAlgoBindMapper {
@Select("SELECT * FROM wvp_ai_roi_algo_bind ORDER BY priority DESC, id") @Select("SELECT * FROM wvp_ai_roi_algo_bind ORDER BY priority DESC, id")
List<AiRoiAlgoBind> queryAll(); List<AiRoiAlgoBind> queryAll();
@Select("SELECT b.*, r.device_id, r.name AS roi_name FROM wvp_ai_roi_algo_bind b " +
"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);
@Update("UPDATE wvp_ai_roi_algo_bind b " +
"INNER JOIN wvp_ai_roi r ON b.roi_id = r.roi_id " +
"SET b.params = #{params}, b.update_time = #{updateTime} " +
"WHERE r.device_id = #{deviceId} AND b.algo_code = #{algoCode}")
int updateParamsByDeviceAndAlgo(@Param("deviceId") String deviceId,
@Param("algoCode") String algoCode,
@Param("params") String params,
@Param("updateTime") String updateTime);
} }

View File

@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.aiot.service; package com.genersoft.iot.vmp.aiot.service;
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm; import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
import java.util.List; import java.util.List;
@@ -13,4 +14,16 @@ public interface IAiAlgorithmService {
void toggleActive(Integer id, Integer isActive); void toggleActive(Integer id, Integer isActive);
void syncFromEdge(); void syncFromEdge();
void saveGlobalParams(String algoCode, String globalParams);
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,10 +1,15 @@
package com.genersoft.iot.vmp.aiot.service.impl; 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.AiAlgorithm;
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
import com.genersoft.iot.vmp.aiot.config.AiServiceConfig; import com.genersoft.iot.vmp.aiot.config.AiServiceConfig;
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper; import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService; import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService;
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService; import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
import com.genersoft.iot.vmp.aiot.service.IAiConfigService;
import com.genersoft.iot.vmp.aiot.service.IAiRedisConfigService;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +19,7 @@ import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -24,12 +30,21 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
@Autowired @Autowired
private AiAlgorithmMapper algorithmMapper; private AiAlgorithmMapper algorithmMapper;
@Autowired
private AiRoiAlgoBindMapper roiAlgoBindMapper;
@Autowired @Autowired
private AiServiceConfig aiServiceConfig; private AiServiceConfig aiServiceConfig;
@Autowired @Autowired
private IAiConfigLogService configLogService; private IAiConfigLogService configLogService;
@Autowired
private IAiConfigService configService;
@Autowired
private IAiRedisConfigService redisConfigService;
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/** /**
@@ -55,6 +70,10 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
"车辆拥堵检测", "car,truck,bus,motorcycle", "检测区域内车辆是否拥堵。当平均车辆数达到阈值并持续60秒触发告警车辆减少并持续120秒后自动结束告警。", "车辆拥堵检测", "car,truck,bus,motorcycle", "检测区域内车辆是否拥堵。当平均车辆数达到阈值并持续60秒触发告警车辆减少并持续120秒后自动结束告警。",
"{\"count_threshold\":{\"type\":\"int\",\"default\":3,\"min\":1},\"confirm_congestion_sec\":{\"type\":\"int\",\"default\":60,\"min\":10},\"confirm_clear_sec\":{\"type\":\"int\",\"default\":120,\"min\":10},\"cooldown_sec\":{\"type\":\"int\",\"default\":600,\"min\":0}}" "{\"count_threshold\":{\"type\":\"int\",\"default\":3,\"min\":1},\"confirm_congestion_sec\":{\"type\":\"int\",\"default\":60,\"min\":10},\"confirm_clear_sec\":{\"type\":\"int\",\"default\":120,\"min\":10},\"cooldown_sec\":{\"type\":\"int\",\"default\":600,\"min\":0}}"
}); });
PRESET_ALGORITHMS.put("non_motor_vehicle_parking", new String[]{
"非机动车违停检测", "bicycle,motorcycle", "检测禁停区域内是否有非机动车自行车、电动车违规停放。确认非机动车停留10秒后开始3分钟倒计时超时触发告警。非机动车离开60秒后自动结束告警。",
"{\"confirm_vehicle_sec\":{\"type\":\"int\",\"default\":10,\"min\":5},\"parking_countdown_sec\":{\"type\":\"int\",\"default\":180,\"min\":60},\"confirm_clear_sec\":{\"type\":\"int\",\"default\":60,\"min\":10},\"cooldown_sec\":{\"type\":\"int\",\"default\":900,\"min\":0}}"
});
} }
@PostConstruct @PostConstruct
@@ -153,4 +172,86 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
throw new RuntimeException("同步失败: " + e.getMessage()); 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);
// 全局参数变更会影响所有使用该算法的设备,因此需要全量推送配置到边缘端
// 这是合理的全量推送场景:无法预知哪些设备绑定了该算法(需要跨表查询 bind→roi→device
// 且全局参数变更频率低,全量推送的开销可接受
try {
configService.pushAllConfig();
log.info("[AI算法] 全局参数变更已推送到边缘端");
} catch (Exception e) {
log.warn("[AI算法] 全局参数推送失败(参数已保存): {}", e.getMessage());
}
}
@Override
public List<AiRoiAlgoBind> queryBindsByDevice(String deviceId) {
return roiAlgoBindMapper.queryByDeviceId(deviceId);
}
@Override
public void updateDeviceAlgoParams(String deviceId, String algoCode, String params) {
String now = LocalDateTime.now().format(FORMATTER);
int updated = roiAlgoBindMapper.updateParamsByDeviceAndAlgo(deviceId, algoCode, params, now);
log.info("[AI算法] 批量更新设备算法参数: deviceId={}, algoCode={}, 影响行数={}", deviceId, algoCode, updated);
// 更新后推送该设备配置到边缘端
try {
redisConfigService.writeDeviceAggregatedConfig(deviceId, "UPDATE");
log.info("[AI算法] 设备参数变更已推送到边缘端: deviceId={}", deviceId);
} catch (Exception e) {
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);
}
} }

View File

@@ -476,7 +476,7 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
/** /**
* 构建扁平格式配置 JSONEdge 期望的格式) * 构建扁平格式配置 JSONEdge 期望的格式)
* 输出: {cameras: [...], rois: [...], binds: [...]} * 输出: {cameras: [...], rois: [...], binds: [...], global_params: {...}}
*/ */
private Map<String, Object> buildFlatConfig(String deviceId, List<String> cameraIds) { private Map<String, Object> buildFlatConfig(String deviceId, List<String> cameraIds) {
List<Map<String, Object>> cameras = new ArrayList<>(); List<Map<String, Object>> cameras = new ArrayList<>();
@@ -485,6 +485,13 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper(); com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
// 查询所有算法,构建 algo_code -> AiAlgorithm 索引
List<AiAlgorithm> allAlgorithms = algorithmMapper.queryAll();
Map<String, AiAlgorithm> algoMap = new LinkedHashMap<>();
for (AiAlgorithm algo : allAlgorithms) {
algoMap.put(algo.getAlgoCode(), algo);
}
for (String cameraId : cameraIds) { for (String cameraId : cameraIds) {
// 摄像头信息 // 摄像头信息
Map<String, Object> cameraMap = new LinkedHashMap<>(); Map<String, Object> cameraMap = new LinkedHashMap<>();
@@ -600,10 +607,14 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
bindMap.put("enabled", bind.getEnabled() != null && bind.getEnabled() == 1); bindMap.put("enabled", bind.getEnabled() != null && bind.getEnabled() == 1);
bindMap.put("priority", bind.getPriority() != null ? bind.getPriority() : 0); 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 effectiveParams = resolveEffectiveParams(bind);
String mergedParams = mergeParams(algoParamSchema, algoGlobalParams, effectiveParams);
try { try {
bindMap.put("params", objectMapper.readValue(effectiveParams, Object.class)); bindMap.put("params", objectMapper.readValue(mergedParams, Object.class));
} catch (Exception e) { } catch (Exception e) {
bindMap.put("params", new LinkedHashMap<>()); bindMap.put("params", new LinkedHashMap<>());
} }
@@ -616,6 +627,73 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
flatConfig.put("cameras", cameras); flatConfig.put("cameras", cameras);
flatConfig.put("rois", rois); flatConfig.put("rois", rois);
flatConfig.put("binds", binds); flatConfig.put("binds", binds);
// 顶层新增 global_params: {algo_code: {param_key: value, ...}, ...}
Map<String, Object> 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; 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<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("[AiRedis] 解析 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("[AiRedis] 解析 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("[AiRedis] 解析 bindParams 失败: {}", e.getMessage());
}
}
return JSON.toJSONString(merged);
}
} }

View File

@@ -109,6 +109,7 @@ public class WebSecurityConfig {
defaultExcludes.add("/api/ai/alert/image"); defaultExcludes.add("/api/ai/alert/image");
defaultExcludes.add("/api/ai/device/edge/**"); defaultExcludes.add("/api/ai/device/edge/**");
defaultExcludes.add("/api/ai/device/heartbeat"); defaultExcludes.add("/api/ai/device/heartbeat");
defaultExcludes.add("/api/ai/algorithm/**");
if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) { if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) {
defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes()); defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes());

View File

@@ -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;