云
2026-10-25 4229657c5b6be8a1da9e59a40e2426cf6b0995c2
feat(hrm): 新增人事管理模块功能

- 添加转正申请功能,包括创建、编辑、审批流程
- 添加薪酬结构管理功能,支持基本工资、绩效占比等配置
- 添加员工薪酬档案管理功能,可单独维护员工个人薪酬
- 实现转正申请的提交、审核、驳回等完整流程
- 添加薪酬相关的API接口和服务
- 集成员工选择组件优化用户体验
- 完善相关表单验证和错误处理机制
已修改15个文件
139 ■■■■■ 文件已修改
yudao-framework/yudao-common/pom.xml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-biz-ip/pom.xml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/CrmSaleQuotationAiController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/MesProAiController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/MesQcAiController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/pom.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/java/cn/iocoder/yudao/server/YudaoServerApplication.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application.yaml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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