From 780650f0cc22527765f9db738196b30c36ced00d Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Wed, 29 Apr 2026 10:00:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=91=84=E5=83=8F=E5=A4=B4?= =?UTF-8?q?=E5=A2=9E=E5=88=A0=E6=94=B9=E9=85=8D=E7=BD=AE=E4=B8=8B=E5=8F=91?= =?UTF-8?q?=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:前端添加摄像头后配置未下发到边缘端,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 --- .../impl/AiRedisConfigServiceImpl.java | 26 ++++++-- .../streamProxy/dao/StreamProxyMapper.java | 6 ++ .../service/impl/StreamProxyServiceImpl.java | 59 +++++++++++++++++-- 3 files changed, 82 insertions(+), 9 deletions(-) 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 3ee5d3e8a..60c99b2be 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 @@ -438,11 +438,29 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService { } try { - // 1. 查询该设备下所有摄像头 - List cameraIds = roiMapper.queryDistinctCameraIdsByDeviceId(deviceId); - if (cameraIds == null || cameraIds.isEmpty()) { + // 1. 查询该设备下所有摄像头(优先从 stream_proxy 表查询,确保包含无 ROI 的摄像头) + // 合并两个来源:stream_proxy 表的 edge_device_id 关联 + ROI 表的 device_id 关联 + Set cameraIdSet = new LinkedHashSet<>(); + + // 来源1:stream_proxy 表中 edge_device_id 匹配的摄像头 + List proxyCameraIds = streamProxyMapper.selectCameraCodesByEdgeDeviceId(deviceId); + if (proxyCameraIds != null && !proxyCameraIds.isEmpty()) { + cameraIdSet.addAll(proxyCameraIds); + log.debug("[AiRedis] 从 stream_proxy 表查询到 {} 个摄像头: deviceId={}", proxyCameraIds.size(), deviceId); + } + + // 来源2:ROI 表中 device_id 关联的摄像头(兼容旧数据或边缘情况) + List roiCameraIds = roiMapper.queryDistinctCameraIdsByDeviceId(deviceId); + if (roiCameraIds != null && !roiCameraIds.isEmpty()) { + cameraIdSet.addAll(roiCameraIds); + log.debug("[AiRedis] 从 ROI 表查询到 {} 个摄像头: deviceId={}", roiCameraIds.size(), deviceId); + } + + List cameraIds = new ArrayList<>(cameraIdSet); + if (cameraIds.isEmpty()) { log.info("[AiRedis] 设备 {} 下无关联摄像头,写入空配置", deviceId); - cameraIds = Collections.emptyList(); + } else { + log.info("[AiRedis] 设备 {} 下共 {} 个摄像头(去重后)", deviceId, cameraIds.size()); } // 2. 构建扁平格式 JSON diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java index 6bc8ee6d6..22cf8bc04 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java @@ -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 selectCameraCodesByEdgeDeviceId(@Param("edgeDeviceId") String edgeDeviceId); } diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java index 1761c63fa..6accf8acd 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java @@ -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] 新增摄像头后推送配置到 Edge,edgeDeviceId={}, 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] 删除摄像头后推送配置到 Edge,camera_code={}", cameraCode); + redisConfigService.writeDeviceAggregatedConfig(edgeDeviceId, "UPDATE"); + log.info("[StreamProxy] 删除摄像头后推送配置到 Edge,edgeDeviceId={}", 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] 更新摄像头后推送配置到旧 Edge,oldEdgeDeviceId={}", oldEdgeDeviceId); + } catch (Exception e) { + log.error("[StreamProxy] 更新摄像头后推送配置到旧 Edge 失败,oldEdgeDeviceId={}", oldEdgeDeviceId, e); + } + try { + redisConfigService.writeDeviceAggregatedConfig(newEdgeDeviceId, "UPDATE"); + log.info("[StreamProxy] 更新摄像头后推送配置到新 Edge,newEdgeDeviceId={}", 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] 更新摄像头后推送配置到 Edge,edgeDeviceId={}", newEdgeDeviceId); + } catch (Exception e) { + log.error("[StreamProxy] 更新摄像头后推送配置失败,edgeDeviceId={}", newEdgeDeviceId, e); + } + } else { + log.warn("[StreamProxy] 更新摄像头但 edgeDeviceId 为空,跳过推送配置"); + } return true; }