package cn.iocoder.yudao.module.hrm.service.salary; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSalaryPaymentDetailPageReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSalaryPaymentPageReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryCalculationDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryPaymentDetailDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryPaymentDO; import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryCalculationMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryPaymentDetailMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryPaymentMapper; import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO; import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; /** * 薪资发放 Service 实现类 * * @author 芋道源码 */ @Service @Validated public class HrmSalaryPaymentServiceImpl implements HrmSalaryPaymentService { @Resource private HrmSalaryPaymentMapper salaryPaymentMapper; @Resource private HrmSalaryPaymentDetailMapper salaryPaymentDetailMapper; @Resource private HrmSalaryCalculationMapper salaryCalculationMapper; @Resource private HrmNoRedisDAO noRedisDAO; @Resource private StorageAttachmentApi storageAttachmentApi; @Override public PageResult getSalaryPaymentPage(HrmSalaryPaymentPageReqVO pageReqVO) { return salaryPaymentMapper.selectPage(pageReqVO); } @Override public HrmSalaryPaymentDO getSalaryPayment(Long id) { return salaryPaymentMapper.selectById(id); } @Override @Transactional(rollbackFor = Exception.class) public Long generatePayment(String period) { // 1. 检查是否已存在该周期的发放台账 HrmSalaryPaymentDO existPayment = salaryPaymentMapper.selectByPeriod(period); if (existPayment != null) { throw exception(SALARY_PAYMENT_EXISTS); } // 2. 查询该周期已确认的薪酬核算记录(状态=10) List calculationList = salaryCalculationMapper.selectListByPeriodAndStatus(period, 10); if (CollUtil.isEmpty(calculationList)) { throw exception(SALARY_CALCULATION_NO_CONFIRMED); } // 3. 生成发放台账 String no = generatePaymentNo(); BigDecimal totalAmount = calculationList.stream() .map(c -> c.getActualSalary() != null ? c.getActualSalary() : BigDecimal.ZERO) .reduce(BigDecimal.ZERO, BigDecimal::add); Integer totalCount = calculationList.size(); HrmSalaryPaymentDO payment = HrmSalaryPaymentDO.builder() .no(no) .period(period) .totalAmount(totalAmount) .totalCount(totalCount) .status(0) // 待发放 .build(); salaryPaymentMapper.insert(payment); // 4. 生成发放明细(不更新核算状态) for (HrmSalaryCalculationDO calculation : calculationList) { HrmSalaryPaymentDetailDO detail = HrmSalaryPaymentDetailDO.builder() .paymentId(payment.getId()) .calculationId(calculation.getId()) .userId(calculation.getUserId()) .deptId(calculation.getDeptId()) .actualSalary(calculation.getActualSalary()) .status(0) // 待发放 .build(); salaryPaymentDetailMapper.insert(detail); } return payment.getId(); } private String generatePaymentNo() { String no = noRedisDAO.generateSalaryPaymentNo(); while (salaryPaymentMapper.selectByNo(no) != null) { no = noRedisDAO.generateSalaryPaymentNo(); } return no; } @Override @Transactional(rollbackFor = Exception.class) public void paySalary(Long id) { // 1. 校验发放台账存在 HrmSalaryPaymentDO payment = validateSalaryPaymentExists(id); if (payment.getStatus() != 0) { throw exception(SALARY_PAYMENT_ALREADY_PAY); } // 2. 查询待发放的明细 List detailList = salaryPaymentDetailMapper.selectListByPaymentIdAndStatus(id, 0); if (CollUtil.isEmpty(detailList)) { throw exception(SALARY_PAYMENT_DETAIL_NO_EXISTS); } // 3. 批量发放明细,并更新核算状态 LocalDateTime paymentTime = LocalDateTime.now(); for (HrmSalaryPaymentDetailDO detail : detailList) { detail.setStatus(10); // 已发放 detail.setPaymentTime(paymentTime); salaryPaymentDetailMapper.updateById(detail); // 更新薪酬核算状态为已发放(20) HrmSalaryCalculationDO calculation = salaryCalculationMapper.selectById(detail.getCalculationId()); if (calculation != null) { calculation.setStatus(20); calculation.setPaymentTime(paymentTime); salaryCalculationMapper.updateById(calculation); } } // 4. 更新发放台账状态 payment.setStatus(10); // 已发放 payment.setPaymentTime(paymentTime); salaryPaymentMapper.updateById(payment); } @Override @Transactional(rollbackFor = Exception.class) public void paySalaryDetail(List detailIds) { if (CollUtil.isEmpty(detailIds)) { return; } LocalDateTime paymentTime = LocalDateTime.now(); Long paymentId = null; for (Long detailId : detailIds) { HrmSalaryPaymentDetailDO detail = salaryPaymentDetailMapper.selectById(detailId); if (detail == null) { throw exception(SALARY_PAYMENT_DETAIL_NOT_EXISTS); } if (detail.getStatus() != 0) { throw exception(SALARY_PAYMENT_DETAIL_ALREADY_PAY); } detail.setStatus(10); // 已发放 detail.setPaymentTime(paymentTime); salaryPaymentDetailMapper.updateById(detail); paymentId = detail.getPaymentId(); // 更新薪酬核算状态为已发放(20) HrmSalaryCalculationDO calculation = salaryCalculationMapper.selectById(detail.getCalculationId()); if (calculation != null) { calculation.setStatus(20); calculation.setPaymentTime(paymentTime); salaryCalculationMapper.updateById(calculation); } } // 检查该台账下是否所有明细都已发放,如果是则更新台账状态 if (paymentId != null) { updatePaymentStatusIfAllPaid(paymentId); } } /** * 检查该台账下是否所有明细都已发放,如果是则更新台账状态为已发放 */ private void updatePaymentStatusIfAllPaid(Long paymentId) { // 查询该台账下所有明细 List allDetails = salaryPaymentDetailMapper.selectListByPaymentId(paymentId); if (CollUtil.isEmpty(allDetails)) { return; } // 检查是否全部已发放 boolean allPaid = allDetails.stream().allMatch(d -> d.getStatus() == 10); if (!allPaid) { return; } // 更新台账状态为已发放 HrmSalaryPaymentDO payment = salaryPaymentMapper.selectById(paymentId); if (payment != null && payment.getStatus() == 0) { payment.setStatus(10); payment.setPaymentTime(LocalDateTime.now()); salaryPaymentMapper.updateById(payment); } } private HrmSalaryPaymentDO validateSalaryPaymentExists(Long id) { HrmSalaryPaymentDO payment = salaryPaymentMapper.selectById(id); if (payment == null) { throw exception(SALARY_PAYMENT_NOT_EXISTS); } return payment; } @Override public PageResult getSalaryPaymentDetailPage(HrmSalaryPaymentDetailPageReqVO pageReqVO) { return salaryPaymentDetailMapper.selectPage(pageReqVO); } @Override public List getSalaryPaymentDetailListByPaymentId(Long paymentId) { return salaryPaymentDetailMapper.selectListByPaymentId(paymentId); } @Override @Transactional(rollbackFor = Exception.class) public void revokeSalaryPaymentDetail(List detailIds) { if (CollUtil.isEmpty(detailIds)) { return; } Long paymentId = null; for (Long detailId : detailIds) { HrmSalaryPaymentDetailDO detail = salaryPaymentDetailMapper.selectById(detailId); if (detail == null) { throw exception(SALARY_PAYMENT_DETAIL_NOT_EXISTS); } // 校验状态必须是已发放(10) if (detail.getStatus() != 10) { throw exception(SALARY_PAYMENT_REVOKE_FAIL); } // 恢复明细状态为待发放 detail.setStatus(0); detail.setPaymentTime(null); salaryPaymentDetailMapper.updateById(detail); paymentId = detail.getPaymentId(); // 恢复薪酬核算状态为已确认(10) HrmSalaryCalculationDO calculation = salaryCalculationMapper.selectById(detail.getCalculationId()); if (calculation != null) { calculation.setStatus(10); calculation.setPaymentTime(null); salaryCalculationMapper.updateById(calculation); } } // 更新发放台账状态为待发放(如果有明细被撤销) if (paymentId != null) { HrmSalaryPaymentDO payment = salaryPaymentMapper.selectById(paymentId); if (payment != null && payment.getStatus() == 10) { payment.setStatus(0); payment.setPaymentTime(null); salaryPaymentMapper.updateById(payment); } } } @Override @Transactional(rollbackFor = Exception.class) public void deleteSalaryPayment(Long id) { HrmSalaryPaymentDO payment = validateSalaryPaymentExists(id); // 仅待发放状态可删除 if (payment.getStatus() != 0) { throw exception(SALARY_PAYMENT_DELETE_FAIL); } // 检查是否存在已发放明细 List details = salaryPaymentDetailMapper.selectListByPaymentId(id); boolean hasPaidDetail = details.stream().anyMatch(d -> d.getStatus() == 10); if (hasPaidDetail) { throw exception(SALARY_PAYMENT_HAS_PAID_DETAIL); } // 删除所有明细 for (HrmSalaryPaymentDetailDO detail : details) { salaryPaymentDetailMapper.deleteById(detail.getId()); } // 删除附件关联 storageAttachmentApi.deleteAttachmentsByRecord("hrm_salary_payment", id); // 删除发放台账 salaryPaymentMapper.deleteById(id); } }