You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

90 lines
4.0 KiB

package com.project.interaction.utils;
1 month ago
import cn.hutool.core.date.DateUtil;
import com.github.tingyugetc520.ali.dingtalk.api.DtService;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMessage;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMsgSendResult;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtMessage;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
1 month ago
import java.time.Duration;
import java.util.ArrayList;
1 month ago
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class NotifyUtil {
@Autowired
private DtService dtService;
@Autowired
private RedissonClient redissonClient;
// 钉钉通知相关配置
1 month ago
private final List<String> WARN_USER_LIST = Lists.newArrayList(
"283712335120874575",
"01231011386731976125",
1 month ago
"590204300932000212"
);
1 month ago
// 防重复缓存 key 前缀
private static final String WARN_LOCK_KEY_PREFIX = "question:gen:warn:lock:";
1 month ago
// 重复发送间隔:1 小时(3600 秒)
private static final long WARN_LOCK_EXPIRE_SECONDS = 3600L;
public void notify(Long taskId,Long clusterId) {
1 month ago
if (taskId == null || clusterId == null) {
return;
}
1 month ago
// 构建 Redis 缓存 key(按 taskId 唯一)
String cacheKey = WARN_LOCK_KEY_PREFIX + taskId;
1 month ago
// 核心逻辑:使用原子操作 setIfAbsent 防止并发重复发送
// 返回 true 表示成功设置(之前不存在),返回 false 表示 1 小时内已发送过
boolean acquired = redissonClient.getBucket(cacheKey).setIfAbsent(
"SEND_SUCCESS",
Duration.ofSeconds(WARN_LOCK_EXPIRE_SECONDS)
);
if (!acquired) {
return;
}
try {
1 month ago
String now = DateUtil.now();
1 month ago
String markdown = String.format("### 【告警】算法生题 API 调用失败通知 \n\n") +
String.format("## 核心信息\n") +
1 month ago
String.format("- 考试任务 ID:%d\n", taskId) +
String.format("- 知识点簇 ID:%d\n", clusterId) +
1 month ago
String.format("- 当前时间:%s\n\n", now) +
String.format("## 异常说明\n") +
String.format("算法服务生成题目时调用失败,可能导致考试题目不完整或无法正常作答,请尽快排查以下问题:\n") +
String.format("- 算法服务是否正常运行\n");
DtCorpConversationMessage corpConversationMessage = DtCorpConversationMessage.builder()
.agentId(dtService.getDtConfigStorage().getAgentId())
.userIds(WARN_USER_LIST)
.msg(DtMessage.MARKDOWN()
1 month ago
.content("算法生题 API 调用失败通知")
.text(markdown)
.build())
.build();
DtCorpConversationMsgSendResult result = dtService.getCorpConversationMsgService().send(corpConversationMessage);
if (result.getErrCode() == 0) {
1 month ago
log.info(">>> [算法生题 API 调用失败通知] 成功发送通知,考试任务 ID:{} ,知识点簇 ID:{}", taskId, clusterId);
} else {
1 month ago
log.error(">>> [算法生题 API 调用失败通知] 发送失败,考试任务 ID:{} ,知识点簇 ID:{}, 错误码:{}, 错误信息:{}", taskId, clusterId,
result.getErrCode(), result.getErrMsg());
}
} catch (Exception e) {
1 month ago
log.error(">>> [算法生题 API 调用失败通知] 钉钉接口调用异常,考试任务 ID:{} ,知识点簇 ID:{}, 原因:{}",taskId, clusterId, e.getMessage());
}
}
}