From 24681c81c09022f584a57006f2534b5f74723414 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 30 六月 2026 09:27:31 +0800
Subject: [PATCH] 初始化项目

---
 yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java |   69 ++++++++++++++++++++++++----------
 1 files changed, 48 insertions(+), 21 deletions(-)

diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
index 1518523..d687c54 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
@@ -8,22 +8,23 @@
 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 cn.hutool.extra.spring.SpringUtil;
 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.google.genai.GoogleGenAiChatOptions;
 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 org.springframework.core.env.Environment;
 
 import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_CONFIG_PLACEHOLDER_NOT_RESOLVED;
 
 /**
  * Spring AI 宸ュ叿绫�
@@ -36,13 +37,42 @@
     public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
 
     /**
+     * 瑙f瀽 DB 绛夊姩鎬侀厤缃噷鐨� Spring 鍗犱綅绗︼紝渚嬪 ${OPENAI_API_KEY}
+     *
+     * @param value 寰呰В鏋愮殑閰嶇疆鍊�
+     * @return 瑙f瀽鍚庣殑閰嶇疆鍊�
+     */
+    public static String resolveSpringPlaceholders(String value) {
+        if (StrUtil.isBlank(value) || !StrUtil.contains(value, "${")) {
+            return value;
+        }
+        try {
+            return SpringUtil.getBean(Environment.class).resolveRequiredPlaceholders(value);
+        } catch (IllegalArgumentException ex) {
+            throw exception(API_CONFIG_PLACEHOLDER_NOT_RESOLVED, value);
+        }
+    }
+
+    /**
+     * 鏍¢獙 API Key锛岄伩鍏嶉泦鎴愭祴璇曚娇鐢ㄩ粯璁ゅ崰浣嶅�煎彂璧疯皟鐢ㄣ��
+     *
+     * @param apiKey API Key
+     */
+    public static void validateApiKey(String apiKey) {
+        if (StrUtil.isBlank(apiKey) || "sk-xxxx".equals(apiKey)) {
+            throw new IllegalStateException("apiKey 涓嶈兘涓虹┖");
+        }
+    }
+
+    /**
      * 閫氫箟鍗冮棶鏀寔澶氭ā鎬佺殑妯″瀷
      *
      * @see <a href="https://bailian.console.aliyun.com/cn-beijing/?tab=model#/model-market/all?providers=qwen&capabilities=VU">妯″瀷骞垮満</a>
      * @see <a href="https://help.aliyun.com/zh/model-studio/error-code#error-url">蹇呴』寮�鍚� withMultiModel 鍙傛暟</a>
      */
     public static final Set<String> 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,32 +102,29 @@
                         .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 YI_YAN: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
+            case ZHI_PU: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
             case XING_HUO: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
+            case MINI_MAX: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
+            case MOONSHOT: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
+            case BAI_CHUAN: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
+            case STEP_FUN: // 澶嶇敤 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 瀹㈡埛绔�
             case GROK: // 澶嶇敤 OpenAI 瀹㈡埛绔�
                 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
                         .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
+            case GEMINI:
+                return GoogleGenAiChatOptions.builder().model(model).temperature(temperature).maxOutputTokens(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 +186,4 @@
         return MapUtil.getStr(output.getMetadata(), "reasoningContent");
     }
 
-}
\ No newline at end of file
+}

--
Gitblit v1.9.3