diff --git a/src/com/engine/salary/cache/SalaryCacheKey.java b/src/com/engine/salary/cache/SalaryCacheKey.java index 549dccd31..76d19ea7d 100644 --- a/src/com/engine/salary/cache/SalaryCacheKey.java +++ b/src/com/engine/salary/cache/SalaryCacheKey.java @@ -1,11 +1,7 @@ package com.engine.salary.cache; /** - * @description: 用于定义本服务内部使用的缓存key - * @author: xiajun - * @modified By: xiajun - * @date: Created in 12/13/21 11:12 AM - * @version:v1.0 + * 用于定义本服务内部使用的缓存key */ public class SalaryCacheKey { diff --git a/src/com/engine/salary/maintainer/salaryacct/SalaryAcctManager.java b/src/com/engine/salary/maintainer/salaryacct/SalaryAcctManager.java new file mode 100644 index 000000000..05f17cb7b --- /dev/null +++ b/src/com/engine/salary/maintainer/salaryacct/SalaryAcctManager.java @@ -0,0 +1,351 @@ +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); + } + + +} diff --git a/src/com/engine/salary/maintainer/salaryacct/SalaryAcctSupplementParam.java b/src/com/engine/salary/maintainer/salaryacct/SalaryAcctSupplementParam.java new file mode 100644 index 000000000..a4bcd3c64 --- /dev/null +++ b/src/com/engine/salary/maintainer/salaryacct/SalaryAcctSupplementParam.java @@ -0,0 +1,34 @@ +package com.engine.salary.maintainer.salaryacct; + +import com.engine.salary.util.valid.DataCheck; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Collection; + +/** + * 薪资补充核算的参数 + *

Copyright: Copyright (c) 2022

+ *

Company: 泛微软件

+ * + * @author qiantao + * @version 1.0 + **/ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SalaryAcctSupplementParam { + + //核算人员的id,不是employeeId而是salaryAcctEmpId + private Collection ids; + + @DataCheck(require = true,message = "参数错误,薪资核算记录ID不能为空") + private Long salaryAcctRecordId; + + @DataCheck(require = true,message = "参数错误,薪资项目ID不能为空") + private String salaryItemIds; + +}