gaoluyang
2 小时以前 d37c463e8dcdc72edf992f8c010b5c9037a3dceb
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
@@ -44,6 +44,7 @@
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -85,6 +86,8 @@
    private HrmEmployeeSocialSecurityMapper employeeSocialSecurityMapper;
    @Resource
    private HrmSocialSecuritySchemeMapper socialSecuritySchemeMapper;
    @Resource
    private HrmSocialSecuritySchemeService socialSecuritySchemeService;
    @Resource
    private HrmNoRedisDAO noRedisDAO;
    @Resource
@@ -140,6 +143,8 @@
                .toList();
        // 2. 遍历员工进行薪酬核算
        // 部门 -> 方案 的解析结果在本次核算内缓存,避免逐员工重复查库
        Map<Long, HrmSocialSecuritySchemeDO> deptSchemeCache = new HashMap<>();
        for (HrmEmployeeDO employee : employees) {
            // employeeId 是员工ID,用于关联薪酬档案、社保档案、核算记录
            Long employeeId = employee.getId();
@@ -165,11 +170,8 @@
            // 2.3 获取员工社保公积金档案(按员工ID查询,取最新生效的)
            HrmEmployeeSocialSecurityDO socialSecurity = employeeSocialSecurityMapper.selectLatestByUserId(employeeId);
            // 2.4 如果有社保档案,获取绑定的方案
            HrmSocialSecuritySchemeDO scheme = null;
            if (socialSecurity != null && socialSecurity.getSocialSecuritySchemeId() != null) {
                scheme = socialSecuritySchemeMapper.selectById(socialSecurity.getSocialSecuritySchemeId());
            }
            // 2.4 获取方案:员工档案指定 > 所在部门绑定 > 全局默认
            HrmSocialSecuritySchemeDO scheme = resolveScheme(socialSecurity, employee, effectiveDate, deptSchemeCache);
            // 2.5 构建核算记录(包含扣除计算,通过方案获取比例)
            HrmSalaryCalculationDO calculation = buildSalaryCalculation(period, employeeId, employee, employeeSalary, socialSecurity, scheme, effectiveDate);
@@ -245,13 +247,22 @@
        BigDecimal totalIncome = baseSalary.add(performanceSalary).add(mealAllowance)
                .add(transportAllowance).add(otherAllowance).add(overtimePay);
        // 获取社保基数:员工社保档案 > 员工基本工资
        // 获取社保基数:员工社保档案 > 方案默认缴费基数 > 员工基本工资
        BigDecimal socialSecurityBase = (socialSecurity != null && socialSecurity.getSocialSecurityBase() != null)
                ? socialSecurity.getSocialSecurityBase() : baseSalary;
                ? socialSecurity.getSocialSecurityBase()
                : (scheme != null && scheme.getDefaultSocialSecurityBase() != null
                        ? scheme.getDefaultSocialSecurityBase() : baseSalary);
        // 获取公积金基数:员工社保档案 > 员工基本工资
        BigDecimal housingFundBase = (socialSecurity != null && socialSecurity.getHousingFundBase() != null)
                ? socialSecurity.getHousingFundBase() : baseSalary;
        // 获取公积金基数:员工社保档案 > 方案默认公积金基数(大于 0 时)> 社保基数
        BigDecimal housingFundBase;
        if (socialSecurity != null && socialSecurity.getHousingFundBase() != null) {
            housingFundBase = socialSecurity.getHousingFundBase();
        } else if (scheme != null && scheme.getDefaultHousingFundBase() != null
                && scheme.getDefaultHousingFundBase().compareTo(BigDecimal.ZERO) > 0) {
            housingFundBase = scheme.getDefaultHousingFundBase();
        } else {
            housingFundBase = socialSecurityBase;
        }
        // 从方案中获取缴纳比例(优先使用方案中的比例,否则使用默认比例)
        BigDecimal pensionPersonalRatio = (scheme != null && scheme.getPensionPersonalRatio() != null)
@@ -316,6 +327,30 @@
                .totalWorkHours(totalWorkHours)
                .status(0) // 待确认
                .build();
    }
    /**
     * 解析员工核算用的社保公积金方案
     *
     * 优先级:员工社保档案指定的方案(个别覆盖)> 员工所在部门绑定的方案 > 全局默认方案。
     * 都取不到时返回 null,由 {@link #buildSalaryCalculation} 使用默认比例常量。
     *
     * @param deptSchemeCache 本次核算内「部门 -> 方案」的缓存(允许 null 值,用于缓存"该部门无方案")
     */
    private HrmSocialSecuritySchemeDO resolveScheme(HrmEmployeeSocialSecurityDO socialSecurity, HrmEmployeeDO employee,
                                                    LocalDate periodDate,
                                                    Map<Long, HrmSocialSecuritySchemeDO> deptSchemeCache) {
        if (socialSecurity != null && socialSecurity.getSocialSecuritySchemeId() != null) {
            HrmSocialSecuritySchemeDO scheme = socialSecuritySchemeMapper.selectById(socialSecurity.getSocialSecuritySchemeId());
            if (scheme != null) {
                return scheme;
            }
        }
        Long deptId = employee.getDeptId();
        if (!deptSchemeCache.containsKey(deptId)) {
            deptSchemeCache.put(deptId, socialSecuritySchemeService.resolveSchemeByDeptId(deptId, periodDate));
        }
        return deptSchemeCache.get(deptId);
    }
    /**
@@ -531,6 +566,8 @@
        // 2. 遍历员工实时计算薪酬
        List<HrmSalaryCalculationDO> resultList = new java.util.ArrayList<>();
        // 部门 -> 方案 的解析结果在本次预览内缓存,避免逐员工重复查库
        Map<Long, HrmSocialSecuritySchemeDO> deptSchemeCache = new HashMap<>();
        for (HrmEmployeeDO employee : employees) {
            Long employeeId = employee.getId();
@@ -543,11 +580,8 @@
            // 2.2 获取员工社保公积金档案(按员工ID查询,取最新生效的)
            HrmEmployeeSocialSecurityDO socialSecurity = employeeSocialSecurityMapper.selectLatestByUserId(employeeId);
            // 2.3 如果有社保档案,获取绑定的方案
            HrmSocialSecuritySchemeDO scheme = null;
            if (socialSecurity != null && socialSecurity.getSocialSecuritySchemeId() != null) {
                scheme = socialSecuritySchemeMapper.selectById(socialSecurity.getSocialSecuritySchemeId());
            }
            // 2.3 获取方案:员工档案指定 > 所在部门绑定 > 全局默认
            HrmSocialSecuritySchemeDO scheme = resolveScheme(socialSecurity, employee, effectiveDate, deptSchemeCache);
            // 2.4 构建核算记录(实时计算,不存储)
            HrmSalaryCalculationDO calculation = buildSalaryCalculation(period, employeeId, employee, employeeSalary, socialSecurity, scheme, effectiveDate);