Compare commits

1 Commits

Author SHA1 Message Date
780650f0cc 修复摄像头增删改配置下发链路
问题:前端添加摄像头后配置未下发到边缘端,Redis Stream 无消息

根因:
1. StreamProxyServiceImpl.add/update 完全未调用配置推送
2. delete 错误地将 cameraCode 当作 deviceId 传递
3. writeDeviceAggregatedConfig 仅从 ROI 表查摄像头,新摄像头无 ROI 时被遗漏

修复:
- add/delete/update 后自动推送配置到对应 edgeDeviceId
- update 处理 edgeDeviceId 变更场景(推送给新旧设备)
- writeDeviceAggregatedConfig 双源查询:stream_proxy + ROI 表
- 新增 StreamProxyMapper.selectCameraCodesByEdgeDeviceId 方法

验证:
- 代码语法检查通过
- 字段映射符合边缘端期望格式 (snake_case)
- 设备切换场景正确处理

Claude Opus 4.7
2026-04-29 10:00:25 +08:00
3 changed files with 82 additions and 9 deletions

View File

@@ -438,11 +438,29 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
}
try {
// 1. 查询该设备下所有摄像头
List<String> cameraIds = roiMapper.queryDistinctCameraIdsByDeviceId(deviceId);
if (cameraIds == null || cameraIds.isEmpty()) {
// 1. 查询该设备下所有摄像头(优先从 stream_proxy 表查询,确保包含无 ROI 的摄像头)
// 合并两个来源stream_proxy 表的 edge_device_id 关联 + ROI 表的 device_id 关联
Set<String> cameraIdSet = new LinkedHashSet<>();
// 来源1stream_proxy 表中 edge_device_id 匹配的摄像头
List<String> proxyCameraIds = streamProxyMapper.selectCameraCodesByEdgeDeviceId(deviceId);
if (proxyCameraIds != null && !proxyCameraIds.isEmpty()) {
cameraIdSet.addAll(proxyCameraIds);
log.debug("[AiRedis] 从 stream_proxy 表查询到 {} 个摄像头: deviceId={}", proxyCameraIds.size(), deviceId);
}
// 来源2ROI 表中 device_id 关联的摄像头(兼容旧数据或边缘情况)
List<String> roiCameraIds = roiMapper.queryDistinctCameraIdsByDeviceId(deviceId);
if (roiCameraIds != null && !roiCameraIds.isEmpty()) {
cameraIdSet.addAll(roiCameraIds);
log.debug("[AiRedis] 从 ROI 表查询到 {} 个摄像头: deviceId={}", roiCameraIds.size(), deviceId);
}
List<String> cameraIds = new ArrayList<>(cameraIdSet);
if (cameraIds.isEmpty()) {
log.info("[AiRedis] 设备 {} 下无关联摄像头,写入空配置", deviceId);
cameraIds = Collections.emptyList();
} else {
log.info("[AiRedis] 设备 {} 下共 {} 个摄像头(去重后)", deviceId, cameraIds.size());
}
// 2. 构建扁平格式 JSON

View File

@@ -133,4 +133,10 @@ public interface StreamProxyMapper {
*/
@Select("SELECT edge_device_id FROM wvp_stream_proxy WHERE camera_code = #{cameraCode}")
String selectEdgeDeviceIdByCameraCode(@Param("cameraCode") String cameraCode);
/**
* 根据 edge_device_id 查询关联的所有摄像头编码
*/
@Select("SELECT camera_code FROM wvp_stream_proxy WHERE edge_device_id = #{edgeDeviceId} AND camera_code IS NOT NULL AND camera_code != '' ORDER BY camera_name")
List<String> selectCameraCodesByEdgeDeviceId(@Param("edgeDeviceId") String edgeDeviceId);
}

View File

@@ -169,6 +169,21 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
streamProxyMapper.add(streamProxy);
streamProxy.setDataType(ChannelDataType.STREAM_PROXY);
streamProxy.setDataDeviceId(streamProxy.getId());
// 推送配置到 Edge新增摄像头
String edgeDeviceId = streamProxy.getEdgeDeviceId();
if (edgeDeviceId != null && !edgeDeviceId.isEmpty()) {
try {
redisConfigService.writeDeviceAggregatedConfig(edgeDeviceId, "UPDATE");
log.info("[StreamProxy] 新增摄像头后推送配置到 EdgeedgeDeviceId={}, cameraCode={}",
edgeDeviceId, cameraCode);
} catch (Exception e) {
log.error("[StreamProxy] 新增摄像头后推送配置失败edgeDeviceId={}, cameraCode={}",
edgeDeviceId, cameraCode, e);
}
} else {
log.warn("[StreamProxy] 新增摄像头但 edgeDeviceId 为空跳过推送配置cameraCode={}", cameraCode);
}
} catch (DuplicateKeyException e) {
throw new RuntimeException("生成 camera_code 失败,请重试");
}
@@ -185,7 +200,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
private void delete(StreamProxy streamProxy) {
Assert.notNull(streamProxy, "代理不可为NULL");
String cameraCode = streamProxy.getCameraCode();
String edgeDeviceId = streamProxy.getEdgeDeviceId();
if (streamProxy.getPulling() != null && streamProxy.getPulling()) {
playService.stopProxy(streamProxy);
}
@@ -194,13 +209,15 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
}
streamProxyMapper.delete(streamProxy.getId());
// 推送配置到 Edge删除摄像头
if (cameraCode != null) {
if (edgeDeviceId != null && !edgeDeviceId.isEmpty()) {
try {
redisConfigService.writeDeviceAggregatedConfig(cameraCode, "UPDATE");
log.info("[StreamProxy] 删除摄像头后推送配置到 Edgecamera_code={}", cameraCode);
redisConfigService.writeDeviceAggregatedConfig(edgeDeviceId, "UPDATE");
log.info("[StreamProxy] 删除摄像头后推送配置到 EdgeedgeDeviceId={}", edgeDeviceId);
} catch (Exception e) {
log.error("[StreamProxy] 删除摄像头后推送配置失败,camera_code={}", cameraCode, e);
log.error("[StreamProxy] 删除摄像头后推送配置失败,edgeDeviceId={}", edgeDeviceId, e);
}
} else {
log.warn("[StreamProxy] 删除摄像头但 edgeDeviceId 为空,跳过推送配置");
}
}
@@ -244,6 +261,38 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
gbChannelService.add(streamProxy.buildCommonGBChannel());
}
}
// 推送配置到 Edge更新摄像头
String oldEdgeDeviceId = streamProxyInDb.getEdgeDeviceId();
String newEdgeDeviceId = streamProxy.getEdgeDeviceId();
if (oldEdgeDeviceId != null && !oldEdgeDeviceId.isEmpty() &&
newEdgeDeviceId != null && !newEdgeDeviceId.isEmpty() &&
!oldEdgeDeviceId.equals(newEdgeDeviceId)) {
// edgeDeviceId 变更:先推送给老设备(移除),再推送给新设备(添加)
try {
redisConfigService.writeDeviceAggregatedConfig(oldEdgeDeviceId, "UPDATE");
log.info("[StreamProxy] 更新摄像头后推送配置到旧 EdgeoldEdgeDeviceId={}", oldEdgeDeviceId);
} catch (Exception e) {
log.error("[StreamProxy] 更新摄像头后推送配置到旧 Edge 失败oldEdgeDeviceId={}", oldEdgeDeviceId, e);
}
try {
redisConfigService.writeDeviceAggregatedConfig(newEdgeDeviceId, "UPDATE");
log.info("[StreamProxy] 更新摄像头后推送配置到新 EdgenewEdgeDeviceId={}", newEdgeDeviceId);
} catch (Exception e) {
log.error("[StreamProxy] 更新摄像头后推送配置到新 Edge 失败newEdgeDeviceId={}", newEdgeDeviceId, e);
}
} else if (newEdgeDeviceId != null && !newEdgeDeviceId.isEmpty()) {
// edgeDeviceId 未变更或原为空:推送给当前设备
try {
redisConfigService.writeDeviceAggregatedConfig(newEdgeDeviceId, "UPDATE");
log.info("[StreamProxy] 更新摄像头后推送配置到 EdgeedgeDeviceId={}", newEdgeDeviceId);
} catch (Exception e) {
log.error("[StreamProxy] 更新摄像头后推送配置失败edgeDeviceId={}", newEdgeDeviceId, e);
}
} else {
log.warn("[StreamProxy] 更新摄像头但 edgeDeviceId 为空,跳过推送配置");
}
return true;
}