feat(mqtt): 添加报警检查服务实现多阶段报警处理

- 实现 AlarmCheckService 提供延迟队列消费者功能
- 添加 AlarmDelayProvider 接口定义延迟检查任务
- 集成 AlarmStageConfig 支持租户配置报警阶段延迟时间
- 重构 AliyunVoiceUtil 返回完整响应对象而非字符串
- 在 AppDeviceController 中新增 AlarmList 接口查询设备告警列表
- 扩展设备相关控制器支持数据来源枚举参数传递
- 新增 Xinghan 指令控制器提供 HBY018A 设备专用接口
- 定义 DataSourceEnum 枚举区分 APP 和 Web 数据来源
- 扩展 Device 实体类增加紧急联系人和通知配置字段
- 添加 DeviceAlarm 实体类告警状态和等级属性
- 新增 DeviceContactPhoneBo 处理设备联系人信息
- 优化设备操作记录日志支持数据来源标识
- 实现设备自定义语音短信消息编辑功能
- 添加设备通知开关和紧急联系人设置接口
This commit is contained in:
2026-05-26 15:38:18 +08:00
parent 7fcbb81317
commit c291e47ae8
33 changed files with 2116 additions and 72 deletions

View File

@ -0,0 +1,110 @@
package com.fuyuanshen.global.queue;
import com.fuyuanshen.global.Provider.RedissonAlarmDelayProvider;
import com.fuyuanshen.global.mqtt.service.AlarmCheckService;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RedissonClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@Slf4j
@RequiredArgsConstructor
public class RedissonAlarmConsumer implements CommandLineRunner {
private final RedissonClient redissonClient;
private final AlarmCheckService alarmCheckService;
private volatile boolean running = true;
private Thread consumerThread;
private ExecutorService bizExecutor;
private static final int BIZ_THREADS = 4; // 业务处理线程数
private static final int BIZ_QUEUE_CAPACITY = 200; // 有界队列容量
@Override
public void run(String... args) {
// 初始化业务处理线程池(有界队列 + 调用者运行拒绝策略,避免 OOM
bizExecutor = Executors.newFixedThreadPool(
BIZ_THREADS,
new ThreadFactory() {
private final AtomicInteger counter = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "Alarm-Biz-" + counter.getAndIncrement());
t.setDaemon(false);
return t;
}
}
);
// 启动消费线程
consumerThread = new Thread(() -> {
RBlockingQueue<Long> blockingQueue = redissonClient.getBlockingQueue(RedissonAlarmDelayProvider.QUEUE_NAME);
log.info("Redisson 延迟报警监听线程已启动...");
while (running && !Thread.currentThread().isInterrupted()) {
try {
Long alarmId = blockingQueue.poll(1, TimeUnit.SECONDS); // 改用带超时的 poll可响应中断
if (alarmId != null) {
// 提交到业务线程池异步处理,避免阻塞队列拉取
bizExecutor.submit(() -> processAlarm(alarmId));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.info("Redisson 消费线程被中断,退出循环");
break;
} catch (Exception e) {
log.error("Redisson 延迟队列消费异常", e);
// 发生非中断异常时短暂休眠,避免日志风暴
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException interrupted) {
Thread.currentThread().interrupt();
break;
}
}
}
log.info("Redisson 延迟报警消费线程结束");
}, "Alarm-Consumer-Thread");
consumerThread.setDaemon(false);
consumerThread.start();
}
private void processAlarm(Long alarmId) {
try {
alarmCheckService.executeCheck(alarmId);
} catch (Exception e) {
log.error("处理报警 ID [{}] 时发生异常", alarmId, e);
// 可在此补充重试或死信逻辑
}
}
@PreDestroy
public void destroy() {
log.info("开始关闭 Redisson 报警消费者...");
running = false;
if (consumerThread != null) {
consumerThread.interrupt(); // 中断阻塞在 poll 上的线程
}
if (bizExecutor != null) {
bizExecutor.shutdown();
try {
if (!bizExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
bizExecutor.shutdownNow();
}
} catch (InterruptedException e) {
bizExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
log.info("Redisson 报警消费者已关闭");
}
}