From 38b6a20e4530dd11011fb82290d3cc2ba30dd3ed Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Tue, 17 Mar 2026 17:47:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=9A=E8=BE=B9=E7=BC=98?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=BF=83=E8=B7=B3=20HTTP=20=E7=AB=AF?= =?UTF-8?q?=E7=82=B9=20+=20stream=5Fcount/config=5Fversion=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 POST /api/ai/device/heartbeat 端点,接收边缘端心跳上报 - 心跳端点加入安全白名单(无需认证) - AiEdgeDevice 新增 streamCount、configVersion 字段 - Mapper INSERT/UPDATE 同步新增字段 - Service 从心跳 payload 提取并保存新字段 - 数据库升级 SQL 添加 stream_count、config_version 列 Co-Authored-By: Claude Sonnet 4.6 --- .../iot/vmp/aiot/bean/AiEdgeDevice.java | 6 ++++++ .../aiot/controller/AiEdgeDeviceController.java | 17 +++++++++++++++++ .../iot/vmp/aiot/dao/AiEdgeDeviceMapper.java | 5 +++-- .../service/impl/AiEdgeDeviceServiceImpl.java | 4 ++++ .../vmp/conf/security/WebSecurityConfig.java | 1 + 数据库/aiot/升级-mysql-aiot-方案B.sql | 6 ++++++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiEdgeDevice.java b/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiEdgeDevice.java index 8951cd4d9..1b7641344 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiEdgeDevice.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/bean/AiEdgeDevice.java @@ -31,6 +31,12 @@ public class AiEdgeDevice { @Schema(description = "流统计信息JSON") private String streamStats; + @Schema(description = "活跃视频流数量") + private Integer streamCount; + + @Schema(description = "当前配置版本") + private String configVersion; + @Schema(description = "更新时间") private String updatedAt; } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiEdgeDeviceController.java b/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiEdgeDeviceController.java index 038f7877e..d3c597c7e 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiEdgeDeviceController.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiEdgeDeviceController.java @@ -56,4 +56,21 @@ public class AiEdgeDeviceController { public WVPResult> statistics() { return WVPResult.success(edgeDeviceService.getStatistics()); } + + @Operation(summary = "边缘设备心跳上报") + @PostMapping("/heartbeat") + public WVPResult heartbeat(@RequestBody String payload) { + try { + com.alibaba.fastjson2.JSONObject json = com.alibaba.fastjson2.JSON.parseObject(payload); + String deviceId = json.getString("device_id"); + if (deviceId == null || deviceId.isEmpty()) { + return WVPResult.fail(400, "device_id 不能为空"); + } + edgeDeviceService.saveOrUpdateHeartbeat(deviceId, payload); + return WVPResult.success("ok"); + } catch (Exception e) { + log.error("[AiEdgeDevice] 心跳上报处理失败", e); + return WVPResult.fail(500, "心跳处理失败: " + e.getMessage()); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiEdgeDeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiEdgeDeviceMapper.java index 37856f359..7e8e2ee46 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiEdgeDeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/dao/AiEdgeDeviceMapper.java @@ -9,15 +9,16 @@ import java.util.List; public interface AiEdgeDeviceMapper { @Insert("INSERT INTO wvp_ai_edge_device (device_id, status, last_heartbeat, uptime_seconds, " + - "frames_processed, alerts_generated, stream_stats, updated_at) " + + "frames_processed, alerts_generated, stream_stats, stream_count, config_version, updated_at) " + "VALUES (#{deviceId}, #{status}, #{lastHeartbeat}, #{uptimeSeconds}, " + - "#{framesProcessed}, #{alertsGenerated}, #{streamStats}, #{updatedAt})") + "#{framesProcessed}, #{alertsGenerated}, #{streamStats}, #{streamCount}, #{configVersion}, #{updatedAt})") @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") int add(AiEdgeDevice device); @Update("UPDATE wvp_ai_edge_device SET status=#{status}, last_heartbeat=#{lastHeartbeat}, " + "uptime_seconds=#{uptimeSeconds}, frames_processed=#{framesProcessed}, " + "alerts_generated=#{alertsGenerated}, stream_stats=#{streamStats}, " + + "stream_count=#{streamCount}, config_version=#{configVersion}, " + "updated_at=#{updatedAt} WHERE device_id=#{deviceId}") int updateByDeviceId(AiEdgeDevice device); diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiEdgeDeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiEdgeDeviceServiceImpl.java index 4d055f640..6709e0704 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiEdgeDeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiEdgeDeviceServiceImpl.java @@ -46,6 +46,8 @@ public class AiEdgeDeviceServiceImpl implements IAiEdgeDeviceService { device.setAlertsGenerated(status != null ? status.getLong("alerts_generated") : null); device.setStreamStats(status != null && status.containsKey("stream_stats") ? status.getJSONObject("stream_stats").toJSONString() : null); + device.setStreamCount(status != null ? status.getInteger("stream_count") : null); + device.setConfigVersion(status != null ? status.getString("config_version") : null); device.setUpdatedAt(now); deviceMapper.add(device); log.info("[AiEdgeDevice] 新设备上线: deviceId={}", deviceId); @@ -57,6 +59,8 @@ public class AiEdgeDeviceServiceImpl implements IAiEdgeDeviceService { device.setAlertsGenerated(status != null ? status.getLong("alerts_generated") : null); device.setStreamStats(status != null && status.containsKey("stream_stats") ? status.getJSONObject("stream_stats").toJSONString() : null); + device.setStreamCount(status != null ? status.getInteger("stream_count") : null); + device.setConfigVersion(status != null ? status.getString("config_version") : null); device.setUpdatedAt(now); deviceMapper.updateByDeviceId(device); log.debug("[AiEdgeDevice] 心跳更新: deviceId={}", deviceId); 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 5de05670e..32df9d80f 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 @@ -108,6 +108,7 @@ public class WebSecurityConfig { defaultExcludes.add("/api/ai/alert/edge/**"); defaultExcludes.add("/api/ai/alert/image"); defaultExcludes.add("/api/ai/device/edge/**"); + defaultExcludes.add("/api/ai/device/heartbeat"); if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) { defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes()); diff --git a/数据库/aiot/升级-mysql-aiot-方案B.sql b/数据库/aiot/升级-mysql-aiot-方案B.sql index dd2f4eac4..85845a144 100644 --- a/数据库/aiot/升级-mysql-aiot-方案B.sql +++ b/数据库/aiot/升级-mysql-aiot-方案B.sql @@ -53,10 +53,16 @@ CREATE TABLE IF NOT EXISTS wvp_ai_edge_device ( frames_processed BIGINT NULL COMMENT '已处理帧数', alerts_generated BIGINT NULL COMMENT '已生成告警数', stream_stats TEXT NULL COMMENT 'JSON流统计', + stream_count INT NULL COMMENT '活跃视频流数量', + config_version VARCHAR(64) NULL COMMENT '当前配置版本', updated_at VARCHAR(50) NULL COMMENT '更新时间', UNIQUE KEY uk_device_id (device_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='边缘设备状态'; +-- 升级:wvp_ai_edge_device 新增字段(已有表执行) +ALTER TABLE wvp_ai_edge_device ADD COLUMN IF NOT EXISTS stream_count INT NULL COMMENT '活跃视频流数量' AFTER stream_stats; +ALTER TABLE wvp_ai_edge_device ADD COLUMN IF NOT EXISTS config_version VARCHAR(64) NULL COMMENT '当前配置版本' AFTER stream_count; + -- 4. 算法参数模板表 CREATE TABLE IF NOT EXISTS wvp_ai_algo_template ( id INT PRIMARY KEY AUTO_INCREMENT,