From e9ae4196e612a1348a9d01d6cc3d36cee056bab6 Mon Sep 17 00:00:00 2001
From: "1351515658@qq.com" <1351515658@qq.com>
Date: Mon, 24 Feb 2025 19:33:12 +0800
Subject: [PATCH 1/4] =?UTF-8?q?fix(protection):=20=E4=BF=AE=E5=A4=8DHTTP?=
=?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=AD=BE=E5=90=8D=20API=E9=87=8D=E5=A4=8D?=
=?UTF-8?q?=E8=AF=B7=E6=B1=82=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在 ApiSignatureAspect 中添加了对重复请求的检查逻辑
- 修改 ApiSignatureRedisDAO 中的 setNonce 方法,使用 setIfAbsent 代替 set
- 优化了日志记录,增加了重复请求的相关信息
---
.../signature/core/aop/ApiSignatureAspect.java | 13 +++++++++----
.../signature/core/redis/ApiSignatureRedisDAO.java | 8 ++++----
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
index c1c78ac57..59f4500e7 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
@@ -69,13 +70,17 @@ public class ApiSignatureAspect {
// 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
String nonce = request.getHeader(signature.nonce());
- signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit());
+ if (!signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit())) {
+ String timestamp = request.getHeader(signature.timestamp());
+ log.info("[verifySignature][appId({}) timestamp({}) nonce({}) sign({}) 存在重复请求]", appId, timestamp, nonce, clientSignature);
+ throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), "存在重复请求");
+ }
return true;
}
/**
* 校验请求头加签参数
- *
+ *
* 1. appId 是否为空
* 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
* 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
@@ -118,7 +123,7 @@ public class ApiSignatureAspect {
/**
* 构建签名字符串
- *
+ *
* 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
*
* @param signature signature
@@ -139,7 +144,7 @@ public class ApiSignatureAspect {
/**
* 获取请求头加签参数 Map
*
- * @param request 请求
+ * @param request 请求
* @param signature 签名注解
* @return signature params
*/
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java
index 11fe384da..7f3b119d5 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java
@@ -17,7 +17,7 @@ public class ApiSignatureRedisDAO {
/**
* 验签随机数
- *
+ *
* KEY 格式:signature_nonce:%s // 参数为 随机数
* VALUE 格式:String
* 过期时间:不固定
@@ -26,7 +26,7 @@ public class ApiSignatureRedisDAO {
/**
* 签名密钥
- *
+ *
* HASH 结构
* KEY 格式:%s // 参数为 appid
* VALUE 格式:String
@@ -40,8 +40,8 @@ public class ApiSignatureRedisDAO {
return stringRedisTemplate.opsForValue().get(formatNonceKey(appId, nonce));
}
- public void setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
- stringRedisTemplate.opsForValue().set(formatNonceKey(appId, nonce), "", time, timeUnit);
+ public Boolean setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
+ return stringRedisTemplate.opsForValue().setIfAbsent(formatNonceKey(appId, nonce), "", time, timeUnit);
}
private static String formatNonceKey(String appId, String nonce) {
From fe3e20bc227b699774517bfd95ca71ad7d8fcd09 Mon Sep 17 00:00:00 2001
From: "1351515658@qq.com" <1351515658@qq.com>
Date: Wed, 26 Feb 2025 15:07:36 +0800
Subject: [PATCH 2/4] =?UTF-8?q?refactor(yudao-spring-boot-starter-protecti?=
=?UTF-8?q?on):=20=E4=BC=98=E5=8C=96=20API=20=E7=AD=BE=E5=90=8D=E9=AA=8C?=
=?UTF-8?q?=E8=AF=81=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 引入 BooleanUtil 来处理布尔值判断,提高代码可读性和健壮性
---
.../yudao/framework/signature/core/aop/ApiSignatureAspect.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
index 59f4500e7..e568346aa 100644
--- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
+++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.signature.core.aop;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
@@ -70,7 +71,7 @@ public class ApiSignatureAspect {
// 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
String nonce = request.getHeader(signature.nonce());
- if (!signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit())) {
+ if (BooleanUtil.isFalse(signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit()))) {
String timestamp = request.getHeader(signature.timestamp());
log.info("[verifySignature][appId({}) timestamp({}) nonce({}) sign({}) 存在重复请求]", appId, timestamp, nonce, clientSignature);
throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), "存在重复请求");
From 3156c6320edc39cef6c769d62f318dc9a9ec686e Mon Sep 17 00:00:00 2001
From: YunaiV
Date: Sat, 12 Apr 2025 11:56:13 +0800
Subject: [PATCH 3/4] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT=20?=
=?UTF-8?q?=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88BPM?=
=?UTF-8?q?=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../yudao-spring-boot-starter-ai/pom.xml | 12 +
.../framework/ai/mcp/DouBaoMcpTests.java | 122 ++++++++
...piTests.java => WenDuoDuoPptApiTests.java} | 36 +--
...ptApiTests.java => XunFeiPptApiTests.java} | 47 +--
.../bpm/dal/redis/BpmProcessIdRedisDAO.java | 10 +-
.../enums/codegen/CodegenFrontTypeEnum.java | 7 +-
.../service/codegen/inner/CodegenEngine.java | 69 +++--
.../ant_design_vue/index.vue.vm | 0
.../vue3_vben_next/schema/api/api.ts.vm | 118 ++++++++
.../vue3_vben_next/schema/views/data.ts.vm | 276 ++++++++++++++++++
.../vue3_vben_next/schema/views/form.vue.vm | 118 ++++++++
.../vue3_vben_next/schema/views/index.vue.vm | 181 ++++++++++++
.../codegen/CodegenServiceImplTest.java | 7 +-
.../codegen/inner/CodegenEngineVue2Test.java | 10 +-
.../codegen/inner/CodegenEngineVue3Test.java | 10 +-
.../kefu/vo/message/AppKeFuMessageRespVO.java | 42 ---
.../service/kefu/KeFuMessageServiceImpl.java | 19 +-
.../TradeDeliveryPriceCalculator.java | 2 +-
18 files changed, 953 insertions(+), 133 deletions(-)
create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mcp/DouBaoMcpTests.java
rename yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/{WddPptApiTests.java => WenDuoDuoPptApiTests.java} (91%)
rename yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/xunfei/{XunfeiPptApiTests.java => XunFeiPptApiTests.java} (88%)
create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/ant_design_vue/index.vue.vm
create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/api/api.ts.vm
create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm
create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm
create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm
delete mode 100644 yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessageRespVO.java
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
index 682850b98..12a63f3d4 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -112,6 +112,18 @@
dev.tinyflow
tinyflow-java-core
${tinyflow.version}
+
+
+
+ com.agentsflex
+ agents-flex-store-elasticsearch
+
+
+
+ org.codehaus.groovy
+ groovy-all
+
+
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mcp/DouBaoMcpTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mcp/DouBaoMcpTests.java
new file mode 100644
index 000000000..a97bd0a5c
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mcp/DouBaoMcpTests.java
@@ -0,0 +1,122 @@
+package cn.iocoder.yudao.framework.ai.mcp;
+
+import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.ai.tool.method.MethodToolCallbackProvider;
+
+public class DouBaoMcpTests {
+
+ private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+ .openAiApi(OpenAiApi.builder()
+ .baseUrl(DouBaoChatModel.BASE_URL)
+ .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
+ .build())
+ .defaultOptions(OpenAiChatOptions.builder()
+ .model("doubao-1-5-lite-32k-250115") // 模型(doubao)
+ .temperature(0.7)
+ .build())
+ .build();
+
+ private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);
+
+ private final MethodToolCallbackProvider provider = MethodToolCallbackProvider.builder()
+ .toolObjects(new UserService())
+ .build();
+
+ private final ChatClient chatClient = ChatClient.builder(chatModel)
+ .defaultTools(provider)
+ .build();
+
+ @Test
+ public void testMcpGetUserInfo() {
+
+ // 打印结果
+ System.out.println(chatClient.prompt()
+ .user("目前有哪些工具可以使用")
+ .call()
+ .content());
+ System.out.println("====================================");
+ // 打印结果
+ System.out.println(chatClient.prompt()
+ .user("小新的年龄是多少")
+ .call()
+ .content());
+ System.out.println("====================================");
+ // 打印结果
+ System.out.println(chatClient.prompt()
+ .user("获取小新的基本信息")
+ .call()
+ .content());
+ System.out.println("====================================");
+ // 打印结果
+ System.out.println(chatClient.prompt()
+ .user("小新是什么职业的")
+ .call()
+ .content());
+ System.out.println("====================================");
+ // 打印结果
+ System.out.println(chatClient.prompt()
+ .user("小新的教育背景")
+ .call()
+ .content());
+ System.out.println("====================================");
+ // 打印结果
+ System.out.println(chatClient.prompt()
+ .user("小新的兴趣爱好是什么")
+ .call()
+ .content());
+ System.out.println("====================================");
+
+ }
+
+
+ static class UserService {
+
+ @Tool(name = "getUserAge", description = "获取用户年龄")
+ public String getUserAge(String userName) {
+ return "《" + userName + "》的年龄为:18";
+ }
+
+ @Tool(name = "getUserSex", description = "获取用户性别")
+ public String getUserSex(String userName) {
+ return "《" + userName + "》的性别为:男";
+ }
+
+ @Tool(name = "getUserBasicInfo", description = "获取用户基本信息,包括姓名、年龄、性别等")
+ public String getUserBasicInfo(String userName) {
+ return "《" + userName + "》的基本信息:\n姓名:" + userName + "\n年龄:18\n性别:男\n身高:175cm\n体重:65kg";
+ }
+
+ @Tool(name = "getUserContact", description = "获取用户联系方式,包括电话、邮箱等")
+ public String getUserContact(String userName) {
+ return "《" + userName + "》的联系方式:\n电话:138****1234\n邮箱:" + userName.toLowerCase() + "@example.com\nQQ:123456789";
+ }
+
+ @Tool(name = "getUserAddress", description = "获取用户地址信息")
+ public String getUserAddress(String userName) {
+ return "《" + userName + "》的地址信息:北京市朝阳区科技园区88号";
+ }
+
+ @Tool(name = "getUserJob", description = "获取用户职业信息")
+ public String getUserJob(String userName) {
+ return "《" + userName + "》的职业信息:软件工程师,就职于ABC科技有限公司,工作年限5年";
+ }
+
+ @Tool(name = "getUserHobbies", description = "获取用户兴趣爱好")
+ public String getUserHobbies(String userName) {
+ return "《" + userName + "》的兴趣爱好:编程、阅读、旅游、摄影、打篮球";
+ }
+
+ @Tool(name = "getUserEducation", description = "获取用户教育背景")
+ public String getUserEducation(String userName) {
+ return "《" + userName + "》的教育背景:\n本科:计算机科学与技术专业,北京大学\n硕士:软件工程专业,清华大学";
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WddPptApiTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java
similarity index 91%
rename from yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WddPptApiTests.java
rename to yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java
index 3bb9898ad..54c8cffc5 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WddPptApiTests.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.ai.ppt.wdd;
-import cn.iocoder.yudao.framework.ai.core.model.wenduoduo.api.WddPptApi;
+import cn.iocoder.yudao.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -10,24 +10,23 @@ import java.util.Map;
import java.util.Objects;
/**
- * {@link WddPptApi} 集成测试
+ * {@link WenDuoDuoPptApi} 集成测试
*
* @author xiaoxin
*/
-public class WddPptApiTests {
-
- private final WddPptApi wddPptApi = new WddPptApi("https://docmee.cn");
+public class WenDuoDuoPptApiTests {
private final String token = ""; // API Token
+ private final WenDuoDuoPptApi wenDuoDuoPptApi = new WenDuoDuoPptApi(token);
@Test
@Disabled
public void testCreateApiToken() {
// 准备参数
String apiKey = "";
- WddPptApi.CreateTokenRequest request = new WddPptApi.CreateTokenRequest(apiKey);
+ WenDuoDuoPptApi.CreateTokenRequest request = new WenDuoDuoPptApi.CreateTokenRequest(apiKey);
// 调用方法
- String token = wddPptApi.createApiToken(request);
+ String token = wenDuoDuoPptApi.createApiToken(request);
// 打印结果
System.out.println(token);
}
@@ -38,7 +37,7 @@ public class WddPptApiTests {
@Test
@Disabled
public void testCreateTask() {
- WddPptApi.ApiResponse apiResponse = wddPptApi.createTask(token, 1, "dify 介绍", null);
+ WenDuoDuoPptApi.ApiResponse apiResponse = wenDuoDuoPptApi.createTask(1, "dify 介绍", null);
System.out.println(apiResponse);
}
@@ -46,10 +45,10 @@ public class WddPptApiTests {
@Test // 创建大纲
@Disabled
public void testGenerateOutlineRequest() {
- WddPptApi.CreateOutlineRequest request = new WddPptApi.CreateOutlineRequest(
+ WenDuoDuoPptApi.CreateOutlineRequest request = new WenDuoDuoPptApi.CreateOutlineRequest(
"1901539019628613632", "medium", null, null, null, null);
// 调用
- Flux