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<HrmSocialSecuritySchemeDO> getSchemeList(Integer status) {
|
return schemeMapper.selectListByStatus(status);
|
}
|
|
@Override
|
public List<Long> getSchemeDeptIds(Long schemeId) {
|
return schemeDeptMapper.selectListBySchemeId(schemeId).stream()
|
.map(HrmSocialSecuritySchemeDeptDO::getDeptId)
|
.toList();
|
}
|
|
@Override
|
public Map<Long, List<Long>> getSchemeDeptIdMap(Collection<Long> 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<Long> boundSchemeIds = schemeDeptMapper.selectListByDeptId(deptId).stream()
|
.map(HrmSocialSecuritySchemeDeptDO::getSchemeId)
|
.toList();
|
HrmSocialSecuritySchemeDO scheme = pickLatestEnabled(boundSchemeIds, periodDate);
|
if (scheme != null) {
|
return scheme;
|
}
|
}
|
// 2. 兜底取全局默认方案:未绑定任何部门的方案中生效日期最新者
|
Set<Long> boundSchemeIds = schemeDeptMapper.selectAllBoundSchemeIds();
|
List<Long> globalSchemeIds = schemeMapper.selectListByStatus(STATUS_ENABLE).stream()
|
.map(HrmSocialSecuritySchemeDO::getId)
|
.filter(id -> !boundSchemeIds.contains(id))
|
.toList();
|
return pickLatestEnabled(globalSchemeIds, periodDate);
|
}
|
|
/**
|
* 从候选方案中取「生效日期不晚于核算期间」的最新一条启用方案
|
*
|
* 若全部候选方案都晚于核算期间(例如刚建好下月才生效的方案就提前核算),则退化为取其中生效日期最新的一条,
|
* 避免核算金额静默落到代码里的默认比例上。
|
*/
|
private HrmSocialSecuritySchemeDO pickLatestEnabled(Collection<Long> schemeIds, LocalDate periodDate) {
|
if (CollUtil.isEmpty(schemeIds)) {
|
return null;
|
}
|
List<HrmSocialSecuritySchemeDO> schemes = schemeMapper.selectList(
|
new LambdaQueryWrapperX<HrmSocialSecuritySchemeDO>()
|
.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<Long> deptIds) {
|
List<Long> 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<Long> deptIds) {
|
List<HrmSocialSecuritySchemeDeptDO> 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;
|
}
|
|
}
|