@ -6,15 +6,15 @@ import cn.iocoder.yudao.module.hand.service.TdengineService;
import cn.iocoder.yudao.module.hand.vo.AlarmDispatchEvent ;
import cn.iocoder.yudao.module.hand.vo.AlarmMessageLog ;
import cn.iocoder.yudao.module.mqtt.mqtt.Client ;
import com.fasterxml.jackson.core.JsonProcessingException ;
import com.google.common.util.concurrent.RateLimiter ;
import jakarta.annotation.Resource ;
import lombok.extern.slf4j.Slf4j ;
import org.apache.commons.lang3.StringUtils ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.stereotype.Component ;
import java.sql.Timestamp ;
import java.util.ArrayList ;
import java.util.Date ;
import java.util.List ;
import java.util.Map ;
@ -22,7 +22,12 @@ import java.util.Map;
@Component
public class HandAlarmMessageProcess {
private final RateLimiter mqttRateLimiter = RateLimiter . create ( 3000 . 0 ) ;
/ * *
* 【 问题6 FIX 】 限流速率从配置文件读取 , 不再硬编码
* 配置项示例 : mqtt . alarm . rate - limit = 3000 . 0
* /
private final RateLimiter mqttRateLimiter ;
@Resource
private Client mqttClient ;
@Resource
@ -30,6 +35,11 @@ public class HandAlarmMessageProcess {
@Resource
private TdengineService tdengineService ;
public HandAlarmMessageProcess (
@Value ( "${mqtt.alarm.rate-limit:3000.0}" ) double rateLimit ) {
this . mqttRateLimiter = RateLimiter . create ( rateLimit ) ;
}
public void processSingle ( String jsonValue ) {
// 1. 解析 Kafka 消息
AlarmDispatchEvent event = JsonUtils . parseObject ( jsonValue , AlarmDispatchEvent . class ) ;
@ -39,7 +49,7 @@ public class HandAlarmMessageProcess {
}
try {
// 2. 【查库】 根据部门/租户/源设备SN,查询需要推送的目标设备列表
// 2. 根据部门/租户/源设备SN,查询需要推送的目标设备列表
List < String > targetSns = handDetectorService . getSnListByDept (
event . getDeptId ( ) ,
event . getTenantId ( ) ,
@ -50,23 +60,28 @@ public class HandAlarmMessageProcess {
log . info ( "[报警推送] 无需推送,目标列表为空. SourceSN: {}" , event . getSourceSn ( ) ) ;
return ;
}
// 3. 执行推送逻辑
this . publishAlarmToMqtt ( targetSns , event . getMsgContent ( ) ) ;
// 记录报警消息日志
AlarmMessageLog alarmMessageLog = new AlarmMessageLog ( ) ;
alarmMessageLog . setDetectorId ( event . getId ( ) ) ;
alarmMessageLog . setHolderName ( event . getUserName ( ) ) ;
alarmMessageLog . setSn ( event . getSourceSn ( ) ) ;
alarmMessageLog . setDeptId ( event . getDeptId ( ) ) ;
alarmMessageLog . setTenantId ( event . getTenantId ( ) ) ;
alarmMessageLog . setMessage ( event . getMsgContent ( ) ) ;
alarmMessageLog . setRemark ( "系统自动触发报警推送" ) ;
alarmMessageLog . setPushSnList ( StringUtils . join ( targetSns , "," ) ) ;
ArrayList < AlarmMessageLog > objects = new ArrayList < > ( ) ;
objects . add ( alarmMessageLog ) ;
tdengineService . createAlarmRecord ( objects ) ;
// 3. 执行推送,收集实际成功推送的 SN 列表
// 【问题3 FIX】由 publishAlarmToMqtt 返回实际推送成功的列表,日志只记录成功项
List < String > succeededSns = publishAlarmToMqtt ( targetSns , event . getMsgContent ( ) ) ;
// 4. 记录报警消息日志(仅记录实际推送成功的 SN)
if ( ! succeededSns . isEmpty ( ) ) {
AlarmMessageLog alarmMessageLog = new AlarmMessageLog ( ) ;
alarmMessageLog . setDetectorId ( event . getId ( ) ) ;
alarmMessageLog . setHolderName ( event . getUserName ( ) ) ;
alarmMessageLog . setSn ( event . getSourceSn ( ) ) ;
alarmMessageLog . setDeptId ( event . getDeptId ( ) ) ;
alarmMessageLog . setTenantId ( event . getTenantId ( ) ) ;
alarmMessageLog . setMessage ( event . getMsgContent ( ) ) ;
alarmMessageLog . setRemark ( "系统自动触发报警推送" ) ;
alarmMessageLog . setPushSnList ( StringUtils . join ( succeededSns , "," ) ) ;
alarmMessageLog . setTs ( new Timestamp ( System . currentTimeMillis ( ) ) ) ;
tdengineService . createAlarmRecord ( List . of ( alarmMessageLog ) ) ;
}
} catch ( Exception e ) {
log . error ( "[报警推送] 处理异常 SourceSN: {}" , event . getSourceSn ( ) , e ) ;
@ -74,30 +89,35 @@ public class HandAlarmMessageProcess {
}
/ * *
* 执行 MQTT 推送 ( 包含限流 )
* 执行 MQTT 推送 ( 含限流 )
*
* /
private void publishAlarmToMqtt ( List < String > targetSns , String message ) {
if ( message = = null ) {
return ;
private List < String > publishAlarmToMqtt ( List < String > targetSns , String message ) {
List < String > succeededSns = new ArrayList < > ( ) ;
// 【问题5 FIX】同时检查 null 和 blank,避免推送空消息给设备
if ( StringUtils . isBlank ( message ) ) {
log . warn ( "[MQTT推送] 消息内容为空,跳过推送" ) ;
return succeededSns ;
}
// 构造 MQTT 消息体
Map < String , String > payload = Map . of ( "message" , message ) ;
String jsonPayload = JsonUtils . toJsonString ( payload ) ;
for ( String sn : targetSns ) {
if ( StringUtils . isBlank ( sn ) ) continue ;
mqttRateLimiter . acquire ( ) ;
try {
// 【BUG-2 FIX】JSON 构造移至 try 块内,确保异常不会绕过限流语义
String topic = sn + "/zds_down" ;
// 发送
String jsonPayload = JsonUtils . toJsonString ( Map . of ( "message" , message ) ) ;
mqttClient . publish ( topic , jsonPayload ) ;
succeededSns . add ( sn ) ;
} catch ( Exception e ) {
// 5. 【隔离】单个设备推送失败,不要影响列表里的下一个 设备
// 单个设备推送失败,隔离异常不影响后续 设备
log . error ( "[MQTT推送] 单个发送失败,SN: {}" , sn , e ) ;
}
}
return succeededSns ;
}
}