package cn.iocoder.yudao.module.hrm.service.salary; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSocialSecuritySchemeSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDeptDO; import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSocialSecuritySchemeDeptMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSocialSecuritySchemeMapper; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDate; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.SOCIAL_SECURITY_SCHEME_DEPT_EXISTS; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.SOCIAL_SECURITY_SCHEME_NOT_EXISTS; /** * 社保公积金方案 Service 实现类 * * 方案的缴纳比例按「方案 - 适用部门」关联表生效;一个部门只能绑定一个方案,未绑定任何部门的方案作为全局默认方案。 * * @author 超级管理员 */ @Service @Validated public class HrmSocialSecuritySchemeServiceImpl implements HrmSocialSecuritySchemeService { /** * 启用状态 */ private static final Integer STATUS_ENABLE = 0; @Resource private HrmSocialSecuritySchemeMapper schemeMapper; @Resource private HrmSocialSecuritySchemeDeptMapper schemeDeptMapper; @Resource private DeptApi deptApi; @Override @Transactional(rollbackFor = Exception.class) public Long createScheme(HrmSocialSecuritySchemeSaveReqVO createReqVO) { HrmSocialSecuritySchemeDO scheme = BeanUtils.toBean(createReqVO, HrmSocialSecuritySchemeDO.class); schemeMapper.insert(scheme); saveSchemeDepts(scheme.getId(), createReqVO.getName(), createReqVO.getDeptIds()); return scheme.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateScheme(HrmSocialSecuritySchemeSaveReqVO updateReqVO) { validateSchemeExists(updateReqVO.getId()); schemeMapper.updateById(BeanUtils.toBean(updateReqVO, HrmSocialSecuritySchemeDO.class)); saveSchemeDepts(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getDeptIds()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteScheme(Long id) { validateSchemeExists(id); schemeDeptMapper.deleteBySchemeId(id); schemeMapper.deleteById(id); } @Override public HrmSocialSecuritySchemeDO getScheme(Long id) { return schemeMapper.selectById(id); } @Override public List getSchemeList(Integer status) { return schemeMapper.selectListByStatus(status); } @Override public List getSchemeDeptIds(Long schemeId) { return schemeDeptMapper.selectListBySchemeId(schemeId).stream() .map(HrmSocialSecuritySchemeDeptDO::getDeptId) .toList(); } @Override public Map> getSchemeDeptIdMap(Collection schemeIds) { if (CollUtil.isEmpty(schemeIds)) { return Map.of(); } return schemeDeptMapper.selectListBySchemeIds(schemeIds).stream() .collect(Collectors.groupingBy(HrmSocialSecuritySchemeDeptDO::getSchemeId, Collectors.mapping(HrmSocialSecuritySchemeDeptDO::getDeptId, Collectors.toList()))); } @Override public HrmSocialSecuritySchemeDO resolveSchemeByDeptId(Long deptId, LocalDate periodDate) { // 1. 优先取该部门绑定的方案 if (deptId != null) { List boundSchemeIds = schemeDeptMapper.selectListByDeptId(deptId).stream() .map(HrmSocialSecuritySchemeDeptDO::getSchemeId) .toList(); HrmSocialSecuritySchemeDO scheme = pickLatestEnabled(boundSchemeIds, periodDate); if (scheme != null) { return scheme; } } // 2. 兜底取全局默认方案:未绑定任何部门的方案中生效日期最新者 Set boundSchemeIds = schemeDeptMapper.selectAllBoundSchemeIds(); List globalSchemeIds = schemeMapper.selectListByStatus(STATUS_ENABLE).stream() .map(HrmSocialSecuritySchemeDO::getId) .filter(id -> !boundSchemeIds.contains(id)) .toList(); return pickLatestEnabled(globalSchemeIds, periodDate); } /** * 从候选方案中取「生效日期不晚于核算期间」的最新一条启用方案 * * 若全部候选方案都晚于核算期间(例如刚建好下月才生效的方案就提前核算),则退化为取其中生效日期最新的一条, * 避免核算金额静默落到代码里的默认比例上。 */ private HrmSocialSecuritySchemeDO pickLatestEnabled(Collection schemeIds, LocalDate periodDate) { if (CollUtil.isEmpty(schemeIds)) { return null; } List schemes = schemeMapper.selectList( new LambdaQueryWrapperX() .in(HrmSocialSecuritySchemeDO::getId, schemeIds) .eq(HrmSocialSecuritySchemeDO::getStatus, STATUS_ENABLE) .orderByDesc(HrmSocialSecuritySchemeDO::getEffectiveDate) .orderByDesc(HrmSocialSecuritySchemeDO::getId)); if (CollUtil.isEmpty(schemes)) { return null; } return schemes.stream() .filter(scheme -> scheme.getEffectiveDate() == null || !scheme.getEffectiveDate().isAfter(periodDate)) .findFirst() .orElse(schemes.get(0)); } /** * 整组替换方案的适用部门:先校验,再删旧行、插新行 */ private void saveSchemeDepts(Long schemeId, String schemeName, List deptIds) { List distinctDeptIds = CollUtil.isEmpty(deptIds) ? List.of() : deptIds.stream().filter(Objects::nonNull).distinct().toList(); if (CollUtil.isNotEmpty(distinctDeptIds)) { // 部门必须存在 deptApi.validateDeptList(distinctDeptIds); // 一个部门只能绑定一个方案 validateDeptNotBoundByOtherScheme(schemeId, schemeName, distinctDeptIds); } schemeDeptMapper.deleteBySchemeId(schemeId); distinctDeptIds.forEach(deptId -> schemeDeptMapper.insert( HrmSocialSecuritySchemeDeptDO.builder().schemeId(schemeId).deptId(deptId).build())); } /** * 校验这些部门没有被别的方案占用 */ private void validateDeptNotBoundByOtherScheme(Long schemeId, String schemeName, List deptIds) { List bindings = schemeDeptMapper.selectListByDeptIds(deptIds); if (CollUtil.isEmpty(bindings)) { return; } // 名称解析只放在异常分支,避免正常保存多查表 for (HrmSocialSecuritySchemeDeptDO binding : bindings) { if (Objects.equals(binding.getSchemeId(), schemeId)) { continue; } HrmSocialSecuritySchemeDO otherScheme = schemeMapper.selectById(binding.getSchemeId()); DeptRespDTO dept = deptApi.getDept(binding.getDeptId()); throw exception(SOCIAL_SECURITY_SCHEME_DEPT_EXISTS, dept != null ? dept.getName() : "ID=" + binding.getDeptId(), otherScheme != null ? otherScheme.getName() : "ID=" + binding.getSchemeId(), schemeName); } } private HrmSocialSecuritySchemeDO validateSchemeExists(Long id) { HrmSocialSecuritySchemeDO scheme = schemeMapper.selectById(id); if (scheme == null) { throw exception(SOCIAL_SECURITY_SCHEME_NOT_EXISTS); } return scheme; } }