fix: 电话通知判断逻辑问题修复。

This commit is contained in:
tianyongbao
2026-03-10 00:02:37 +08:00
parent 1676ef7599
commit 60879f6e8b
2 changed files with 56 additions and 18 deletions

View File

@@ -506,8 +506,12 @@ public class DeviceDataHandler {
}
String pondName = warnList.isEmpty() ? deviceName : warnList.get(0).getPondName();
// 抑制时间:根据配置的小时数展开,查找 callStatus=3回执成功的记录
LocalDateTime intervalTime = LocalDateTime.now().minusHours(aliyunIotProperties.getCallSuccessSuppressHours());
// 抑制时间:查找 callStatus=3回执成功的记录
LocalDateTime suppressTime = LocalDateTime.now().minusHours(aliyunIotProperties.getCallSuccessSuppressHours());
// 活跍批次查找窗口:一个完整通知批次的最大时长 = 重试次数 * 手机号数,每次间隔 CALL_NOTICE_MINUTE_INTERVAL 分钟
// 这里简化用 2 倍的全周期时间保证导式内等待记录能被查到
int maxBatchMinutes = CALL_NOTICE_RETRY_COUNT * CALL_NOTICE_MINUTE_INTERVAL * 2;
LocalDateTime activeBatchStart = LocalDateTime.now().minusMinutes(maxBatchMinutes);
LocalDateTime periodStart = getCurrentPeriodStart();
// 按 title 分组,每种类型独立判断间隔并发送通知
@@ -533,18 +537,25 @@ public class DeviceDataHandler {
noDisturbEnabled = true;
}
// 按设备+title检查是否已通知
// 按设备+title检查是否已通知(双重抑制)
long recentCount;
if (noDisturbEnabled) {
// 免打扰开启:当前时段内已通知过则跳过
// 免打扰开启:当前时段内已有任意状态的通知就跳过
recentCount = warnCallNoticeMapper.countByDeviceAndTitleInPeriod(deviceId, title, periodStart);
if (recentCount > 0) {
log.debug("[告警] {} title={} 免打扰:当前时段内已通知,跳过", deviceName, title);
continue;
}
} else {
// 免打扰关闭:查找指定小时内是否已有回执成功的通知
recentCount = warnCallNoticeMapper.countByDeviceAndTitleAfter(deviceId, title, intervalTime);
// 免打扰关闭:双重抑制
// 1. 先查活跍批次(未完成的通知批次),防止当前轮重复创建
long activeCount = warnCallNoticeMapper.countActiveByDeviceAndTitleAfter(deviceId, title, activeBatchStart);
if (activeCount > 0) {
log.debug("[告警] {} title={} - 当前有活跍通知批次在进行,跳过", deviceName, title);
continue;
}
// 2. 再查N小时内是否已有回执成功的记录
recentCount = warnCallNoticeMapper.countSuccessByDeviceAndTitleAfter(deviceId, title, suppressTime);
if (recentCount > 0) {
log.debug("[告警] {} title={} - {}h内已有回执成功的通知跳过", deviceName, title, aliyunIotProperties.getCallSuccessSuppressHours());
continue;
@@ -793,11 +804,18 @@ public class DeviceDataHandler {
return;
}
// 检查告警间隔(防止频繁通知
LocalDateTime intervalTime = LocalDateTime.now().minusMinutes(aliyunIotProperties.getAlarmNotificationIntervalMinutes());
long recentCount = warnCallNoticeMapper.countByDeviceAndTitleAfter(deviceId, "设备离线", intervalTime);
// 双重抑制1) 活跃批次检查(防止当前轮重复); 2) 成功通知抑制N小时内已成功则不再打
int maxBatchMinutesOff = CALL_NOTICE_RETRY_COUNT * CALL_NOTICE_MINUTE_INTERVAL * 2;
LocalDateTime activeBatchStartOff = LocalDateTime.now().minusMinutes(maxBatchMinutesOff);
long activeCountOff = warnCallNoticeMapper.countActiveByDeviceAndTitleAfter(deviceId, "设备离线", activeBatchStartOff);
if (activeCountOff > 0) {
log.debug("[离线告警] 当前有活跃通知批次在进行,跳过: {}", deviceName);
return;
}
LocalDateTime suppressTimeOff = LocalDateTime.now().minusHours(aliyunIotProperties.getCallSuccessSuppressHours());
long recentCount = warnCallNoticeMapper.countSuccessByDeviceAndTitleAfter(deviceId, "设备离线", suppressTimeOff);
if (recentCount > 0) {
log.debug("[离线告警] {}分钟内已通知,跳过: {}", aliyunIotProperties.getAlarmNotificationIntervalMinutes(), deviceName);
log.debug("[离线告警] {}h内已有回执成功的通知,跳过: {}", aliyunIotProperties.getCallSuccessSuppressHours(), deviceName);
return;
}

View File

@@ -19,10 +19,10 @@ public interface AquWarnCallNoticeMapper extends BaseMapper<AquWarnCallNotice> {
/**
* 查询指定设备、告警标题、时间范围内「回执成功」的通知数量
* 只有 callStatus=3回执成功的记录才算真正通知到人才会触发抑制
* 只有 callStatus=3回执成功的记录才算真正通知到人用于长时间N小时抑制判断
*
* @param deviceId 设备ID
* @param title 告警标题(如"溶解氧"、"设备离线"等)
* @param title 告警标题
* @param afterTime 起始时间
* @return 满足条件的通知数量
*/
@@ -33,14 +33,34 @@ public interface AquWarnCallNoticeMapper extends BaseMapper<AquWarnCallNotice> {
"AND w.title = #{title} " +
"AND n.call_status = 3 " +
"AND n.call_time >= #{afterTime}")
long countByDeviceAndTitleAfter(@Param("deviceId") Long deviceId,
@Param("title") String title,
@Param("afterTime") LocalDateTime afterTime);
long countSuccessByDeviceAndTitleAfter(@Param("deviceId") Long deviceId,
@Param("title") String title,
@Param("afterTime") LocalDateTime afterTime);
/**
* 查询指定设备、告警标题、时范围内「回执成功」的通知数量(免打扰模式:每个时段只通知一次)
* 查询指定设备、告警标题、时范围内「活跍通知批次」的数量
* 活跍 = callStatus IN (0,1,2),即本轮通知进行中,用于防止在当前属于未处理完成的通知批次内重复创建
*
* @param deviceId 设备ID
* @param title 告警标题
* @param afterTime 起始时间
* @return 满足条件的通知数量
*/
@Select("SELECT COUNT(1) FROM aqu_call_notice n " +
"INNER JOIN aqu_map_message_warn_call_notice m ON m.call_notice_id = n.id " +
"INNER JOIN aqu_message_warn w ON w.id = m.message_warn_id " +
"WHERE n.device_id = #{deviceId} " +
"AND w.title = #{title} " +
"AND n.call_status IN (0, 1, 2) " +
"AND n.call_time >= #{afterTime}")
long countActiveByDeviceAndTitleAfter(@Param("deviceId") Long deviceId,
@Param("title") String title,
@Param("afterTime") LocalDateTime afterTime);
/**
* 查询指定设备、告警标题、时段范围内「活跍或成功」的通知数量(免打扰模式)
* 时段划分08:00-14:00、14:00-20:00、20:00-08:00
* 只有 callStatus=3回执成功的记录才算真正通知到人才会触发抑制
* 包含 callStatus IN (0,1,2,3),只要当前时段内有任意状态的通知就不再重复创建
*
* @param deviceId 设备ID
* @param title 告警标题
@@ -52,7 +72,7 @@ public interface AquWarnCallNoticeMapper extends BaseMapper<AquWarnCallNotice> {
"INNER JOIN aqu_message_warn w ON w.id = m.message_warn_id " +
"WHERE n.device_id = #{deviceId} " +
"AND w.title = #{title} " +
"AND n.call_status = 3 " +
"AND n.call_status IN (0, 1, 2, 3) " +
"AND n.call_time >= #{periodStart}")
long countByDeviceAndTitleInPeriod(@Param("deviceId") Long deviceId,
@Param("title") String title,