Prechádzať zdrojové kódy

一键呼叫功能-报警信息查询并推送

wwh 3 týždňov pred
rodič
commit
1089d76cb9

+ 20 - 0
app-admin/pom.xml

@@ -80,6 +80,12 @@
         <dependency>
             <groupId>com.ruoyi</groupId>
             <artifactId>app-quartz</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <!--事件处理-->
@@ -128,6 +134,20 @@
             <artifactId>jsch</artifactId>
             <version>0.1.54</version>
         </dependency>
+        <!-- Spring Boot Web -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>2.5.15</version>
+        </dependency>
+
+        <!-- Spring Boot WebSocket -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+            <version>2.5.15</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 23 - 5
app-admin/src/main/java/com/ruoyi/web/controller/chenyanlogin/controller/GetHuiCunController.java

@@ -11,6 +11,11 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.bind.annotation.*;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
 @Api(tags ="慧村接口 同步")
 @RestController
 @RequestMapping("/huicun/")
@@ -49,19 +54,32 @@ public class GetHuiCunController {
 
     @ApiOperation("一键呼叫数据查询接口(新)")
     @GetMapping("/getZhwlDeviceData/list")
-    public  AjaxResult getZhwlDeviceData2(@RequestParam String deviceModelName,
-                                          @RequestParam Integer state,
-                                          @RequestParam Integer fullFlag,
+    public  AjaxResult getZhwlDeviceData2(@RequestParam(required = false) String deviceModelName,
+                                          @RequestParam(required = false) Integer state,
+                                          @RequestParam(required = false) Integer fullFlag,
                                           @RequestParam Integer pageNum,
                                           @RequestParam Integer pageSize) {
         ZhwlCaller.DeviceListQueryVO request = new ZhwlCaller.DeviceListQueryVO();
-        request.setDeviceModelName(deviceModelName);
-        request.setState(state);
+        request.setDeviceModelName(ObjectUtils.isEmpty(deviceModelName) ? "" : deviceModelName);
+        request.setState(ObjectUtils.isEmpty(state) ? 0 : state);
         request.setFullFlag(ObjectUtils.isEmpty(fullFlag) ? 0 : fullFlag);
         request.setPageNum(ObjectUtils.isEmpty(pageNum) ? 1 : pageNum);
         request.setPageSize(ObjectUtils.isEmpty(pageSize) ? 1 : pageSize);
         return huiCunService.getZhwlDeviceData(request);
     }
+    @ApiOperation("一键呼叫查询报警信息")
+    @GetMapping("/getZhwlAlarmData/list")
+    public  AjaxResult getZhwlAlarmData(@RequestParam(required = false) String startTime,
+                                        @RequestParam(required = false) String endTime,
+                                        @RequestParam Integer pageNum) {
+        ZhwlCaller.AlarmRecordQueryVO request = new ZhwlCaller.AlarmRecordQueryVO();
+        LocalDateTime startOfDay = LocalDate.now().atTime(LocalTime.MIN);
+        LocalDateTime endOfDay = LocalDate.now().atTime(LocalTime.MAX);
+        request.setStartTime(ObjectUtils.isEmpty(startTime) ? startOfDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : startTime);
+        request.setEndTime(ObjectUtils.isEmpty(endTime) ? endOfDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : endTime);
+        request.setPageNum(ObjectUtils.isEmpty(pageNum) ? 1 : pageNum);
+        return huiCunService.getZhwlAlarmData(request);
+    }
 
     //积分排行数据查询接口
     @ApiOperation("积分排行数据查询接口")

+ 1 - 0
app-admin/src/main/java/com/ruoyi/web/controller/chenyanlogin/service/HuiCunService.java

@@ -14,6 +14,7 @@ public interface HuiCunService {
 
     AjaxResult getZhwlDeviceData();
     AjaxResult getZhwlDeviceData(ZhwlCaller.DeviceListQueryVO queryVO);
+    AjaxResult getZhwlAlarmData(ZhwlCaller.AlarmRecordQueryVO queryVO);
 
     AjaxResult getPointRankData();
 

+ 28 - 59
app-admin/src/main/java/com/ruoyi/web/controller/chenyanlogin/service/impl/HuiCunServiceImpl.java

@@ -8,7 +8,6 @@ import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.web.controller.chenyanlogin.ChengYanAesUtil;
 import com.ruoyi.web.controller.chenyanlogin.dto.*;
 import com.ruoyi.web.controller.chenyanlogin.dto.HouseVillagerRelation;
@@ -84,13 +83,9 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, BASIC_DATA);
         if (responseData.get("code").equals(200)) {
-            Object data = responseData.get("data");
-            if ( data instanceof  JSONObject) {
-                JSONObject dataStr = (JSONObject) responseData.get("data");
-                BasicDataDto basicDataDto = JSONUtil.toBean(dataStr, BasicDataDto.class);
-                return success(basicDataDto);
-            }
-            return success(null);
+            JSONObject dataStr = (JSONObject) responseData.get("data");
+            BasicDataDto basicDataDto = JSONUtil.toBean(dataStr, BasicDataDto.class);
+            return success(basicDataDto);
         } else {
             return error("同步失败" + responseData.get("message"));
         }
@@ -104,21 +99,10 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("nonce", UUID.randomUUID().toString());
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, CARE_PEOPLE_DATA);
-        System.out.println(responseData);
-
-
-//        String data = (String)responseData.get("data");
-        Object data = responseData.get("data");
-        if (responseData.get("code").equals(200) ) {
-            if ( data instanceof  JSONObject){
-                JSONObject dataStr = (JSONObject) responseData.get("data");
-                CarePeopleData carePeopleData = JSONUtil.toBean(dataStr, CarePeopleData.class);
-                return success(carePeopleData);
-            }else {
-                return success(null);
-            }
-
-
+        if (responseData.get("code").equals(200)) {
+            JSONObject dataStr = (JSONObject) responseData.get("data");
+            CarePeopleData carePeopleData = JSONUtil.toBean(dataStr, CarePeopleData.class);
+            return success(carePeopleData);
         } else {
             return error("同步失败" + responseData.get("message"));
         }
@@ -132,13 +116,9 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, SNAPSHOT_REPORT_DATA);
         if (responseData.get("code").equals(200)) {
-            Object data = responseData.get("data");
-            if ( data instanceof  JSONObject) {
-                JSONObject dataStr = (JSONObject) responseData.get("data");
-                SnapshotReportDto snapshotReportDto = JSONUtil.toBean(dataStr, SnapshotReportDto.class);
-                return success(snapshotReportDto);
-            }
-            return success(null);
+            JSONObject dataStr = (JSONObject) responseData.get("data");
+            SnapshotReportDto snapshotReportDto = JSONUtil.toBean(dataStr, SnapshotReportDto.class);
+            return success(snapshotReportDto);
         } else {
             return error("同步失败" + responseData.get("message"));
         }
@@ -153,13 +133,9 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, ZHWL_DEVICE_DATA);
         if (responseData.get("code").equals(200)) {
-            Object data = responseData.get("data");
-            if ( data instanceof  JSONObject) {
-                JSONObject dataStr = (JSONObject) responseData.get("data");
-                ZhwlDeviceDataDto snapshotReportDto = JSONUtil.toBean(dataStr, ZhwlDeviceDataDto.class);
-                return success(snapshotReportDto);
-            }
-            return success(null);
+            JSONObject dataStr = (JSONObject) responseData.get("data");
+            ZhwlDeviceDataDto snapshotReportDto = JSONUtil.toBean(dataStr, ZhwlDeviceDataDto.class);
+            return success(snapshotReportDto);
         } else {
             return error("同步失败" + responseData.get("message"));
         }
@@ -188,6 +164,12 @@ public class HuiCunServiceImpl implements HuiCunService {
         return success(response);
     }
 
+    @Override
+    public AjaxResult getZhwlAlarmData(ZhwlCaller.AlarmRecordQueryVO queryVO) {
+        List<ZhwlCaller.AlarmRecordDTO> response = zhwlCaller.getAlarmList(queryVO);
+        return success(response);
+    }
+
     //积分排行数据查询接口
     @Override
     public AjaxResult getPointRankData() {
@@ -196,14 +178,9 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("nonce", UUID.randomUUID().toString());
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, POINT_RANK_DATA);
-        System.out.println(responseData);
         if (responseData.get("code").equals(200)) {
-            Object data = responseData.get("data");
-            if ( data instanceof  JSONArray) {
-                List<JSONObject> dataStr = (List<JSONObject>) responseData.get("data");
-                return success(dataStr);
-            }
-            return  success(null);
+            List<JSONObject> dataStr = (List<JSONObject>) responseData.get("data");
+            return success(dataStr);
         } else {
             return error("同步失败" + responseData.get("message"));
         }
@@ -218,13 +195,9 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, CIVILIZED_DATA);
         if (responseData.get("code").equals(200)) {
-            Object data = responseData.get("data");
-            if ( data instanceof  JSONObject) {
-                JSONObject dataStr = (JSONObject) responseData.get("data");
-                CivilizedDataDto civilizedDataDto = JSONUtil.toBean(dataStr, CivilizedDataDto.class);
-                return success(civilizedDataDto);
-            }
-            return success(null);
+            JSONObject dataStr = (JSONObject) responseData.get("data");
+            CivilizedDataDto civilizedDataDto = JSONUtil.toBean(dataStr, CivilizedDataDto.class);
+            return success(civilizedDataDto);
         } else {
             return error("同步失败" + responseData.get("message"));
         }
@@ -237,16 +210,12 @@ public class HuiCunServiceImpl implements HuiCunService {
         requestParam.put("nonce", UUID.randomUUID().toString());
         requestParam.put("timestamp", System.currentTimeMillis());
         Map<String, Object> responseData = getMap(requestParam, VILLAGE_NEWS_DATA);
-        System.out.println(responseData);
         if (responseData.get("code").equals(200)) {
-            Object data = responseData.get("data");
-            if ( data instanceof  JSONArray) {
+            System.out.println(responseData);
 //            List<VillageNewsDataDto> dataStr = (List<VillageNewsDataDto>) responseData.get("data");
-                List<VillageNewsDataDto> dataStr = JSONUtil.toList((JSONArray) responseData.get("data"), VillageNewsDataDto.class
-                );
-                return success(dataStr);
-            }
-           return success(null);
+            List<VillageNewsDataDto> dataStr =   JSONUtil.toList( (JSONArray) responseData.get("data"), VillageNewsDataDto.class
+            );
+            return success(dataStr);
         } else {
             return error("同步失败" + responseData.get("message"));
         }

+ 210 - 9
app-admin/src/main/java/com/ruoyi/web/controller/chenyanlogin/service/impl/ZhwlCaller.java

@@ -3,35 +3,39 @@ package com.ruoyi.web.controller.chenyanlogin.service.impl;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
 import lombok.Data;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.http.*;
 import org.springframework.stereotype.Component;
 import org.springframework.util.DigestUtils;
+import org.springframework.util.ObjectUtils;
 import org.springframework.web.client.RestTemplate;
 
 import java.io.Serializable;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.time.LocalDateTime;
+import java.util.*;
 
 @Component
 public class ZhwlCaller {
 
     private static final Logger log = LoggerFactory.getLogger(ZhwlCaller.class);
 
-//    @Value("${cloud.platform.appKey}")
+    //    @Value("${zhwl.platform.appKey}")
     private String appKey = "Kg2pE5V0";
 
-//    @Value("${cloud.platform.appSecret}")
+    //    @Value("${zhwl.platform.appSecret}")
     private String appSecret = "7a3bbfd6656e1bdef7290b714c1d1c73578a4ce2";
 
-//    @Value("${cloud.platform.baseUrl}")
+    //    @Value("${zhwl.platform.baseUrl}")
     private String baseUrl = "https://webapi.nbiotyun.com"; // e.g., http://domain/api/v1
 
     private final RestTemplate restTemplate = new RestTemplate();
@@ -39,6 +43,9 @@ public class ZhwlCaller {
     // 接口路径常量(便于管理)
     private static final String GET_DEVICE_LIST_PATH = "/api/v1/dev/getList";
 
+    // 接口路径常量(便于管理)    https://webapi.nbiotyun.com/api/v1/dev/alarmRecord
+    private static final String GET_ALARM_LIST_PATH = "/api/v1/dev/alarmRecord";
+
     /**
      * 公共方法:构建带认证信息的请求头
      *
@@ -133,7 +140,7 @@ public class ZhwlCaller {
         }
 
         //2. 打印原始响应(调试用,上线可关闭)
-         log.debug("原始响应: {}", body);
+        log.debug("原始响应: {}", body);
 
         try {
             //3. 先解析外层 JSON
@@ -172,6 +179,47 @@ public class ZhwlCaller {
         }
     }
 
+    /**
+     * 查询报警信息列表
+     */
+    public List<AlarmRecordDTO> getAlarmList(AlarmRecordQueryVO queryVO) {
+        ResponseEntity<String> response = postForString(GET_ALARM_LIST_PATH, queryVO);
+        //1. 检查 HTTP 状态码
+        if (!response.getStatusCode().is2xxSuccessful()) {
+            log.info("HTTP {} - 请求失败,响应内容: {}",response.getStatusCode() ,response.getBody());
+            return new ArrayList<>();
+        }
+
+        String body = response.getBody();
+        if (body == null || body.trim().isEmpty()) {
+            log.info("云平台返回空响应体");
+            return new ArrayList();
+        }
+
+        //2. 打印原始响应(调试用,上线可关闭)
+        log.debug("原始响应: {}", body);
+
+        try {
+            //3. 先解析外层 JSON
+            Map<String, Object> map = JSON.parseObject(body, Map.class);
+
+            //4. 提取 code、success
+            String code = (String) map.get("code");
+            Boolean success = (Boolean) map.get("success");
+            Object messageObj = map.getOrDefault("message", null);
+            if (ObjectUtils.isEmpty(messageObj) ||  !(messageObj instanceof List)) {
+                //throw new RuntimeException("响应中的 message 字段为空或不是约定的对象类型: " + messageObj);
+                log.warn("响应中的 message 字段为空或不是约定的对象类型: {}",messageObj);
+                return new ArrayList<>();
+            }
+            //5. 构建返回 DTO
+            return JSON.parseArray(JSON.toJSONString(messageObj), AlarmRecordDTO.class);
+
+        } catch (Exception e) {
+            throw new RuntimeException("解析响应数据失败,响应内容: " + body, e);
+        }
+    }
+
 
     @Data
     public static class DeviceListQueryVO {
@@ -230,4 +278,157 @@ public class ZhwlCaller {
         }
     }
 
+
+    @Data
+    public static class AlarmRecordQueryVO {
+
+        private Integer pageNum = 1;
+
+        private String startTime;
+        private String endTime;
+
+        private List<String> imeiList; // 可以通过批量 imei 号来查询
+    }
+
+
+    /**
+     * 报警记录 DTO
+     */
+    @Data
+    public static class AlarmRecordDTO implements Serializable {
+
+        /**
+         * 报警记录 id
+         */
+        private Long id;
+
+        /**
+         * 报警时间
+         */
+        private String alarmTime;
+
+        /**
+         * 设备 imei
+         */
+        private String deviceImei;
+
+        /**
+         * 设备所属单位名称
+         */
+        private String deptName;
+
+        /**
+         * 设备名称
+         */
+        private String deviceTypeName;
+
+        /**
+         * 设备状态
+         */
+        private String deviceState;
+
+        /**
+         * 设备型号名称
+         */
+        private String deviceVersionName;
+
+        /**
+         * 事件名称
+         */
+        private String deviceEventName;
+
+        /**
+         * 设备属性
+         */
+        private String deviceAttrValue;
+
+        /**
+         * 处理时间
+         */
+        private String hanldeTime; // 注意:字段名可能是 handleTime(拼写建议修正)
+
+        /**
+         * 处理人
+         */
+        private String handler;
+
+        /**
+         * 接警联系人列表
+         */
+        private List<Contact> contactList;
+
+        /**
+         * 住户信息列表
+         */
+        private List<Household> householdList;
+
+        /**
+         * 人工确认报警原因
+         */
+        private String alarmReason;
+
+        /**
+         * 设备详细安装地址
+         */
+        private String detailedLocation;
+
+        /**
+         * 备注
+         */
+        private String remark;
+
+        /**
+         * 创建时间
+         */
+        @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+        @JsonSerialize(using = LocalDateTimeSerializer.class)
+        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+        private LocalDateTime createTime;
+
+        /**
+         * 更新时间
+         */
+        @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+        @JsonSerialize(using = LocalDateTimeSerializer.class)
+        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+        private LocalDateTime updateTime;
+
+
+        // ========= 内部类:接警联系人 =========
+        @Data
+        public static class Contact {
+            /**
+             * 联系人名称
+             */
+            private String name;
+
+            /**
+             * 手机号码
+             */
+            private String phonenumber;
+
+            /**
+             * 接警人类型:1-一级, 2-二级, 3-三级, 4-四级
+             */
+            private Integer contactType;
+        }
+
+
+        // ========= 内部类:住户信息 =========
+        @Data
+        public static class Household {
+            /**
+             * 住户名称
+             */
+            private String name;
+
+            /**
+             * 手机号码
+             */
+            private String phonenumber;
+        }
+    }
+
 }

+ 1 - 0
app-admin/src/main/java/com/ruoyi/web/controller/chenyanlogin/timer/GetHuivCunTimer.java

@@ -48,6 +48,7 @@ public class GetHuivCunTimer {
 
     //一键呼叫数据查询接口
     @Scheduled(cron = "0 0 * * * ? ")
+//    @Scheduled(cron = "*/30 * * * * ?")
     public  void getZhwlDeviceData(){
         //huiCunService.getZhwlDeviceData();
         ZhwlCaller.DeviceListQueryVO queryVO = new ZhwlCaller.DeviceListQueryVO();

+ 23 - 0
app-admin/src/main/java/com/ruoyi/web/core/config/WebSocketConfig.java

@@ -0,0 +1,23 @@
+package com.ruoyi.web.core.config;
+
+import com.ruoyi.web.service.impl.AlarmWebSocketHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig implements WebSocketConfigurer {
+
+    private final AlarmWebSocketHandler webSocketHandler;
+
+    public WebSocketConfig(AlarmWebSocketHandler webSocketHandler) {
+        this.webSocketHandler = webSocketHandler;
+    }
+
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+        registry.addHandler(webSocketHandler, "/ws/alarm").setAllowedOrigins("*");
+    }
+}

+ 90 - 0
app-admin/src/main/java/com/ruoyi/web/service/impl/AlarmTaskProcessor.java

@@ -0,0 +1,90 @@
+package com.ruoyi.web.service.impl;
+
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.web.controller.chenyanlogin.service.impl.HuiCunServiceImpl;
+import com.ruoyi.web.controller.chenyanlogin.service.impl.ZhwlCaller;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+@Component
+public class AlarmTaskProcessor {
+
+    @Resource
+    private HuiCunServiceImpl huiCunService;
+
+    private final AlarmWebSocketHandler webSocketHandler;
+    private final BlockingQueue<ZhwlCaller.AlarmRecordDTO> alarmQueue = new LinkedBlockingQueue<>();
+
+    // 假设 alarmTime 格式为 "yyyy-MM-dd HH:mm:ss"
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    private static final DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder()
+            .appendPattern("yyyy-MM-dd HH:mm:ss")
+            .optionalStart()
+            .appendFraction(java.time.temporal.ChronoField.MILLI_OF_SECOND, 0, 3, true)
+            .optionalEnd()
+            .toFormatter();
+
+    public AlarmTaskProcessor(AlarmWebSocketHandler webSocketHandler) {
+        this.webSocketHandler = webSocketHandler;
+    }
+
+    /**
+     * 每隔 20 秒执行一次
+     */
+    @Scheduled(fixedRate = 20_000, initialDelay = 60_000) // 10秒间隔,首次延迟1分钟
+    public void processAlarms() {
+        System.out.println("定时任务执行: " + LocalDateTime.now());
+
+//        LocalDateTime now = LocalDateTime.now();
+//        LocalDateTime tenSecondsAgo = now.minusSeconds(30);
+        //todo test only
+        LocalDateTime now = LocalDateTime.parse("2025-10-13 19:00:00", FORMATTER);
+        LocalDateTime tenSecondsAgo = LocalDateTime.parse("2025-10-13 18:00:00", FORMATTER);
+
+        //
+        AjaxResult result = getZhwlAlarmData(tenSecondsAgo, now, 1);
+        if(ObjectUtils.isEmpty(result) || !result.isSuccess() || ObjectUtils.isEmpty(result.get(AjaxResult.DATA_TAG))) {
+            return;
+        }
+        List<ZhwlCaller.AlarmRecordDTO> currentAlarmList = (List<ZhwlCaller.AlarmRecordDTO>) result.get(AjaxResult.DATA_TAG);
+        for (ZhwlCaller.AlarmRecordDTO record : currentAlarmList) {
+            try {
+                LocalDateTime alarmDateTime = LocalDateTime.parse(record.getAlarmTime(), inputFormatter);
+                if (!alarmDateTime.isBefore(tenSecondsAgo) && !alarmDateTime.isAfter(now)) {
+                    // 时间在 [now - 10s, now] 范围内
+                    //alarmQueue.offer(record); // 放入队列
+                    webSocketHandler.sendMessage(record); // 立即推送
+                }
+            } catch (Exception e) {
+                // 解析失败跳过
+                System.err.println("时间解析失败: " + record.getAlarmTime() + ", ID: " + record.getId());
+            }
+        }
+    }
+
+    private AjaxResult getZhwlAlarmData(LocalDateTime startTime, LocalDateTime endTime, Integer pageNum) {
+        ZhwlCaller.AlarmRecordQueryVO request = new ZhwlCaller.AlarmRecordQueryVO();
+        request.setStartTime(startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+        request.setEndTime(endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+        request.setPageNum(ObjectUtils.isEmpty(pageNum) ? 1 : pageNum);
+        return huiCunService.getZhwlAlarmData(request);
+    }
+
+
+    // 可选:提供方法获取队列中的数据
+    public BlockingQueue<ZhwlCaller.AlarmRecordDTO> getAlarmQueue() {
+        return alarmQueue;
+    }
+}

+ 49 - 0
app-admin/src/main/java/com/ruoyi/web/service/impl/AlarmWebSocketHandler.java

@@ -0,0 +1,49 @@
+package com.ruoyi.web.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.web.controller.chenyanlogin.service.impl.ZhwlCaller;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+@Component
+public class AlarmWebSocketHandler extends TextWebSocketHandler {
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    private final CopyOnWriteArraySet<WebSocketSession> sessions = new CopyOnWriteArraySet<>();
+
+    public void sendMessage(ZhwlCaller.AlarmRecordDTO record) {
+        String payload;
+        try {
+            payload = objectMapper.writeValueAsString(record);
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+            return;
+        }
+
+        for (WebSocketSession session : sessions) {
+            if (session.isOpen()) {
+                try {
+                    session.sendMessage(new TextMessage(payload));
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) {
+        sessions.add(session);
+    }
+
+    @Override
+    public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) {
+        sessions.remove(session);
+    }
+}