package com.engine.salary.maintainer.salaryacct; import com.engine.common.util.ServiceUtil; import com.engine.core.impl.Service; import com.engine.salary.entity.datacollection.DataCollectionEmployee; import com.engine.salary.entity.datacollection.dto.AttendQuoteFieldListDTO; import com.engine.salary.entity.salaryacct.bo.SalaryAcctCalculateBO; import com.engine.salary.entity.salaryacct.bo.SalaryAcctCalculatePriorityBO; import com.engine.salary.entity.salaryacct.bo.SalaryAcctResultBO; import com.engine.salary.entity.salaryacct.dto.SalaryAcctProgressDTO; import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO; import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO; import com.engine.salary.entity.salaryacct.po.SalaryAcctResultPO; import com.engine.salary.entity.salaryacct.po.SalaryAcctResultTempPO; import com.engine.salary.entity.salaryformula.ExpressFormula; import com.engine.salary.entity.salaryitem.po.SalaryItemPO; import com.engine.salary.entity.salarysob.dto.SalarySobCycleDTO; import com.engine.salary.entity.salarysob.po.SalarySobAdjustRulePO; import com.engine.salary.entity.salarysob.po.SalarySobBackItemPO; import com.engine.salary.entity.salarysob.po.SalarySobItemPO; import com.engine.salary.entity.salarysob.po.SalarySobPO; import com.engine.salary.exception.SalaryRunTimeException; import com.engine.salary.mapper.salaryacct.SalaryAcctResultMapper; import com.engine.salary.service.*; import com.engine.salary.service.impl.*; import com.engine.salary.util.SalaryEntityUtil; import com.engine.salary.util.SalaryI18nUtil; import com.engine.salary.util.db.MapperProxyFactory; import com.google.common.collect.Lists; import com.weaver.util.threadPool.ThreadPoolUtil; import com.weaver.util.threadPool.constant.ModulePoolEnum; import com.weaver.util.threadPool.entity.LocalRunnable; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import weaver.hrm.User; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.BlockingDeque; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingDeque; import java.util.stream.Collectors; /** * 薪资核算维护类 *

Copyright: Copyright (c) 2022

*

Company: 泛微软件

* * @author qiantao * @version 1.0 **/ @Slf4j public class SalaryAcctManager extends Service { private SalaryAcctResultMapper getSalaryAcctResultMapper() { return MapperProxyFactory.getProxy(SalaryAcctResultMapper.class); } private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) { return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user); } private SalaryAcctResultService getSalaryAcctResultService(User user) { return ServiceUtil.getService(SalaryAcctResultServiceImpl.class, user); } private SalarySobItemService getSalarySobItemService(User user) { return ServiceUtil.getService(SalarySobItemServiceImpl.class, user); } private SalaryItemService getSalaryItemService(User user) { return ServiceUtil.getService(SalaryItemServiceImpl.class, user); } private SalarySobEmpFieldService getSalarySobEmpFieldService(User user) { return ServiceUtil.getService(SalarySobEmpFieldServiceImpl.class, user); } private SalarySobService getSalarySobService(User user) { return ServiceUtil.getService(SalarySobServiceImpl.class, user); } private SalaryAcctRecordService getSalaryAcctRecordService(User user) { return ServiceUtil.getService(SalaryAcctRecordServiceImpl.class, user); } private TaxAgentService getTaxAgentService(User user) { return ServiceUtil.getService(TaxAgentServiceImpl.class, user); } private SalaryEmployeeService getSalaryEmployeeService(User user) { return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user); } private SalaryFormulaService getSalaryFormulaService(User user) { return ServiceUtil.getService(SalaryFormulaServiceImpl.class, user); } private SalarySobAdjustRuleService getSalarySobAdjustRuleService(User user) { return ServiceUtil.getService(SalarySobAdjustRuleServiceImpl.class, user); } private SalarySobItemHideService getSalarySobItemHideService(User user) { return ServiceUtil.getService(SalarySobItemHideServiceImpl.class, user); } private SalaryAcctCalculateService getSalaryAcctCalculateService(User user) { return ServiceUtil.getService(SalaryAcctCalculateServiceImpl.class, user); } private SalaryAcctProgressService getSalaryAcctProgressService(User user) { return ServiceUtil.getService(SalaryAcctProgressServiceImpl.class, user); } private DataSourceTransactionManager dataSourceTransactionManager; private SalaryAcctResultTempService getSalaryAcctResultTempService(User user) { return ServiceUtil.getService(SalaryAcctResultTempServiceImpl.class, user); } // private LoggerTemplate salaryAcctRecordLoggerTemplate; private SIAccountService getSIAccountService(User user) { return ServiceUtil.getService(SIAccountServiceImpl.class, user); } private AttendQuoteFieldService getAttendQuoteFieldService(User user) { return ServiceUtil.getService(AttendQuoteFieldServiceImpl.class, user); } private SalaryAcctReportService getSalaryAcctReportService(User user) { return ServiceUtil.getService(SalaryAcctReportServiceImpl.class, user); } private SalarySobItemGroupService getSalarySobItemGroupService(User user) { return ServiceUtil.getService(SalarySobItemGroupServiceImpl.class, user); } private SalarySobBackItemService getSalarySobBackItemService(User user) { return ServiceUtil.getService(SalarySobBackItemServiceImpl.class, user); } /** * 补充核算 * 核算已归档后,需要重新核算某一项 */ public void supplementAcctRecord(SalaryAcctSupplementParam param) { } /** * 薪资核算-核算 * * @param calculateParam 薪资核算的参数 */ public void calculate(SalaryAcctSupplementParam calculateParam) { log.info("开始核算V1{}", calculateParam); //当前登陆人员 DataCollectionEmployee simpleEmployee = new DataCollectionEmployee(); simpleEmployee.setEmployeeId((long) user.getUID()); // 检查是否正在核算中 SalaryAcctProgressDTO salaryAcctProgressDTO = getSalaryAcctProgressService(user).getProgress("SalaryCacheKey.ACCT_PROGRESS" + calculateParam.getSalaryAcctRecordId()); if (Objects.nonNull(salaryAcctProgressDTO) && salaryAcctProgressDTO.isStatus() && Optional.ofNullable(salaryAcctProgressDTO.getProgress()).orElse(BigDecimal.ZERO).compareTo(BigDecimal.ONE) < 0) { return; } // 初始化进度 SalaryAcctProgressDTO initProgress = new SalaryAcctProgressDTO() .setTitle(SalaryI18nUtil.getI18nLabel(97515, "核算中")) .setTitleLabelId(97515L) .setTotalQuantity(NumberUtils.INTEGER_ONE) .setCalculatedQuantity(NumberUtils.INTEGER_ZERO) .setProgress(BigDecimal.ZERO) .setStatus(true) .setMessage(StringUtils.EMPTY); getSalaryAcctProgressService(user).initProgress("SalaryCacheKey.ACCT_PROGRESS" + calculateParam.getSalaryAcctRecordId(), initProgress); new Thread() { public void run() { calculate(calculateParam, simpleEmployee); } }.start(); } public void calculate(SalaryAcctSupplementParam calculateParam, DataCollectionEmployee simpleEmployee) { try { // 1、查询薪资核算记录 SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(calculateParam.getSalaryAcctRecordId()); if (Objects.isNull(salaryAcctRecordPO)) { throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除")); } //查询对应账套 SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId()); if (Objects.isNull(salarySobPO)) { throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资账套不存在或已被删除")); } // 2、查询薪资核算记录的薪资周期、考勤周期等 SalarySobCycleDTO salarySobCycleDTO = getSalaryAcctRecordService(user).getSalarySobCycleById(calculateParam.getSalaryAcctRecordId()); // 3、查询薪资核算记录所用薪资账套的薪资项目副本 List salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId()); if (CollectionUtils.isEmpty(salarySobItemPOS)) { throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(99151, "当前所用的薪资账套未选择任何薪资项目,无法核算")); } // 回算薪资项目 List salarySobBackItems = Collections.emptyList(); if (Objects.equals(salaryAcctRecordPO.getBackCalcStatus(), 1)) { salarySobBackItems = getSalarySobBackItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId()); } // 4、查询当前租户的所有薪资项目 List salaryItemPOS = getSalaryItemService(user).listAll(); // 5、查询薪资核算记录所用薪资账套的调薪计薪规则 List salarySobAdjustRulePOS = getSalarySobAdjustRuleService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId()); // 6、查询社保福利的所有字段 Map welfareColumns = getSIAccountService(user).welfareColumns(); // 7、查询考勤引用的所有字段 List attendQuoteFieldListDTOS = getAttendQuoteFieldService(user).listAll(); //核算锁定值 List lockSalaryItemIds = salaryAcctRecordPO.getLockSalaryItemIds(); Map acctResults = new HashMap<>(); if (CollectionUtils.isNotEmpty(lockSalaryItemIds)) { List acctResultPOS = getSalaryAcctResultService(user).listBySalaryAcctRecordIds(Collections.singleton(calculateParam.getSalaryAcctRecordId())); acctResults = Optional.ofNullable(acctResultPOS) .orElse(new ArrayList<>()) .stream() .filter(po -> lockSalaryItemIds.contains(po.getSalaryItemId())) .collect(Collectors.toMap(po -> po.getSalaryItemId() + "_" + po.getSalaryAcctEmpId(), a -> a, (a, b) -> a)); } // 8、查询公式详情 Set formulaIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getFormulaId); formulaIds.addAll(SalaryEntityUtil.properties(salaryItemPOS, SalaryItemPO::getFormulaId)); formulaIds.addAll(SalaryEntityUtil.properties(salarySobBackItems, SalarySobBackItemPO::getFormulaId)); List expressFormulas = getSalaryFormulaService(user).listExpressFormula(formulaIds); // 本次运算的回算薪资项目所涉及的变量 Set issuedFieldIds = new HashSet<>(); // 9、计算薪资项目的运算优先级 List> salarySobItemsWithPriority = SalaryAcctCalculatePriorityBO.calculatePriority(salarySobItemPOS, salaryItemPOS, expressFormulas, salarySobBackItems, issuedFieldIds); // 10、根据id查询其他合并计税的薪资核算记录 List otherSalaryAcctRecordPOS = getSalaryAcctRecordService(user).listById4OtherConsolidatedTax(salaryAcctRecordPO.getId()); // 11、查询本次核算人员 List salaryAcctEmployeePOS; if (CollectionUtils.isEmpty(calculateParam.getIds())) { salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(salaryAcctRecordPO.getId()); } else { salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listByIds(calculateParam.getIds()); } if (CollectionUtils.isEmpty(salaryAcctEmployeePOS)) { throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(103378, "薪资核算人员不能为空")); } // 11.1、初始化进度 SalaryAcctProgressDTO initProgress = new SalaryAcctProgressDTO().setTitle(SalaryI18nUtil.getI18nLabel(97515, "核算中")).setTitleLabelId(97515L).setTotalQuantity(salaryAcctEmployeePOS.size() * 2 + 1).setCalculatedQuantity(0).setProgress(BigDecimal.ZERO).setStatus(true).setMessage(StringUtils.EMPTY); getSalaryAcctProgressService(user).initProgress("SalaryCacheKey.ACCT_PROGRESS" + calculateParam.getSalaryAcctRecordId(), initProgress); // 12、对薪资核算人员进行拆分 List> partition = Lists.partition(salaryAcctEmployeePOS, 5000); // 12.1、监控子线程的任务执行 CountDownLatch childMonitor = new CountDownLatch(partition.size()); // 12.2、记录子线程的执行结果 BlockingDeque calculateResults = new LinkedBlockingDeque<>(partition.size()); // 12.3、生成本次运算的key String calculateKey = UUID.randomUUID().toString(); // 12.4、多线程运算,运算结果存放在临时表中 for (List acctEmployeePOS : partition) { SalaryAcctCalculateBO salaryAcctCalculateBO = new SalaryAcctCalculateBO() .setSalaryAcctRecordPO(salaryAcctRecordPO) .setSalarySobPO(salarySobPO) .setSalarySobCycleDTO(salarySobCycleDTO) .setOtherSalaryAcctRecordPOS(otherSalaryAcctRecordPOS) .setSalaryAcctLockResultPOS(MapUtils.emptyIfNull(acctResults)) .setLockSalaryItemIds(lockSalaryItemIds) .setSalarySobItemPOS(salarySobItemPOS) .setSalaryItemIdWithPriorityList(salarySobItemsWithPriority) .setExpressFormulas(expressFormulas) .setSalaryItemPOS(salaryItemPOS) .setSalarySobAdjustRulePOS(salarySobAdjustRulePOS) .setWelfareColumns(MapUtils.emptyIfNull(welfareColumns)) .setAttendQuoteFieldListDTOS(attendQuoteFieldListDTOS) .setSalaryAcctEmployeePOS(acctEmployeePOS) .setIssuedFieldIds(issuedFieldIds) .setChildMonitor(childMonitor) .setResults(calculateResults) .setCalculateKey(calculateKey); List finalSalarySobBackItems = salarySobBackItems; LocalRunnable localRunnable = new LocalRunnable() { @Override public void execute() { getSalaryAcctCalculateService(user).calculate(salaryAcctCalculateBO, simpleEmployee, finalSalarySobBackItems); } }; ThreadPoolUtil.fixedPoolExecute(ModulePoolEnum.HRM, "salaryAcctCalculateV2", localRunnable); } // 13、等待所有子线程执行完毕 childMonitor.await(); // 14、判断子线程执行结果 boolean allSuccess = calculateResults.stream().allMatch(SalaryAcctCalculateBO.Result::isStatus); if (!allSuccess) { // 薪资核算实现的线程的错误信息 String errorMsg = calculateResults.stream().filter(result -> !result.isStatus()).map(SalaryAcctCalculateBO.Result::getErrMsg).collect(Collectors.joining("|")); getSalaryAcctProgressService(user).fail("SalaryCacheKey.ACCT_PROGRESS" + calculateParam.getSalaryAcctRecordId(), errorMsg); // 删除薪资核算临时存储表中的数据 getSalaryAcctResultTempService(user).deleteByCalculateKey(calculateKey); return; } // 15、处理核算结果临时表数据 handleSalaryAcctResultTemp(calculateParam, calculateKey); getSalaryAcctProgressService(user).finish("SalaryCacheKey.ACCT_PROGRESS" + calculateParam.getSalaryAcctRecordId(), true); } catch (Exception e) { log.info("薪资核算出错:{}", e.getMessage(), e); // throw new SalaryRunTimeException(e); getSalaryAcctProgressService(user).fail("SalaryCacheKey.ACCT_PROGRESS" + calculateParam.getSalaryAcctRecordId(), SalaryI18nUtil.getI18nLabel(99642, "薪资核算出错") + ": " + e.getMessage()); } finally { // 数据库字段加密用 } } /** * 处理薪资核算临时存储表中的数据 * * @param calculateParam * @param calculateKey */ private void handleSalaryAcctResultTemp(SalaryAcctSupplementParam calculateParam, String calculateKey) { // 查询薪资核算结果的临时存储 List salaryAcctResultTempPOS = getSalaryAcctResultTempService(user).listByCalculateKey(calculateKey); // 删除原来的薪资核算结果 if (CollectionUtils.isNotEmpty(calculateParam.getIds())) { getSalaryAcctResultMapper().deleteBySalaryAcctEmpIds(calculateParam.getIds()); } else { getSalaryAcctResultMapper().deleteBySalaryAcctRecordIds(Collections.singleton(calculateParam.getSalaryAcctRecordId())); getSalaryAcctReportService(user).deleteBySalaryAcctRecordId(calculateParam.getSalaryAcctRecordId()); } // 保存薪资的薪资核算结果 List salaryAcctResultPOS = SalaryAcctResultBO.convert2ResultPO(salaryAcctResultTempPOS); // batchSave(salaryAcctResultPOS); //保存核算报表数据 // List salaryAcctResultReportPOS = SalaryAcctResultReportBO.convert2ReportPO(salaryAcctResultTempPOS, calculateParam.getEmps()); // getSalaryAcctReportService(user).batchSave(salaryAcctResultReportPOS); // 删除薪资核算临时存储表中的数据 getSalaryAcctResultTempService(user).deleteByCalculateKey(calculateKey); } }