From 62d8fa4cfd3a019289d27e8ed7d67fe7b09429cf Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 27 Jun 2026 10:43:13 -0700 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=88=9D=E5=A7=8B=E5=8C=96=20spri?= =?UTF-8?q?ng=20boot=204.x=20=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 +- yudao-dependencies/pom.xml | 35 +- yudao-framework/yudao-common/pom.xml | 9 +- .../framework/common/util/json/JsonUtils.java | 28 +- .../util/json/databind/NumberSerializer.java | 13 +- .../TimestampLocalDateTimeDeserializer.java | 13 +- .../TimestampLocalDateTimeSerializer.java | 20 +- .../common/util/servlet/ServletUtils.java | 3 +- .../config/YudaoTenantAutoConfiguration.java | 1 + .../TenantKafkaEnvironmentPostProcessor.java | 2 +- .../main/resources/META-INF/spring.factories | 2 +- .../yudao-spring-boot-starter-monitor/pom.xml | 7 +- .../config/YudaoMetricsAutoConfiguration.java | 3 +- .../YudaoRabbitMQAutoConfiguration.java | 6 +- .../yudao-spring-boot-starter-mybatis/pom.xml | 12 +- .../YudaoDataSourceAutoConfiguration.java | 2 +- .../IdTypeEnvironmentPostProcessor.java | 2 +- .../config/YudaoMybatisAutoConfiguration.java | 12 +- .../main/resources/META-INF/spring.factories | 2 +- .../yudao-spring-boot-starter-redis/pom.xml | 8 +- .../config/YudaoCacheAutoConfiguration.java | 2 +- .../config/YudaoRedisAutoConfiguration.java | 13 +- .../pom.xml | 2 +- .../YudaoWebSecurityConfigurerAdapter.java | 3 +- .../test/config/RedisTestConfiguration.java | 6 +- .../SqlInitializationTestConfiguration.java | 4 +- .../test/core/ut/BaseDbAndRedisUnitTest.java | 14 +- .../test/core/ut/BaseDbUnitTest.java | 6 +- .../test/core/ut/BaseRedisUnitTest.java | 8 +- .../yudao-spring-boot-starter-web/pom.xml | 8 + .../core/filter/ApiAccessLogFilter.java | 2 +- .../core/base/annotation/DesensitizeBy.java | 2 +- .../StringDesensitizeSerializer.java | 21 +- .../config/YudaoJacksonAutoConfiguration.java | 35 +- .../web/config/YudaoWebAutoConfiguration.java | 6 +- .../core/handler/GlobalExceptionHandler.java | 2 +- .../xss/config/YudaoXssAutoConfiguration.java | 15 +- .../core/json/XssStringJsonDeserializer.java | 17 +- .../filter/logging/AccessLogFilter.java | 5 +- .../security/TokenAuthenticationFilter.java | 2 +- .../handler/GlobalExceptionHandler.java | 2 +- .../GatewayJacksonAutoConfiguration.java | 52 ++- .../gateway/util/SecurityFrameworkUtils.java | 2 +- .../yudao/gateway/util/WebFrameworkUtils.java | 3 +- .../src/main/resources/application.yaml | 8 - .../yudao-module-ai-server/pom.xml | 46 +-- .../ai/config/AiAutoConfiguration.java | 238 ++++++++++-- .../ai/config/YudaoAiProperties.java | 76 ++++ .../ai/core/model/AiModelFactoryImpl.java | 343 ++++++------------ .../model/baichuan/BaiChuanChatModel.java | 9 +- .../ai/core/model/doubao/DouBaoChatModel.java | 9 +- .../ai/core/model/gemini/GeminiChatModel.java | 9 +- .../ai/core/model/grok/GrokChatModel.java | 9 +- .../core/model/hunyuan/HunYuanChatModel.java | 9 +- .../core/model/minimax/MiniMaxChatModel.java | 52 +++ .../model/moonshot/MoonshotChatModel.java | 54 +++ .../siliconflow/SiliconFlowChatModel.java | 10 +- .../siliconflow/SiliconFlowImageApi.java | 27 +- .../siliconflow/SiliconFlowImageModel.java | 149 ++++---- .../core/model/xinghuo/XingHuoChatModel.java | 9 +- .../ai/core/model/yiyan/YiYanChatModel.java | 52 +++ .../ai/core/model/zhipu/ZhiPuChatModel.java | 52 +++ .../config/SecurityConfiguration.java | 49 +-- .../chat/AiChatMessageServiceImpl.java | 16 +- .../ai/service/image/AiImageServiceImpl.java | 11 - .../AiKnowledgeSegmentServiceImpl.java | 4 +- .../iocoder/yudao/module/ai/util/AiUtils.java | 28 +- ...aultToolExecutionEligibilityPredicate.java | 29 ++ .../ToolExecutionEligibilityPredicate.java | 21 ++ .../src/main/resources/application-dev.yaml | 16 +- .../src/main/resources/application-local.yaml | 18 +- .../src/main/resources/application.yaml | 17 +- .../model/chat/AnthropicChatModelTest.java | 10 +- .../model/chat/AzureOpenAIChatModelTests.java | 71 ---- .../model/chat/BaiChuanChatModelTests.java | 5 +- .../core/model/chat/CozeChatModelTests.java | 9 +- .../model/chat/DeepSeekChatModelTests.java | 5 +- .../core/model/chat/DifyChatModelTests.java | 9 +- .../core/model/chat/DouBaoChatModelTests.java | 2 +- .../model/chat/FastGPTChatModelTests.java | 9 +- .../core/model/chat/GeminiChatModelTests.java | 6 +- .../model/chat/HunYuanChatModelTests.java | 4 +- .../core/model/chat/LlamaChatModelTests.java | 2 +- .../model/chat/MiniMaxChatModelTests.java | 24 +- .../model/chat/MoonshotChatModelTests.java | 28 +- .../core/model/chat/OllamaChatModelTests.java | 2 +- .../core/model/chat/OpenAIChatModelTests.java | 21 +- .../model/chat/SiliconFlowChatModelTests.java | 2 +- .../model/chat/XingHuoChatModelTests.java | 2 +- .../core/model/chat/YiYanChatModelTests.java | 29 +- .../model/chat/ZhiPuAiChatModelTests.java | 27 +- .../model/image/OpenAiImageModelTests.java | 13 +- .../core/model/image/QianFanImageTests.java | 43 --- .../model/image/ZhiPuAiImageModelTests.java | 35 -- .../ai/core/model/mcp/DouBaoMcpTests.java | 14 +- .../core/util/BpmHttpRequestUtils.java | 31 +- .../flowable/core/util/FlowableUtils.java | 2 +- .../trigger/form/BpmFormDeleteTrigger.java | 2 +- .../trigger/form/BpmFormUpdateTrigger.java | 2 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../dal/dataobject/file/FileConfigDO.java | 2 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- yudao-module-iot/yudao-module-iot-api/pom.xml | 2 +- .../yudao-module-iot-core/pom.xml | 7 +- .../IotMessageBusAutoConfiguration.java | 2 +- .../core/kafka/IotKafkaMessageBus.java | 2 +- .../yudao-module-iot-gateway/pom.xml | 4 + .../upstream/IotHttpAbstractHandler.java | 2 +- .../device/remote/IotDeviceApiImpl.java | 4 +- .../data/action/IotHttpDataSinkAction.java | 4 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../dto/kd100/Kd100ExpressQueryRespDTO.java | 4 +- .../dto/kdniao/KdNiaoExpressQueryRespDTO.java | 4 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../mp/config/YudaoWxMpConfiguration.java | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../dal/dataobject/channel/PayChannelDO.java | 4 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 2 + .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 12 +- .../src/main/resources/application-dev.yaml | 1 + .../src/main/resources/application-local.yaml | 1 + .../src/main/resources/application.yaml | 21 +- 166 files changed, 1393 insertions(+), 1152 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java delete mode 100644 yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java delete mode 100644 yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java delete mode 100644 yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java diff --git a/pom.xml b/pom.xml index 366940545..aaf59b512 100644 --- a/pom.xml +++ b/pom.xml @@ -38,15 +38,15 @@ 2026.05-SNAPSHOT - 17 + 25 ${java.version} ${java.version} - 3.5.3 + 3.5.5 3.14.0 1.7.2 1.18.46 - 3.5.15 + 4.1.0 1.6.3 UTF-8 diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 013853c5e..0224c519c 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -17,11 +17,11 @@ 2026.05-SNAPSHOT 1.7.2 - 3.5.15 - 2025.0.1 - 2025.0.0.0 + 4.1.0 + 2025.1.2 + 2025.1.0.0 - 2.8.17 + 3.0.3 4.5.0 1.2.28 @@ -45,7 +45,7 @@ 2.2.7 9.6.0 - 3.5.9 + 4.1.1 0.33.0 8.0.2.RELEASE @@ -88,7 +88,7 @@ 2.3.4 2.3.2 4.8.4-20260623.211820 - 1.80 + 1.84 4.40.865.ALL @@ -159,6 +159,11 @@ spring-boot-configuration-processor ${spring.boot.version} + + org.springframework.boot + spring-boot-jackson + ${spring.boot.version} + cn.iocoder.cloud @@ -216,7 +221,7 @@ com.alibaba - druid-spring-boot-3-starter + druid-spring-boot-4-starter ${druid.version} @@ -227,7 +232,7 @@ com.baomidou - mybatis-plus-spring-boot3-starter + mybatis-plus-spring-boot4-starter ${mybatis-plus.version} @@ -242,7 +247,7 @@ com.baomidou - dynamic-datasource-spring-boot3-starter + dynamic-datasource-spring-boot4-starter ${dynamic-datasource.version} @@ -287,18 +292,6 @@ org.redisson redisson-spring-boot-starter ${redisson.version} - - - org.redisson - - redisson-spring-data-41 - - - - - org.redisson - redisson-spring-data-35 - ${redisson.version} diff --git a/yudao-framework/yudao-common/pom.xml b/yudao-framework/yudao-common/pom.xml index 4446762ee..e03f3b173 100644 --- a/yudao-framework/yudao-common/pom.xml +++ b/yudao-framework/yudao-common/pom.xml @@ -44,7 +44,6 @@ spring-boot-configuration-processor true - org.springframework @@ -104,17 +103,17 @@ com.fasterxml.jackson.core - jackson-databind + jackson-annotations provided - com.fasterxml.jackson.core + tools.jackson.core jackson-core provided - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 + tools.jackson.core + jackson-databind provided diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java index 9452ff442..558c8d293 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java @@ -6,20 +6,18 @@ import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JacksonException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import tools.jackson.core.JacksonException; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; -import java.io.IOException; import java.lang.reflect.Type; import java.time.LocalDateTime; import java.util.ArrayList; @@ -38,14 +36,14 @@ public class JsonUtils { private static ObjectMapper objectMapper = buildObjectMapper(); private static ObjectMapper buildObjectMapper() { - SimpleModule simpleModule = new JavaTimeModule() + SimpleModule simpleModule = new SimpleModule() // 解决 LocalDateTime 的序列化 .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); return JsonMapper.builder() .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .defaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL)) + .changeDefaultPropertyInclusion(value -> JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL)) .addModule(simpleModule) .build(); } @@ -120,7 +118,7 @@ public class JsonUtils { } try { return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type)); - } catch (IOException e) { + } catch (JacksonException e) { log.error("json parse err,json:{}", text, e); throw new RuntimeException(e); } @@ -148,7 +146,7 @@ public class JsonUtils { } try { return objectMapper.readValue(bytes, clazz); - } catch (IOException e) { + } catch (JacksonException e) { log.error("json parse err,json:{}", bytes, e); throw new RuntimeException(e); } @@ -251,7 +249,7 @@ public class JsonUtils { public static JsonNode parseTree(byte[] text) { try { return objectMapper.readTree(text); - } catch (IOException e) { + } catch (JacksonException e) { log.error("json parse err,json:{}", text, e); throw new RuntimeException(e); } diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java index 35fc9f72c..0e7d052b8 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java @@ -1,10 +1,9 @@ package cn.iocoder.yudao.framework.common.util.json.databind; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; - -import java.io.IOException; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.annotation.JacksonStdImpl; /** * Long 序列化规则 @@ -14,7 +13,7 @@ import java.io.IOException; * @author 星语 */ @JacksonStdImpl -public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer { +public class NumberSerializer extends tools.jackson.databind.ser.jdk.NumberSerializer { private static final long MAX_SAFE_INTEGER = 9007199254740991L; private static final long MIN_SAFE_INTEGER = -9007199254740991L; @@ -26,7 +25,7 @@ public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.Num } @Override - public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + public void serialize(Number value, JsonGenerator gen, SerializationContext serializers) throws JacksonException { // 超出范围 序列化位字符串 if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { super.serialize(value, gen, serializers); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java index 5bf5d6c63..4d639ba8e 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java @@ -1,25 +1,24 @@ package cn.iocoder.yudao.framework.common.util.json.databind; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import java.io.IOException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; /** * 基于时间戳的 LocalDateTime 反序列化器 * * @author 老五 */ -public class TimestampLocalDateTimeDeserializer extends JsonDeserializer { +public class TimestampLocalDateTimeDeserializer extends ValueDeserializer { public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); @Override - public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException { // 将 Long 时间戳,转换为 LocalDateTime 对象 return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); } diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java index 4e422feef..fc49e805e 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java @@ -5,12 +5,12 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; import lombok.extern.slf4j.Slf4j; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ser.std.StdScalarSerializer; -import java.io.IOException; import java.lang.reflect.Field; import java.time.LocalDateTime; import java.time.ZoneId; @@ -25,18 +25,22 @@ import java.util.concurrent.ConcurrentHashMap; * @author 老五 */ @Slf4j -public class TimestampLocalDateTimeSerializer extends JsonSerializer { +public class TimestampLocalDateTimeSerializer extends StdScalarSerializer { public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer(); private static final Map, Map> FIELD_CACHE = new ConcurrentHashMap<>(); + public TimestampLocalDateTimeSerializer() { + super(LocalDateTime.class); + } + @Override - public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + public void serialize(LocalDateTime value, JsonGenerator gen, SerializationContext serializers) throws JacksonException { // 情况一:有 JsonFormat 自定义注解,则使用它。https://github.com/YunaiV/ruoyi-vue-pro/pull/1019 - String fieldName = gen.getOutputContext().getCurrentName(); + String fieldName = gen.streamWriteContext().currentName(); if (fieldName != null) { - Object currentValue = gen.getOutputContext().getCurrentValue(); + Object currentValue = gen.currentValue(); if (currentValue != null) { Class clazz = currentValue.getClass(); Map fieldMap = FIELD_CACHE.computeIfAbsent(clazz, this::buildFieldMap); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java index 905f5d541..260eee858 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java @@ -26,10 +26,9 @@ public class ServletUtils { * @param response 响应 * @param object 对象,会序列化成 JSON 字符串 */ - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 public static void writeJSON(HttpServletResponse response, Object object) { String content = JsonUtils.toJsonString(object); - JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); + JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"); } /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index 0a79468b3..e636624f0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -129,6 +129,7 @@ public class YudaoTenantAutoConfiguration { * * @return 忽略租户的 URL 集合 */ + @SuppressWarnings("removal") private Set getTenantIgnoreUrls() { Set ignoreUrls = new HashSet<>(); // 获得接口对应的 HandlerMethod 集合 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java index 8bf7cc1a8..55f982db2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.tenant.core.mq.kafka; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.EnvironmentPostProcessor; import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories index a495842a0..560a54170 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ -org.springframework.boot.env.EnvironmentPostProcessor=\ +org.springframework.boot.EnvironmentPostProcessor=\ cn.iocoder.yudao.framework.tenant.core.mq.kafka.TenantKafkaEnvironmentPostProcessor diff --git a/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml b/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml index de0a0ed74..89a2939eb 100644 --- a/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml @@ -24,7 +24,7 @@ org.springframework.boot - spring-boot-starter-aop + spring-boot-starter-aspectj @@ -41,6 +41,11 @@ + + org.springframework.boot + spring-boot-starter-actuator + + io.opentracing opentracing-util diff --git a/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java index 9b75d960c..688601f21 100644 --- a/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java @@ -2,12 +2,11 @@ package cn.iocoder.yudao.framework.tracer.config; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.micrometer.metrics.autoconfigure.MeterRegistryCustomizer; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; /** * Metrics 配置类 diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java index af1467376..4d6dd1944 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.mq.rabbitmq.config; import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.JacksonJsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -18,11 +18,11 @@ import org.springframework.context.annotation.Bean; public class YudaoRabbitMQAutoConfiguration { /** - * Jackson2JsonMessageConverter Bean:使用 jackson 序列化消息 + * JacksonJsonMessageConverter Bean:使用 jackson 序列化消息 */ @Bean public MessageConverter createMessageConverter() { - return new Jackson2JsonMessageConverter(); + return new JacksonJsonMessageConverter(); } } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index cd3f70c2e..12eb52d4a 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -66,11 +66,11 @@ com.alibaba - druid-spring-boot-3-starter + druid-spring-boot-4-starter com.baomidou - mybatis-plus-spring-boot3-starter + mybatis-plus-spring-boot4-starter com.baomidou @@ -78,13 +78,7 @@ com.baomidou - dynamic-datasource-spring-boot3-starter - - - org.springframework.boot - spring-boot-starter-undertow - - + dynamic-datasource-spring-boot4-starter diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/config/YudaoDataSourceAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/config/YudaoDataSourceAutoConfiguration.java index 879a19aae..30063d9da 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/config/YudaoDataSourceAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/datasource/config/YudaoDataSourceAutoConfiguration.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.datasource.config; import cn.iocoder.yudao.framework.datasource.core.filter.DruidAdRemoveFilter; -import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.spring.boot4.autoconfigure.properties.DruidStatProperties; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java index 0ea6bf252..cf1797a7f 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.IdType; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.EnvironmentPostProcessor; import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java index 775447a06..e5fb82eaa 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java @@ -9,19 +9,19 @@ import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.baomidou.mybatisplus.extension.handlers.Jackson3TypeHandler; import com.baomidou.mybatisplus.extension.incrementer.*; import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.ibatis.annotations.Mapper; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.core.env.ConfigurableEnvironment; +import tools.jackson.databind.ObjectMapper; import java.util.List; import java.util.concurrent.TimeUnit; @@ -81,15 +81,15 @@ public class YudaoMybatisAutoConfiguration { throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType)); } - @Bean // 特殊:返回结果使用 Object 而不用 JacksonTypeHandler 的原因,避免因为 JacksonTypeHandler 被 mybatis 全局使用! + @Bean // 特殊:返回结果使用 Object 而不用 Jackson3TypeHandler 的原因,避免因为 Jackson3TypeHandler 被 mybatis 全局使用! public Object jacksonTypeHandler(List objectMappers) { - // 特殊:设置 JacksonTypeHandler 的 ObjectMapper! + // 特殊:设置 Jackson3TypeHandler 的 ObjectMapper! ObjectMapper objectMapper = CollUtil.getFirst(objectMappers); if (objectMapper == null) { objectMapper = JsonUtils.getObjectMapper(); } - JacksonTypeHandler.setObjectMapper(objectMapper); - return new JacksonTypeHandler(Object.class); + Jackson3TypeHandler.setObjectMapper(objectMapper); + return new Jackson3TypeHandler(Object.class); } } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories index eb172e4ce..ff83b2a62 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ -org.springframework.boot.env.EnvironmentPostProcessor=\ +org.springframework.boot.EnvironmentPostProcessor=\ cn.iocoder.yudao.framework.mybatis.config.IdTypeEnvironmentPostProcessor diff --git a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml index e5d3a9a7c..d1e25c8f9 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml @@ -26,10 +26,6 @@ org.redisson redisson-spring-boot-starter - - org.redisson - redisson-spring-data-35 - org.springframework.boot @@ -37,8 +33,8 @@ - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 + org.springframework.boot + spring-boot-jackson diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java index afdb32cc7..cc9409406 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.redis.config; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.cache.CacheProperties; +import org.springframework.boot.cache.autoconfigure.CacheProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java index b6374e9f0..87d0b7214 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java @@ -1,9 +1,6 @@ package cn.iocoder.yudao.framework.redis.config; -import cn.hutool.core.util.ReflectUtil; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.redisson.spring.starter.RedissonAutoConfigurationV2; +import org.redisson.spring.starter.RedissonAutoConfigurationV4; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -13,7 +10,7 @@ import org.springframework.data.redis.serializer.RedisSerializer; /** * Redis 配置类 */ -@AutoConfiguration(before = RedissonAutoConfigurationV2.class) // 目的:使用自己定义的 RedisTemplate Bean +@AutoConfiguration(before = RedissonAutoConfigurationV4.class) // 目的:使用自己定义的 RedisTemplate Bean public class YudaoRedisAutoConfiguration { /** @@ -35,11 +32,11 @@ public class YudaoRedisAutoConfiguration { return template; } + @SuppressWarnings("UnnecessaryLocalVariable") public static RedisSerializer buildRedisSerializer() { RedisSerializer json = RedisSerializer.json(); - // 解决 LocalDateTime 的序列化 - ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper"); - objectMapper.registerModules(new JavaTimeModule()); + // 特殊:spring boot 4.x 无需解决 LocalDateTime 的序列化 + // 原因:Spring Data Redis 4 使用 Jackson 3,RedisSerializer.json() 已支持 Java Time 类型 return json; } diff --git a/yudao-framework/yudao-spring-boot-starter-security/pom.xml b/yudao-framework/yudao-spring-boot-starter-security/pom.xml index c30bd7b2b..8a6445bda 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-security/pom.xml @@ -27,7 +27,7 @@ org.springframework.boot - spring-boot-starter-aop + spring-boot-starter-aspectj diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java index 6172bf6fd..f20d4f6ee 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java @@ -156,6 +156,7 @@ public class YudaoWebSecurityConfigurerAdapter { return webProperties.getAppApi().getPrefix() + url; } + @SuppressWarnings("removal") private Multimap getPermitAllUrlsFromAnnotations() { Multimap result = HashMultimap.create(); // 获得接口对应的 HandlerMethod 集合 @@ -166,7 +167,7 @@ public class YudaoWebSecurityConfigurerAdapter { for (Map.Entry entry : handlerMethodMap.entrySet()) { HandlerMethod handlerMethod = entry.getValue(); if (!handlerMethod.hasMethodAnnotation(PermitAll.class) // 方法级 - && !handlerMethod.getBeanType().isAnnotationPresent(PermitAll.class)) { // 接口级 + && !handlerMethod.getBeanType().isAnnotationPresent(PermitAll.class)) { // 接口级 continue; } Set urls = new HashSet<>(); diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/RedisTestConfiguration.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/RedisTestConfiguration.java index 46222911e..592b2b2bb 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/RedisTestConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/RedisTestConfiguration.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.framework.test.config; import com.github.fppt.jedismock.RedisServer; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @@ -16,14 +16,14 @@ import java.io.IOException; */ @Configuration(proxyBeanMethods = false) @Lazy(false) // 禁止延迟加载 -@EnableConfigurationProperties(RedisProperties.class) +@EnableConfigurationProperties(DataRedisProperties.class) public class RedisTestConfiguration { /** * 创建模拟的 Redis Server 服务器 */ @Bean - public RedisServer redisServer(RedisProperties properties) throws IOException { + public RedisServer redisServer(DataRedisProperties properties) throws IOException { RedisServer redisServer = new RedisServer(properties.getPort()); // 一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样,就导致端口被占用,无法启动。。。 try { diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/SqlInitializationTestConfiguration.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/SqlInitializationTestConfiguration.java index abaec9d84..583acd836 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/SqlInitializationTestConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/config/SqlInitializationTestConfiguration.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.test.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; -import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties; +import org.springframework.boot.sql.autoconfigure.init.SqlInitializationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer; @@ -17,7 +17,7 @@ import javax.sql.DataSource; /** * SQL 初始化的测试 Configuration * - * 为什么不使用 org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration 呢? + * 为什么不使用 org.springframework.boot.sql.autoconfigure.init.DataSourceInitializationConfiguration 呢? * 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true,开启延迟加载。此时,会导致 DataSourceInitializationConfiguration 初始化 * 不过呢,当前类的实现代码,基本是复制 DataSourceInitializationConfiguration 的哈! * diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java index ef0ea0558..acd2bb106 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java @@ -6,12 +6,12 @@ import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; import cn.iocoder.yudao.framework.test.config.RedisTestConfiguration; import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration; -import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure; +import com.alibaba.druid.spring.boot4.autoconfigure.DruidDataSourceAutoConfigure; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; -import org.redisson.spring.starter.RedissonAutoConfigurationV2; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.redisson.spring.starter.RedissonAutoConfigurationV4; +import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; @@ -43,8 +43,8 @@ public class BaseDbAndRedisUnitTest { // Redis 配置类 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类 - RedisAutoConfiguration.class, // Spring Redis 自动配置类 - RedissonAutoConfigurationV2.class, // Redisson 自动配置类 + DataRedisAutoConfiguration.class, // Spring Redis 自动配置类 + RedissonAutoConfigurationV4.class, // Redisson 自动配置类 // 其它配置类 SpringUtil.class diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbUnitTest.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbUnitTest.java index 98b06f95f..5b66f6507 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbUnitTest.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbUnitTest.java @@ -4,11 +4,11 @@ import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration; -import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure; +import com.alibaba.druid.spring.boot4.autoconfigure.DruidDataSourceAutoConfigure; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.github.yulichang.autoconfigure.MybatisPlusJoinAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java index 1d1c4fb0d..a4598232e 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.framework.test.core.ut; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; import cn.iocoder.yudao.framework.test.config.RedisTestConfiguration; -import org.redisson.spring.starter.RedissonAutoConfigurationV2; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.redisson.spring.starter.RedissonAutoConfigurationV4; +import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; @@ -23,9 +23,9 @@ public class BaseRedisUnitTest { @Import({ // Redis 配置类 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer - RedisAutoConfiguration.class, // Spring Redis 自动配置类 + DataRedisAutoConfiguration.class, // Spring Redis 自动配置类 YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类 - RedissonAutoConfigurationV2.class, // Redisson 自动配置类 + RedissonAutoConfigurationV4.class, // Redisson 自动配置类 // 其它配置类 SpringUtil.class diff --git a/yudao-framework/yudao-spring-boot-starter-web/pom.xml b/yudao-framework/yudao-spring-boot-starter-web/pom.xml index 196237ad3..8e106f5fc 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-web/pom.xml @@ -33,6 +33,14 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-jackson + + + org.springframework.boot + spring-boot-restclient + org.springframework.boot diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java index efaed77f1..cd0b2fabc 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java @@ -19,7 +19,6 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.FilterChain; @@ -35,6 +34,7 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Iterator; import java.util.Map; +import tools.jackson.databind.JsonNode; import static cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor.ATTRIBUTE_HANDLER_METHOD; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java index 1e252c052..a62d7b5e3 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.desensitize.core.base.annotation; import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler; import cn.iocoder.yudao.framework.desensitize.core.base.serializer.StringDesensitizeSerializer; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import tools.jackson.databind.annotation.JsonSerialize; import java.lang.annotation.*; diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java index 566b0d176..987336606 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java @@ -7,16 +7,15 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy; import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; import lombok.Getter; import lombok.Setter; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; @@ -28,7 +27,7 @@ import java.lang.reflect.Field; * @author gaibu */ @SuppressWarnings("rawtypes") -public class StringDesensitizeSerializer extends StdSerializer implements ContextualSerializer { +public class StringDesensitizeSerializer extends StdSerializer { @Getter @Setter @@ -39,7 +38,7 @@ public class StringDesensitizeSerializer extends StdSerializer implement } @Override - public JsonSerializer createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) { + public ValueSerializer createContextual(SerializationContext serializerProvider, BeanProperty beanProperty) { DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class); if (annotation == null) { return this; @@ -52,7 +51,7 @@ public class StringDesensitizeSerializer extends StdSerializer implement @Override @SuppressWarnings("unchecked") - public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException { + public void serialize(String value, JsonGenerator gen, SerializationContext serializerProvider) throws JacksonException { if (StrUtil.isBlank(value)) { gen.writeNull(); return; @@ -83,7 +82,7 @@ public class StringDesensitizeSerializer extends StdSerializer implement * @return 字段 */ private Field getField(JsonGenerator generator) { - String currentName = generator.getOutputContext().getCurrentName(); + String currentName = generator.streamWriteContext().currentName(); Object currentValue = generator.currentValue(); Class currentValueClass = currentValue.getClass(); return ReflectUtil.getField(currentValueClass, currentName); diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java index 280f8da34..3225d4d68 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java @@ -4,18 +4,18 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer; import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; -import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration; +import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.LocalDateDeserializer; +import tools.jackson.databind.ext.javatime.deser.LocalTimeDeserializer; +import tools.jackson.databind.ext.javatime.ser.LocalDateSerializer; +import tools.jackson.databind.ext.javatime.ser.LocalTimeSerializer; +import tools.jackson.databind.module.SimpleModule; import java.time.LocalDate; import java.time.LocalDateTime; @@ -29,26 +29,15 @@ public class YudaoJacksonAutoConfiguration { * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求) */ @Bean - public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() { - return builder -> builder - // Long -> Number - .serializerByType(Long.class, NumberSerializer.INSTANCE) - .serializerByType(Long.TYPE, NumberSerializer.INSTANCE) - // LocalDate / LocalTime - .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE) - .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE) - .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE) - .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE) - // LocalDateTime < - > EpochMillis - .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) - .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); + public JsonMapperBuilderCustomizer ldtEpochMillisCustomizer(JacksonModule timestampSupportModuleBean) { + return builder -> builder.addModule(timestampSupportModuleBean); } /** * 以 Bean 形式暴露 Module(Boot 会自动注册到所有 ObjectMapper) */ @Bean - public Module timestampSupportModuleBean() { + public JacksonModule timestampSupportModuleBean() { SimpleModule m = new SimpleModule("TimestampSupportModule"); // Long -> Number,避免前端精度丢失 m.addSerializer(Long.class, NumberSerializer.INSTANCE); diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java index 745c9775e..a559c80c7 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java @@ -14,11 +14,11 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.restclient.RestTemplateBuilder; +import org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.webmvc.autoconfigure.WebMvcRegistrations; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 91dc38967..8821683b3 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -15,7 +15,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.google.common.util.concurrent.UncheckedExecutionException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; @@ -39,6 +38,7 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.resource.NoResourceFoundException; +import tools.jackson.databind.exc.InvalidFormatException; import java.time.LocalDateTime; import java.util.List; diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java index fcac987a7..4a983e11e 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java @@ -5,12 +5,11 @@ import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner; import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner; import cn.iocoder.yudao.framework.xss.core.filter.XssFilter; import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -38,17 +37,17 @@ public class YudaoXssAutoConfiguration implements WebMvcConfigurer { /** * 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤 * - * @return Jackson2ObjectMapperBuilderCustomizer + * @return JsonMapperBuilderCustomizer */ @Bean @ConditionalOnMissingBean(name = "xssJacksonCustomizer") @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true") - public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties, - PathMatcher pathMatcher, - XssCleaner xssCleaner) { + public JsonMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties, + PathMatcher pathMatcher, + XssCleaner xssCleaner) { // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理 - return builder -> - builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner)); + return builder -> builder.addModule(new tools.jackson.databind.module.SimpleModule("XssStringModule") + .addDeserializer(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner))); } /** diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java index 047b19d38..4874ebd00 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java @@ -3,16 +3,15 @@ package cn.iocoder.yudao.framework.xss.core.json; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.xss.config.XssProperties; import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StringDeserializer; import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.util.PathMatcher; - -import java.io.IOException; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.deser.jdk.StringDeserializer; /** * XSS 过滤 jackson 反序列化器。 @@ -36,19 +35,19 @@ public class XssStringJsonDeserializer extends StringDeserializer { private final XssCleaner xssCleaner; @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + public String deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException { // 1. 白名单 URL 的处理 HttpServletRequest request = ServletUtils.getRequest(); if (request != null) { String uri = ServletUtils.getRequest().getRequestURI(); if (properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri))) { - return p.getText(); + return p.getString(); } } // 2. 真正使用 xssCleaner 进行过滤 if (p.hasToken(JsonToken.VALUE_STRING)) { - return xssCleaner.clean(p.getText()); + return xssCleaner.clean(p.getString()); } JsonToken t = p.currentToken(); // [databind#381] diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java index 9a29f7a36..6e4a1c582 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java @@ -107,6 +107,7 @@ public class AccessLogFilter implements GlobalFilter, Ordered { } @Override + @SuppressWarnings("removal") public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 将 Request 中可以直接获取到的参数,设置到网关日志 ServerHttpRequest request = exchange.getRequest(); @@ -117,7 +118,7 @@ public class AccessLogFilter implements GlobalFilter, Ordered { gatewayLog.setRequestMethod(request.getMethod().name()); gatewayLog.setRequestUrl(request.getURI().getRawPath()); gatewayLog.setQueryParams(request.getQueryParams()); - gatewayLog.setRequestHeaders(request.getHeaders()); + gatewayLog.setRequestHeaders(request.getHeaders().asMultiValueMap()); gatewayLog.setStartTime(LocalDateTime.now()); gatewayLog.setUserIp(WebFrameworkUtils.getClientIP(exchange)); @@ -192,7 +193,7 @@ public class AccessLogFilter implements GlobalFilter, Ordered { // 设置其它字段 gatewayLog.setUserId(SecurityFrameworkUtils.getLoginUserId(exchange)); gatewayLog.setUserType(SecurityFrameworkUtils.getLoginUserType(exchange)); - gatewayLog.setResponseHeaders(response.getHeaders()); + gatewayLog.setResponseHeaders(response.getHeaders().asMultiValueMap()); gatewayLog.setHttpStatus((HttpStatus) response.getStatusCode()); // 获取响应类型,如果是 json 就打印 diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/security/TokenAuthenticationFilter.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/security/TokenAuthenticationFilter.java index f2e96719c..8daff57c3 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/security/TokenAuthenticationFilter.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/security/TokenAuthenticationFilter.java @@ -9,7 +9,7 @@ import cn.iocoder.yudao.gateway.util.SecurityFrameworkUtils; import cn.iocoder.yudao.gateway.util.WebFrameworkUtils; import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO; -import com.fasterxml.jackson.core.type.TypeReference; +import tools.jackson.core.type.TypeReference; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction; diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java index e967cb775..5757352ac 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/handler/GlobalExceptionHandler.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.gateway.handler; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.gateway.util.WebFrameworkUtils; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.boot.webflux.error.ErrorWebExceptionHandler; import org.springframework.core.annotation.Order; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/jackson/GatewayJacksonAutoConfiguration.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/jackson/GatewayJacksonAutoConfiguration.java index c7ccadf70..7877f157f 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/jackson/GatewayJacksonAutoConfiguration.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/jackson/GatewayJacksonAutoConfiguration.java @@ -4,20 +4,21 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer; import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Configuration; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; -import org.springframework.boot.web.codec.CodecCustomizer; +import org.springframework.boot.http.codec.CodecCustomizer; +import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; -import org.springframework.http.codec.json.Jackson2JsonDecoder; -import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.codec.json.JacksonJsonDecoder; +import org.springframework.http.codec.json.JacksonJsonEncoder; +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.LocalDateDeserializer; +import tools.jackson.databind.ext.javatime.deser.LocalTimeDeserializer; +import tools.jackson.databind.ext.javatime.ser.LocalDateSerializer; +import tools.jackson.databind.ext.javatime.ser.LocalTimeSerializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; import java.time.LocalDate; import java.time.LocalDateTime; @@ -31,26 +32,15 @@ public class GatewayJacksonAutoConfiguration { * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求) */ @Bean - public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() { - return builder -> builder - // Long -> Number - .serializerByType(Long.class, NumberSerializer.INSTANCE) - .serializerByType(Long.TYPE, NumberSerializer.INSTANCE) - // LocalDate / LocalTime - .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE) - .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE) - .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE) - .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE) - // LocalDateTime < - > EpochMillis - .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) - .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); + public JsonMapperBuilderCustomizer ldtEpochMillisCustomizer(JacksonModule timestampSupportModuleBean) { + return builder -> builder.addModule(timestampSupportModuleBean); } /** * 以 Bean 形式暴露 Module(Boot 会自动注册到所有 ObjectMapper) */ @Bean - public Module timestampSupportModuleBean() { + public JacksonModule timestampSupportModuleBean() { SimpleModule m = new SimpleModule("TimestampSupportModule"); // Long -> Number m.addSerializer(Long.class, NumberSerializer.INSTANCE); @@ -81,12 +71,12 @@ public class GatewayJacksonAutoConfiguration { * WebFlux 场景:强制默认编解码器使用同一个 ObjectMapper */ @Bean - public CodecCustomizer unifyJackson(ObjectMapper om) { + public CodecCustomizer unifyJackson(JsonMapper om) { return configurer -> { - Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(om); - Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(om); - configurer.defaultCodecs().jackson2JsonDecoder(decoder); - configurer.defaultCodecs().jackson2JsonEncoder(encoder); + JacksonJsonDecoder decoder = new JacksonJsonDecoder(om); + JacksonJsonEncoder encoder = new JacksonJsonEncoder(om); + configurer.defaultCodecs().jacksonJsonDecoder(decoder); + configurer.defaultCodecs().jacksonJsonEncoder(encoder); }; } } diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java index d1c0eeaf0..03fbbdfef 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/SecurityFrameworkUtils.java @@ -69,7 +69,7 @@ public class SecurityFrameworkUtils { */ public static ServerWebExchange removeLoginUser(ServerWebExchange exchange) { // 如果不包含,直接返回 - if (!exchange.getRequest().getHeaders().containsKey(LOGIN_USER_HEADER)) { + if (!exchange.getRequest().getHeaders().containsHeader(LOGIN_USER_HEADER)) { return exchange; } // 如果包含,则移除。参考 RemoveRequestHeaderGatewayFilterFactory 实现 diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java index bc28664bc..001f6fbb8 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java @@ -54,11 +54,10 @@ public class WebFrameworkUtils { * @param exchange 响应 * @param object 对象,会序列化成 JSON 字符串 */ - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 public static Mono writeJSON(ServerWebExchange exchange, Object object) { // 设置 header ServerHttpResponse response = exchange.getResponse(); - response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); // 设置 body return response.writeWith(Mono.fromSupplier(() -> { DataBufferFactory bufferFactory = response.bufferFactory(); diff --git a/yudao-gateway/src/main/resources/application.yaml b/yudao-gateway/src/main/resources/application.yaml index 4b97b5a8c..68dfe1ade 100644 --- a/yudao-gateway/src/main/resources/application.yaml +++ b/yudao-gateway/src/main/resources/application.yaml @@ -9,14 +9,6 @@ spring: codecs: max-in-memory-size: 10MB # 调整缓冲区大小https://gitee.com/zhijiantianya/yudao-cloud/pulls/176 - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean - main: allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 diff --git a/yudao-module-ai/yudao-module-ai-server/pom.xml b/yudao-module-ai/yudao-module-ai-server/pom.xml index 0871d8e75..d9613ccf1 100644 --- a/yudao-module-ai/yudao-module-ai-server/pom.xml +++ b/yudao-module-ai/yudao-module-ai-server/pom.xml @@ -19,9 +19,9 @@ 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno - 1.1.5 + 2.0.0 - 1.1.2.2 + 2.0.0-M1.1 1.2.6 @@ -122,11 +122,6 @@ - - org.springframework.ai - spring-ai-starter-model-azure-openai - ${spring-ai.version} - org.springframework.ai spring-ai-starter-model-anthropic @@ -147,18 +142,6 @@ spring-ai-starter-model-stability-ai ${spring-ai.version} - - - org.springframework.ai - spring-ai-starter-model-zhipuai - ${spring-ai.version} - - - org.springframework.ai - spring-ai-starter-model-minimax - ${spring-ai.version} - - com.alibaba.cloud.ai @@ -166,19 +149,6 @@ ${alibaba-ai.version} - - - org.springaicommunity - qianfan-spring-boot-starter - 1.0.0 - - - - org.springaicommunity - moonshot-spring-boot-starter - 1.0.0 - - @@ -247,12 +217,22 @@ + + org.springframework.ai + spring-ai-autoconfigure-mcp-server-common + ${spring-ai.version} + org.springframework.ai spring-ai-starter-mcp-client ${spring-ai.version} + + org.springframework.ai + spring-ai-autoconfigure-mcp-client-common + ${spring-ai.version} + @@ -315,4 +295,4 @@ - \ No newline at end of file + diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java index 9009cbc8c..61336f148 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java @@ -10,25 +10,38 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient; import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient; import cn.iocoder.yudao.module.ai.tool.method.PersonService; +import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; +import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; +import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel; +import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions; +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; import io.micrometer.observation.ObservationRegistry; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; import org.springframework.ai.deepseek.api.DeepSeekApi; +import org.springframework.ai.document.MetadataMode; import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.retry.RetryUtils; import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; import org.springframework.ai.tokenizer.TokenCountEstimator; @@ -40,11 +53,11 @@ import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStorePr import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; -import java.util.Optional; /** * 芋道 AI 自动配置 @@ -74,6 +87,73 @@ public class AiAutoConfiguration { // ========== 各种 AI Client 创建 ========== + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.ai.dashscope.api-key") + public DashScopeChatModel dashScopeChatModel(@Value("${spring.ai.dashscope.api-key}") String apiKey, + ToolCallingManager toolCallingManager, + ObservationRegistry observationRegistry) { + return buildTongYiChatModel(apiKey, toolCallingManager, observationRegistry); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.ai.dashscope.api-key") + public DashScopeImageModel dashScopeImageModel(@Value("${spring.ai.dashscope.api-key}") String apiKey, + ObservationRegistry observationRegistry) { + return buildTongYiImagesModel(apiKey, observationRegistry); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.ai.dashscope.api-key") + public DashScopeEmbeddingModel dashScopeEmbeddingModel(@Value("${spring.ai.dashscope.api-key}") String apiKey, + ObservationRegistry observationRegistry) { + return buildTongYiEmbeddingModel(apiKey, null, observationRegistry); + } + + public static DashScopeChatModel buildTongYiChatModel(String apiKey) { + return buildTongYiChatModel(apiKey, getToolCallingManager(), getObservationRegistry()); + } + + private static DashScopeChatModel buildTongYiChatModel(String apiKey, ToolCallingManager toolCallingManager, + ObservationRegistry observationRegistry) { + DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); + DashScopeChatOptions options = DashScopeChatOptions.builder() + .model(DashScopeApi.DEFAULT_CHAT_MODEL) + .temperature(0.7) + .build(); + return new DashScopeChatModel(dashScopeApi, options, toolCallingManager, RetryUtils.DEFAULT_RETRY_TEMPLATE, + observationRegistry); + } + + public static DashScopeImageModel buildTongYiImagesModel(String apiKey) { + return buildTongYiImagesModel(apiKey, getObservationRegistry()); + } + + private static DashScopeImageModel buildTongYiImagesModel(String apiKey, ObservationRegistry observationRegistry) { + DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(apiKey).build(); + DashScopeImageOptions options = DashScopeImageOptions.builder() + .model(DashScopeImageApi.DEFAULT_IMAGE_MODEL) + .build(); + return new DashScopeImageModel(dashScopeImageApi, options, RetryUtils.DEFAULT_RETRY_TEMPLATE, + observationRegistry); + } + + public static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { + return buildTongYiEmbeddingModel(apiKey, model, getObservationRegistry()); + } + + private static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model, + ObservationRegistry observationRegistry) { + DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); + DashScopeEmbeddingOptions options = DashScopeEmbeddingOptions.builder() + .model(StrUtil.blankToDefault(model, DashScopeApi.DEFAULT_EMBEDDING_MODEL)) + .build(); + return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, options, RetryUtils.DEFAULT_RETRY_TEMPLATE, + observationRegistry); + } + @Bean @ConditionalOnProperty(value = "yudao.ai.gemini.enable", havingValue = "true") public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) { @@ -86,18 +166,14 @@ public class AiAutoConfiguration { properties.setModel(GeminiChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl(GeminiChatModel.BASE_URL) - .completionsPath(GeminiChatModel.COMPLETE_PATH) .apiKey(properties.getApiKey()) - .build()) - .defaultOptions(OpenAiChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - .toolCallingManager(getToolCallingManager()) .build(); return new GeminiChatModel(openAiChatModel); } @@ -114,18 +190,14 @@ public class AiAutoConfiguration { properties.setModel(DouBaoChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl(DouBaoChatModel.BASE_URL) - .completionsPath(DouBaoChatModel.COMPLETE_PATH) .apiKey(properties.getApiKey()) - .build()) - .defaultOptions(OpenAiChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - .toolCallingManager(getToolCallingManager()) .build(); return new DouBaoChatModel(openAiChatModel); } @@ -146,13 +218,12 @@ public class AiAutoConfiguration { .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) .apiKey(properties.getApiKey()) .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - .toolCallingManager(getToolCallingManager()) .build(); return new SiliconFlowChatModel(openAiChatModel); } @@ -181,13 +252,12 @@ public class AiAutoConfiguration { .completionsPath(HunYuanChatModel.COMPLETE_PATH) .apiKey(properties.getApiKey()) .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - .toolCallingManager(getToolCallingManager()) .build(); return new HunYuanChatModel(openAiChatModel); } @@ -203,23 +273,17 @@ public class AiAutoConfiguration { if (StrUtil.isEmpty(properties.getModel())) { properties.setModel(XingHuoChatModel.MODEL_DEFAULT); } - OpenAiApi.Builder builder = OpenAiApi.builder() - .baseUrl(XingHuoChatModel.BASE_URL_V1) - .apiKey(properties.getAppKey() + ":" + properties.getSecretKey()); - if ("x1".equals(properties.getModel())) { - builder.baseUrl(XingHuoChatModel.BASE_URL_V2) - .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2); - } + String baseUrl = "x1".equals(properties.getModel()) ? XingHuoChatModel.BASE_URL_V2 + : XingHuoChatModel.BASE_URL_V1; OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(builder.build()) - .defaultOptions(OpenAiChatOptions.builder() + .options(OpenAiChatOptions.builder() + .baseUrl(baseUrl) + .apiKey(properties.getAppKey() + ":" + properties.getSecretKey()) .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - // TODO @芋艿:星火的 function call 有 bug,会报 ToolResponseMessage must have an id 错误!!! - .toolCallingManager(getToolCallingManager()) .build(); return new XingHuoChatModel(openAiChatModel); } @@ -236,21 +300,116 @@ public class AiAutoConfiguration { properties.setModel(BaiChuanChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl(BaiChuanChatModel.BASE_URL) .apiKey(properties.getApiKey()) - .build()) - .defaultOptions(OpenAiChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - .toolCallingManager(getToolCallingManager()) .build(); return new BaiChuanChatModel(openAiChatModel); } + @Bean + @ConditionalOnProperty(value = "yudao.ai.yiyan.enable", havingValue = "true") + public YiYanChatModel yiYanChatClient(YudaoAiProperties yudaoAiProperties) { + YudaoAiProperties.YiYan properties = yudaoAiProperties.getYiyan(); + return buildYiYanChatClient(properties); + } + + public YiYanChatModel buildYiYanChatClient(YudaoAiProperties.YiYan properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(YiYanChatModel.MODEL_DEFAULT); + } + return new YiYanChatModel(buildDeepSeekCompatibleChatModel( + StrUtil.blankToDefault(properties.getBaseUrl(), YiYanChatModel.BASE_URL), + null, properties.getApiKey(), properties.getModel(), properties.getTemperature(), + properties.getMaxTokens(), properties.getTopP())); + } + + @Bean + @ConditionalOnProperty(value = "yudao.ai.zhipu.enable", havingValue = "true") + public ZhiPuChatModel zhiPuChatClient(YudaoAiProperties yudaoAiProperties) { + YudaoAiProperties.ZhiPu properties = yudaoAiProperties.getZhipu(); + return buildZhiPuChatClient(properties); + } + + public ZhiPuChatModel buildZhiPuChatClient(YudaoAiProperties.ZhiPu properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(ZhiPuChatModel.MODEL_DEFAULT); + } + DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder() + .deepSeekApi(DeepSeekApi.builder() + .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), ZhiPuChatModel.BASE_URL)) + .apiKey(properties.getApiKey()) + .build()) + .options(DeepSeekChatOptions.builder() + .model(properties.getModel()) + .temperature(properties.getTemperature()) + .maxTokens(properties.getMaxTokens()) + .topP(properties.getTopP()) + .build()) + .build(); + return new ZhiPuChatModel(deepSeekChatModel); + } + + @Bean + @ConditionalOnProperty(value = "yudao.ai.minimax.enable", havingValue = "true") + public MiniMaxChatModel miniMaxChatClient(YudaoAiProperties yudaoAiProperties) { + YudaoAiProperties.MiniMax properties = yudaoAiProperties.getMinimax(); + return buildMiniMaxChatClient(properties); + } + + public MiniMaxChatModel buildMiniMaxChatClient(YudaoAiProperties.MiniMax properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(MiniMaxChatModel.MODEL_DEFAULT); + } + return new MiniMaxChatModel(buildDeepSeekCompatibleChatModel( + StrUtil.blankToDefault(properties.getBaseUrl(), MiniMaxChatModel.BASE_URL), + null, properties.getApiKey(), properties.getModel(), properties.getTemperature(), + properties.getMaxTokens(), properties.getTopP())); + } + + @Bean + @ConditionalOnProperty(value = "yudao.ai.moonshot.enable", havingValue = "true") + public MoonshotChatModel moonshotChatClient(YudaoAiProperties yudaoAiProperties) { + YudaoAiProperties.Moonshot properties = yudaoAiProperties.getMoonshot(); + return buildMoonshotChatClient(properties); + } + + public MoonshotChatModel buildMoonshotChatClient(YudaoAiProperties.Moonshot properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(MoonshotChatModel.MODEL_DEFAULT); + } + return new MoonshotChatModel(buildDeepSeekCompatibleChatModel( + StrUtil.blankToDefault(properties.getBaseUrl(), MoonshotChatModel.BASE_URL), + MoonshotChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(), + properties.getTemperature(), properties.getMaxTokens(), properties.getTopP())); + } + + private static DeepSeekChatModel buildDeepSeekCompatibleChatModel(String baseUrl, String completionsPath, + String apiKey, String model, + Double temperature, Integer maxTokens, + Double topP) { + DeepSeekApi.Builder apiBuilder = DeepSeekApi.builder() + .baseUrl(baseUrl) + .apiKey(apiKey); + if (StrUtil.isNotEmpty(completionsPath)) { + apiBuilder.completionsPath(completionsPath); + } + return DeepSeekChatModel.builder() + .deepSeekApi(apiBuilder.build()) + .options(DeepSeekChatOptions.builder() + .model(model) + .temperature(temperature) + .maxTokens(maxTokens) + .topP(topP) + .build()) + .build(); + } + @Bean @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true") public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) { @@ -269,21 +428,16 @@ public class AiAutoConfiguration { properties.setModel(GrokChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() - .baseUrl(Optional.ofNullable(properties.getBaseUrl()) - .orElse(GrokChatModel.BASE_URL)) - .completionsPath(GrokChatModel.COMPLETE_PATH) + .options(OpenAiChatOptions.builder() + .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), GrokChatModel.BASE_URL)) .apiKey(properties.getApiKey()) - .build()) - .defaultOptions(OpenAiChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) - .toolCallingManager(getToolCallingManager()) .build(); - return new DouBaoChatModel(openAiChatModel); + return new GrokChatModel(openAiChatModel); } // ========== RAG 相关 ========== @@ -302,6 +456,10 @@ public class AiAutoConfiguration { return SpringUtil.getBean(ToolCallingManager.class); } + private static ObservationRegistry getObservationRegistry() { + return SpringUtil.getBean(ObservationRegistry.class); + } + // ========== Web Search 相关 ========== @Bean @@ -320,4 +478,4 @@ public class AiAutoConfiguration { return List.of(ToolCallbacks.from(personService)); } -} \ No newline at end of file +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java index 986c24c18..e750eb0c3 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java @@ -43,6 +43,26 @@ public class YudaoAiProperties { */ private BaiChuan baichuan; + /** + * 文心一言 + */ + private YiYan yiyan; + + /** + * 智谱 + */ + private ZhiPu zhipu; + + /** + * MiniMax + */ + private MiniMax minimax; + + /** + * 月之暗面 + */ + private Moonshot moonshot; + /** * Midjourney 绘图 */ @@ -140,6 +160,62 @@ public class YudaoAiProperties { } + @Data + public static class YiYan { + + private String enable; + private String baseUrl; + private String apiKey; + + private String model; + private Double temperature; + private Integer maxTokens; + private Double topP; + + } + + @Data + public static class ZhiPu { + + private String enable; + private String baseUrl; + private String apiKey; + + private String model; + private Double temperature; + private Integer maxTokens; + private Double topP; + + } + + @Data + public static class MiniMax { + + private String enable; + private String baseUrl; + private String apiKey; + + private String model; + private Double temperature; + private Integer maxTokens; + private Double topP; + + } + + @Data + public static class Moonshot { + + private String enable; + private String baseUrl; + private String apiKey; + + private String model; + private Double temperature; + private Integer maxTokens; + private Double topP; + + } + @Data public static class Midjourney { diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java index f8067dea2..1604d012b 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java @@ -8,7 +8,6 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration; import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties; @@ -17,12 +16,16 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel; import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration; import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration; import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration; @@ -30,30 +33,14 @@ import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; -import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; -import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; +import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel; +import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; -import com.azure.ai.openai.OpenAIClientBuilder; -import com.azure.core.credential.KeyCredential; import io.micrometer.observation.ObservationRegistry; import io.milvus.client.MilvusServiceClient; import io.qdrant.client.QdrantClient; import io.qdrant.client.QdrantGrpcClient; import lombok.SneakyThrows; -import org.springaicommunity.moonshot.MoonshotChatModel; -import org.springaicommunity.moonshot.MoonshotChatOptions; -import org.springaicommunity.moonshot.api.MoonshotApi; -import org.springaicommunity.moonshot.autoconfigure.MoonshotChatAutoConfiguration; -import org.springaicommunity.qianfan.QianFanChatModel; -import org.springaicommunity.qianfan.QianFanEmbeddingModel; -import org.springaicommunity.qianfan.QianFanEmbeddingOptions; -import org.springaicommunity.qianfan.QianFanImageModel; -import org.springaicommunity.qianfan.api.QianFanApi; -import org.springaicommunity.qianfan.api.QianFanImageApi; -import org.springaicommunity.qianfan.autoconfigure.QianFanChatAutoConfiguration; -import org.springaicommunity.qianfan.autoconfigure.QianFanEmbeddingAutoConfiguration; -import org.springframework.ai.azure.openai.AzureOpenAiChatModel; -import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; @@ -63,27 +50,13 @@ import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention; import org.springframework.ai.image.ImageModel; -import org.springframework.ai.minimax.MiniMaxChatModel; -import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.minimax.MiniMaxEmbeddingModel; -import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; -import org.springframework.ai.minimax.api.MiniMaxApi; import org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration; -import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration; -import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration; -import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties; import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; -import org.springframework.ai.model.minimax.autoconfigure.MiniMaxChatAutoConfiguration; -import org.springframework.ai.model.minimax.autoconfigure.MiniMaxEmbeddingAutoConfiguration; import org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration; import org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration; import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration; import org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration; import org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration; -import org.springframework.ai.model.tool.ToolCallingManager; -import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration; -import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiEmbeddingAutoConfiguration; -import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfiguration; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaEmbeddingModel; import org.springframework.ai.ollama.api.OllamaApi; @@ -92,11 +65,10 @@ import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.OpenAiEmbeddingOptions; import org.springframework.ai.openai.OpenAiImageModel; -import org.springframework.ai.openai.api.OpenAiApi; -import org.springframework.ai.openai.api.OpenAiImageApi; -import org.springframework.ai.openai.api.common.OpenAiApiConstants; import org.springframework.ai.anthropic.AnthropicChatModel; -import org.springframework.ai.anthropic.api.AnthropicApi; +import org.springframework.ai.anthropic.AnthropicChatOptions; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.ai.openai.OpenAiImageOptions; import org.springframework.ai.stabilityai.StabilityAiImageModel; import org.springframework.ai.stabilityai.api.StabilityAiApi; import org.springframework.ai.vectorstore.SimpleVectorStore; @@ -114,14 +86,13 @@ import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStore import org.springframework.ai.vectorstore.redis.RedisVectorStore; import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration; import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties; -import org.springframework.ai.zhipuai.*; -import org.springframework.ai.zhipuai.api.ZhiPuAiApi; -import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; -import org.springframework.web.client.RestClient; -import redis.clients.jedis.JedisPooled; +import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.RedisClient; import java.io.File; import java.time.Duration; @@ -131,7 +102,6 @@ import java.util.Timer; import java.util.TimerTask; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE; /** * AI Model 模型工厂的实现类 @@ -179,7 +149,7 @@ public class AiModelFactoryImpl implements AiModelFactory { case OLLAMA: return buildOllamaChatModel(url); case GROK: - return buildGrokChatModel(apiKey,url); + return buildGrokChatModel(apiKey, url); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -193,7 +163,7 @@ public class AiModelFactoryImpl implements AiModelFactory { case TONG_YI: return SpringUtil.getBean(DashScopeChatModel.class); case YI_YAN: - return SpringUtil.getBean(QianFanChatModel.class); + return SpringUtil.getBean(YiYanChatModel.class); case DEEP_SEEK: return SpringUtil.getBean(DeepSeekChatModel.class); case DOU_BAO: @@ -203,7 +173,7 @@ public class AiModelFactoryImpl implements AiModelFactory { case SILICON_FLOW: return SpringUtil.getBean(SiliconFlowChatModel.class); case ZHI_PU: - return SpringUtil.getBean(ZhiPuAiChatModel.class); + return SpringUtil.getBean(ZhiPuChatModel.class); case MINI_MAX: return SpringUtil.getBean(MiniMaxChatModel.class); case MOONSHOT: @@ -214,8 +184,6 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(BaiChuanChatModel.class); case OPENAI: return SpringUtil.getBean(OpenAiChatModel.class); - case AZURE_OPENAI: - return SpringUtil.getBean(AzureOpenAiChatModel.class); case ANTHROPIC: return SpringUtil.getBean(AnthropicChatModel.class); case GEMINI: @@ -233,10 +201,6 @@ public class AiModelFactoryImpl implements AiModelFactory { switch (platform) { case TONG_YI: return SpringUtil.getBean(DashScopeImageModel.class); - case YI_YAN: - return SpringUtil.getBean(QianFanImageModel.class); - case ZHI_PU: - return SpringUtil.getBean(ZhiPuAiImageModel.class); case SILICON_FLOW: return SpringUtil.getBean(SiliconFlowImageModel.class); case OPENAI: @@ -254,10 +218,6 @@ public class AiModelFactoryImpl implements AiModelFactory { switch (platform) { case TONG_YI: return buildTongYiImagesModel(apiKey); - case YI_YAN: - return buildQianFanImageModel(apiKey); - case ZHI_PU: - return buildZhiPuAiImageModel(apiKey, url); case OPENAI: return buildOpenAiImageModel(apiKey, url); case SILICON_FLOW: @@ -294,16 +254,8 @@ public class AiModelFactoryImpl implements AiModelFactory { switch (platform) { case TONG_YI: return buildTongYiEmbeddingModel(apiKey, model); - case YI_YAN: - return buildYiYanEmbeddingModel(apiKey, model); - case ZHI_PU: - return buildZhiPuEmbeddingModel(apiKey, url, model); - case MINI_MAX: - return buildMiniMaxEmbeddingModel(apiKey, url, model); case OPENAI: return buildOpenAiEmbeddingModel(apiKey, url, model); - case AZURE_OPENAI: - return buildAzureOpenAiEmbeddingModel(apiKey, url, model); case OLLAMA: return buildOllamaEmbeddingModel(url, model); default: @@ -347,50 +299,20 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link DashScopeChatAutoConfiguration} 的 dashscopeChatModel 方法 */ private static DashScopeChatModel buildTongYiChatModel(String key) { - DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(key).build(); - DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) - .withTemperature(0.7).build(); - return DashScopeChatModel.builder() - .dashScopeApi(dashScopeApi) - .defaultOptions(options) - .toolCallingManager(getToolCallingManager()) - .build(); + return AiAutoConfiguration.buildTongYiChatModel(key); } /** * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法 */ private static DashScopeImageModel buildTongYiImagesModel(String key) { - DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(key).build(); - return DashScopeImageModel.builder() - .dashScopeApi(dashScopeImageApi) - .build(); + return AiAutoConfiguration.buildTongYiImagesModel(key); } - /** - * 可参考 {@link QianFanChatAutoConfiguration} 的 qianFanChatModel 方法 - */ - private static QianFanChatModel buildYiYanChatModel(String key) { - // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 - List keys = StrUtil.split(key, '|'); - Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); - String appKey = keys.get(0); - String secretKey = keys.get(1); - QianFanApi qianFanApi = new QianFanApi(appKey, secretKey); - return new QianFanChatModel(qianFanApi); - } - - /** - * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanImageModel 方法 - */ - private QianFanImageModel buildQianFanImageModel(String key) { - // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 - List keys = StrUtil.split(key, '|'); - Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); - String appKey = keys.get(0); - String secretKey = keys.get(1); - QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey); - return new QianFanImageModel(qianFanApi); + private ChatModel buildYiYanChatModel(String apiKey) { + YudaoAiProperties.YiYan properties = new YudaoAiProperties.YiYan() + .setApiKey(apiKey); + return new AiAutoConfiguration().buildYiYanChatClient(properties); } /** @@ -402,8 +324,7 @@ public class AiModelFactoryImpl implements AiModelFactory { .temperature(0.7).build(); return DeepSeekChatModel.builder() .deepSeekApi(deepSeekApi) - .defaultOptions(options) - .toolCallingManager(getToolCallingManager()) + .options(options) .build(); } @@ -435,52 +356,30 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法 + * 可参考 {@link AiAutoConfiguration#zhiPuChatClient(YudaoAiProperties)} */ - private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { - ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey); - if (StrUtil.isNotEmpty(url)) { - zhiPuAiApiBuilder.baseUrl(url); - } - ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); - return new ZhiPuAiChatModel(zhiPuAiApiBuilder.build(), options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE, - getObservationRegistry().getIfAvailable()); + private ZhiPuChatModel buildZhiPuChatModel(String apiKey, String url) { + YudaoAiProperties.ZhiPu properties = new YudaoAiProperties.ZhiPu() + .setBaseUrl(url).setApiKey(apiKey); + return new AiAutoConfiguration().buildZhiPuChatClient(properties); } /** - * 可参考 {@link ZhiPuAiImageAutoConfiguration} 的 zhiPuAiImageModel 方法 - */ - private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { - ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey) - : new ZhiPuAiImageApi(url, apiKey, RestClient.builder()); - return new ZhiPuAiImageModel(zhiPuAiApi); - } - - /** - * 可参考 {@link MiniMaxChatAutoConfiguration} 的 miniMaxChatModel 方法 + * 可参考 {@link AiAutoConfiguration#miniMaxChatClient(YudaoAiProperties)} */ private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { - MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) - : new MiniMaxApi(url, apiKey); - MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); - return new MiniMaxChatModel(miniMaxApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE); + YudaoAiProperties.MiniMax properties = new YudaoAiProperties.MiniMax() + .setBaseUrl(url).setApiKey(apiKey); + return new AiAutoConfiguration().buildMiniMaxChatClient(properties); } /** - * 可参考 {@link MoonshotChatAutoConfiguration} 的 moonshotChatModel 方法 + * 可参考 {@link AiAutoConfiguration#moonshotChatClient(YudaoAiProperties)} */ private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { - MoonshotApi.Builder moonshotApiBuilder = MoonshotApi.builder() - .apiKey(apiKey); - if (StrUtil.isNotEmpty(url)) { - moonshotApiBuilder.baseUrl(url); - } - MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); - return MoonshotChatModel.builder() - .moonshotApi(moonshotApiBuilder.build()) - .defaultOptions(options) - .toolCallingManager(getToolCallingManager()) - .build(); + YudaoAiProperties.Moonshot properties = new YudaoAiProperties.Moonshot() + .setBaseUrl(url).setApiKey(apiKey); + return new AiAutoConfiguration().buildMoonshotChatClient(properties); } /** @@ -507,39 +406,37 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link OpenAiChatAutoConfiguration} 的 openAiChatModel 方法 */ private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { - url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); - OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); return OpenAiChatModel.builder() - .openAiApi(openAiApi) - .toolCallingManager(getToolCallingManager()) + .options(buildOpenAiChatOptions(openAiToken, url).build()) .build(); } - /** - * 可参考 {@link AzureOpenAiChatAutoConfiguration} - */ - private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) { - // TODO @芋艿:使用前,请测试,暂时没密钥!!! - OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() - .endpoint(url).credential(new KeyCredential(apiKey)); - return AzureOpenAiChatModel.builder() - .openAIClientBuilder(openAIClientBuilder) - .toolCallingManager(getToolCallingManager()) + private static OpenAiChatModel buildAzureOpenAiChatModel(String openAiToken, String url) { + return OpenAiChatModel.builder() + .options(buildOpenAiChatOptions(openAiToken, url) + .azure(true) + .build()) .build(); } + private static OpenAiChatOptions.Builder buildOpenAiChatOptions(String apiKey, String url) { + OpenAiChatOptions.Builder optionsBuilder = OpenAiChatOptions.builder().apiKey(apiKey); + if (StrUtil.isNotEmpty(url)) { + optionsBuilder.baseUrl(url); + } + return optionsBuilder; + } + /** * 可参考 {@link AnthropicChatAutoConfiguration} 的 anthropicApi 方法 */ private static AnthropicChatModel buildAnthropicChatModel(String apiKey, String url) { - AnthropicApi.Builder builder = AnthropicApi.builder().apiKey(apiKey); + AnthropicChatOptions.Builder optionsBuilder = AnthropicChatOptions.builder().apiKey(apiKey); if (StrUtil.isNotEmpty(url)) { - builder.baseUrl(url); + optionsBuilder.baseUrl(url); } - AnthropicApi anthropicApi = builder.build(); return AnthropicChatModel.builder() - .anthropicApi(anthropicApi) - .toolCallingManager(getToolCallingManager()) + .options(optionsBuilder.build()) .build(); } @@ -556,9 +453,13 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法 */ private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { - url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); - OpenAiImageApi openAiApi = OpenAiImageApi.builder().baseUrl(url).apiKey(openAiToken).build(); - return new OpenAiImageModel(openAiApi); + OpenAiImageOptions.Builder optionsBuilder = OpenAiImageOptions.builder().apiKey(openAiToken); + if (StrUtil.isNotEmpty(url)) { + optionsBuilder.baseUrl(url); + } + return OpenAiImageModel.builder() + .options(optionsBuilder.build()) + .build(); } /** @@ -577,7 +478,6 @@ public class AiModelFactoryImpl implements AiModelFactory { OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); return OllamaChatModel.builder() .ollamaApi(ollamaApi) - .toolCallingManager(getToolCallingManager()) .build(); } @@ -600,47 +500,10 @@ public class AiModelFactoryImpl implements AiModelFactory { // ========== 各种创建 EmbeddingModel 的方法 ========== /** - * 可参考 {@link DashScopeEmbeddingAutoConfiguration} 的 dashscopeEmbeddingModel 方法 + * 可参考 {@link DashScopeEmbeddingAutoConfiguration} 的 DashScopeEmbeddingModel 方法 */ private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { - DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); - DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); - return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); - } - - /** - * 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 - */ - private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { - ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey); - if (StrUtil.isNotEmpty(url)) { - zhiPuAiApiBuilder.baseUrl(url); - } - ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build(); - return new ZhiPuAiEmbeddingModel(zhiPuAiApiBuilder.build(), MetadataMode.EMBED, zhiPuAiEmbeddingOptions); - } - - /** - * 可参考 {@link MiniMaxEmbeddingAutoConfiguration} 的 miniMaxEmbeddingModel 方法 - */ - private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) { - MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey) - : new MiniMaxApi(url, apiKey); - MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build(); - return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions); - } - - /** - * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanEmbeddingModel 方法 - */ - private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) { - List keys = StrUtil.split(key, '|'); - Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); - String appKey = keys.get(0); - String secretKey = keys.get(1); - QianFanApi qianFanApi = new QianFanApi(appKey, secretKey); - QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build(); - return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions); + return AiAutoConfiguration.buildTongYiEmbeddingModel(apiKey, model); } private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { @@ -648,7 +511,7 @@ public class AiModelFactoryImpl implements AiModelFactory { OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder().model(model).build(); return OllamaEmbeddingModel.builder() .ollamaApi(ollamaApi) - .defaultOptions(ollamaOptions) + .options(ollamaOptions) .build(); } @@ -656,25 +519,16 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link OpenAiEmbeddingAutoConfiguration} 的 openAiEmbeddingModel 方法 */ private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) { - url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); - OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); - OpenAiEmbeddingOptions openAiEmbeddingProperties = OpenAiEmbeddingOptions.builder().model(model).build(); - return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties); - } - - /** - * 可参考 {@link AzureOpenAiEmbeddingAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 - */ - private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) { - // TODO @芋艿:手头暂时没密钥,使用建议再测试下 - AzureOpenAiEmbeddingAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiEmbeddingAutoConfiguration(); - // 创建 OpenAIClientBuilder 对象 - OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() - .endpoint(url).credential(new KeyCredential(apiKey)); - // 获取 AzureOpenAiChatProperties 对象 - AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class); - return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClientBuilder, embeddingProperties, - getObservationRegistry(), getEmbeddingModelObservationConvention()); + OpenAiEmbeddingOptions.Builder optionsBuilder = OpenAiEmbeddingOptions.builder() + .apiKey(openAiToken) + .model(model); + if (StrUtil.isNotEmpty(url)) { + optionsBuilder.baseUrl(url); + } + return OpenAiEmbeddingModel.builder() + .metadataMode(MetadataMode.EMBED) + .options(optionsBuilder.build()) + .build(); } // ========== 各种创建 VectorStore 的方法 ========== @@ -737,13 +591,11 @@ public class AiModelFactoryImpl implements AiModelFactory { */ private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel, Map> metadataFields) { - // 创建 JedisPooled 对象 - RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); - JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort(), - redisProperties.getUsername(), redisProperties.getPassword()); + // 创建 RedisClient 对象 + RedisClient redisClient = buildRedisClient(); // 创建 RedisVectorStoreProperties 对象 RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class); - RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel) + RedisVectorStore redisVectorStore = RedisVectorStore.builder(redisClient, embeddingModel) .indexName(properties.getIndexName()).prefix(properties.getPrefix()) .initializeSchema(properties.isInitializeSchema()) .metadataFields(convertList(metadataFields.entrySet(), entry -> { @@ -766,6 +618,43 @@ public class AiModelFactoryImpl implements AiModelFactory { return redisVectorStore; } + private RedisClient buildRedisClient() { + DataRedisProperties redisProperties = SpringUtil.getBean(DataRedisProperties.class); + Assert.isNull(redisProperties.getCluster(), "RedisVectorStore 暂不支持 Redis Cluster 模式"); + Assert.isNull(redisProperties.getSentinel(), "RedisVectorStore 暂不支持 Redis Sentinel 模式"); + Assert.isNull(redisProperties.getMasterreplica(), "RedisVectorStore 暂不支持 Redis Master-Replica 模式"); + if (StrUtil.isNotEmpty(redisProperties.getUrl())) { + return RedisClient.create(redisProperties.getUrl()); + } + DefaultJedisClientConfig.Builder clientConfigBuilder = DefaultJedisClientConfig.builder() + .ssl(redisProperties.getSsl().isEnabled()) + .database(redisProperties.getDatabase()); + if (StrUtil.isNotEmpty(redisProperties.getUsername())) { + clientConfigBuilder.user(redisProperties.getUsername()); + } + if (StrUtil.isNotEmpty(redisProperties.getPassword())) { + clientConfigBuilder.password(redisProperties.getPassword()); + } + if (StrUtil.isNotEmpty(redisProperties.getClientName())) { + clientConfigBuilder.clientName(redisProperties.getClientName()); + } + if (redisProperties.getTimeout() != null) { + clientConfigBuilder.socketTimeoutMillis(toMillis(redisProperties.getTimeout())); + } + if (redisProperties.getConnectTimeout() != null) { + clientConfigBuilder.connectionTimeoutMillis(toMillis(redisProperties.getConnectTimeout())); + } + JedisClientConfig clientConfig = clientConfigBuilder.build(); + return RedisClient.builder() + .hostAndPort(new HostAndPort(redisProperties.getHost(), redisProperties.getPort())) + .clientConfig(clientConfig) + .build(); + } + + private static int toMillis(Duration duration) { + return Math.toIntExact(duration.toMillis()); + } + /** * 参考 {@link MilvusVectorStoreAutoConfiguration} 的 vectorStore 方法 */ @@ -827,10 +716,6 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(BatchingStrategy.class); } - private static ToolCallingManager getToolCallingManager() { - return SpringUtil.getBean(ToolCallingManager.class); - } - private static ObjectProvider getEmbeddingModelObservationConvention() { return new ObjectProvider<>() { diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java index 5fb71c942..18b53c54b 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java @@ -38,8 +38,15 @@ public class BaiChuanChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java index a542cb372..a049947ca 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java @@ -38,8 +38,15 @@ public class DouBaoChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java index 378a0af1f..ef887a0be 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java @@ -39,8 +39,15 @@ public class GeminiChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java index 06eed2504..3a9f5b1f9 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java @@ -37,8 +37,15 @@ public class GrokChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java index 9513c6c5f..4461c5c60 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java @@ -45,8 +45,15 @@ public class HunYuanChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java new file mode 100644 index 000000000..d464ab86a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import reactor.core.publisher.Flux; + +/** + * MiniMax {@link ChatModel} 实现类 + * + * @author 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class MiniMaxChatModel implements ChatModel { + + public static final String BASE_URL = "https://api.minimaxi.com/v1"; + + public static final String MODEL_DEFAULT = "MiniMax-M3"; + + /** + * 兼容 OpenAI 接口,复用 DeepSeek 客户端 + */ + private final DeepSeekChatModel deepSeekChatModel; + + @Override + public ChatResponse call(Prompt prompt) { + return deepSeekChatModel.call(prompt); + } + + @Override + public Flux stream(Prompt prompt) { + return deepSeekChatModel.stream(prompt); + } + + @Override + public ChatOptions getOptions() { + return deepSeekChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") + public ChatOptions getDefaultOptions() { + return getOptions(); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java new file mode 100644 index 000000000..391cec54a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import reactor.core.publisher.Flux; + +/** + * 月之暗面 {@link ChatModel} 实现类 + * + * @author 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class MoonshotChatModel implements ChatModel { + + public static final String BASE_URL = "https://api.moonshot.cn"; + + public static final String COMPLETE_PATH = "/v1/chat/completions"; + + public static final String MODEL_DEFAULT = "kimi-k2.6"; + + /** + * 兼容 OpenAI 接口,复用 DeepSeek 客户端 + */ + private final DeepSeekChatModel deepSeekChatModel; + + @Override + public ChatResponse call(Prompt prompt) { + return deepSeekChatModel.call(prompt); + } + + @Override + public Flux stream(Prompt prompt) { + return deepSeekChatModel.stream(prompt); + } + + @Override + public ChatOptions getOptions() { + return deepSeekChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") + public ChatOptions getDefaultOptions() { + return getOptions(); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java index a910e3403..9d66f25c1 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java @@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.openai.OpenAiChatModel; import reactor.core.publisher.Flux; /** @@ -36,8 +35,15 @@ public class SiliconFlowChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java index f9cd81cb3..006658ea7 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java @@ -21,18 +21,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.springframework.ai.model.ApiKey; import org.springframework.ai.model.NoopApiKey; import org.springframework.ai.model.SimpleApiKey; -import org.springframework.ai.openai.api.OpenAiImageApi; import org.springframework.ai.retry.RetryUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MultiValueMap; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; -import java.util.Map; - /** * 硅基流动 Image API * @@ -58,15 +54,15 @@ public class SiliconFlowImageApi { public SiliconFlowImageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { - this(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler); + this(baseUrl, apiKey, new HttpHeaders(), restClientBuilder, responseErrorHandler); } - public SiliconFlowImageApi(String baseUrl, String apiKey, MultiValueMap headers, + public SiliconFlowImageApi(String baseUrl, String apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler); } - public SiliconFlowImageApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, + public SiliconFlowImageApi(String baseUrl, ApiKey apiKey, HttpHeaders headers, RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { // @formatter:off @@ -83,7 +79,7 @@ public class SiliconFlowImageApi { // @formatter:on } - public ResponseEntity createImage(SiliconflowImageRequest siliconflowImageRequest) { + public ResponseEntity createImage(SiliconflowImageRequest siliconflowImageRequest) { Assert.notNull(siliconflowImageRequest, "Image request cannot be null."); Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty."); @@ -91,7 +87,7 @@ public class SiliconFlowImageApi { .uri("v1/images/generations") .body(siliconflowImageRequest) .retrieve() - .toEntity(OpenAiImageApi.OpenAiImageResponse.class); + .toEntity(SiliconFlowImageResponse.class); } @@ -112,4 +108,15 @@ public class SiliconFlowImageApi { } } + public record SiliconFlowImageResponse( + @JsonProperty("created") Long created, + @JsonProperty("data") java.util.List data) { + + public record Entry( + @JsonProperty("url") String url, + @JsonProperty("b64_json") String b64Json, + @JsonProperty("revised_prompt") String revisedPrompt) { + } + } + } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java index f205b80fc..d6d5b7be0 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java @@ -27,12 +27,11 @@ import org.springframework.ai.image.observation.ImageModelObservationConvention; import org.springframework.ai.image.observation.ImageModelObservationDocumentation; import org.springframework.ai.model.ModelOptionsUtils; import org.springframework.ai.openai.OpenAiImageModel; -import org.springframework.ai.openai.api.OpenAiImageApi; import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata; import org.springframework.ai.retry.RetryUtils; +import org.springframework.core.retry.RetryTemplate; import org.springframework.http.ResponseEntity; -import org.springframework.lang.Nullable; -import org.springframework.retry.support.RetryTemplate; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import java.util.List; @@ -46,102 +45,112 @@ import java.util.List; */ public class SiliconFlowImageModel implements ImageModel { - private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class); + private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class); - private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention(); + private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention(); - private final SiliconFlowImageOptions defaultOptions; + private final SiliconFlowImageOptions defaultOptions; - private final RetryTemplate retryTemplate; + private final RetryTemplate retryTemplate; - private final SiliconFlowImageApi siliconFlowImageApi; + private final SiliconFlowImageApi siliconFlowImageApi; - private final ObservationRegistry observationRegistry; + private final ObservationRegistry observationRegistry; @Setter - private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION; + private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION; - public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) { - this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); - } + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) { + this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); + } - public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) { - this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP); - } + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) { + this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP); + } - public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate, + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate, ObservationRegistry observationRegistry) { - Assert.notNull(siliconFlowImageApi, "OpenAiImageApi must not be null"); - Assert.notNull(options, "options must not be null"); - Assert.notNull(retryTemplate, "retryTemplate must not be null"); - Assert.notNull(observationRegistry, "observationRegistry must not be null"); - this.siliconFlowImageApi = siliconFlowImageApi; - this.defaultOptions = options; - this.retryTemplate = retryTemplate; - this.observationRegistry = observationRegistry; - } + Assert.notNull(siliconFlowImageApi, "SiliconFlowImageApi must not be null"); + Assert.notNull(options, "options must not be null"); + Assert.notNull(retryTemplate, "retryTemplate must not be null"); + Assert.notNull(observationRegistry, "observationRegistry must not be null"); + this.siliconFlowImageApi = siliconFlowImageApi; + this.defaultOptions = options; + this.retryTemplate = retryTemplate; + this.observationRegistry = observationRegistry; + } - @Override - public ImageResponse call(ImagePrompt imagePrompt) { + @Override + public ImageResponse call(ImagePrompt imagePrompt) { SiliconFlowImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions); SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions); - var observationContext = ImageModelObservationContext.builder() - .imagePrompt(imagePrompt) - .provider(SiliconFlowApiConstants.PROVIDER_NAME) - .imagePrompt(imagePrompt) - .build(); + var observationContext = ImageModelObservationContext.builder() + .imagePrompt(imagePrompt) + .provider(SiliconFlowApiConstants.PROVIDER_NAME) + .imagePrompt(imagePrompt) + .build(); - return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION - .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, - this.observationRegistry) - .observe(() -> { - ResponseEntity imageResponseEntity = this.retryTemplate - .execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest)); + return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION + .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, + this.observationRegistry) + .observe(() -> { + ResponseEntity imageResponseEntity = RetryUtils.execute( + this.retryTemplate, () -> this.siliconFlowImageApi.createImage(imageRequest)); - ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest); + ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest); - observationContext.setResponse(imageResponse); + observationContext.setResponse(imageResponse); - return imageResponse; - }); - } + return imageResponse; + }); + } - private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt, + private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt, SiliconFlowImageOptions requestImageOptions) { - String instructions = imagePrompt.getInstructions().get(0).getText(); + String instructions = imagePrompt.getInstructions().getFirst().getText(); - SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions, - SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL); + return new SiliconFlowImageApi.SiliconflowImageRequest( + instructions, + ModelOptionsUtils.mergeOption(requestImageOptions.getModel(), SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL), + requestImageOptions.getN(), + requestImageOptions.getNegativePrompt(), + requestImageOptions.getSeed(), + requestImageOptions.getNumInferenceSteps(), + requestImageOptions.getGuidanceScale(), + requestImageOptions.getImage()); + } - return ModelOptionsUtils.merge(requestImageOptions, imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class); - } + private ImageResponse convertResponse(ResponseEntity imageResponseEntity, + SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) { + SiliconFlowImageApi.SiliconFlowImageResponse imageApiResponse = imageResponseEntity.getBody(); + if (imageApiResponse == null) { + logger.warn("No image response returned for request: {}", siliconflowImageRequest); + return new ImageResponse(List.of()); + } - private ImageResponse convertResponse(ResponseEntity imageResponseEntity, - SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) { - OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody(); - if (imageApiResponse == null) { - logger.warn("No image response returned for request: {}", siliconflowImageRequest); - return new ImageResponse(List.of()); - } + List imageGenerationList = imageApiResponse.data() + .stream() + .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()), + new OpenAiImageGenerationMetadata(entry.revisedPrompt()))) + .toList(); - List imageGenerationList = imageApiResponse.data() - .stream() - .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()), - new OpenAiImageGenerationMetadata(entry.revisedPrompt()))) - .toList(); - - ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created()); - return new ImageResponse(imageGenerationList, openAiImageResponseMetadata); - } + ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created()); + return new ImageResponse(imageGenerationList, openAiImageResponseMetadata); + } private SiliconFlowImageOptions mergeOptions(@Nullable ImageOptions runtimeOptions, SiliconFlowImageOptions defaultOptions) { - var runtimeOptionsForProvider = ModelOptionsUtils.copyToTarget(runtimeOptions, ImageOptions.class, - SiliconFlowImageOptions.class); - - if (runtimeOptionsForProvider == null) { + if (runtimeOptions == null) { return defaultOptions; } + SiliconFlowImageOptions runtimeOptionsForProvider = runtimeOptions instanceof SiliconFlowImageOptions siliconFlowImageOptions + ? siliconFlowImageOptions + : SiliconFlowImageOptions.builder() + .model(runtimeOptions.getModel()) + .batchSize(runtimeOptions.getN()) + .width(runtimeOptions.getWidth()) + .height(runtimeOptions.getHeight()) + .build(); return SiliconFlowImageOptions.builder() // Handle portable image options diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java index cbac3b6df..e565061b6 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java @@ -43,8 +43,15 @@ public class XingHuoChatModel implements ChatModel { } @Override + public ChatOptions getOptions() { + return openAiChatModelV1.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") public ChatOptions getDefaultOptions() { - return openAiChatModelV1.getDefaultOptions(); + return getOptions(); } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java new file mode 100644 index 000000000..b84316b3d --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import reactor.core.publisher.Flux; + +/** + * 文心一言 {@link ChatModel} 实现类 + * + * @author 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class YiYanChatModel implements ChatModel { + + public static final String BASE_URL = "https://qianfan.baidubce.com/v2"; + + public static final String MODEL_DEFAULT = "ernie-4.0-8k-latest"; + + /** + * 兼容 OpenAI 接口,复用 DeepSeek 客户端 + */ + private final DeepSeekChatModel deepSeekChatModel; + + @Override + public ChatResponse call(Prompt prompt) { + return deepSeekChatModel.call(prompt); + } + + @Override + public Flux stream(Prompt prompt) { + return deepSeekChatModel.stream(prompt); + } + + @Override + public ChatOptions getOptions() { + return deepSeekChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") + public ChatOptions getDefaultOptions() { + return getOptions(); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java new file mode 100644 index 000000000..f22c77c0c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import reactor.core.publisher.Flux; + +/** + * 智谱 {@link ChatModel} 实现类 + * + * @author 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class ZhiPuChatModel implements ChatModel { + + public static final String BASE_URL = "https://open.bigmodel.cn/api/paas/v4"; + + public static final String MODEL_DEFAULT = "glm-5.2"; + + /** + * 兼容 OpenAI 接口,复用 DeepSeek 客户端 + */ + private final DeepSeekChatModel deepSeekChatModel; + + @Override + public ChatResponse call(Prompt prompt) { + return deepSeekChatModel.call(prompt); + } + + @Override + public Flux stream(Prompt prompt) { + return deepSeekChatModel.stream(prompt); + } + + @Override + public ChatOptions getOptions() { + return deepSeekChatModel.getOptions(); + } + + @Override + @Deprecated(forRemoval = true) + @SuppressWarnings("removal") + public ChatOptions getDefaultOptions() { + return getOptions(); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java index aedd493c3..c7632605f 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java @@ -1,27 +1,25 @@ package cn.iocoder.yudao.module.ai.framework.security.config; import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; -import cn.iocoder.yudao.module.infra.enums.ApiConstants; -import jakarta.annotation.Resource; -import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerSseProperties; -import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties; +import cn.hutool.core.util.StrUtil; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; -import java.util.Optional; - /** * AI 模块的 Security 配置 */ @Configuration(proxyBeanMethods = false, value = "aiSecurityConfiguration") public class SecurityConfiguration { - @Resource - private Optional mcpServerSseProperties; - @Resource - private Optional mcpServerStreamableHttpProperties; + @Value("${spring.ai.mcp.server.sse-endpoint:/sse}") + private String mcpSseEndpoint; + @Value("${spring.ai.mcp.server.sse-message-endpoint:/mcp/message}") + private String mcpSseMessageEndpoint; + @Value("${spring.ai.mcp.server.streamable-http-endpoint:/mcp}") + private String mcpStreamableHttpEndpoint; @Bean("aiAuthorizeRequestsCustomizer") public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { @@ -29,28 +27,15 @@ public class SecurityConfiguration { @Override public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { - // Swagger 接口文档 - registry.requestMatchers("/v3/api-docs/**").permitAll() - .requestMatchers("/webjars/**").permitAll() - .requestMatchers("/swagger-ui").permitAll() - .requestMatchers("/swagger-ui/**").permitAll(); - // Spring Boot Actuator 的安全配置 - registry.requestMatchers("/actuator").permitAll() - .requestMatchers("/actuator/**").permitAll(); - // Druid 监控 - registry.requestMatchers("/druid/**").permitAll(); - - // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案 - // RPC 服务的安全配置 - registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); - - // MCP Server - mcpServerSseProperties.ifPresent(properties -> { - registry.requestMatchers(properties.getSseEndpoint()).permitAll(); - registry.requestMatchers(properties.getSseMessageEndpoint()).permitAll(); - }); - mcpServerStreamableHttpProperties.ifPresent(properties -> - registry.requestMatchers(properties.getMcpEndpoint()).permitAll()); + if (StrUtil.isNotBlank(mcpSseEndpoint)) { + registry.requestMatchers(mcpSseEndpoint).permitAll(); + } + if (StrUtil.isNotBlank(mcpSseMessageEndpoint)) { + registry.requestMatchers(mcpSseMessageEndpoint).permitAll(); + } + if (StrUtil.isNotBlank(mcpStreamableHttpEndpoint)) { + registry.requestMatchers(mcpStreamableHttpEndpoint).permitAll(); + } } }; diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index bdd2ba2a1..e211af05b 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -49,10 +49,10 @@ import org.springframework.ai.chat.model.StreamingChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; -import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.resolution.ToolCallbackResolver; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; @@ -130,9 +130,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @Autowired(required = false) // 由于 yudao.ai.mcp.client.enable 配置项,可以关闭 McpSyncClient 的功能,所以这里只能不强制注入 private List mcpClients; - @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") - @Autowired(required = false) // 由于 yudao.ai.mcp.client.enable 配置项,可以关闭 McpSyncClient 的功能,所以这里只能不强制注入 - private McpClientCommonProperties mcpClientCommonProperties; + @Value("${spring.ai.mcp.client.name:mcp}") + private String mcpClientName; @Resource private ToolCallbackResolver toolCallbackResolver; @@ -410,13 +409,16 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { if (CollUtil.isNotEmpty(mcpClients) && CollUtil.isNotEmpty(chatRole.getMcpClientNames())) { chatRole.getMcpClientNames().forEach(mcpClientName -> { // 2.1 标准化名字,参考 McpClientAutoConfiguration 的 connectedClientName 方法 - String finalMcpClientName = mcpClientCommonProperties.getName() + " - " + mcpClientName; + String finalMcpClientName = this.mcpClientName + " - " + mcpClientName; // 2.2 匹配对应的 McpSyncClient mcpClients.forEach(mcpClient -> { if (ObjUtil.notEqual(mcpClient.getClientInfo().name(), finalMcpClientName)) { return; } - ToolCallback[] mcpToolCallBacks = new SyncMcpToolCallbackProvider(mcpClient).getToolCallbacks(); + ToolCallback[] mcpToolCallBacks = SyncMcpToolCallbackProvider.builder() + .mcpClients(mcpClient) + .build() + .getToolCallbacks(); CollUtil.addAll(toolCallbacks, mcpToolCallBacks); }); }); @@ -539,7 +541,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { public void deleteChatMessageByConversationId(Long conversationId, Long userId) { // 1. 校验消息存在 List messages = chatMessageMapper.selectListByConversationId(conversationId); - if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.get(0).getUserId(), userId)) { + if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.getFirst().getUserId(), userId)) { throw exception(CHAT_MESSAGE_NOT_EXIST); } // 2. 执行删除 diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java index 3def2a9c7..0163edbc0 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java @@ -29,14 +29,12 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springaicommunity.qianfan.QianFanImageOptions; import org.springframework.ai.image.ImageModel; import org.springframework.ai.image.ImageOptions; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; import org.springframework.ai.openai.OpenAiImageOptions; import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; -import org.springframework.ai.zhipuai.ZhiPuAiImageOptions; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -168,15 +166,6 @@ public class AiImageServiceImpl implements AiImageService { .model(model.getModel()).n(1) .height(draw.getHeight()).width(draw.getWidth()) .build(); - } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) { - return QianFanImageOptions.builder() - .model(model.getModel()).N(1) - .height(draw.getHeight()).width(draw.getWidth()) - .build(); - } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) { - return ZhiPuAiImageOptions.builder() - .model(model.getModel()) - .build(); } throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform()); } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index b5038412b..45ef1256d 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -166,7 +166,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); // 3. 删除向量存储中的段落 - VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId()); + VectorStore vectorStore = getVectorStoreById(segments.getFirst().getKnowledgeId()); vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId)); } @@ -299,7 +299,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService // 2. Rerank 重排序 if (rerankModel != null) { RerankResponse rerankResponse = rerankModel.call(new RerankRequest(reqBO.getContent(), documents, - DashScopeRerankOptions.builder().withTopN(topK).build())); + DashScopeRerankOptions.builder().topN(topK).build())); documents = convertList(rerankResponse.getResults(), documentWithScore -> documentWithScore.getScore() >= similarityThreshold ? documentWithScore.getOutput() : null); diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java index 15185235a..f61d97dc7 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java @@ -8,20 +8,15 @@ import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; -import org.springaicommunity.moonshot.MoonshotChatOptions; -import org.springaicommunity.qianfan.QianFanChatOptions; import org.springframework.ai.anthropic.AnthropicChatOptions; -import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.deepseek.DeepSeekAssistantMessage; import org.springframework.ai.deepseek.DeepSeekChatOptions; -import org.springframework.ai.minimax.MiniMaxChatOptions; import org.springframework.ai.ollama.api.OllamaChatOptions; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.tool.ToolCallback; -import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; import java.util.*; @@ -42,7 +37,8 @@ public class AiUtils { * @see 必须开启 withMultiModel 参数 */ public static final Set TONG_YI_MULTI_MODELS = SetUtils.asSet( - // qwen3.5 / 3.6 系列(统一多模态主干) + // qwen3.5 / 3.6 / 3.7 系列(统一多模态主干) + "qwen3.7-max", "qwen3.7-plus", "qwen3.7-flash", "qwen3.6-plus", "qwen3.6-flash", "qwen3.5-plus", "qwen3.5-flash", // qwen-vl 视觉理解 @@ -72,24 +68,17 @@ public class AiUtils { .enableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置 .multiModel(TONG_YI_MULTI_MODELS.contains(model)) // 是否多模态模型 .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); - case YI_YAN: - return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case DEEP_SEEK: case DOU_BAO: // 复用 DeepSeek 客户端 case HUN_YUAN: // 复用 DeepSeek 客户端 case SILICON_FLOW: // 复用 DeepSeek 客户端 case XING_HUO: // 复用 DeepSeek 客户端 + case YI_YAN: // 复用 DeepSeek 客户端 + case ZHI_PU: // 复用 DeepSeek 客户端 + case MINI_MAX: // 复用 DeepSeek 客户端 + case MOONSHOT: // 复用 DeepSeek 客户端 return DeepSeekChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); - case ZHI_PU: - return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); - case MINI_MAX: - return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); - case MOONSHOT: - return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); case OPENAI: case GEMINI: // 复用 OpenAI 客户端 case BAI_CHUAN: // 复用 OpenAI 客户端 @@ -97,7 +86,8 @@ public class AiUtils { return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); case AZURE_OPENAI: - return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) + return OpenAiChatOptions.builder().model(model).deploymentName(model).azure(true) + .temperature(temperature).maxTokens(maxTokens) .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); case ANTHROPIC: return AnthropicChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) @@ -159,4 +149,4 @@ public class AiUtils { return MapUtil.getStr(output.getMetadata(), "reasoningContent"); } -} \ No newline at end of file +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java new file mode 100644 index 000000000..41f5d06a6 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java @@ -0,0 +1,29 @@ +package org.springframework.ai.model.tool; + +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; + +import java.lang.reflect.Method; + +/** + * TODO 芋艿:spring-ai-alibaba 2.0.0-M1.1 仍依赖旧的 Spring AI ToolExecutionEligibilityPredicate, + * 临时补齐;升级到兼容 Spring AI 2.0.0 的 spring-ai-alibaba 版本后,删除本包下的 shim。 + */ +public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate { + + @Override + public boolean test(ChatOptions promptOptions, ChatResponse chatResponse) { + return isInternalToolExecutionEnabled(promptOptions) && chatResponse != null && chatResponse.hasToolCalls(); + } + + private static boolean isInternalToolExecutionEnabled(ChatOptions promptOptions) { + try { + Method method = promptOptions.getClass().getMethod("getInternalToolExecutionEnabled"); + Object result = method.invoke(promptOptions); + return result == null || Boolean.TRUE.equals(result); + } catch (ReflectiveOperationException | SecurityException ignored) { + return true; + } + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java new file mode 100644 index 000000000..c26781077 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java @@ -0,0 +1,21 @@ +package org.springframework.ai.model.tool; + +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.util.Assert; + +import java.util.function.BiPredicate; + +/** + * TODO 芋艿:spring-ai-alibaba 2.0.0-M1.1 仍依赖旧的 Spring AI ToolExecutionEligibilityPredicate, + * 临时补齐;升级到兼容 Spring AI 2.0.0 的 spring-ai-alibaba 版本后,删除本包下的 shim。 + */ +public interface ToolExecutionEligibilityPredicate extends BiPredicate { + + default boolean isToolExecutionRequired(ChatOptions promptOptions, ChatResponse chatResponse) { + Assert.notNull(promptOptions, "promptOptions cannot be null"); + Assert.notNull(chatResponse, "chatResponse cannot be null"); + return test(promptOptions, chatResponse); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml index d9ca9753b..85e4e343e 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml @@ -17,11 +17,25 @@ spring: --- #################### 数据库相关配置 #################### spring: - # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration # 禁用 AI 模块的 Redis,手动创建 - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) + # TODO 芋艿:spring-ai-alibaba 2.0.0-M1.1 的 DashScope 自动配置暂不兼容 Spring AI 2.0.0 + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeVideoAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioSpeechAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioTranscriptionAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeRerankAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeMultimodalEmbeddingAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAsyncToolCallingManagerAutoConfiguration + # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml index 5cb4c2e79..a293bc108 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml @@ -17,11 +17,25 @@ spring: --- #################### 数据库相关配置 #################### spring: - # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration # 禁用 AI 模块的 Redis,手动创建 - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) + # TODO 芋艿:spring-ai-alibaba 2.0.0-M1.1 的 DashScope 自动配置暂不兼容 Spring AI 2.0.0 + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeVideoAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioSpeechAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioTranscriptionAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeRerankAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeMultimodalEmbeddingAutoConfiguration + - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAsyncToolCallingManagerAutoConfiguration + # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: @@ -141,4 +155,4 @@ yudao: security: mock-enable: true access-log: # 访问日志的配置项 - enable: false \ No newline at end of file + enable: false diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml index fd7b0d126..7c05e63cb 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: @@ -37,11 +33,6 @@ spring: server: port: 48090 - servlet: - encoding: - enabled: true - charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 - force: true logging: file: diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java index bbcc055a2..7c1fe4183 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.anthropic.AnthropicChatModel; import org.springframework.ai.anthropic.AnthropicChatOptions; -import org.springframework.ai.anthropic.api.AnthropicApi; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; @@ -23,12 +22,10 @@ import java.util.List; public class AnthropicChatModelTest { private final AnthropicChatModel chatModel = AnthropicChatModel.builder() - .anthropicApi(AnthropicApi.builder() + .options(AnthropicChatOptions.builder() .apiKey("sk-muubv7cXeLw0Etgs743f365cD5Ea44429946Fa7e672d8942") .baseUrl("https://aihubmix.com") - .build()) - .defaultOptions(AnthropicChatOptions.builder() - .model(AnthropicApi.ChatModel.CLAUDE_SONNET_4_5) + .model("claude-sonnet-4-5") .temperature(0.7) .maxTokens(4096) .build()) @@ -70,8 +67,7 @@ public class AnthropicChatModelTest { List messages = new ArrayList<>(); messages.add(new UserMessage("thkinking 下,1+1 为什么等于 2 ")); AnthropicChatOptions options = AnthropicChatOptions.builder() - .model(AnthropicApi.ChatModel.CLAUDE_SONNET_4_5) - .thinking(AnthropicApi.ThinkingType.ENABLED, 3096) + .model("claude-sonnet-4-5") .temperature(1D) .build(); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java deleted file mode 100644 index 69776d8e6..000000000 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; - -import com.azure.ai.openai.OpenAIClientBuilder; -import com.azure.core.credential.AzureKeyCredential; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.ai.azure.openai.AzureOpenAiChatModel; -import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.messages.SystemMessage; -import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.prompt.Prompt; -import reactor.core.publisher.Flux; - -import java.util.ArrayList; -import java.util.List; - -import static org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME; - -/** - * {@link AzureOpenAiChatModel} 集成测试 - * - * @author 芋道源码 - */ -public class AzureOpenAIChatModelTests { - - // TODO @芋艿:晚点在调整 - private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder() - .endpoint("https://eastusprejade.openai.azure.com") - .credential(new AzureKeyCredential("xxx")); - private final AzureOpenAiChatModel chatModel = AzureOpenAiChatModel.builder() - .openAIClientBuilder(openAiApi) - .defaultOptions(AzureOpenAiChatOptions.builder() - .deploymentName(DEFAULT_DEPLOYMENT_NAME) - .build()) - .build(); - - @Test - @Disabled - public void testCall() { - // 准备参数 - List messages = new ArrayList<>(); - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); - messages.add(new UserMessage("1 + 1 = ?")); - - // 调用 - ChatResponse response = chatModel.call(new Prompt(messages)); - // 打印结果 - System.out.println(response); - System.out.println(response.getResult().getOutput()); - } - - @Test - @Disabled - public void testStream() { - // 准备参数 - List messages = new ArrayList<>(); - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); - messages.add(new UserMessage("1 + 1 = ?")); - - // 调用 - Flux flux = chatModel.stream(new Prompt(messages)); - // 打印结果 - flux.doOnNext(response -> { -// System.out.println(response); - System.out.println(response.getResult().getOutput()); - }).then().block(); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java index 06b0b2565..97c5277fb 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java @@ -10,7 +10,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -24,11 +23,9 @@ import java.util.List; public class BaiChuanChatModelTests { private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl(BaiChuanChatModel.BASE_URL) .apiKey("sk-61b6766a94c70786ed02673f5e16af3c") // apiKey - .build()) - .defaultOptions(OpenAiChatOptions.builder() .model("Baichuan4-Turbo") // 模型(https://platform.baichuan-ai.com/docs/api) .temperature(0.7) .build()) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java index 9a9314e99..bc6f4a919 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java @@ -8,11 +8,12 @@ import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.openai.OpenAiChatOptions; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * 基于 {@link OpenAiChatModel} 集成 Coze 测试 @@ -22,7 +23,7 @@ import java.util.List; public class CozeChatModelTests { private final OpenAiChatModel chatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl("http://127.0.0.1:3000") .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey .build()) @@ -40,7 +41,7 @@ public class CozeChatModelTests { ChatResponse response = chatModel.call(new Prompt(messages)); // 打印结果 System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); } @Test @@ -56,7 +57,7 @@ public class CozeChatModelTests { // 打印结果 flux.doOnNext(response -> { // System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); }).then().block(); } diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java index 2fbe0ee5d..2ca32d60e 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java @@ -26,8 +26,9 @@ public class DeepSeekChatModelTests { .deepSeekApi(DeepSeekApi.builder() .apiKey("sk-eaf4172a057344dd9bc64b1f806b6axx") // apiKey .build()) - .defaultOptions(DeepSeekChatOptions.builder() - .model("deepseek-chat") // 模型 + .options(DeepSeekChatOptions.builder() + .model("deepseek-v4-flash") // 模型 +// .model("deepseek-v4-pro") // 模型 .temperature(0.7) .build()) .build(); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java index b9feaf532..cde8d6009 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java @@ -8,11 +8,12 @@ import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.openai.OpenAiChatOptions; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * 基于 {@link OpenAiChatModel} 集成 Dify 测试 @@ -22,7 +23,7 @@ import java.util.List; public class DifyChatModelTests { private final OpenAiChatModel chatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl("http://127.0.0.1:3000") .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey .build()) @@ -40,7 +41,7 @@ public class DifyChatModelTests { ChatResponse response = chatModel.call(new Prompt(messages)); // 打印结果 System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); } @Test @@ -56,7 +57,7 @@ public class DifyChatModelTests { // 打印结果 flux.doOnNext(response -> { // System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); }).then().block(); } diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java index 38c4f0b01..327924e63 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java @@ -32,7 +32,7 @@ public class DouBaoChatModelTests { .completionsPath(DouBaoChatModel.COMPLETE_PATH) .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() .model("doubao-1-5-lite-32k-250115") // 模型(doubao) // .model("doubao-seed-1-6-thinking-250715") // 模型(doubao) // .model("deepseek-r1-250120") // 模型(deepseek) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java index 458500a8b..09e5153f6 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java @@ -8,11 +8,12 @@ import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.openai.OpenAiChatOptions; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * 基于 {@link OpenAiChatModel} 集成 FastGPT 测试 @@ -22,7 +23,7 @@ import java.util.List; public class FastGPTChatModelTests { private final OpenAiChatModel chatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl("https://cloud.fastgpt.cn/api") .apiKey("fastgpt-aqcc61kFtF8CeaglnGAfQOCIDWwjGdJVJHv6hIlMo28otFlva2aZNK") // apiKey .build()) @@ -40,7 +41,7 @@ public class FastGPTChatModelTests { ChatResponse response = chatModel.call(new Prompt(messages)); // 打印结果 System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); } @Test @@ -56,7 +57,7 @@ public class FastGPTChatModelTests { // 打印结果 flux.doOnNext(response -> { // System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); }).then().block(); } diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java index 964a5f3c3..fd66ad725 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java @@ -10,7 +10,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -24,12 +23,9 @@ import java.util.List; public class GeminiChatModelTests { private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl(GeminiChatModel.BASE_URL) - .completionsPath(GeminiChatModel.COMPLETE_PATH) .apiKey("AIzaSyAVoBxgoFvvte820vEQMma2LKBnC98bqMQ") - .build()) - .defaultOptions(OpenAiChatOptions.builder() .model(GeminiChatModel.MODEL_DEFAULT) // 模型 .temperature(0.7) .build()) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java index eeafef261..e09622767 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java @@ -29,7 +29,7 @@ public class HunYuanChatModelTests { .completionsPath(HunYuanChatModel.COMPLETE_PATH) .apiKey("sk-abc") // apiKey .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() .model(HunYuanChatModel.MODEL_DEFAULT) // 模型 .temperature(0.7) .build()) @@ -91,7 +91,7 @@ public class HunYuanChatModelTests { .completionsPath(HunYuanChatModel.COMPLETE_PATH) .apiKey("sk-abc") // apiKey .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() // .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型("deepseek-v3") .model("deepseek-r1") // 模型("deepseek-r1") .temperature(0.7) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java index 080146e5a..cafb36023 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java @@ -27,7 +27,7 @@ public class LlamaChatModelTests { .ollamaApi(OllamaApi.builder() .baseUrl("http://127.0.0.1:11434") // Ollama 服务地址 .build()) - .defaultOptions(OllamaChatOptions.builder() + .options(OllamaChatOptions.builder() .model(OllamaModel.LLAMA3.getName()) // 模型 .build()) .build(); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java index 8fb133dbb..7a52907f6 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; @@ -7,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.minimax.MiniMaxChatModel; -import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.minimax.api.MiniMaxApi; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.deepseek.api.DeepSeekApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -22,11 +23,15 @@ import java.util.List; */ public class MiniMaxChatModelTests { - private final MiniMaxChatModel chatModel = new MiniMaxChatModel( - new MiniMaxApi("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLnjovmlofmlowiLCJVc2VyTmFtZSI6IueOi-aWh-aWjCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxODk3Mjg3MjQ5NDU2ODA4MzQ2IiwiUGhvbmUiOiIxNTYwMTY5MTM5OSIsIkdyb3VwSUQiOiIxODk3Mjg3MjQ5NDQ4NDE5NzM4IiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDMtMTEgMTI6NTI6MDIiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.aAuB7gWW_oA4IYhh-CF7c9MfWWxKN49B_HK-DYjXaDwwffhiG-H1571z1WQhp9QytWG-DqgLejneeSxkiq1wQIe3FsEP2wz4BmGBct31LehbJu8ehLxg_vg75Uod1nFAHbm5mZz6JSVLNIlSo87Xr3UtSzJhAXlapEkcqlA4YOzOpKrZ8l5_OJPTORTCmHWZYgJcRS-faNiH62ZnUEHUozesTFhubJHo5GfJCw_edlnmfSUocERV1BjWvenhZ9My-aYXNktcW9WaSj9l6gayV7A0Ium_PL55T9ln1PcI8gayiVUKJGJDoqNyF1AF9_aF9NOKtTnQzwNqnZdlTYH6hw"), // 密钥 - MiniMaxChatOptions.builder() - .model(MiniMaxApi.ChatModel.ABAB_6_5_G_Chat.getValue()) // 模型 - .build()); + private final MiniMaxChatModel chatModel = new MiniMaxChatModel(DeepSeekChatModel.builder() + .deepSeekApi(DeepSeekApi.builder() + .baseUrl(MiniMaxChatModel.BASE_URL) + .apiKey("sk-api-xxx") // 密钥 + .build()) + .options(DeepSeekChatOptions.builder() + .model(MiniMaxChatModel.MODEL_DEFAULT) // 模型 + .build()) + .build()); @Test @Disabled public void testCall() { @@ -59,14 +64,13 @@ public class MiniMaxChatModelTests { }).then().block(); } - // TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复 @Test @Disabled public void testStream_thinking() { // 准备参数 List messages = new ArrayList<>(); messages.add(new UserMessage("详细分析下,如何设计一个电商系统?")); - MiniMaxChatOptions options = MiniMaxChatOptions.builder() + DeepSeekChatOptions options = DeepSeekChatOptions.builder() .model("MiniMax-M1") .build(); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java index b50ab80f4..bbef36696 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java @@ -1,15 +1,16 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.springaicommunity.moonshot.MoonshotChatModel; -import org.springaicommunity.moonshot.MoonshotChatOptions; -import org.springaicommunity.moonshot.api.MoonshotApi; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.deepseek.api.DeepSeekApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -22,14 +23,17 @@ import java.util.List; */ public class MoonshotChatModelTests { - private final MoonshotChatModel chatModel = MoonshotChatModel.builder() - .moonshotApi(MoonshotApi.builder() - .apiKey("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA") // 密钥 + private final MoonshotChatModel chatModel = new MoonshotChatModel(DeepSeekChatModel.builder() + .deepSeekApi(DeepSeekApi.builder() + .baseUrl(MoonshotChatModel.BASE_URL) + .completionsPath(MoonshotChatModel.COMPLETE_PATH) + .apiKey("sk-xxx") // 密钥 .build()) - .defaultOptions(MoonshotChatOptions.builder() - .model("kimi-k2-0711-preview") // 模型 + .options(DeepSeekChatOptions.builder() + .model(MoonshotChatModel.MODEL_DEFAULT) // 模型 + .temperature(1D) .build()) - .build(); + .build()); @Test @Disabled @@ -63,16 +67,14 @@ public class MoonshotChatModelTests { }).then().block(); } - // TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复 @Test @Disabled public void testStream_thinking() { // 准备参数 List messages = new ArrayList<>(); messages.add(new UserMessage("详细分析下,如何设计一个电商系统?")); - MoonshotChatOptions options = MoonshotChatOptions.builder() -// .model("kimi-k2-0711-preview") - .model("kimi-thinking-preview") + DeepSeekChatOptions options = DeepSeekChatOptions.builder() + .model(MoonshotChatModel.MODEL_DEFAULT) .build(); // 调用 diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java index c03a5de15..119d569b3 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java @@ -26,7 +26,7 @@ public class OllamaChatModelTests { .ollamaApi(OllamaApi.builder() .baseUrl("http://127.0.0.1:11434") // Ollama 服务地址 .build()) - .defaultOptions(OllamaChatOptions.builder() + .options(OllamaChatOptions.builder() // .model("qwen") // 模型(https://ollama.com/library/qwen) .model("deepseek-r1") // 模型(https://ollama.com/library/deepseek-r1) .build()) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java index 5bae6c694..e3fbedacf 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; -import com.azure.ai.openai.models.ReasoningEffortValue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; @@ -10,11 +9,11 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * {@link OpenAiChatModel} 集成测试 @@ -24,13 +23,11 @@ import java.util.List; public class OpenAIChatModelTests { private final OpenAiChatModel chatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.builder() .baseUrl("https://api.holdai.top") - .apiKey("sk-z5joyRoV1iFEnh2SAi8QPNrIZTXyQSyxTmD5CoNDQbFixK2l") // apiKey - .build()) - .defaultOptions(OpenAiChatOptions.builder() + .apiKey("sk-xxx") // apiKey .model("gpt-5-nano-2025-08-07") // 模型 -// .model(OpenAiApi.ChatModel.O1) // 模型 +// .model("o1") // 模型 .temperature(0.7) .build()) .build(); @@ -47,7 +44,7 @@ public class OpenAIChatModelTests { ChatResponse response = chatModel.call(new Prompt(messages)); // 打印结果 System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); } @Test @@ -63,7 +60,7 @@ public class OpenAIChatModelTests { // 打印结果 flux.doOnNext(response -> { // System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); }).then().block(); } @@ -76,9 +73,9 @@ public class OpenAIChatModelTests { messages.add(new UserMessage("详细分析下,如何设计一个电商系统?")); OpenAiChatOptions options = OpenAiChatOptions.builder() .model("gpt-5") -// .model(OpenAiApi.ChatModel.O4_MINI) +// .model("o4-mini") // .model("o3-pro") - .reasoningEffort(ReasoningEffortValue.LOW.getValue()) + .reasoningEffort("low") .build(); // 调用 @@ -86,7 +83,7 @@ public class OpenAIChatModelTests { // 打印结果 flux.doOnNext(response -> { // System.out.println(response); - System.out.println(response.getResult().getOutput()); + System.out.println(Objects.requireNonNull(response.getResult()).getOutput()); }).then().block(); } diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java index 3bb58e68e..7578f6e56 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java @@ -29,7 +29,7 @@ public class SiliconFlowChatModelTests { .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型 // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费 // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费 diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java index 77dbd2bc6..e940b41bf 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java @@ -29,7 +29,7 @@ public class XingHuoChatModelTests { .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2) .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey .build()) - .defaultOptions(DeepSeekChatOptions.builder() + .options(DeepSeekChatOptions.builder() // .model("generalv3.5") // 模型 .model("x1") // 模型 .temperature(0.7) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java index cb7be2a29..fd06b1aef 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java @@ -1,41 +1,42 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.springaicommunity.qianfan.QianFanChatModel; -import org.springaicommunity.qianfan.QianFanChatOptions; -import org.springaicommunity.qianfan.api.QianFanApi; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.deepseek.api.DeepSeekApi; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; -// TODO @芋艿:百度千帆 API 提供了 V2 版本,目前 Spring AI 不兼容,可关键 进展 /** - * {@link QianFanChatModel} 的集成测试 + * {@link YiYanChatModel} 的集成测试 * * @author fansili */ public class YiYanChatModelTests { - private final QianFanChatModel chatModel = new QianFanChatModel( - new QianFanApi("DGnyzREuaY7av7c38bOM9Ji2", "9aR8myflEOPDrEeLhoXv0FdqANOAyIZW"), // 密钥 - QianFanChatOptions.builder() - .model("ERNIE-4.5-8K-Preview") - .build() - ); + private final YiYanChatModel chatModel = new YiYanChatModel(DeepSeekChatModel.builder() + .deepSeekApi(DeepSeekApi.builder() + .baseUrl(YiYanChatModel.BASE_URL) + .apiKey("bce-v3/xxx") // 密钥 + .build()) + .options(DeepSeekChatOptions.builder() + .model(YiYanChatModel.MODEL_DEFAULT) + .build()) + .build()); @Test @Disabled public void testCall() { // 准备参数 List messages = new ArrayList<>(); - // TODO @芋艿:文心一言,只要带上 system message 就报错,已经各种测试,很莫名! -// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); messages.add(new UserMessage("1 + 1 = ?")); // 调用 @@ -49,8 +50,6 @@ public class YiYanChatModelTests { public void testStream() { // 准备参数 List messages = new ArrayList<>(); - // TODO @芋艿:文心一言,只要带上 system message 就报错,已经各种测试,很莫名! -// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); messages.add(new UserMessage("1 + 1 = ?")); // 调用 diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java index 18c317989..eb6dad05e 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; @@ -7,27 +8,30 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.zhipuai.ZhiPuAiChatModel; -import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; -import org.springframework.ai.zhipuai.api.ZhiPuAiApi; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.deepseek.api.DeepSeekApi; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; /** - * {@link ZhiPuAiChatModel} 的集成测试 + * {@link ZhiPuChatModel} 的集成测试 * * @author 芋道源码 */ public class ZhiPuAiChatModelTests { - private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel( - ZhiPuAiApi.builder().apiKey("2f35fb6ca4ea41fab898729b7fac086c.6ESSfPcCkxaKEUlR").build(), // 密钥 - ZhiPuAiChatOptions.builder() - .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型 + private final ZhiPuChatModel chatModel = new ZhiPuChatModel(DeepSeekChatModel.builder() + .deepSeekApi(DeepSeekApi.builder() + .baseUrl(ZhiPuChatModel.BASE_URL) + .apiKey("sk-xxx") // 密钥 + .build()) + .options(DeepSeekChatOptions.builder() + .model(ZhiPuChatModel.MODEL_DEFAULT) // 模型 .build() - ); + ).build()); @Test @Disabled @@ -61,15 +65,14 @@ public class ZhiPuAiChatModelTests { }).then().block(); } - // TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复 @Test @Disabled public void testStream_thinking() { // 准备参数 List messages = new ArrayList<>(); messages.add(new UserMessage("详细分析下,如何设计一个电商系统?")); - ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder() - .model("GLM-4.5") + DeepSeekChatOptions options = DeepSeekChatOptions.builder() + .model(ZhiPuChatModel.MODEL_DEFAULT) .build(); // 调用 diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java index 1b124529d..a30e7cfb6 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java @@ -7,7 +7,6 @@ import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.OpenAiImageOptions; -import org.springframework.ai.openai.api.OpenAiImageApi; /** * {@link OpenAiImageModel} 集成测试类 @@ -16,17 +15,19 @@ import org.springframework.ai.openai.api.OpenAiImageApi; */ public class OpenAiImageModelTests { - private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder() - .baseUrl("https://api.holdai.top") // apiKey - .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") - .build()); + private final OpenAiImageModel imageModel = OpenAiImageModel.builder() + .options(OpenAiImageOptions.builder() + .baseUrl("https://api.holdai.top") // apiKey + .apiKey("sk-xxx") + .build()) + .build(); @Test @Disabled public void testCall() { // 准备参数 ImageOptions options = OpenAiImageOptions.builder() - .model(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜 + .model("dall-e-2") // 这个模型比较便宜 .height(256).width(256) .build(); ImagePrompt prompt = new ImagePrompt("中国长城!", options); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java deleted file mode 100644 index 156360f25..000000000 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.ai.framework.ai.core.model.image; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springaicommunity.qianfan.QianFanImageModel; -import org.springaicommunity.qianfan.QianFanImageOptions; -import org.springaicommunity.qianfan.api.QianFanImageApi; -import org.springframework.ai.image.ImagePrompt; -import org.springframework.ai.image.ImageResponse; - -import static cn.iocoder.yudao.module.ai.framework.ai.core.model.image.StabilityAiImageModelTests.viewImage; - -// TODO @芋艿:百度千帆 API 提供了 V2 版本,目前 Spring AI 不兼容,可关键 进展 - -/** - * {@link QianFanImageModel} 集成测试类 - */ -public class QianFanImageTests { - - private final QianFanImageModel imageModel = new QianFanImageModel( - new QianFanImageApi("qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e")); // 密钥 - - @Test - @Disabled - public void testCall() { - // 准备参数 - // 只支持 1024x1024、768x768、768x1024、1024x768、576x1024、1024x576 - QianFanImageOptions imageOptions = QianFanImageOptions.builder() - .model(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue()) - .width(1024).height(1024) - .N(1) - .build(); - ImagePrompt prompt = new ImagePrompt("good", imageOptions); - - // 方法调用 - ImageResponse response = imageModel.call(prompt); - // 打印结果 - String b64Json = response.getResult().getOutput().getB64Json(); - System.out.println(response); - viewImage(b64Json); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java deleted file mode 100644 index bb16aac90..000000000 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.ai.framework.ai.core.model.image; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.ai.image.ImagePrompt; -import org.springframework.ai.image.ImageResponse; -import org.springframework.ai.zhipuai.ZhiPuAiImageModel; -import org.springframework.ai.zhipuai.ZhiPuAiImageOptions; -import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; - -/** - * {@link ZhiPuAiImageModel} 集成测试 - */ -public class ZhiPuAiImageModelTests { - - private final ZhiPuAiImageModel imageModel = new ZhiPuAiImageModel( - new ZhiPuAiImageApi("78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy") // 密钥 - ); - - @Test - @Disabled - public void testCall() { - // 准备参数 - ZhiPuAiImageOptions imageOptions = ZhiPuAiImageOptions.builder() - .model(ZhiPuAiImageApi.ImageModel.CogView_3.getValue()) - .build(); - ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions); - - // 方法调用 - ImageResponse response = imageModel.call(prompt); - // 打印结果 - System.out.println(response); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java index b50caef5e..d38313cb5 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java @@ -6,7 +6,6 @@ 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; @@ -14,11 +13,9 @@ import org.springframework.ai.tool.method.MethodToolCallbackProvider; public class DouBaoMcpTests { private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() + .options(OpenAiChatOptions.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()) @@ -36,44 +33,47 @@ public class DouBaoMcpTests { @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("===================================="); - } @@ -121,4 +121,4 @@ public class DouBaoMcpTests { } -} \ No newline at end of file +} diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java index 6de7f9dfa..3aa0a9951 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import com.fasterxml.jackson.core.type.TypeReference; +import tools.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.http.*; @@ -45,7 +45,7 @@ public class BpmHttpRequestUtils { BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); // 1.1 设置请求头 - MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); + HttpHeaders headers = buildHttpHeaders(processInstance, headerParams); // 1.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, bodyParams); @@ -113,7 +113,7 @@ public class BpmHttpRequestUtils { } public static ResponseEntity sendHttpRequest(String url, - MultiValueMap headers, + HttpHeaders headers, Object body, RestTemplate restTemplate) { HttpEntity requestEntity = new HttpEntity<>(body, headers); @@ -128,12 +128,12 @@ public class BpmHttpRequestUtils { return responseEntity; } - public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, - List headerSettings) { - MultiValueMap headers = new LinkedMultiValueMap<>(); + public static HttpHeaders buildHttpHeaders(ProcessInstance processInstance, + List headerSettings) { + HttpHeaders headers = new HttpHeaders(); headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); Map processVariables = processInstance.getProcessVariables(); - addHttpRequestParam(headers, headerSettings, processVariables); + addHttpRequestHeader(headers, headerSettings, processVariables); return headers; } @@ -172,10 +172,25 @@ public class BpmHttpRequestUtils { /** * 添加 HTTP 请求参数。请求头或者请求体 * - * @param params HTTP 请求参数 + * @param headers HTTP 请求头 * @param paramSettings HTTP 请求参数设置 * @param processVariables 流程变量 */ + public static void addHttpRequestHeader(HttpHeaders headers, + List paramSettings, + Map processVariables) { + if (CollUtil.isEmpty(paramSettings)) { + return; + } + paramSettings.forEach(item -> { + if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { + headers.add(item.getKey(), item.getValue()); + } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { + headers.add(item.getKey(), processVariables.get(item.getValue()).toString()); + } + }); + } + public static void addHttpRequestParam(MultiValueMap params, List paramSettings, Map processVariables) { diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index fc21f7054..a14be42f4 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormFi import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; -import com.fasterxml.jackson.databind.JsonNode; +import tools.jackson.databind.JsonNode; import lombok.SneakyThrows; import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.api.variable.VariableContainer; diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java index c441c5f9b..4b81cda3e 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; -import com.fasterxml.jackson.core.type.TypeReference; +import tools.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java index 4940d697d..d6c4c0e97 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; -import com.fasterxml.jackson.core.type.TypeReference; +import tools.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-dev.yaml b/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-dev.yaml index d9e5bc2d5..b29f7fb80 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-dev.yaml +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-local.yaml b/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-local.yaml index a1cee3ded..d8f27b309 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-local.yaml +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application.yaml b/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application.yaml index 048cd7136..f67c14c63 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application.yaml +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-dev.yaml b/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-dev.yaml index 522c0145e..fc2830742 100644 --- a/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-dev.yaml +++ b/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-local.yaml b/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-local.yaml index 394c2ad55..34635e161 100644 --- a/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-local.yaml +++ b/yudao-module-crm/yudao-module-crm-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-crm/yudao-module-crm-server/src/main/resources/application.yaml b/yudao-module-crm/yudao-module-crm-server/src/main/resources/application.yaml index 0be7c718f..60377d599 100644 --- a/yudao-module-crm/yudao-module-crm-server/src/main/resources/application.yaml +++ b/yudao-module-crm/yudao-module-crm-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-dev.yaml b/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-dev.yaml index 522c0145e..fc2830742 100644 --- a/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-dev.yaml +++ b/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-local.yaml b/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-local.yaml index 4197dcb90..1348dae46 100644 --- a/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-local.yaml +++ b/yudao-module-erp/yudao-module-erp-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-erp/yudao-module-erp-server/src/main/resources/application.yaml b/yudao-module-erp/yudao-module-erp-server/src/main/resources/application.yaml index cb7cf1332..a52113d2e 100644 --- a/yudao-module-erp/yudao-module-erp-server/src/main/resources/application.yaml +++ b/yudao-module-erp/yudao-module-erp-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-im/yudao-module-im-server/src/main/resources/application-dev.yaml b/yudao-module-im/yudao-module-im-server/src/main/resources/application-dev.yaml index 52ad3f857..0711e732b 100644 --- a/yudao-module-im/yudao-module-im-server/src/main/resources/application-dev.yaml +++ b/yudao-module-im/yudao-module-im-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-im/yudao-module-im-server/src/main/resources/application-local.yaml b/yudao-module-im/yudao-module-im-server/src/main/resources/application-local.yaml index 18a28074a..fd4c10685 100644 --- a/yudao-module-im/yudao-module-im-server/src/main/resources/application-local.yaml +++ b/yudao-module-im/yudao-module-im-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-im/yudao-module-im-server/src/main/resources/application.yaml b/yudao-module-im/yudao-module-im-server/src/main/resources/application.yaml index a2774a929..9112628d9 100644 --- a/yudao-module-im/yudao-module-im-server/src/main/resources/application.yaml +++ b/yudao-module-im/yudao-module-im-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java b/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java index 92d617e33..81d243432 100755 --- a/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java +++ b/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java @@ -15,7 +15,7 @@ import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; -import com.fasterxml.jackson.core.type.TypeReference; +import tools.jackson.core.type.TypeReference; import lombok.*; import java.lang.reflect.Field; diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-dev.yaml b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-dev.yaml index 04cd6e3bf..49ac684c4 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-dev.yaml +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml index ea0b06cc1..2ad626a31 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml @@ -20,7 +20,9 @@ spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application.yaml b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application.yaml index 60cfc3d90..1be49e1af 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application.yaml +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-iot/yudao-module-iot-api/pom.xml b/yudao-module-iot/yudao-module-iot-api/pom.xml index 742a4f84c..a11352f4d 100644 --- a/yudao-module-iot/yudao-module-iot-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-api/pom.xml @@ -32,7 +32,7 @@ - com.fasterxml.jackson.core + tools.jackson.core jackson-databind provided diff --git a/yudao-module-iot/yudao-module-iot-core/pom.xml b/yudao-module-iot/yudao-module-iot-core/pom.xml index bebd5c7b6..4d88c4d54 100644 --- a/yudao-module-iot/yudao-module-iot-core/pom.xml +++ b/yudao-module-iot/yudao-module-iot-core/pom.xml @@ -60,6 +60,11 @@ spring-kafka true + + org.springframework.boot + spring-boot-kafka + true + @@ -69,4 +74,4 @@ - \ No newline at end of file + diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java index 4f14057eb..39f59f303 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java @@ -19,11 +19,11 @@ import org.redisson.api.RedissonClient; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.kafka.KafkaProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.kafka.autoconfigure.KafkaProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/kafka/IotKafkaMessageBus.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/kafka/IotKafkaMessageBus.java index d859f6734..6644dcb28 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/kafka/IotKafkaMessageBus.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/kafka/IotKafkaMessageBus.java @@ -11,7 +11,7 @@ import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; -import org.springframework.boot.autoconfigure.kafka.KafkaProperties; +import org.springframework.boot.kafka.autoconfigure.KafkaProperties; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; diff --git a/yudao-module-iot/yudao-module-iot-gateway/pom.xml b/yudao-module-iot/yudao-module-iot-gateway/pom.xml index 46668895c..8843edbab 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/pom.xml +++ b/yudao-module-iot/yudao-module-iot-gateway/pom.xml @@ -27,6 +27,10 @@ org.springframework spring-web + + org.springframework.boot + spring-boot-restclient + diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAbstractHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAbstractHandler.java index c403ee973..f7fef40fd 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAbstractHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAbstractHandler.java @@ -115,7 +115,7 @@ public abstract class IotHttpAbstractHandler implements Handler public static void writeResponse(RoutingContext context, CommonResult data) { context.response() .setStatusCode(200) - .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .end(serializeResponse(data)); } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java index ed46a9afb..788ad9e89 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -41,7 +41,7 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi { public void init() { IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc(); restTemplate = new RestTemplateBuilder() - .rootUri(rpc.getUrl()) + .baseUri(rpc.getUrl()) .readTimeout(rpc.getReadTimeout()) .connectTimeout(rpc.getConnectTimeout()) .build(); diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java index 3f4b8eb02..7d385d1e8 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java +++ b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotHttpDataSinkAction.java @@ -70,7 +70,7 @@ public class IotHttpDataSinkAction implements IotDataRuleAction { requestBody = new HashMap<>(); } requestBody.put("message", message); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); } @@ -89,4 +89,4 @@ public class IotHttpDataSinkAction implements IotDataRuleAction { } } -} \ No newline at end of file +} diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-dev.yaml b/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-dev.yaml index 3129314b3..dfab06ba6 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-dev.yaml +++ b/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-local.yaml b/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-local.yaml index 8ae2df864..b91abd9a1 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-local.yaml +++ b/yudao-module-iot/yudao-module-iot-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/resources/application.yaml b/yudao-module-iot/yudao-module-iot-server/src/main/resources/application.yaml index 8e2036988..51edd54c8 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/resources/application.yaml +++ b/yudao-module-iot/yudao-module-iot-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-mall/yudao-module-product-server/src/main/resources/application-dev.yaml b/yudao-module-mall/yudao-module-product-server/src/main/resources/application-dev.yaml index 6dffa16b5..f45b72fa4 100644 --- a/yudao-module-mall/yudao-module-product-server/src/main/resources/application-dev.yaml +++ b/yudao-module-mall/yudao-module-product-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-product-server/src/main/resources/application-local.yaml b/yudao-module-mall/yudao-module-product-server/src/main/resources/application-local.yaml index 2557d8fe9..c04f3aa3d 100644 --- a/yudao-module-mall/yudao-module-product-server/src/main/resources/application-local.yaml +++ b/yudao-module-mall/yudao-module-product-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-product-server/src/main/resources/application.yaml b/yudao-module-mall/yudao-module-product-server/src/main/resources/application.yaml index 53556bad5..f6984010f 100644 --- a/yudao-module-mall/yudao-module-product-server/src/main/resources/application.yaml +++ b/yudao-module-mall/yudao-module-product-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-dev.yaml b/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-dev.yaml index 6dffa16b5..f45b72fa4 100644 --- a/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-dev.yaml +++ b/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-local.yaml b/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-local.yaml index 1c63522a8..b7b0cac76 100644 --- a/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-local.yaml +++ b/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application.yaml b/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application.yaml index 429ab9ec4..6b0661c8c 100644 --- a/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application.yaml +++ b/yudao-module-mall/yudao-module-promotion-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-dev.yaml b/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-dev.yaml index 6dffa16b5..f45b72fa4 100644 --- a/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-dev.yaml +++ b/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-local.yaml b/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-local.yaml index 0450915d8..ace2e65c2 100644 --- a/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-local.yaml +++ b/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application.yaml b/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application.yaml index 40fd663fb..a7a5603db 100644 --- a/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application.yaml +++ b/yudao-module-mall/yudao-module-statistics-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java index 37a1a9fad..d055d47ab 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import lombok.Data; +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer; import java.time.LocalDateTime; import java.util.List; diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java index 04a7c1431..0470e81f8 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import lombok.Data; +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer; import java.time.LocalDateTime; import java.util.List; diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-dev.yaml b/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-dev.yaml index c0dcf916c..79971794f 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-dev.yaml +++ b/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-local.yaml b/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-local.yaml index 4fccd3f11..f35471178 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-local.yaml +++ b/yudao-module-mall/yudao-module-trade-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/resources/application.yaml b/yudao-module-mall/yudao-module-trade-server/src/main/resources/application.yaml index 291586cdf..43fd16c39 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/resources/application.yaml +++ b/yudao-module-mall/yudao-module-trade-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-member/yudao-module-member-server/src/main/resources/application-dev.yaml b/yudao-module-member/yudao-module-member-server/src/main/resources/application-dev.yaml index 52ad3f857..0711e732b 100644 --- a/yudao-module-member/yudao-module-member-server/src/main/resources/application-dev.yaml +++ b/yudao-module-member/yudao-module-member-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-member/yudao-module-member-server/src/main/resources/application-local.yaml b/yudao-module-member/yudao-module-member-server/src/main/resources/application-local.yaml index 322b1baf7..593fc65f2 100644 --- a/yudao-module-member/yudao-module-member-server/src/main/resources/application-local.yaml +++ b/yudao-module-member/yudao-module-member-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-member/yudao-module-member-server/src/main/resources/application.yaml b/yudao-module-member/yudao-module-member-server/src/main/resources/application.yaml index d96acfca6..0dca3c983 100644 --- a/yudao-module-member/yudao-module-member-server/src/main/resources/application.yaml +++ b/yudao-module-member/yudao-module-member-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-dev.yaml b/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-dev.yaml index 522c0145e..fc2830742 100644 --- a/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-dev.yaml +++ b/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-local.yaml b/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-local.yaml index bc7958160..2510b671d 100644 --- a/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-local.yaml +++ b/yudao-module-mes/yudao-module-mes-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mes/yudao-module-mes-server/src/main/resources/application.yaml b/yudao-module-mes/yudao-module-mes-server/src/main/resources/application.yaml index 4e4328edb..bbe3517d8 100644 --- a/yudao-module-mes/yudao-module-mes-server/src/main/resources/application.yaml +++ b/yudao-module-mes/yudao-module-mes-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-mp/yudao-module-mp-server/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/config/YudaoWxMpConfiguration.java b/yudao-module-mp/yudao-module-mp-server/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/config/YudaoWxMpConfiguration.java index f3692a0a1..200e6d8a8 100644 --- a/yudao-module-mp/yudao-module-mp-server/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/config/YudaoWxMpConfiguration.java +++ b/yudao-module-mp/yudao-module-mp-server/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/config/YudaoWxMpConfiguration.java @@ -34,11 +34,21 @@ import org.springframework.data.redis.core.StringRedisTemplate; @Slf4j public class YudaoWxMpConfiguration { + @Bean + @ConditionalOnProperty(prefix = WxMpProperties.PREFIX + ".config-storage", name = "type", + havingValue = "memory", matchIfMissing = true) + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpMemoryConfigStorage(WxMpProperties properties) { + WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); + applyWxMpConfig(config, properties); + return config; + } + @Bean @ConditionalOnProperty(prefix = WxMpProperties.PREFIX + ".config-storage", name = "type", havingValue = "redistemplate") @ConditionalOnClass(StringRedisTemplate.class) @ConditionalOnMissingBean(WxMpConfigStorage.class) - public WxMpConfigStorage wxMpConfigStorage(WxMpProperties properties, ApplicationContext applicationContext) { + public WxMpConfigStorage wxMpRedisTemplateConfigStorage(WxMpProperties properties, ApplicationContext applicationContext) { StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); WxMpRedisConfigImpl config = new WxMpRedisConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix()); diff --git a/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-dev.yaml b/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-dev.yaml index 311a0c1b4..cdae1af12 100644 --- a/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-dev.yaml +++ b/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-local.yaml b/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-local.yaml index c37c20cd8..30bf35c1d 100644 --- a/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-local.yaml +++ b/yudao-module-mp/yudao-module-mp-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-mp/yudao-module-mp-server/src/main/resources/application.yaml b/yudao-module-mp/yudao-module-mp-server/src/main/resources/application.yaml index 09e77d09b..31f987f86 100644 --- a/yudao-module-mp/yudao-module-mp-server/src/main/resources/application.yaml +++ b/yudao-module-mp/yudao-module-mp-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/channel/PayChannelDO.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/channel/PayChannelDO.java index 00397863c..86f71520e 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/channel/PayChannelDO.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/channel/PayChannelDO.java @@ -8,15 +8,13 @@ import cn.iocoder.yudao.module.pay.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.NonePayClientConfig; -import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayAppPayClient; import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin.WxPayClientConfig; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import com.fasterxml.jackson.core.type.TypeReference; +import tools.jackson.core.type.TypeReference; import lombok.*; import java.lang.reflect.Field; diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-dev.yaml b/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-dev.yaml index e9bb6e9b9..67ba0518e 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-dev.yaml +++ b/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-local.yaml b/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-local.yaml index 0fa0c1b66..75e6a9d64 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-local.yaml +++ b/yudao-module-pay/yudao-module-pay-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/resources/application.yaml b/yudao-module-pay/yudao-module-pay-server/src/main/resources/application.yaml index 08e604694..17665c3d5 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/resources/application.yaml +++ b/yudao-module-pay/yudao-module-pay-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-report/yudao-module-report-server/src/main/resources/application-dev.yaml b/yudao-module-report/yudao-module-report-server/src/main/resources/application-dev.yaml index 6d6c51a86..e92a3d441 100644 --- a/yudao-module-report/yudao-module-report-server/src/main/resources/application-dev.yaml +++ b/yudao-module-report/yudao-module-report-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-report/yudao-module-report-server/src/main/resources/application-local.yaml b/yudao-module-report/yudao-module-report-server/src/main/resources/application-local.yaml index 763128de8..ae1e3754e 100644 --- a/yudao-module-report/yudao-module-report-server/src/main/resources/application-local.yaml +++ b/yudao-module-report/yudao-module-report-server/src/main/resources/application-local.yaml @@ -20,7 +20,9 @@ spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-report/yudao-module-report-server/src/main/resources/application.yaml b/yudao-module-report/yudao-module-report-server/src/main/resources/application.yaml index 90b417921..f5f0ba61b 100644 --- a/yudao-module-report/yudao-module-report-server/src/main/resources/application.yaml +++ b/yudao-module-report/yudao-module-report-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-system/yudao-module-system-server/src/main/resources/application-dev.yaml b/yudao-module-system/yudao-module-system-server/src/main/resources/application-dev.yaml index 28b362e6b..311018796 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/resources/application-dev.yaml +++ b/yudao-module-system/yudao-module-system-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml b/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml index 31c55ece9..0a1a2ef1f 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml +++ b/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-system/yudao-module-system-server/src/main/resources/application.yaml b/yudao-module-system/yudao-module-system-server/src/main/resources/application.yaml index 1c1e27d2e..cfa8acd28 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/resources/application.yaml +++ b/yudao-module-system/yudao-module-system-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml index 522c0145e..fc2830742 100644 --- a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml index ece61d357..32156d3c2 100644 --- a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml @@ -19,7 +19,9 @@ spring: spring: # 数据源配置项 autoconfigure: + # noinspection SpringBootApplicationYaml exclude: + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml index 82dfb4e4a..ed7f0b473 100644 --- a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml @@ -20,14 +20,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index 4113d93df..8c35b726d 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -9,6 +9,7 @@ spring: exclude: - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index e2e24fb72..17fedfe9b 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -8,6 +8,7 @@ spring: exclude: - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.dromara.trans.config.TransServiceConfig # TODO 芋艿:easy-trans 兼容 Spring Boot 4 后移除(TransServiceConfig 引用了已移除的 RestTemplateBuilder) # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index f02cbf706..79452f27c 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -15,14 +15,10 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true # Cache 配置项 cache: @@ -30,13 +26,6 @@ spring: redis: time-to-live: 1h # 设置过期时间为 1 小时 -server: - servlet: - encoding: - enabled: true - charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 - force: true - --- #################### Spring Cloud 禁用配置 #################### spring: @@ -375,4 +364,4 @@ yudao: message-bus: type: redis # 消息总线的类型 -debug: false \ No newline at end of file +debug: false