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,