yudao-framework/yudao-common/pom.xml
@@ -74,6 +74,7 @@ <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> yudao-framework/yudao-spring-boot-starter-biz-ip/pom.xml
@@ -35,6 +35,7 @@ <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
@@ -84,6 +84,17 @@ <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot4-starter</artifactId> <!-- 多数据源 --> <exclusions> <!-- 新高电力只使用 MySQL,排除动态数据源默认带入的 Oracle UCP 与 JDBC 驱动 --> <exclusion> <groupId>com.oracle.database.spring</groupId> <artifactId>oracle-spring-boot-starter-ucp</artifactId> </exclusion> <exclusion> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ucp</artifactId> </exclusion> </exclusions> </dependency> <dependency> yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/CrmSaleQuotationAiController.java
@@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -18,6 +19,7 @@ @RestController @RequestMapping("/crm/sale-quotation/ai") @Validated @ConditionalOnProperty(prefix = "yudao.ai", name = "enabled", havingValue = "true") public class CrmSaleQuotationAiController { @Resource yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiServiceImpl.java
@@ -17,6 +17,7 @@ import org.apache.poi.ss.usermodel.*; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.ByteArrayInputStream; @@ -76,7 +77,7 @@ @Resource private SystemStorageBlobService blobService; @Resource @Autowired(required = false) private AiChatApi aiChatApi; @Override yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
@@ -62,6 +62,8 @@ ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接"); ErrorCode DATA_SOURCE_CONFIG_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_001_007_002, "导入数据源配置数据不能为空!"); ErrorCode DATA_SOURCE_CONFIG_IMPORT_NAME_EXISTS = new ErrorCode(1_001_007_003, "已存在数据源配置【{}】,且未开启覆盖!"); ErrorCode DATA_SOURCE_CONFIG_DB_TYPE_UNSUPPORTED = new ErrorCode(1_001_007_004, "当前轻量版仅支持 MySQL 数据源,不支持该连接地址:{}"); // ========== 学生 1-001-201-000 ========== ErrorCode DEMO01_CONTACT_NOT_EXISTS = new ErrorCode(1_001_201_000, "示例联系人不存在"); yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java
@@ -27,6 +27,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_IMPORT_LIST_IS_EMPTY; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_IMPORT_NAME_EXISTS; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_DB_TYPE_UNSUPPORTED; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK; @@ -105,9 +106,19 @@ } private void validateConnectionOK(DataSourceConfigDO config) { validateDatabaseTypeSupported(config.getUrl()); boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword()); if (!success) { throw exception(DATA_SOURCE_CONFIG_NOT_OK); } } /** * 默认轻量包只携带 MySQL 驱动。提前给出明确提示,避免缺少驱动时表现成含糊的连接失败。 */ private void validateDatabaseTypeSupported(String url) { if (!StrUtil.startWithIgnoreCase(url, "jdbc:mysql:")) { throw exception(DATA_SOURCE_CONFIG_DB_TYPE_UNSUPPORTED, url); } } @@ -137,6 +148,7 @@ .build(); importDataSourceConfigs.forEach(importConfig -> { try { validateDatabaseTypeSupported(importConfig.getUrl()); DataSourceConfigDO existConfig = dataSourceConfigMapper.selectByName(importConfig.getName()); if (existConfig == null) { // 新增 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/MesProAiController.java
@@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -15,6 +16,7 @@ @Tag(name = "管理后台 - MES 生产 AI 辅助分析") @RestController @RequestMapping("/mes/pro/ai") @ConditionalOnProperty(prefix = "yudao.ai", name = "enabled", havingValue = "true") public class MesProAiController { @Resource yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/MesQcAiController.java
@@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -16,6 +17,7 @@ @Tag(name = "管理后台 - MES QC AI 辅助判定") @RestController @RequestMapping("/mes/qc/ai") @ConditionalOnProperty(prefix = "yudao.ai", name = "enabled", havingValue = "true") public class MesQcAiController { @Resource yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiServiceImpl.java
@@ -39,6 +39,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; @@ -181,7 +182,7 @@ private MdmItemApi mdmItemApi; @Resource private CrmCustomerApi crmCustomerApi; @Resource @Autowired(required = false) private AiChatApi aiChatApi; // ==================== 1. 物料短缺预测 ==================== yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiServiceImpl.java
@@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; @@ -85,7 +86,7 @@ @Resource private CrmCustomerApi crmCustomerApi; @Resource @Autowired(required = false) private AiChatApi aiChatApi; @Override yudao-server/pom.xml
@@ -101,13 +101,6 @@ <version>${revision}</version> </dependency> <!-- AI 大模型相关模块。默认注释,保证编译速度 --> <dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-module-ai</artifactId> <version>${revision}</version> </dependency> <!-- IoT 物联网相关模块。默认注释,保证编译速度 --> <!-- <dependency>--> <!-- <groupId>cn.iocoder.boot</groupId>--> @@ -188,6 +181,15 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <configuration> <!-- Lombok 仅用于编译期注解处理,不应进入可执行 Jar。 --> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> <executions> <execution> <goals> @@ -199,4 +201,21 @@ </plugins> </build> <profiles> <!-- AI 功能默认不参与生产打包,避免将多模型 SDK、向量库和文档解析器打进主服务。 如需恢复 AI 功能,使用:mvn -Pwith-ai -pl yudao-server -am package --> <profile> <id>with-ai</id> <dependencies> <dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-module-ai</artifactId> <version>${revision}</version> </dependency> </dependencies> </profile> </profiles> </project> yudao-server/src/main/java/cn/iocoder/yudao/server/YudaoServerApplication.java
@@ -1,24 +1,6 @@ package cn.iocoder.yudao.server; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration; import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; import org.springframework.ai.model.google.genai.autoconfigure.chat.GoogleGenAiChatAutoConfiguration; import org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiEmbeddingConnectionAutoConfiguration; import org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiTextEmbeddingAutoConfiguration; import org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechAutoConfiguration; import org.springframework.ai.model.openai.autoconfigure.OpenAiAudioTranscriptionAutoConfiguration; 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.openai.autoconfigure.OpenAiModerationAutoConfiguration; import org.springframework.ai.model.ollama.autoconfigure.OllamaApiAutoConfiguration; import org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration; import org.springframework.ai.model.ollama.autoconfigure.OllamaEmbeddingAutoConfiguration; import org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration; import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration; import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration; import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; @@ -40,27 +22,27 @@ // 1. 向量库:AI 模块由 AiModelFactoryImpl 手动构建 VectorStore,三个自动配置同时启用会争抢 vectorStore 这个 bean 名 // 2. 模型:AI 模块的模型由 AiModelFactoryImpl 手动构建(走 DB 里 ai_model 配置的 apiKey),不需要 Spring AI 的模型 Bean; // 且未配置 api-key 时自动配置会在启动阶段直接抛「API key cannot be empty.」 exclude = { excludeName = { // ========== 向量库自动配置 ========== QdrantVectorStoreAutoConfiguration.class, RedisVectorStoreAutoConfiguration.class, MilvusVectorStoreAutoConfiguration.class, "org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration", "org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration", "org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration", // ========== 模型自动配置 ========== OpenAiChatAutoConfiguration.class, OpenAiEmbeddingAutoConfiguration.class, OpenAiImageAutoConfiguration.class, OpenAiAudioSpeechAutoConfiguration.class, OpenAiAudioTranscriptionAutoConfiguration.class, OpenAiModerationAutoConfiguration.class, DeepSeekChatAutoConfiguration.class, AnthropicChatAutoConfiguration.class, GoogleGenAiChatAutoConfiguration.class, GoogleGenAiEmbeddingConnectionAutoConfiguration.class, GoogleGenAiTextEmbeddingAutoConfiguration.class, OllamaApiAutoConfiguration.class, OllamaChatAutoConfiguration.class, OllamaEmbeddingAutoConfiguration.class, StabilityAiImageAutoConfiguration.class, "org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration", "org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration", "org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration", "org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechAutoConfiguration", "org.springframework.ai.model.openai.autoconfigure.OpenAiAudioTranscriptionAutoConfiguration", "org.springframework.ai.model.openai.autoconfigure.OpenAiModerationAutoConfiguration", "org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration", "org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration", "org.springframework.ai.model.google.genai.autoconfigure.chat.GoogleGenAiChatAutoConfiguration", "org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiEmbeddingConnectionAutoConfiguration", "org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiTextEmbeddingAutoConfiguration", "org.springframework.ai.model.ollama.autoconfigure.OllamaApiAutoConfiguration", "org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration", "org.springframework.ai.model.ollama.autoconfigure.OllamaEmbeddingAutoConfiguration", "org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration", }) public class YudaoServerApplication { yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java
@@ -83,10 +83,13 @@ "[支付模块 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/build/ 开启]"); } @RequestMapping(value = { "/admin-api/ai/**"}) @RequestMapping(value = { "/admin-api/ai/**", "/admin-api/mes/qc/ai/**", "/admin-api/mes/pro/ai/**", "/admin-api/crm/sale-quotation/ai/**" }) public CommonResult<Boolean> ai404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[AI 大模型 yudao-module-ai - 已禁用][参考 https://doc.iocoder.cn/ai/build/ 开启]"); "[AI 功能已在新高电力轻量版中禁用]"); } @RequestMapping(value = { "/admin-api/iot/**"}) yudao-server/src/main/resources/application.yaml
@@ -175,6 +175,7 @@ yudao: ai: enabled: false # 轻量版默认关闭 AI 主模块及 MES/CRM 内嵌 AI 接口 web-search: enable: true api-key: sk-40500e52840f4d24b956d0b1d80d9abe