Browse Source

fix: 查询问题

jackzhou 7 months ago
parent
commit
bdd9361e05
11 changed files with 128 additions and 42 deletions
  1. 9 7
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java
  2. 92 20
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/SensorAlarmMessagePushService.java
  3. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WechatMessagePushService.java
  4. 9 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java
  5. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/MonitorDeviceHeartBeatEventListener.java
  6. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/MonitorDeviceLoginEventListener.java
  7. 1 7
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorAlarmEventListener.java
  8. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/renke/config/JfcloudColdChainServerAutoConfiguration.java
  9. 6 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/AppDevicePageParam.java
  10. 2 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/AppDeviceService.java
  11. 5 4
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortarget/service/impl/MonitorTargetServiceImpl.java

+ 9 - 7
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java

@@ -38,16 +38,17 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
             return false;
         }
         final String deviceName = threshold.getMonitorTargetRegion().getName();
+        final String deviceId = threshold.getMonitorTargetRegion().getMonitorTargetId();
         // 检查传感器数据并触发报警
         boolean alarmTriggered = false;
         if (isNull(sensorData.getTemperature())) {
-            alarmTriggered = checkThreshold(sensorData.getTemperature(), threshold.getTemperatureUp(), threshold.getTemperatureDown(), "温度", deviceName);
+            alarmTriggered = checkThreshold(sensorData.getTemperature(), threshold.getTemperatureUp(), threshold.getTemperatureDown(), "温度", deviceName, deviceId);
         }
         if (isNull(sensorData.getHumidity())) {
-            alarmTriggered |= checkThreshold(sensorData.getHumidity(), threshold.getHumidityUp(), threshold.getHumidityDown(), "湿度", deviceName);
+            alarmTriggered |= checkThreshold(sensorData.getHumidity(), threshold.getHumidityUp(), threshold.getHumidityDown(), "湿度", deviceName, deviceId);
         }
         if (isNull(sensorData.getCo2())) {
-            alarmTriggered |= checkThreshold(sensorData.getCo2(), threshold.getCo2Up(), threshold.getCo2Down(), "二氧化碳", deviceName);
+            alarmTriggered |= checkThreshold(sensorData.getCo2(), threshold.getCo2Up(), threshold.getCo2Down(), "二氧化碳", deviceName, deviceId);
         }
         return alarmTriggered;
     }
@@ -62,17 +63,17 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
      * @param deviceName     设备名称
      * @return 是否触发报警
      */
-    private boolean checkThreshold(Float value, Float upperThreshold, Float lowerThreshold, String type, String deviceName) {
+    private boolean checkThreshold(Float value, Float upperThreshold, Float lowerThreshold, String type, String deviceName, String deviceId) {
         boolean alarmTriggered = false;
         String time = DATE_FORMAT.format(new Date()); // 获取当前时间
         String unit = getUnit(type);
         if (value > upperThreshold) {
             // 超过上限,触发超标报警
-            publishAlarm(type + "超标", value, unit, deviceName, time, upperThreshold);
+            publishAlarm(type + "超标", value, unit, deviceName, time, upperThreshold, deviceId);
             alarmTriggered = true;
         } else if (value < lowerThreshold) {
             // 低于下限,触发低于阈值报警
-            publishAlarm(type + "过低", value, unit, deviceName, time, lowerThreshold);
+            publishAlarm(type + "过低", value, unit, deviceName, time, lowerThreshold, deviceId);
             alarmTriggered = true;
         }
         return alarmTriggered;
@@ -88,7 +89,7 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
      * @param deviceName 设备名称
      * @param time       时间戳
      */
-    private void publishAlarm(String alarmType, Float value, String unit, String deviceName, String time, float threshold) {
+    private void publishAlarm(String alarmType, Float value, String unit, String deviceName, String time, float threshold, String deviceId) {
         // 获取对应的报警消息模板
         String alarmMessage = getAlarmMessage(alarmType, value, unit, deviceName, time, threshold);
         // 构建 SensorAlarm 对象
@@ -98,6 +99,7 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
         sensorAlarm.setAlarmTime(DateFormatter.now(new Date()));  // 设置单位(例如 °C, %, ppm)
         sensorAlarm.setSource(deviceName);  // 设置设备名称
         sensorAlarm.setDeviceName(deviceName);  // 设置设备名称
+        sensorAlarm.setDeviceId(deviceId);  // 设置设备名称
         sensorAlarm.setPriority("高");  // 设置设备名称
         sensorAlarm.setStatus("未处理");  // 设置设备名称
         sensorAlarm.setAlarmTime(time);  // 设置报警时间

+ 92 - 20
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/SensorAlarmMessagePushService.java

@@ -1,44 +1,66 @@
 package vip.xiaonuo.coldchain.core.alarm.service.messagepush;
 
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import vip.xiaonuo.coldchain.core.alarm.bean.NotificationChannel;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
 import vip.xiaonuo.coldchain.core.alarm.mapper.SensorAlarmMapper;
+import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 @Service
 @Slf4j
+@RequiredArgsConstructor
 public class SensorAlarmMessagePushService {
-
-    // 用一个 Map 来存储不同的推送渠道服务
     private final Map<String, MessagePushService> pushServices;
+    private final SensorAlarmMapper sensorAlarmMapper;
+    // 用来记录每个用户对某设备和告警类型的推送历史,key 为 (openid, deviceID, alarmType),value 为推送时间列表
+    private final Map<String, List<Long>> userDeviceAlarmPushHistory = new ConcurrentHashMap<>();
 
-    @Autowired
-    private SensorAlarmMapper sensorAlarmMapper;  // 注入 MyBatis-Plus 的 Mapper
-
-    @Autowired
-    public SensorAlarmMessagePushService(Map<String, MessagePushService> pushServices) {
-        this.pushServices = pushServices;
-    }
 
     /**
      * 根据通知渠道推送消息,并更新通知状态
      *
      * @param alarm 告警信息
      */
+    @Async("coldChainAsyncTask")
     public void pushAlarmMessage(SensorAlarm alarm) {
-        // 遍历每个接收人和通知渠道,发送告警信息
-        for (NotificationChannel channel : alarm.getNotificationChannel()) {
-            MessagePushService pushService = pushServices.get(channel.name());
-            log.info("推送消息渠道: {}", channel.getChannelName());
-            if (pushService != null) {
-                pushService.sendAlarmMessage(alarm);
-            } else {
-                log.info("不支持的通知渠道:" + channel);
+        String deviceID = alarm.getDeviceId(); // 获取设备ID
+        String alarmType = alarm.getAlarmType(); // 获取告警类型
+        List<String> openids = alarm.getAlarmUsers().stream().map(SensorAlarmUser::getOpenId).toList();
+        // 遍历每个 openid,针对每个用户进行推送次数限制和推送
+        for (String openid : openids) {
+            // 判断该用户对该设备和告警类型的推送是否超过限制
+            if (hasExceededPushLimit(openid, deviceID, alarmType)) {
+                log.info("用户 {} 对设备 {} 的告警类型 {} 在过去一个小时内推送次数超过限制,跳过推送", openid, deviceID, alarmType);
+                continue; // 跳过该用户
             }
+            // 遍历每个接收人和通知渠道,发送告警信息
+            for (NotificationChannel channel : alarm.getNotificationChannel()) {
+                MessagePushService pushService = pushServices.get(channel.name());
+                log.info("推送消息渠道: {}", channel.getChannelName());
+
+                if (pushService != null) {
+                    try {
+                        // 调用推送服务
+                        pushService.sendAlarmMessage(alarm);
+                        log.info("告警信息成功推送至渠道: {}", channel.getChannelName());
+                    } catch (Exception e) {
+                        log.error("推送消息失败,渠道: {},错误: {}", channel.getChannelName(), e.getMessage());
+                    }
+                } else {
+                    log.warn("不支持的通知渠道: {}", channel.getChannelName());
+                }
+            }
+            // 记录推送时间
+            recordPushTime(openid, deviceID, alarmType);
         }
         // 设置已通知状态
         alarm.setNotified(true);
@@ -46,13 +68,63 @@ public class SensorAlarmMessagePushService {
         saveAlarmToDatabase(alarm);
     }
 
+    /**
+     * 判断用户对某设备和某告警类型的推送是否超过了推送次数限制
+     *
+     * @param openid    用户的 openid
+     * @param deviceID  设备ID
+     * @param alarmType 告警类型
+     * @return 是否超过限制
+     */
+    private boolean hasExceededPushLimit(String openid, String deviceID, String alarmType) {
+        String key = generatePushHistoryKey(openid, deviceID, alarmType);
+        List<Long> pushTimes = userDeviceAlarmPushHistory.get(key);
+        if (pushTimes == null) {
+            return false; // 没有记录,说明该用户、设备、告警类型组合没有推送记录
+        }
+        // 清理超过时间窗口的记录
+        long currentTime = System.currentTimeMillis();
+        pushTimes.removeIf(time -> currentTime - time > JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW);
+        // 如果该用户对该设备和告警类型组合在当前时间窗口内已经超过最大推送次数,则不再推送
+        return pushTimes.size() >= JfcloudColdChainConstants.MESS_PUSH_MAX_PUSH_COUNT;
+    }
+
+    /**
+     * 生成唯一的推送历史记录的键值,基于用户 openid、设备ID和告警类型
+     *
+     * @param openid    用户的 openid
+     * @param deviceID  设备ID
+     * @param alarmType 告警类型
+     * @return 唯一的键值
+     */
+    private String generatePushHistoryKey(String openid, String deviceID, String alarmType) {
+        return openid + "_" + deviceID + "_" + alarmType;
+    }
+
+    /**
+     * 记录用户对某设备和某告警类型的推送时间
+     *
+     * @param openid    用户的 openid
+     * @param deviceID  设备ID
+     * @param alarmType 告警类型
+     */
+    private void recordPushTime(String openid, String deviceID, String alarmType) {
+        String key = generatePushHistoryKey(openid, deviceID, alarmType);
+        List<Long> pushTimes = userDeviceAlarmPushHistory.computeIfAbsent(key, k -> new ArrayList<>());
+        pushTimes.add(System.currentTimeMillis());
+    }
+
     /**
      * 保存告警信息到数据库
      *
      * @param alarm 告警信息
      */
     private void saveAlarmToDatabase(SensorAlarm alarm) {
-        // 调用 MyBatis-Plus 的 saveOrUpdate 方法保存或更新告警数据
-        sensorAlarmMapper.insert(alarm); // 假设 mapper 有这个方法
+        try {
+            sensorAlarmMapper.insert(alarm);
+            log.info("告警信息已保存到数据库: {}", alarm.getId());
+        } catch (Exception e) {
+            log.error("保存告警信息到数据库失败: {}", e.getMessage());
+        }
     }
 }

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WechatMessagePushService.java

@@ -21,7 +21,7 @@ public class WechatMessagePushService implements MessagePushService {
 
     @Override
     public boolean sendAlarmMessage(SensorAlarm alarm/*, SensorAlarmUser user*/) {
-        PushParam pushParam = new PushParam(alarm.getValue() + "");
+        PushParam pushParam = new PushParam(alarm.getValue() + "/"+alarm.getThreshold());
         if (alarm.getMessage().length() > 17) {
             pushParam.setContext(alarm.getMessage().substring(0, 17) + "...");
         } else {

+ 9 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java

@@ -53,4 +53,13 @@ public interface JfcloudColdChainConstants {
      * 每次重试的间隔时间,单位:毫秒
      */
     int RETRY_DELAY_MS = 5000;
+
+    /**
+     * 消息推送限制固定时间内的最大推送次数
+     */
+    int MESS_PUSH_MAX_PUSH_COUNT = 5;
+    /**
+     * 消息推送限制的时间窗口,单位为毫秒(例如 1 小时)
+     */
+    long MESS_PUSH_TIME_WINDOW = 1 * 60 * 60 * 1000;
 }

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/MonitorDeviceHeartBeatEventListener.java

@@ -25,7 +25,7 @@ public class MonitorDeviceHeartBeatEventListener {
     private final MonitorDeviceService monitorDeviceService;
     private final MonitorTargetService monitorTargetService;
 
-    @Async
+    @Async("coldChainAsyncTask")
     @EventListener
     public void handleMonitorDeviceHeartBeatEvent(MonitorDeviceHeartBeatEvent event) {
         Integer deviceId = event.getDeviceId();

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/MonitorDeviceLoginEventListener.java

@@ -25,7 +25,7 @@ public class MonitorDeviceLoginEventListener {
     private final MonitorDeviceService monitorDeviceService;
     private final MonitorTargetService monitorTargetService;
 
-    @Async
+    @Async("coldChainAsyncTask")
     @EventListener
     public void handleMonitorDeviceLoginEvent(MonitorDeviceLoginEvent event) {
         Integer deviceId = event.getDeviceId();

+ 1 - 7
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorAlarmEventListener.java

@@ -8,18 +8,12 @@ package vip.xiaonuo.coldchain.core.event;
  * @date 2024/11/12 15:31:43
  */
 
-import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 import vip.xiaonuo.coldchain.core.alarm.service.messagepush.SensorAlarmMessagePushService;
-import vip.xiaonuo.coldchain.modular.push.param.PushParam;
-import vip.xiaonuo.coldchain.modular.push.utils.PushUtil;
-
-import java.util.Date;
-import java.util.List;
 
 @Slf4j
 @RequiredArgsConstructor
@@ -27,7 +21,7 @@ import java.util.List;
 public class SensorAlarmEventListener {
     private final SensorAlarmMessagePushService sensorAlarmMessagePushService;
 
-    @Async
+    @Async("coldChainAsyncTask")
     @EventListener
     public void handleSensorAlarmEvent(SensorAlarmEvent event) {
         sensorAlarmMessagePushService.pushAlarmMessage(event.getSensorAlarm());

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/renke/config/JfcloudColdChainServerAutoConfiguration.java

@@ -74,7 +74,7 @@ public class JfcloudColdChainServerAutoConfiguration {
      *
      * @param rsServer 要启动的 RSServer 实例
      */
-    @Async
+    @Async("coldChainAsyncTask")
     public void startJfcloudColdChainServerAsync(RSServer rsServer) {
         executorService.submit(() -> {
             try {

+ 6 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/AppDevicePageParam.java

@@ -56,6 +56,12 @@ public class AppDevicePageParam {
     @Schema(description = "账号、姓名关键词")
     private String searchKey;
 
+    /**
+     * 名称
+     */
+    @Schema(description = "名称")
+    private String name;
+
     /**
      * 用户状态
      */

+ 2 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/AppDeviceService.java

@@ -69,6 +69,8 @@ public class AppDeviceService {
         // 设置分页参数:当前页和每页记录数
         monitorTargetPageParam.setCurrent(appDevicePageParam.getCurrent());
         monitorTargetPageParam.setSize(appDevicePageParam.getSize());
+        monitorTargetPageParam.setSearchKey(appDevicePageParam.getSearchKey());
+        monitorTargetPageParam.setName(appDevicePageParam.getName());
         // 调用 monitorTargetService 查询当前用户的监控目标数据,分页结果
         Page<MonitorTarget> pageByUser = monitorTargetService.getPageByUser(monitorTargetPageParam);
         // 遍历监控目标分页记录

+ 5 - 4
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortarget/service/impl/MonitorTargetServiceImpl.java

@@ -148,14 +148,15 @@ public class MonitorTargetServiceImpl extends ServiceImpl<MonitorTargetMapper, M
             CommonSortOrderEnum.validate(monitorTargetPageParam.getSortOrder());
             queryWrapper.orderBy(true, monitorTargetPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()), StrUtil.toUnderlineCase(monitorTargetPageParam.getSortField()));
         } else {
-            queryWrapper.lambda().orderByAsc(MonitorTarget::getId);
+            queryWrapper.lambda().orderByDesc(MonitorTarget::getCreateTime);
         }
-        Page<MonitorTarget> page = this.page(CommonPageRequest.defaultPage(), queryWrapper);
+        Page<MonitorTarget> page1 = new Page<>(monitorTargetPageParam.getCurrent(), monitorTargetPageParam.getSize());
+        Page<MonitorTarget> rlt = this.page(page1, queryWrapper);
         //获取设备区域列表并赋值返回
-        page.getRecords().forEach(monitorTarget -> {
+        rlt.getRecords().forEach(monitorTarget -> {
             monitorTarget.setMonitorTargetRegionList(monitorTargetRegionService.getRegionListByTargetId(monitorTarget.getId()));
         });
-        return page;
+        return rlt;
     }
 
     @Override