Bläddra i källkod

fix: 故障通知增加延迟机制,过滤短暂网络波动

故障发生后延迟 3 分钟再发送通知,延迟窗口内自动恢复的不再发送故障/恢复通知,
仅记录日志。有效减少正常网络波动产生的大量无效通知消息。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline 4 dagar sedan
förälder
incheckning
d90982f6a1

+ 4 - 0
car-wash-admin/src/main/resources/application.yml

@@ -101,6 +101,10 @@ oss:
   bucket: kym-static
   prefix: http://static.kuaiyuman.cn  # 前端访问拼接为 prefix+uuid,例 http://static.kuaiyuman.cn /b2ee8dbd259d4f44a63a8e36c8121f89.vue
 
+# 故障通知
+fault:
+  notify-delay: 3  # 故障通知延迟(分钟),故障持续超过此时间才发送通知,过滤短暂网络波动
+
 password:
  privateKey: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAM1zhmu+TcTSZSQ7w0541g45o8ji4ugEC4O31GnS6tfvLTCru+9DPK2/DNdd4z1enz2PuDhktHuoEsEKPdA08fRJSMTXLL0pUEp2OQ+t7tZP6mVLvizasnP+HKAqIndXFr5nXm/okQfL/f/6L2Bben9sItoxC28Z6Y28NfAJPAg1AgMBAAECgYAKOdvQ9RHt4AMEwKzB9SXCY4AReamNntXr4nSCJ+tkgBUhvQqHqDMW+tFqztOGtHT8nXCv7eNF3GHClf3ppRj91utk8zAwZPVAVlRoNcWs60nyKRUO2uhwAV8AE+9UKDoVui7L7UaMcIkssKqQbFGIRXUjjSoPJu0yoHCdp5/3QQJBAOZTbfgmFXVgRSH4IMXJ3aZOqz+Wy3EmvNatYz8NYLBFgLJTWWXtR7URw82R7jL6F9ettfCityhAmXEZnZsEsokCQQDkWkYwFZlUYJ2ctdNEmipXw8tjpCrzQRaZnydXbjviwbSpOvOo5nrxSG5BtL9QDwiy9DL7YLBVJPykAkJm3m1NAkBn2SQTJ7CzLIXfLA4yv7LFYmEKGcZ+rRWlwaWm7zQyJhRB0xzSvSqAtJLRJEP/Dg4j+7m11te4OXA1s3QBShvpAkEAq50gpKCG5D/cE9seVK9b5SuTnmXRlZE0D+3pXi7NOOSFBq30UtosSUs6+YyCPwOdcQhPjFYlD0hFymicSL0e/QJBAOMQaABh/6BcVimWP284x/WxBQ83zzVhcl7fUyqcFvfAw1JeMmRxvm2CWYKQ9MIhQ/9ptFotRCSwMAdJTZceWys=
  publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNc4Zrvk3E0mUkO8NOeNYOOaPI4uLoBAuDt9Rp0urX7y0wq7vvQzytvwzXXeM9Xp89j7g4ZLR7qBLBCj3QNPH0SUjE1yy9KVBKdjkPre7WT+plS74s2rJz/hygKiJ3Vxa+Z15v6JEHy/3/+i9gW3p/bCLaMQtvGemNvDXwCTwINQIDAQAB

+ 25 - 8
car-wash-service/src/main/java/com/kym/service/impl/FaultNotificationServiceImpl.java

@@ -12,6 +12,7 @@ import com.kym.service.FaultRecordService;
 import com.kym.service.FaultSubscriberService;
 import com.kym.service.MpMsgTemplateService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -33,6 +34,9 @@ public class FaultNotificationServiceImpl implements FaultNotificationService {
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
     private static final Duration ANTI_DUP_TTL = Duration.ofHours(2);
 
+    @Value("${fault.notify-delay:3}")
+    private int faultNotifyDelayMinutes;
+
     private final FaultRecordService faultRecordService;
     private final FaultSubscriberService faultSubscriberService;
     private final MpMsgTemplateService mpMsgTemplateService;
@@ -81,8 +85,18 @@ public class FaultNotificationServiceImpl implements FaultNotificationService {
                 .eq(FaultRecord::getIsRecovered, FaultRecord.IS_RECOVERED_未恢复)
                 .list();
 
+        var now = LocalDateTime.now();
+        var delayThreshold = now.minusMinutes(faultNotifyDelayMinutes);
+
         for (var record : unnotifiedList) {
             try {
+                // 延迟未到,暂不通知
+                if (record.getFaultTime() != null && record.getFaultTime().isAfter(delayThreshold)) {
+                    log.debug("故障通知延迟未到,跳过: recordId={}, faultTime={}, delayMinutes={}",
+                            record.getId(), record.getFaultTime(), faultNotifyDelayMinutes);
+                    continue;
+                }
+
                 var redisKey = buildAntiDupKey(record.getFaultType(), record.getDeviceName(), record.getStationId());
                 if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(redisKey))) {
                     faultRecordService.markNotified(record.getId());
@@ -114,13 +128,8 @@ public class FaultNotificationServiceImpl implements FaultNotificationService {
                 .setIsRecovered(FaultRecord.IS_RECOVERED_未恢复);
         faultRecordService.save(record);
 
-        var redisKey = buildAntiDupKey(faultType.getCode(), deviceName, stationId);
-        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(redisKey))) {
-            log.debug("防重复key存在,跳过通知: {}", redisKey);
-            return;
-        }
-
-        doNotify(record);
+        log.info("故障已记录,延迟{}分钟后发送通知: stationId={}, deviceName={}, faultType={}",
+                faultNotifyDelayMinutes, stationId, deviceName, faultType.getCode());
     }
 
     private void handleRecovery(String stationId, String deviceName, FaultType faultType) {
@@ -130,9 +139,17 @@ public class FaultNotificationServiceImpl implements FaultNotificationService {
             return;
         }
 
+        boolean wasNotified = record.getIsNotified() != null && record.getIsNotified() == FaultRecord.IS_NOTIFIED_已通知;
+
         faultRecordService.markRecovered(record.getId());
         stringRedisTemplate.delete(buildAntiDupKey(faultType.getCode(), deviceName, stationId));
-        sendRecoveryNotification(record);
+
+        if (wasNotified) {
+            sendRecoveryNotification(record);
+        } else {
+            log.info("故障在通知延迟内自动恢复,跳过通知: stationId={}, deviceName={}, faultType={}",
+                    stationId, deviceName, faultType.getCode());
+        }
     }
 
     private void doNotify(FaultRecord record) {