Browse Source

优化设备离线通知,改为离线超过5分钟且未恢复则提醒

skyline 2 years ago
parent
commit
ec8eee1e7a

+ 2 - 0
admin/src/main/java/com/kym/admin/AdminApplication.java

@@ -5,11 +5,13 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.FilterType;
+import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 
 @Controller
+@EnableScheduling
 @SpringBootApplication()
 @ComponentScan(value = {"com.kym"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE))
 @MapperScan(basePackages = {"com.kym.mapper"})

+ 64 - 0
admin/src/main/java/com/kym/admin/jobs/OfflineNotify.java

@@ -0,0 +1,64 @@
+package com.kym.admin.jobs;
+
+import cn.hutool.extra.mail.MailUtil;
+import com.kym.common.utils.CommUtil;
+import com.kym.entity.common.RedisKeys;
+import com.kym.service.utils.KymCache;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author skyline
+ * @description 设备离线通知
+ * @date 2023-09-30 16:15
+ */
+@Component
+@Slf4j
+public class OfflineNotify {
+
+    private final RedisTemplate redisTemplate;
+    private final KymCache kymCache;
+
+    @Value("${kym.notify-email}")
+    private String notifyEmail;
+
+    public OfflineNotify(StringRedisTemplate redisTemplate, @Lazy KymCache kymCache) {
+        this.redisTemplate = redisTemplate;
+        this.kymCache = kymCache;
+    }
+
+    // 每5min执行一次
+    @Scheduled(cron = "0 0/5 * * * ? ")
+    public void execute() {
+        // 查询redis离线设备消息队列,存在且超过5min的消息则发送邮件提醒
+        var offlineList = redisTemplate.opsForZSet().rangeByScore(RedisKeys.OFFLINE, System.currentTimeMillis() + 5 * 60 * 1000, System.currentTimeMillis() + 10 * 60 * 1000);
+        if (!CommUtil.isEmptyOrNull(offlineList)) {
+            /**
+             * 格式:
+             * 【设备离线】
+             * 【阳光科创中心离】:001001,001002,001003
+             * 【阳光粤海家园】:002001,002002,002003
+             */
+            var temp = "【%s】:%s\n";
+            var map = new ArrayList<String>(offlineList).stream().collect(Collectors.toMap(kymCache::getShortId, kymCache::getStationNameByConnectorId));
+            // 以站点名称分组
+            var list = map.entrySet().stream().collect(Collectors.groupingBy(Map.Entry::getValue));
+            StringBuilder sb = new StringBuilder();
+            list.forEach((k, v) -> sb.append(temp.formatted(k, StringUtils.join(v.stream().map(Map.Entry::getKey).toArray(), ","))));
+            log.warn(sb.toString());
+            // 格式化信息,发送邮件
+            MailUtil.send(notifyEmail, "【设备离线通知】", sb.toString(), false);
+        }
+
+    }
+}

+ 12 - 0
admin/src/main/resources/mail.setting

@@ -0,0 +1,12 @@
+# 邮件服务器的SMTP地址,可选,默认为smtp.<发件人邮箱后缀>
+host = smtp.exmail.qq.com
+# 邮件服务器的SMTP端口,可选,默认25,企业邮箱中配置的为465
+port = 465
+# 发件人(必须正确,否则发送失败)
+from = system@kuaiyuman.cn
+# 用户名,默认为发件人邮箱前缀,如不行请回到邮箱登陆页查看登录时使用的用户名
+user = system@kuaiyuman.cn
+# 密码(注意,某些邮箱需要为SMTP服务单独设置授权码,详情查看相关帮助)
+pass = jRb8hA48ynfFFjzb
+#使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+starttlsEnable = true

+ 2 - 1
entity/src/main/java/com/kym/entity/common/RedisKeys.java

@@ -7,5 +7,6 @@ package com.kym.entity.common;
  */
 public interface RedisKeys {
     String EN_PLUS_TOKEN = "EN_PLUS_TOKEN";
-    String OFFLINE = "OFFLINE";
+    String OFFLINE = "OFFLINE:";
+    String OFFLINE_EXPIRED = "OFFLINE_EXPIRED:%s";
 }

+ 17 - 15
service/src/main/java/com/kym/service/enplus/impl/EnNotifyServiceImpl.java

@@ -26,7 +26,6 @@ import org.springframework.transaction.annotation.Transactional;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.concurrent.TimeUnit;
 
 /**
  * @author skyline
@@ -89,21 +88,24 @@ public class EnNotifyServiceImpl implements EnNotifyService {
                     .setType(2)
                     .setOfflineStatus(connectorStatusInfo.getStatus());
             monitorLogService.save(monitorLog);
-            redisTemplate.opsForValue().set(RedisKeys.OFFLINE.concat(connectorStatusInfo.getConnectorId()), "", 1, TimeUnit.DAYS);
-            // TODO: 2023-09-14 通知后面改队列处理,统计站点离线总数量 
-            MailUtil.send(notifyEmail, "【设备离线通知】", "站点:%s,设备%s离线"
-                    .formatted(kymCache.getStationName(monitorLog.getStationId()), kymCache.getShortId(monitorLog.getSn())), false);
+
+            // 离线设备放入队列,5分钟之后如果还未恢复则放入长时间离线设备集合中并发送提醒,上线后发送提醒
+            redisTemplate.opsForZSet().add(RedisKeys.OFFLINE, connectorStatusInfo.getConnectorId(), System.currentTimeMillis() + 5 * 60 * 1000);
+
+//            MailUtil.send(notifyEmail, "【设备离线通知】", "站点:%s,设备%s离线"
+//                    .formatted(kymCache.getStationName(monitorLog.getStationId()), kymCache.getShortId(monitorLog.getSn())), false);
         } else {
-            // 查询redis是否有记录,则是之前离线的机器上线了,有就删除并更新数据库恢复时间
-            var exist = redisTemplate.hasKey(RedisKeys.OFFLINE.concat(connectorStatusInfo.getConnectorId()));
-            if (Boolean.TRUE.equals(exist)) {
-                monitorLogService.lambdaUpdate()
-                        .eq(MonitorLog::getSn, connectorStatusInfo.getConnectorId())
-                        .eq(MonitorLog::getIsRecover, MonitorLog.IS_RECOVER_未恢复) // 未恢复的记录
-                        .set(MonitorLog::getRecoverTime, LocalDateTime.now())
-                        .set(MonitorLog::getIsRecover, MonitorLog.IS_RECOVER_已恢复) // 设置为已恢复
-                        .update();
-                redisTemplate.delete(RedisKeys.OFFLINE.concat(connectorStatusInfo.getConnectorId()));
+            // 先删除离线设备队列的记录,再删除离线超时队列中的记录
+            redisTemplate.opsForZSet().remove(RedisKeys.OFFLINE, connectorStatusInfo.getConnectorId());
+            var exist = redisTemplate.opsForSet().remove(RedisKeys.OFFLINE_EXPIRED.formatted(connectorStatusInfo.getConnectorId()));
+            // 更新设备监控表
+            monitorLogService.lambdaUpdate()
+                    .eq(MonitorLog::getSn, connectorStatusInfo.getConnectorId())
+                    .eq(MonitorLog::getIsRecover, MonitorLog.IS_RECOVER_未恢复) // 未恢复的记录
+                    .set(MonitorLog::getRecoverTime, LocalDateTime.now())
+                    .set(MonitorLog::getIsRecover, MonitorLog.IS_RECOVER_已恢复) // 设置为已恢复
+                    .update();
+            if (exist != null && exist > 0) {
                 MailUtil.send(notifyEmail, "【设备上线通知】", "站点:%s,设备%s恢复上线"
                         .formatted(kymCache.getStationNameByConnectorId(connectorStatusInfo.getConnectorId()), kymCache.getShortId(connectorStatusInfo.getConnectorId())), false);
             }

+ 1 - 0
service/src/main/java/com/kym/service/utils/KymCache.java

@@ -88,6 +88,7 @@ public class KymCache {
     }
 
     public String getStationNameByConnectorId(String connectorId) {
+        connectorId = getConnectorId(connectorId);
         var stationId = CONNECTOR_STATION_MAPPING.get(connectorId);
         return getStationName(stationId);
     }