From d9c567c6c398c141e21fbd49964c4450e00ea24c Mon Sep 17 00:00:00 2001
From: luogw <3132758203@qq.com>
Date: Tue, 10 Mar 2026 11:11:36 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BB=A4=E7=89=8C=E6=A1=B6=E9=99=90=E6=B5=81-?=
=?UTF-8?q?=E8=B0=83=E7=94=A8=E7=AE=97=E6=B3=95=E7=94=9F=E9=A2=98=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 8 +++
...stToGenerateQuestionDomainServiceImpl.java | 24 +++++++--
.../project/interaction/utils/NotifyUtil.java | 53 +++++++++++++++++++
.../impl/CheckMilvusDomainServiceImpl.java | 8 +--
.../config/QuestionGenerationConfig.java | 27 ++++++++++
.../impl/QuestionGenerationRateLimiter.java | 36 +++++++++++++
src/main/resources/application-dev.yml | 8 ++-
src/main/resources/application.yml | 8 ++-
8 files changed, 159 insertions(+), 13 deletions(-)
create mode 100644 src/main/java/com/project/interaction/utils/NotifyUtil.java
create mode 100644 src/main/java/com/project/question/config/QuestionGenerationConfig.java
create mode 100644 src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java
diff --git a/pom.xml b/pom.xml
index bda0e0b..220aab2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,6 +140,14 @@
hutool-all
5.8.3
+
+
+
+ com.google.guava
+ guava
+ 31.1-jre
+
+
org.springframework.boot
diff --git a/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java b/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java
index 52f641b..657e995 100644
--- a/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java
+++ b/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java
@@ -1,12 +1,13 @@
package com.project.interaction.domain.service.impl;
import cn.hutool.core.util.RandomUtil;
-import com.project.information.domain.service.KnowledgePointBaseService;
import com.project.interaction.domain.dto.GenerateQuestionRequestDTO;
import com.project.interaction.domain.dto.GenerateQuestionResponseDTO;
import com.project.interaction.domain.service.PostToGenerateQuestionDomainService;
+import com.project.interaction.utils.NotifyUtil;
import com.project.question.domain.dto.QuestionDTO;
import com.project.question.domain.dto.TaskKnowledgePointDTO;
+import com.project.question.domain.service.impl.QuestionGenerationRateLimiter;
import com.project.question.domain.service.SaveQuestionDomainService;
import com.project.task.domain.enums.QuestionTypeEnum;
import jakarta.annotation.Resource;
@@ -37,6 +38,12 @@ public class PostToGenerateQuestionDomainServiceImpl implements PostToGenerateQu
@Autowired
private SaveQuestionDomainService saveQuestionDomainService;
+ @Autowired
+ private QuestionGenerationRateLimiter rateLimiter;
+
+ @Autowired
+ private NotifyUtil notifyUtil;
+
private final String[] optionListStr = {"A", "B", "C", "D"};
private final String[] trueFalseListStr = {"A", "B"};
@@ -59,13 +66,20 @@ public class PostToGenerateQuestionDomainServiceImpl implements PostToGenerateQu
taskId, clusterId, questionType.getDescription(), num);
try {
+ // 应用限流:获取令牌,超过限流速率则等待
+ long waitStart = System.currentTimeMillis();
+ rateLimiter.acquire();
+ long waitTime = System.currentTimeMillis() - waitStart;
+ if (waitTime > 100) {
+ log.info(">>> [题目生成] 因限流等待了 {} ms, TaskId: {}", waitTime, taskId);
+ }
+
//尝试调用算法服务生成题目
GenerateQuestionResponseDTO response = callAlgorithmService(dtoList, questionType, num);
if (response != null && StringUtils.isNotBlank(response.getMessage()) && response.getMessage().contains("success")) {
log.info(">>> [题目生成] 算法服务接受任务成功, 任务: {}, 状态: {}, 信息:{}",
response.getTaskId(), response.getStatus(),response.getMessage());
- return;
}
} catch (Exception e) {
@@ -74,8 +88,10 @@ public class PostToGenerateQuestionDomainServiceImpl implements PostToGenerateQu
}
//熔断降级:算法服务失败时使用硬编码生成默认题目
- log.info(">>> [题目生成] 启用熔断降级,生成默认题目");
- generateFallbackQuestions(dtoList, questionType, num);
+// log.info(">>> [题目生成] 启用熔断降级,生成默认题目");
+// generateFallbackQuestions(dtoList, questionType, num);
+ //发送预警通知
+ notifyUtil.notify(taskId,clusterId);
}
/**
diff --git a/src/main/java/com/project/interaction/utils/NotifyUtil.java b/src/main/java/com/project/interaction/utils/NotifyUtil.java
new file mode 100644
index 0000000..c607de4
--- /dev/null
+++ b/src/main/java/com/project/interaction/utils/NotifyUtil.java
@@ -0,0 +1,53 @@
+package com.project.interaction.utils;
+
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+
+@Slf4j
+@Component
+public class NotifyUtil {
+
+ @Autowired
+ private DtService dtService;
+
+ public void notify(Long taskId,Long clusterId) {
+// "283712335120874575", "01231011386731976125",
+ ArrayList userIDList = Lists.newArrayList( "283712335120874575", "01231011386731976125","590204300932000212");
+
+ try {
+ String markdown = String.format("### 【告警】算法生题API调用失败通知 \n\n") +
+ String.format("## 核心信息\n") +
+ String.format("- 考试任务ID:%d\n", taskId) +
+ String.format("- 知识点簇ID:%d\n\n", clusterId) +
+ String.format("## 异常说明\n") +
+ String.format("算法服务生成题目时调用失败,可能导致考试题目不完整或无法正常作答,请尽快排查以下问题:\n") +
+ String.format("- 算法服务是否正常运行\n");
+ DtCorpConversationMessage corpConversationMessage = DtCorpConversationMessage.builder()
+ .agentId(dtService.getDtConfigStorage().getAgentId())
+ .userIds(Lists.newArrayList(userIDList))
+ .msg(DtMessage.MARKDOWN()
+ .content("算法生题API调用失败通知")
+ .text(markdown)
+ .build())
+ .build();
+ DtCorpConversationMsgSendResult result = dtService.getCorpConversationMsgService().send(corpConversationMessage);
+ if (result.getErrCode() == 0) {
+ log.info(">>> [算法生题API调用失败通知] 成功发送通知 ,考试任务ID:{} ,知识点簇ID:{}", taskId, clusterId);
+ } else {
+ log.error(">>> [算法生题API调用失败通知] 发送失败.考试任务ID:{} ,知识点簇ID:{}, 错误码: {}, 错误信息: {}", taskId, clusterId,
+ result.getErrCode(), result.getErrMsg());
+ }
+
+ } catch (Exception e) {
+ log.error(">>> [算法生题API调用失败通知] 钉钉接口调用异常, 考试任务ID:{} ,知识点簇ID:{}, 原因: {}",taskId, clusterId, e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java b/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java
index fc5b090..00be3f1 100644
--- a/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java
+++ b/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java
@@ -43,9 +43,8 @@ public class CheckMilvusDomainServiceImpl implements CheckMilvusDomainService {
}
List pointIdsList = title.getTaskKpIdsList();
- List titleVectorList = title.getTitleVectorList();
- if (CollectionUtil.isEmpty(pointIdsList) || CollectionUtil.isEmpty(titleVectorList)) {
+ if (CollectionUtil.isEmpty(pointIdsList)) {
throw new MissingParameterException("请求参数缺失或格式错误");
}
@@ -57,11 +56,6 @@ public class CheckMilvusDomainServiceImpl implements CheckMilvusDomainService {
throw new MissingParameterException("考试任务ID不能为空");
}
-
- if(titleVectorList.size() != 1024){
- throw new MissingParameterException("向量格式错误");
- }
-
// 校验 QuestionDetailDTO 及其内部参数
QuestionDTO.QuestionDetailDTO detailDTO = title.QuestionDetailDTO;
if (detailDTO == null) {
diff --git a/src/main/java/com/project/question/config/QuestionGenerationConfig.java b/src/main/java/com/project/question/config/QuestionGenerationConfig.java
new file mode 100644
index 0000000..6d79609
--- /dev/null
+++ b/src/main/java/com/project/question/config/QuestionGenerationConfig.java
@@ -0,0 +1,27 @@
+package com.project.question.config;
+
+import com.project.question.domain.service.impl.QuestionGenerationRateLimiter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 题目生成相关配置
+ */
+@Configuration
+public class QuestionGenerationConfig {
+
+ @Value("${question.generation.rate-limit:5}")
+ private double rateLimit;
+
+ /**
+ * 创建题目生成限流器
+ */
+ @Bean
+ @ConditionalOnMissingBean(QuestionGenerationRateLimiter.class)
+ public QuestionGenerationRateLimiter questionGenerationRateLimiter() {
+ return new QuestionGenerationRateLimiter(this.rateLimit);
+ }
+}
+
diff --git a/src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java b/src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java
new file mode 100644
index 0000000..7705e1c
--- /dev/null
+++ b/src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java
@@ -0,0 +1,36 @@
+package com.project.question.domain.service.impl;
+
+import com.google.common.util.concurrent.RateLimiter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * 题目生成API限流器
+ * 用于控制调用算法服务的请求速率,防止一大批请求同时涌入导致服务压力过大
+ */
+@Component
+@Slf4j
+public class QuestionGenerationRateLimiter {
+
+ /**
+ * RateLimiter 使用令牌桶算法
+ * 每秒允许固定数量的请求通过
+ */
+ private final RateLimiter rateLimiter;
+
+ public QuestionGenerationRateLimiter(@Value("${question.generation.rate-limit:5}") double permitsPerSecond) {
+ this.rateLimiter = RateLimiter.create(permitsPerSecond);
+ log.info(">>> [限流器初始化] 题目生成限流速率设置为: {}/秒", permitsPerSecond);
+ }
+
+ /**
+ * 获取令牌,如果令牌不足则阻塞等待
+ * 调用此方法的线程会被阻塞,直到获取到令牌为止
+ */
+ public void acquire() {
+ rateLimiter.acquire();
+ }
+
+}
+
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index a90680c..6a2ea8c 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -73,4 +73,10 @@ algo:
clusterUrl: /api/algorithm/v1/cluster
baseUrl: /
generateQuestionUrl: /v1/generate/questions_from_cluster
- apiUrl: http://8.129.84.155:8000
\ No newline at end of file
+ apiUrl: http://8.129.84.155:8000
+
+# 题目生成相关配置
+question:
+ generation:
+ # 限流速率:每秒允许的API请求数
+ rate-limit: 20
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 52e5ca4..1d9a178 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -78,4 +78,10 @@ algo:
generateQuestionUrl: /v1/generate/questions_from_cluster
apiUrl: http://8.129.84.155:8000
jwt:
- secret: "my-very-fixed-and-secure-secret-key-1234567890"
\ No newline at end of file
+ secret: "my-very-fixed-and-secure-secret-key-1234567890"
+
+# 题目生成相关配置
+question:
+ generation:
+ # 限流速率:每秒允许的API请求数
+ rate-limit: 20