黄渊昊 hace 2 semanas
padre
commit
fd5e83cc8c
Se han modificado 17 ficheros con 386 adiciones y 6 borrados
  1. 4 0
      snowy-plugin/snowy-plugin-coldchain/pom.xml
  2. 6 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/NotificationChannel.java
  3. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarm.java
  4. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/mapping/SensorAlarmMapper.xml
  5. 156 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/RedisSensorAlarmMessagePushService.java
  6. 55 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WebSocketPushService.java
  7. 14 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/websocket/WebSocketConfig.java
  8. 90 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/websocket/WebSocketEndpoint.java
  9. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java
  10. 5 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/MonitorDataProcessor.java
  11. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/impl/WifiEvnSevenInOne.java
  12. 37 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/impl/WifiN2Processor.java
  13. 1 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/enums/DeviceModelEnum.java
  14. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/entity/MonitorTargetRegion.java
  15. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/param/MonitorTargetRegionAddParam.java
  16. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/param/MonitorTargetRegionEditParam.java
  17. 3 1
      snowy-web-app/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java

+ 4 - 0
snowy-plugin/snowy-plugin-coldchain/pom.xml

@@ -118,5 +118,9 @@
             <artifactId>jfcloud-boot-starter-sms-aliyun</artifactId>
             <version>K7.5.4</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 6 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/NotificationChannel.java

@@ -44,7 +44,12 @@ public enum NotificationChannel {
     /**
      * 系统内部通知
      */
-    SYSTEM("系统通知");
+    SYSTEM("系统通知"),
+
+    /**
+     * websocket通知
+     */
+    WEBSOCKET("websocket");
 
     /**
      * -- GETTER --

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarm.java

@@ -118,7 +118,7 @@ public class SensorAlarm extends OrgEntity {
      */
     @Schema(description = "用户的通知渠道设置,支持选择接收告警的多个渠道,如短信、邮件、APP通知等")
     @TableField(value = "notification_channel", typeHandler = NotificationChannelListTypeHandler.class)
-    private List<NotificationChannel> notificationChannel = List.of(NotificationChannel.WECHAT);
+    private List<NotificationChannel> notificationChannel = List.of(NotificationChannel.WECHAT, NotificationChannel.WEBSOCKET);
 
     /**
      * 用户的通知时间段,限制用户接收告警的时间范围(如仅在工作时间接收告警)

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/mapping/SensorAlarmMapper.xml

@@ -35,7 +35,7 @@
         COUNT(*) AS totalWarnings,
         ANY_VALUE(source) as device_name
         FROM
-        sensor_alarm force index (top10)
+        sensor_alarm
         WHERE
         create_time > #{time}
         AND type IN

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

@@ -1,6 +1,7 @@
 package vip.xiaonuo.coldchain.core.alarm.service.messagepush;
 
 import cn.hutool.core.util.StrUtil;
+import jakarta.annotation.Resource;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Primary;
@@ -13,6 +14,8 @@ import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
 import vip.xiaonuo.coldchain.core.alarm.mapper.SensorAlarmMapper;
 import vip.xiaonuo.coldchain.core.alarm.service.delay.DeviceAlertDelayService;
 import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;
+import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
+import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRegionService;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -34,6 +37,9 @@ public class RedisSensorAlarmMessagePushService {
     private final RedisTemplate<String, Object> redisTemplate;
     private final DeviceAlertDelayService deviceAlertDelayService;
 
+    @Resource
+    private MonitorTargetRegionService monitorTargetRegionService;
+
     @Async("coldChainAsyncTask")
     public void pushAlarmMessage(SensorAlarm alarm) {
         String deviceID = alarm.getDeviceId();
@@ -52,7 +58,7 @@ public class RedisSensorAlarmMessagePushService {
             log.info("No user found for alarm {}", alarm);
             return;
         }
-        List<SensorAlarmUser> alarmUsers = users.values().stream().toList();
+        List<SensorAlarmUser> alarmUsers = new ArrayList<>(users.values().stream().toList());
         for (String openid : users.keySet()) {
             SensorAlarmUser user = users.get(openid);
             // ✅ 1️⃣ 首先判断该设备或该路是否处于预警延期(禁用)状态
@@ -73,14 +79,22 @@ public class RedisSensorAlarmMessagePushService {
             // 只对当前人发送预警消息
             alarm.setAlarmUsers(List.of(user));
             recordPushTime(openid, deviceID, alarm.getSensorRoute(), alarmType);
+            log.info("{}拥有的通知渠道:{}", alarm.getSensorCode(), alarm.getNotificationChannel());
             for (NotificationChannel channel : alarm.getNotificationChannel()) {
                 MessagePushService pushService = pushServices.get(channel.name());
                 if (pushService != null) {
                     try {
                         boolean b = false;
                         Long firstAlarmTime;
+                        for (SensorAlarmUser alarmUser : alarmUsers) {
+                            // 获取redis中记录的告警次数
+                            if (hasExceededPushRegionLimit(alarmUser.getOpenId(), alarm.getSensorCode(), alarm.getSensorRoute(), alarm.getType())) {
+                                alarmUsers.remove(alarmUser);
+                            }
+                        }
                         switch (alarm.getType()) {
                             case "0":
+                                log.info("发送设备 {} 第 {} 路 {} 告警消息", deviceID, alarm.getSensorRoute(), alarm.getAlarmType());
                                 b = pushService.sendAlarmMessage(alarm);
                                 break;
                             case "1":
@@ -167,6 +181,142 @@ public class RedisSensorAlarmMessagePushService {
         return false;
     }
 
+    /**
+     * 检查是否超过推送区域限制
+     *
+     * @param openid 用户OpenID
+     * @param sensorCode 设备ID
+     * @param sensorRoute 传感器路由
+     * @param alarmType 告警类型(0:数据异常, 1:设备离线, 2:恢复通知, 4:其他类型)
+     * @return true-超过限制, false-未超过限制
+     */
+    public boolean hasExceededPushRegionLimit(String openid, String sensorCode, Integer sensorRoute, String alarmType) {
+        // 获取监控目标区域的配置信息
+        MonitorTargetRegion monitorTargetRegion = monitorTargetRegionService.findOneByDeviceCodeAndSensorNo(sensorCode, sensorRoute);
+        if (monitorTargetRegion == null) {
+            log.warn("未找到设备[{}]传感器[{}]的监控区域配置", sensorCode, sensorRoute);
+            return false; // 无配置视为不限制
+        }
+
+        // 生成Redis存储的键
+        String key = generatePushHistoryCountKey(openid, sensorCode, sensorRoute, alarmType);
+
+        // 从Redis获取推送历史计数
+        Object countObj = redisTemplate.opsForValue().get(key);
+
+        // 如果无历史记录,说明是第一次推送
+        if (countObj == null) {
+            log.debug("首次推送,初始化计数器");
+            initializePushCounter(key, alarmType);
+            return false;
+        }
+
+        // 处理计数记录 - 修复类型转换问题
+        Integer currentCount = 0;
+        try {
+            if (countObj instanceof Integer) {
+                currentCount = (Integer) countObj;
+            } else if (countObj instanceof String) {
+                currentCount = Integer.parseInt((String) countObj);
+            } else {
+                log.warn("Redis计数记录类型异常, key: {}, 类型: {}", key, countObj.getClass().getSimpleName());
+                return true; // 类型异常视为超过限制
+            }
+        } catch (NumberFormatException e) {
+            log.error("Redis计数记录格式错误, key: {}, 值: {}", key, countObj, e);
+            return true; // 格式错误视为超过限制
+        }
+
+        // 如果计数为0,重新初始化(可能是过期或异常情况)
+        if (currentCount == 0) {
+            log.debug("计数为0,重新初始化计数器");
+            initializePushCounter(key, alarmType);
+            return false;
+        }
+
+        // 根据告警类型处理业务逻辑
+        return handleAlarmTypeLogic(currentCount, key, alarmType, monitorTargetRegion);
+    }
+
+    /**
+     * 初始化推送计数器
+     */
+    private void initializePushCounter(String key, String alarmType) {
+        switch (alarmType) {
+            case "0", "1" -> {
+                // 数据异常和设备离线类型:初始化为1次,设置过期时间
+                redisTemplate.opsForValue().set(key, 1);
+                log.debug("初始化{}告警计数器为1", alarmType.equals("0") ? "数据异常" : "设备离线");
+            }
+            case "2", "4" -> {
+                // 恢复通知和其他类型:不记录次数,直接删除key
+                redisTemplate.delete(key);
+                log.debug("清理{}告警计数器", alarmType.equals("2") ? "设备恢复" : "其他类型");
+            }
+            default -> log.warn("未知的告警类型: {}", alarmType);
+        }
+    }
+
+    /**
+     * 根据告警类型处理业务逻辑
+     */
+    private boolean handleAlarmTypeLogic(Integer currentCount, String key,
+                                         String alarmType, MonitorTargetRegion region) {
+        try {
+            switch (alarmType) {
+                case "0" -> {
+                    // 数据异常告警:检查是否超过最大推送次数
+                    Integer maxLimit = region.getAlarmMaxPush() != null ? region.getAlarmMaxPush() : Integer.MAX_VALUE;
+                    return checkAndUpdateCounter(currentCount, maxLimit, key,
+                            "数据异常", region.getAlarmMaxPush());
+                }
+                case "1" -> {
+                    // 设备离线告警:检查是否超过最大推送次数
+                    Integer maxLimit = region.getOfflineMaxPush() != null ? region.getOfflineMaxPush() : Integer.MAX_VALUE;
+                    return checkAndUpdateCounter(currentCount, maxLimit, key,
+                            "设备离线", region.getOfflineMaxPush());
+                }
+                case "2", "4" -> {
+                    // 恢复通知和其他类型:不限制次数,清理计数器
+                    log.info("{}告警恢复通知,清理计数器", alarmType.equals("2") ? "设备" : "其他");
+                    redisTemplate.delete(key);
+                    return false;
+                }
+                default -> {
+                    log.warn("未知的告警类型: {}, 默认允许推送", alarmType);
+                    return false;
+                }
+            }
+        } catch (Exception e) {
+            log.error("处理推送计数异常, key: {}, alarmType: {}", key, alarmType, e);
+            return false; // 异常情况下默认允许推送
+        }
+    }
+
+    /**
+     * 检查并更新计数器
+     */
+    private boolean checkAndUpdateCounter(Integer currentCount, Integer maxLimit,
+                                          String key, String alarmDesc, Integer maxPush) {
+        if (maxLimit == null || maxLimit <= 0) {
+            log.debug("{}无推送限制配置,允许推送", alarmDesc);
+            return false;
+        }
+
+        if (currentCount < maxLimit) {
+            // 未超过限制:计数+1并更新
+            Integer newCount = currentCount + 1;
+            redisTemplate.opsForValue().set(key, newCount);
+            log.debug("{}推送计数更新: {}/{}", alarmDesc, newCount, maxLimit);
+            return false;
+        } else {
+            // 超过限制:记录告警日志
+            log.warn("{}推送次数超过限制,当前已推送次数: {}/{}", alarmDesc, currentCount, maxLimit);
+            return true;
+        }
+    }
+
+
     private void recordPushTime(String openid, String deviceID, Integer sensorRoute, String alarmType) {
         log.info("数据存入redis数据库……");
         String key = generatePushHistoryKey(openid, deviceID, sensorRoute, alarmType);
@@ -192,4 +342,9 @@ public class RedisSensorAlarmMessagePushService {
         return JfcloudColdChainConstants.REDIS_ALARM_HISTORY_KEY_PREFIX
                 + ":" + deviceID + ":" + sensorRoute + ":" + alarmType;
     }
+
+    private String generatePushHistoryCountKey(String openid, String deviceID, Integer sensorRoute, String alarmType) {
+        return JfcloudColdChainConstants.REDIS_PUSH_COUNT_KEY_PREFIX
+                + ":" + openid + ":" + deviceID + ":" + sensorRoute + ":" + alarmType;
+    }
 }

+ 55 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WebSocketPushService.java

@@ -0,0 +1,55 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+import cn.hutool.json.JSON;
+import cn.hutool.json.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
+import vip.xiaonuo.coldchain.core.alarm.service.messagepush.MessagePushService;
+import vip.xiaonuo.coldchain.core.alarm.websocket.WebSocketEndpoint;
+import vip.xiaonuo.dev.api.DevMessageApi;
+
+@Service("WEBSOCKET")
+@Slf4j
+public class WebSocketPushService implements MessagePushService {
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm) {
+        try {
+            // 获取alarm对象中的字段值
+            String sensorCode = alarm.getSensorCode();
+            Integer sensorRoute = alarm.getSensorRoute();
+            String alarmType = alarm.getAlarmType();
+
+            // 构建JSON格式数据
+            JSONObject alarmJson = new JSONObject();
+            alarmJson.set("sensorCode", sensorCode != null ? sensorCode : "");
+            alarmJson.set("sensorRoute", sensorRoute != null ? sensorRoute : "");
+            alarmJson.set("value", String.valueOf(alarm.getValue()));
+            alarmJson.set("settingValue", String.valueOf(alarm.getThreshold()));
+            alarmJson.set("alarmType", alarmType != null ? alarmType : "");
+
+            // WebSocket推送逻辑
+            WebSocketEndpoint.sendMessage(String.valueOf(alarmJson));
+            return true;
+
+        } catch (Exception e) {
+            log.error("⚠️ 构建WebSocket告警消息失败", e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean sendAlarmRecoverMessage(SensorAlarm alarm, Long firstAlarmTime) {
+        return false;
+    }
+
+    @Override
+    public boolean sendOfflineMessage(SensorAlarm alarm) {
+        return false;
+    }
+
+    @Override
+    public boolean sendOnlineMessage(SensorAlarm alarm, Long firstAlarmTime) {
+        return false;
+    }
+}

+ 14 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/websocket/WebSocketConfig.java

@@ -0,0 +1,14 @@
+package vip.xiaonuo.coldchain.core.alarm.websocket;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+@Configuration
+public class WebSocketConfig{
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+}

+ 90 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/websocket/WebSocketEndpoint.java

@@ -0,0 +1,90 @@
+package vip.xiaonuo.coldchain.core.alarm.websocket;
+
+import com.google.gson.JsonObject;
+import jakarta.annotation.PostConstruct;
+import jakarta.websocket.OnClose;
+import jakarta.websocket.OnMessage;
+import jakarta.websocket.OnOpen;
+import jakarta.websocket.Session;
+import jakarta.websocket.server.ServerEndpoint;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Component
+@ServerEndpoint("/ws/{type}")
+@Slf4j
+public class WebSocketEndpoint {
+    private static final Map<String, Session> sessions = new ConcurrentHashMap<>(); // 管理所有Session
+
+    private static final long HEARTBEAT_TIMEOUT = 60 * 1000; // 心跳超时时间(60秒)
+
+    // 记录每个会话的最后活跃时间
+    private static final ConcurrentHashMap<String, Long> lastActiveTime = new ConcurrentHashMap<>();
+
+    // 连接建立时触发
+    @OnOpen
+    public void onOpen(Session session) {
+        sessions.put(session.getId(), session);
+        lastActiveTime.put(session.getId(), System.currentTimeMillis());
+        log.info("客户端连接: {}", session.getId());
+    }
+
+    // 收到客户端消息时触发
+    @OnMessage
+    public void onMessage(String message, Session session) {
+        message = message.replaceAll("\\s+", "");
+        if ("HEARTBEAT".equals(message)) {
+            lastActiveTime.put(session.getId(), System.currentTimeMillis());
+            sendMessage("PONG"); // 返回心跳响应[6,8](@ref)
+        }
+    }
+
+    // 连接关闭时触发
+    @OnClose
+    public void onClose(Session session) {
+        sessions.remove(session.getId());
+        lastActiveTime.remove(session.getId());
+        log.info("客户端断开: {}", session.getId());
+    }
+
+    // 发送消息给客户端
+    public static void sendMessage(String message) {
+        sessions.forEach((id, session) -> {
+            JsonObject jsonObject = new JsonObject();
+            jsonObject.addProperty("msg", message);
+            try {
+                session.getBasicRemote().sendText(jsonObject.toString());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    // 向所有客户端发送刷新指令(静态方法供Service调用)
+    @PostConstruct
+    public void initHeartbeatCheck() {
+        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+        scheduler.scheduleAtFixedRate(() -> {
+            long currentTime = System.currentTimeMillis();
+            lastActiveTime.forEach((sessionId, lastTime) -> {
+                if (currentTime - lastTime > HEARTBEAT_TIMEOUT) {
+                    Session session = sessions.get(sessionId);
+                    if (session != null) {
+                        try {
+                            session.close(); // 关闭超时连接[6,8](@ref)
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                }
+            });
+        }, 0, 30, TimeUnit.SECONDS); // 每30秒检查一次
+    }
+}

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

@@ -75,6 +75,9 @@ public interface JfcloudColdChainConstants {
 
     String REDIS_ALARM_HISTORY_KEY_PREFIX = "ALARM_HISTORY";
 
+    // 数据预警推送次数
+    String REDIS_PUSH_COUNT_KEY_PREFIX = "ALARM_PUSH_COUNT";
+
     /**
      * 每 10 分钟定期执行设备缓存更新
      */

+ 5 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/MonitorDataProcessor.java

@@ -62,6 +62,11 @@ public interface MonitorDataProcessor<T> {
      */
     String RS_WS_WIFI5_6J = "RS-WS-WIFI5-6J";
 
+    /**
+     * 氮气
+     */
+    String RS_N2_WIFI_2 = "String RS_N2_WIFI_2";
+
     Boolean processData(T data);
 
     /**

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/impl/WifiEvnSevenInOne.java

@@ -46,7 +46,7 @@ public class WifiEvnSevenInOne extends AbsRenkeMonitorDataProcessor {
                     sensorData.setCo2(floatValue(nodeData.getTem() * 10));
                     break;
                 case 7:
-                    sensorData.setCh2o(floatValue(nodeData.getTem() / 10));
+                    sensorData.setCh2o(floatValue(nodeData.getTem() / 1000));
                     break;
                 case 8:
                     sensorData.setO3(floatValue(nodeData.getTem()));

+ 37 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/impl/WifiN2Processor.java

@@ -0,0 +1,37 @@
+package vip.xiaonuo.coldchain.core.service.dataprocess.dataclean.impl;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
+import rk.netDevice.sdk.p2.NodeData;
+import rk.netDevice.sdk.p2.RealTimeData;
+import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
+import vip.xiaonuo.coldchain.core.service.dataprocess.dataclean.MonitorDataProcessor;
+import vip.xiaonuo.coldchain.modular.monitordevice.enums.DeviceModelEnum;
+
+import java.util.List;
+
+@Slf4j
+@Component(MonitorDataProcessor.RS_N2_WIFI_2)
+public class WifiN2Processor extends AbsRenkeMonitorDataProcessor {
+    public WifiN2Processor(ApplicationEventPublisher applicationEventPublisher) {
+        super(applicationEventPublisher);
+    }
+
+    @Override
+    List<SensorData> transRealTimeData2SensorDatas(RealTimeData data) {
+        List<NodeData> nodeList = data.getNodeList();
+        SensorData sensorData = defaultSensorData(data);
+        NodeData nodeData = nodeList.get(0);
+        sensorData.setN2(floatValue(nodeData.getHum()));
+        sensorData.setRoads(1);
+        log.info("sensorData: {}", sensorData);
+        return Lists.newArrayList(sensorData);
+    }
+
+    @Override
+    DeviceModelEnum deviceModel() {
+        return DeviceModelEnum.RS_N2_WIFI_2;
+    }
+}

+ 1 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/enums/DeviceModelEnum.java

@@ -25,6 +25,7 @@ public enum DeviceModelEnum {
     RS_WD_WIFI5_C3_Y4_5WL(MonitorDataProcessor.RS_WD_WIFI5_C3_Y4_5WL, "WIFI多探头单温度变迭记录仪带电池(4路)"),
     RS_MG111_WIFI_1(MonitorDataProcessor.RS_MG111_WIFI_1, "WIFI环境七合一变送记录仪"),
     RS_WS_WIFI5_6J(MonitorDataProcessor.RS_WS_WIFI5_6J, "WIFI单温度变iete记录仪"),
+    RS_N2_WIFI_2(MonitorDataProcessor.RS_N2_WIFI_2, "氮气变送器"),
     DEFAULT(MonitorDataProcessor.RS_WS_DEFAULT, "默认温湿度处理");
 
 

+ 3 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/entity/MonitorTargetRegion.java

@@ -248,6 +248,9 @@ public class MonitorTargetRegion extends OrgEntity {
     private Float pm10Up;
     private Float pm10Down;
 
+    private Integer offlineMaxPush;
+    private Integer alarmMaxPush;
+
     public void setTemperatureUp(Float temperatureUp) {
         this.temperatureUp = temperatureUp;
     }

+ 3 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/param/MonitorTargetRegionAddParam.java

@@ -210,4 +210,7 @@ public class MonitorTargetRegionAddParam {
     private Float pm25Down;
     private Float pm10Up;
     private Float pm10Down;
+
+    private Integer offlineMaxPush;
+    private Integer alarmMaxPush;
 }

+ 3 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/param/MonitorTargetRegionEditParam.java

@@ -212,4 +212,7 @@ public class MonitorTargetRegionEditParam {
     private Float pm25Down;
     private Float pm10Up;
     private Float pm10Down;
+
+    private Integer offlineMaxPush;
+    private Integer alarmMaxPush;
 }

+ 3 - 1
snowy-web-app/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java

@@ -150,7 +150,9 @@ public class GlobalConfigure implements WebMvcConfigurer {
             /* 全值匹配组织名 */
             "/coldchain/alarmuser/getOrgByName",
             "/coldchain/hmi/roomMonitor",
-            "/coldchain/hmi/getRoomInfoById"
+            "/coldchain/hmi/getRoomInfoById",
+
+            "/ws/**"
     };
 
     /**