From 975eb308a3c83215e6112a9823c12c2dddfc28a8 Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Wed, 18 Mar 2026 22:36:13 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BA=94=E8=AF=A5=E6=AF=8F=E6=AC=A1?= =?UTF-8?q?=E7=94=B5=E8=AF=9D=E6=8A=A5=E8=AD=A6=EF=BC=8C=E5=B0=B1=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=9D=A1=E6=B6=88=E6=81=AF=EF=BC=8C=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E6=AF=8F=E6=AC=A1=E8=B6=85=E8=BF=87=E9=98=88=E5=80=BC=E5=B0=B1?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=B8=80=E6=9D=A1=E6=8A=A5=E8=AD=A6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fishery/mapper/MessageWarnMapper.java | 20 ++++++- .../intc/iot/handler/DeviceDataHandler.java | 53 +++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/mapper/MessageWarnMapper.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/mapper/MessageWarnMapper.java index 37ae332..768382c 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/mapper/MessageWarnMapper.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/mapper/MessageWarnMapper.java @@ -1,8 +1,12 @@ -package com.intc.fishery.mapper; + package com.intc.fishery.mapper; import com.intc.common.mybatis.core.mapper.BaseMapperPlusJoin; import com.intc.fishery.domain.MessageWarn; import com.intc.fishery.domain.vo.MessageWarnVo; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.Date; /** * 设备告警记录Mapper接口 @@ -12,4 +16,18 @@ import com.intc.fishery.domain.vo.MessageWarnVo; */ public interface MessageWarnMapper extends BaseMapperPlusJoin { + /** + * 查询指定设备、告警标题、时间范围内的告警消息数量 + * 用于对未开启电话通知的告警类型做时间窗口抑制,避免每次超阈值都重复写入记录 + * + * @param deviceId 设备ID + * @param title 告警标题 + * @param afterTime 起始时间 + * @return 满足条件的告警消息数量 + */ + @Select("SELECT COUNT(1) FROM aqu_message_warn WHERE device_id = #{deviceId} AND title = #{title} AND create_time >= #{afterTime}") + long countByDeviceAndTitleAfter(@Param("deviceId") Long deviceId, + @Param("title") String title, + @Param("afterTime") Date afterTime); + } diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java index a935611..0eb979c 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java @@ -441,16 +441,55 @@ public class DeviceDataHandler { } } - // 如果有告警,保存全部报警消息记录 + // 如果有告警,处理报警消息记录 if (hasAlarm) { log.warn("[告警] {} - {}", deviceName, alarmMessage); - // 批量保存报警消息(无论是否开启电话通知,告警记录始终生成) + // 计算抑制时间窗口(与电话通知保持一致) + LocalDateTime suppressTime = LocalDateTime.now().minusHours(aliyunIotProperties.getCallSuccessSuppressHours()); + java.util.Date suppressDate = java.util.Date.from(suppressTime.atZone(java.time.ZoneId.systemDefault()).toInstant()); + int maxBatchMinutesForCheck = CALL_NOTICE_RETRY_COUNT * CALL_NOTICE_MINUTE_INTERVAL * 2; + LocalDateTime activeBatchStartForCheck = LocalDateTime.now().minusMinutes(maxBatchMinutesForCheck); + LocalDateTime periodStartForCheck = getCurrentPeriodStart(); + + // 对未开启电话通知的告警类型,做时间窗口抑制后再插入消息记录 + // 对开启了电话通知的类型,MessageWarn 由 triggerAlarmNotification 在通过抑制判断后统一插入 for (MessageWarn warn : warnList) { - messageWarnMapper.insert(warn); + // 跳过已加入电话通知列表的告警(由 triggerAlarmNotification 负责插入) + if (callWarnList.contains(warn)) { + continue; + } + // 判断该类型告警是否开启了免打扰 + boolean noDisturbForWarn = false; + if (ALARM_TYPE_DISSOLVED_OXYGEN.equals(warn.getTitle()) && + device.getOxyWarnCallNoDis() != null && device.getOxyWarnCallNoDis() == 1) { + noDisturbForWarn = true; + } else if ((ALARM_TYPE_TEMPERATURE_HIGH.equals(warn.getTitle()) || ALARM_TYPE_TEMPERATURE_LOW.equals(warn.getTitle())) && + device.getTempWarnCallNoDis() != null && device.getTempWarnCallNoDis() == 1) { + noDisturbForWarn = true; + } else if (ALARM_TYPE_BATTERY.equals(warn.getTitle()) && + device.getBatteryWarnCallNoDis() != null && device.getBatteryWarnCallNoDis() == 1) { + noDisturbForWarn = true; + } + // 按抑制规则判断是否应跳过 + boolean shouldSkip; + if (noDisturbForWarn) { + java.util.Date periodStartDate = java.util.Date.from(periodStartForCheck.atZone(java.time.ZoneId.systemDefault()).toInstant()); + shouldSkip = messageWarnMapper.countByDeviceAndTitleAfter(deviceId, warn.getTitle(), periodStartDate) > 0; + } else { + java.util.Date activeBatchStartDate = java.util.Date.from(activeBatchStartForCheck.atZone(java.time.ZoneId.systemDefault()).toInstant()); + shouldSkip = messageWarnMapper.countByDeviceAndTitleAfter(deviceId, warn.getTitle(), activeBatchStartDate) > 0 + || messageWarnMapper.countByDeviceAndTitleAfter(deviceId, warn.getTitle(), suppressDate) > 0; + } + if (!shouldSkip) { + messageWarnMapper.insert(warn); + log.debug("[告警] {} title={} 新增消息记录", deviceName, warn.getTitle()); + } else { + log.debug("[告警] {} title={} 抑制期内已有记录,跳过消息写入", deviceName, warn.getTitle()); + } } - // 仅对开启了电话通知开关的告警触发电话通知 + // 仅对开启了电话通知开关的告警触发电话通知(MessageWarn 由通知方法内部插入) if (!callWarnList.isEmpty()) { triggerAlarmNotification(device, alarmMessage.toString(), sensorData, callWarnList); } @@ -565,6 +604,12 @@ public class DeviceDataHandler { // 告警内容(取该类型第一条) String typeAlarmMessage = typeWarnList.get(0).getMessage(); + // 通过抑制判断,先插入 MessageWarn 记录(每次电话报警才新增一条消息) + for (MessageWarn warn : typeWarnList) { + messageWarnMapper.insert(warn); + } + log.debug("[告警] {} title={} 通过抑制,已插入 {} 条告警消息记录", deviceName, title, typeWarnList.size()); + // 为每个手机号 × 重试次数,创建通知记录,callTime 按间隔错开 // 参考 C#: CallNoticeRetryCount=3, CallNoticeMinuteInterval=3 // 立即尝试发送第一个号码;若失败则后续通知记录会在定时任务中按时间发送