|
|
@@ -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;
|
|
|
+ }
|
|
|
}
|