forked from dyf/fys-Multi-tenant
feat(mqtt): 添加报警检查服务实现多阶段报警处理
- 实现 AlarmCheckService 提供延迟队列消费者功能 - 添加 AlarmDelayProvider 接口定义延迟检查任务 - 集成 AlarmStageConfig 支持租户配置报警阶段延迟时间 - 重构 AliyunVoiceUtil 返回完整响应对象而非字符串 - 在 AppDeviceController 中新增 AlarmList 接口查询设备告警列表 - 扩展设备相关控制器支持数据来源枚举参数传递 - 新增 Xinghan 指令控制器提供 HBY018A 设备专用接口 - 定义 DataSourceEnum 枚举区分 APP 和 Web 数据来源 - 扩展 Device 实体类增加紧急联系人和通知配置字段 - 添加 DeviceAlarm 实体类告警状态和等级属性 - 新增 DeviceContactPhoneBo 处理设备联系人信息 - 优化设备操作记录日志支持数据来源标识 - 实现设备自定义语音短信消息编辑功能 - 添加设备通知开关和紧急联系人设置接口
This commit is contained in:
@ -0,0 +1,145 @@
|
||||
package com.fuyuanshen.job.integration;
|
||||
|
||||
import com.aizuda.snailjob.client.job.core.openapi.SnailJobOpenApi;
|
||||
import com.aizuda.snailjob.client.job.core.enums.AllocationAlgorithmEnum;
|
||||
import com.aizuda.snailjob.common.core.enums.JobBlockStrategyEnum;
|
||||
import com.aizuda.snailjob.client.job.core.enums.TriggerTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* SnailJob 1.8.1 业务集成客户端
|
||||
* 优化点:增强健壮性、参数校验、日志标准化
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SnailJobClient {
|
||||
|
||||
//执行超时时间
|
||||
private static final int DEFAULT_TIMEOUT = 60;
|
||||
//如果重试开启,两次重试之间间隔几秒
|
||||
private static final int RETRY_INTERVAL = 30;
|
||||
|
||||
/**
|
||||
* 创建离线自动关单任务
|
||||
* 优化:引入 Assert 校验,防止非法参数进入 OpenAPI 导致报错
|
||||
*/
|
||||
public void addRetryTask(Long alarmId, String businessNo, String executorName, int delayMinutes) {
|
||||
Assert.notNull(alarmId, "alarmId cannot be null");
|
||||
Assert.hasText(businessNo, "businessNo cannot be empty");
|
||||
|
||||
try {
|
||||
// 计算新的延迟秒数
|
||||
String triggerInterval = String.valueOf(delayMinutes * 60L);
|
||||
// 1. 计算 5 分钟后时间戳
|
||||
LocalDateTime execTime = LocalDateTime.now().plusMinutes(delayMinutes);
|
||||
long triggerTime = execTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
|
||||
SnailJobOpenApi.addClusterJob()
|
||||
// 设置任务路由策略:随机选择执行器节点
|
||||
.setRouteKey(AllocationAlgorithmEnum.RANDOM)
|
||||
// 设置任务名称(使用业务编号唯一标识)
|
||||
.setJobName(businessNo)
|
||||
// 设置执行器名称(指定任务由哪个执行器执行)
|
||||
.setExecutorInfo(executorName)
|
||||
// 设置执行器超时时间(使用系统默认超时配置)
|
||||
.setExecutorTimeout(DEFAULT_TIMEOUT)
|
||||
// 设置任务描述:设备报警离线自动关单任务
|
||||
.setDescription("设备报警离线自动关单:" + businessNo)
|
||||
// 设置任务阻塞策略:丢弃后续并发请求
|
||||
.setBlockStrategy(JobBlockStrategyEnum.OVERLAY)
|
||||
// 设置最大重试次数:0 代表不重试
|
||||
.setMaxRetryTimes(0)
|
||||
// 设置任务触发类型:定时触发
|
||||
// 改为 CRON 触发
|
||||
.setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
|
||||
// 毫秒级时间戳
|
||||
.setTriggerInterval(triggerInterval)
|
||||
// 添加任务入参:传入报警ID字符串
|
||||
.addArgsStr("alarmId", alarmId.toString())
|
||||
// 设置重试间隔时间(重试时的等待时间)
|
||||
.setRetryInterval(RETRY_INTERVAL)
|
||||
// 执行任务创建/提交操作
|
||||
.execute();
|
||||
|
||||
log.info("[SnailJob] 创建关单任务成功 | businessNo: {} | delay: {}m", businessNo, delayMinutes);
|
||||
} catch (Exception e) {
|
||||
log.error("[SnailJob] 创建关单任务异常 | businessNo: {}", businessNo, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务触发时间(续期)
|
||||
* 优化:增加原子性思考。虽然 1.8.1 必须删后再加,但通过 try-catch 确保删除失败不中断逻辑(可能任务本就不存在)
|
||||
*/
|
||||
public void updateRetryTaskNextTriggerTime(Long alarmId, String businessNo, String executorName, int delayMinutes) {
|
||||
try {
|
||||
// 计算新的延迟秒数
|
||||
String triggerInterval = String.valueOf(delayMinutes * 60L);
|
||||
|
||||
// 只要 JobName (businessNo) 一致,SnailJob 服务端会识别为同一个任务进行更新
|
||||
SnailJobOpenApi.addClusterJob()
|
||||
.setJobName(businessNo) // 关键:保持 JobName 不变
|
||||
.setExecutorInfo(executorName)
|
||||
.setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
|
||||
.setTriggerInterval(triggerInterval)
|
||||
.addArgsStr("alarmId", alarmId.toString())
|
||||
.setRouteKey(AllocationAlgorithmEnum.RANDOM)
|
||||
.setMaxRetryTimes(0)
|
||||
// 强制开启覆盖更新(如果 SDK 支持,部分版本需显式指定,1.8.1 默认通常为 saveOrUpdate 逻辑)
|
||||
.execute();
|
||||
|
||||
log.info("[SnailJob] 任务续期成功(覆盖方式) → businessNo:{}", businessNo);
|
||||
} catch (Exception e) {
|
||||
log.error("[SnailJob] 任务续期失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
* 优化:针对 hashCode() 可能产生的负值进行处理,并使用更稳健的 Set 构造
|
||||
*/
|
||||
public void deleteRetryTask(String businessNo) {
|
||||
if (businessNo == null) return;
|
||||
|
||||
try {
|
||||
// 注意:hashCode 存在冲突可能。在 SnailJob 中若需绝对精确删除,建议存储 addClusterJob 返回的 ID
|
||||
// 这里保留你的逻辑,但对负数哈希取绝对值以符合一般 ID 预期(取决于服务端接收逻辑)
|
||||
long jobId = Math.abs((long) businessNo.hashCode());
|
||||
|
||||
SnailJobOpenApi.deleteJob(Set.of(jobId)).execute();
|
||||
log.info("[SnailJob] 删除任务成功 | businessNo: {} | jobId: {}", businessNo, jobId);
|
||||
} catch (Exception e) {
|
||||
// 删除通常作为补偿操作,记录 warn 即可,无需抛出异常中断业务
|
||||
log.warn("[SnailJob] 删除任务异常 | businessNo: {} | msg: {}", businessNo, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发工作流
|
||||
* 优化:增加对 args 内容的空值保护
|
||||
*/
|
||||
public void startWorkflow(String workflowId, Map<String, Object> args) {
|
||||
Assert.hasText(workflowId, "workflowId cannot be empty");
|
||||
|
||||
try {
|
||||
var request = SnailJobOpenApi.triggerWorkFlow(Long.parseLong(workflowId));
|
||||
|
||||
if (args != null) {
|
||||
args.forEach((k, v) -> request.addArgsStr(k, Objects.toString(v, "")));
|
||||
}
|
||||
|
||||
request.execute();
|
||||
log.info("[SnailJob] 触发工作流成功 | workflowId: {}", workflowId);
|
||||
} catch (Exception e) {
|
||||
log.error("[SnailJob] 触发工作流失败 | workflowId: {}", workflowId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user