feat(aiot): AiConfigService支持Redis配置同步

- 新增 pushConfigToRedis 方法
- pushConfig 改为通过 Redis 推送配置
- 支持配置快照生成

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-05 13:32:07 +08:00
parent 44170f2d9a
commit 0a6931e093
2 changed files with 49 additions and 42 deletions

View File

@@ -6,5 +6,9 @@ public interface IAiConfigService {
Map<String, Object> exportConfig(String cameraId);
void pushConfig(String cameraId);
/**
* 推送配置到边缘端Redis写入 + Pub/Sub通知
* @return 推送结果,含版本号
*/
Map<String, Object> pushConfig(String cameraId);
}

View File

@@ -1,21 +1,21 @@
package com.genersoft.iot.vmp.aiot.service.impl;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
import com.genersoft.iot.vmp.aiot.bean.AiConfigSnapshot;
import com.genersoft.iot.vmp.aiot.bean.AiRoi;
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
import com.genersoft.iot.vmp.aiot.config.AiMqttConfig;
import com.genersoft.iot.vmp.aiot.config.AiServiceConfig;
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
import com.genersoft.iot.vmp.aiot.dao.AiRoiMapper;
import com.genersoft.iot.vmp.aiot.service.IAiConfigService;
import com.genersoft.iot.vmp.aiot.service.MqttService;
import com.genersoft.iot.vmp.aiot.service.IAiConfigSnapshotService;
import com.genersoft.iot.vmp.aiot.service.IAiRedisConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.*;
@@ -33,13 +33,11 @@ public class AiConfigServiceImpl implements IAiConfigService {
private AiAlgorithmMapper algorithmMapper;
@Autowired
private AiServiceConfig aiServiceConfig;
private IAiRedisConfigService redisConfigService;
@Lazy
@Autowired
private AiMqttConfig mqttConfig;
@Autowired
private MqttService mqttService;
private IAiConfigSnapshotService snapshotService;
private final ObjectMapper objectMapper = new ObjectMapper();
@@ -56,6 +54,7 @@ public class AiConfigServiceImpl implements IAiConfigService {
roiMap.put("roi_type", roi.getRoiType());
roiMap.put("name", roi.getName());
roiMap.put("enabled", roi.getEnabled() == 1);
roiMap.put("priority", roi.getPriority());
try {
roiMap.put("coordinates", objectMapper.readValue(roi.getCoordinates(), Object.class));
@@ -67,13 +66,23 @@ public class AiConfigServiceImpl implements IAiConfigService {
List<Map<String, Object>> algoList = new ArrayList<>();
for (AiRoiAlgoBind bind : binds) {
Map<String, Object> algoMap = new LinkedHashMap<>();
algoMap.put("bind_id", bind.getBindId());
algoMap.put("algo_code", bind.getAlgoCode());
algoMap.put("enabled", bind.getEnabled() == 1);
algoMap.put("priority", bind.getPriority());
algoMap.put("template_id", bind.getTemplateId());
algoMap.put("param_override", bind.getParamOverride());
try {
algoMap.put("params", objectMapper.readValue(bind.getParams(), Object.class));
} catch (Exception e) {
algoMap.put("params", bind.getParams());
}
// 获取算法信息
AiAlgorithm algo = algorithmMapper.queryByCode(bind.getAlgoCode());
if (algo != null) {
algoMap.put("algo_name", algo.getAlgoName());
algoMap.put("target_class", algo.getTargetClass());
}
algoList.add(algoMap);
}
roiMap.put("algorithms", algoList);
@@ -84,38 +93,32 @@ public class AiConfigServiceImpl implements IAiConfigService {
}
@Override
public void pushConfig(String cameraId) {
public Map<String, Object> pushConfig(String cameraId) {
// 1. 构建完整配置JSON
Map<String, Object> config = exportConfig(cameraId);
if (mqttConfig.isEnabled()) {
// MQTT推送优先
try {
String configJson = objectMapper.writeValueAsString(config);
mqttService.pushConfig(cameraId, configJson);
log.info("MQTT配置推送成功: cameraId={}", cameraId);
} catch (Exception e) {
log.error("MQTT配置推送失败: cameraId={}", cameraId, e);
throw new RuntimeException("MQTT推送失败: " + e.getMessage());
}
} else if (aiServiceConfig.isEnabled()) {
// HTTP REST推送
try {
RestTemplate restTemplate = new RestTemplate();
String url = aiServiceConfig.getUrl() + "/api/config/receive";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(config, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("推送失败,边缘端返回: " + response.getStatusCode());
}
log.info("HTTP配置推送成功: cameraId={}", cameraId);
} catch (Exception e) {
log.error("HTTP配置推送失败: cameraId={}", cameraId, e);
throw new RuntimeException("推送失败: " + e.getMessage());
}
} else {
throw new RuntimeException("AI配置推送未启用请设置ai.mqtt.enabled=true或ai.service.enabled=true");
}
// 2. 保存快照到 wvp_ai_config_snapshot (版本化)
AiConfigSnapshot snapshot = snapshotService.createSnapshot(
"CAMERA", cameraId, cameraId,
JSON.toJSONString(config),
"PUSH", "推送配置到边缘端", null);
// 3. 将每个ROI和Bind写入Redis
redisConfigService.syncCameraConfigToRedis(cameraId);
// 4. 发布 config_update 到 Redis Pub/Sub 通知边缘端
List<String> ids = new ArrayList<>();
ids.add(cameraId);
redisConfigService.publishConfigUpdate("full", ids);
// 5. 返回推送结果
Map<String, Object> result = new LinkedHashMap<>();
result.put("camera_id", cameraId);
result.put("version", snapshot.getVersion());
result.put("push_status", "success");
result.put("message", "配置已推送到Redis并通知边缘端");
log.info("[AiConfig] 配置推送完成: cameraId={}, version={}", cameraId, snapshot.getVersion());
return result;
}
}