package com.engine.salary.entity.salaryacct.bo; import com.engine.salary.constant.SalaryFormulaFieldConstant; import com.engine.salary.entity.salaryformula.ExpressFormula; import com.engine.salary.entity.salaryformula.po.FormulaVar; import com.engine.salary.entity.salaryitem.po.SalaryItemPO; import com.engine.salary.entity.salarysob.po.SalarySobBackItemPO; import com.engine.salary.entity.salarysob.po.SalarySobItemPO; import com.engine.salary.enums.salaryformula.SalaryFormulaReferenceEnum; import com.engine.salary.exception.SalaryRunTimeException; import com.engine.salary.util.SalaryEntityUtil; import com.engine.salary.util.SalaryI18nUtil; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import weaver.hrm.User; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * 薪资核算-薪资项目运算优先级 *

Copyright: Copyright (c) 2022

*

Company: 泛微软件

* * @author qiantao * @version 1.0 **/ public class SalaryAcctCalculatePriorityBO { /** * 公式中变量的fieldId的正则表达式 */ private static final String SALARY_REGEX = "(\\w+)" + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR + "(\\w+)"; /** * 解析公式中变量的fieldId的正则表达式 */ private static final Pattern SALARY_PATTERN = Pattern.compile(SALARY_REGEX); /** * 计算优先级 * * @param salarySobItems * @param salaryItems * @param expressFormulas * @return 根据计算优先级已经排好序,集合中是薪资账套中的薪资项目 */ public static List> calculatePriority(List salarySobItems, List salaryItems, List expressFormulas, List salarySobBackItems, Set issuedFieldIds, User user) { // 公式详情 Map> formulaIdKeyMap = ExpressFormulaBO.buildFormulaVar(expressFormulas); // key:薪资项目的id,value:薪资项目的po Map salaryItemPOMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getId); // key:薪资项目的code,value:薪资项目的po Map codeKeySalaryItemPOMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getCode); // key:薪资项目的id,value:薪资账套下的薪资项目副本的po Map salaryItemIdKeySalarySobItemPOMap = SalaryEntityUtil.convert2Map(salarySobItems, SalarySobItemPO::getSalaryItemId); // 薪资账套项目+薪资回算项目 Set salarySobItemsAndBackItems = SalaryEntityUtil.properties(salarySobItems, SalarySobItemPO::getSalaryItemId); salarySobItemsAndBackItems.addAll(SalaryEntityUtil.properties(salarySobBackItems, SalarySobBackItemPO::getSalaryItemId)); Map salaryItemIdWithPriorityMap = Maps.newHashMapWithExpectedSize(salarySobItemsAndBackItems.size()); // key:薪资回算项目id,value:薪资回算项目副本PO Map salarySobBackItemPOMap = SalaryEntityUtil.convert2Map(salarySobBackItems, SalarySobBackItemPO::getSalaryItemId); for (Long salaryItemId : salarySobItemsAndBackItems) { calculate(salaryItemId, salaryItemPOMap, codeKeySalaryItemPOMap, salaryItemIdKeySalarySobItemPOMap, formulaIdKeyMap, salaryItemIdWithPriorityMap, null, salarySobBackItemPOMap, issuedFieldIds, user); } return SalaryEntityUtil.group2Map(salaryItemIdWithPriorityMap.values(), SalaryItemIdWithPriority::getPriority).values().stream() .sorted(Comparator.comparingInt(list -> list.get(0).getPriority())) .map(list -> SalaryEntityUtil.properties(list, SalaryItemIdWithPriority::getSalaryItemId, Collectors.toList())) .collect(Collectors.toList()); } /** * 计算薪资账套中的薪资项目的计算优先级 * * @param currentSalaryItemId * @param salaryItemPOMap * @param salaryItemIdKeySalarySobItemPOMap * @param codeKeySalaryItemPOMap * @param formulaIdKeyMap * @param salaryItemIdWithPriorityMap * @param pre */ private static void calculate(Long currentSalaryItemId, Map salaryItemPOMap, Map codeKeySalaryItemPOMap, Map salaryItemIdKeySalarySobItemPOMap, Map> formulaIdKeyMap, Map salaryItemIdWithPriorityMap, SalaryItemIdWithPriority pre, Map salarySobBackItemPOMap, Set issuedFieldIds, User user) { List salaryItemIds = Lists.newArrayList(); // 获取公式详情 List formulaVars; if (salaryItemIdKeySalarySobItemPOMap.containsKey(currentSalaryItemId)) { // 如果薪资项目在薪资账套中有副本,则取薪资账套中设置的公式 SalarySobItemPO salarySobItemPO = salaryItemIdKeySalarySobItemPOMap.get(currentSalaryItemId); formulaVars = formulaIdKeyMap.getOrDefault(salarySobItemPO.getFormulaId(), Collections.emptyList()); } else if(salarySobBackItemPOMap.containsKey(currentSalaryItemId)){ // 如果薪资项目在薪资账套中没有副本,则取薪资回算中设置的公式 SalarySobBackItemPO salarySobBackItemPO = salarySobBackItemPOMap.get(currentSalaryItemId); formulaVars = formulaIdKeyMap.getOrDefault(salarySobBackItemPO.getFormulaId(), Collections.emptyList()); } else { // 如果薪资项目在薪资账套及回算薪资项目中没有有副本,则取薪资项目中设置的公式 SalaryItemPO salaryItemPO = salaryItemPOMap.get(currentSalaryItemId); formulaVars = formulaIdKeyMap.getOrDefault(salaryItemPO.getFormulaId(), Collections.emptyList()); } // 解析公式详情中的变量,找出引用了哪些其他的薪资项目(需要先计算出引用的薪资项目才能计算当前的薪资项目) for (FormulaVar formulaVar : formulaVars) { String fieldId = formulaVar.getFieldId(); if (StringUtils.isEmpty(fieldId)) { continue; } Matcher matcher = SALARY_PATTERN.matcher(fieldId); if (matcher.find()) { SalaryFormulaReferenceEnum referenceEnum = SalaryFormulaReferenceEnum.parseByValue(matcher.group(1)); // 分析公式中的回算变量包含哪些 loadSalaryCalcFormula(referenceEnum, issuedFieldIds, matcher.group(2)); if (referenceEnum == SalaryFormulaReferenceEnum.SALARY_ITEM) { SalaryItemPO salaryItemPO = codeKeySalaryItemPOMap.get(matcher.group(2)); if (salaryItemPO == null) { continue; } salaryItemIds.add(salaryItemPO.getId()); } } } if (CollectionUtils.isEmpty(salaryItemIds)) { SalaryItemIdWithPriority current = salaryItemIdWithPriorityMap.computeIfAbsent(currentSalaryItemId, k -> SalaryItemIdWithPriority.builder() .priority(0) .salaryItemId(currentSalaryItemId) .preList(Collections.emptyList()) .build()); addPre(current, pre, salaryItemPOMap, user); updatePriority(current); return; } for (Long salaryItemId : salaryItemIds) { SalaryItemIdWithPriority current = salaryItemIdWithPriorityMap.computeIfAbsent(currentSalaryItemId, k -> SalaryItemIdWithPriority.builder() .priority(1) .salaryItemId(currentSalaryItemId) .preList(Collections.emptyList()) .build()); addPre(current, pre, salaryItemPOMap, user); updatePriority(current); calculate(salaryItemId, salaryItemPOMap, codeKeySalaryItemPOMap, salaryItemIdKeySalarySobItemPOMap, formulaIdKeyMap, salaryItemIdWithPriorityMap, current, Collections.emptyMap(), issuedFieldIds, user); } } private static void loadSalaryCalcFormula(SalaryFormulaReferenceEnum referenceEnum, Set issuedFieldIds, String fieldId) { if (referenceEnum == SalaryFormulaReferenceEnum.ISSUED) { issuedFieldIds.add(fieldId); } } /** * 薪资项目被哪些薪资项目引用 * * @param current * @param pre */ private static void addPre(SalaryItemIdWithPriority current, SalaryItemIdWithPriority pre, Map salaryItemPOMap, User user) { if (pre == null) { return; } checkLoop(current, pre, salaryItemPOMap, user); if (CollectionUtils.isEmpty(current.getPreList())) { current.setPreList(Lists.newArrayList(pre)); } else { boolean isExist = current.getPreList().stream().anyMatch(e -> Objects.equals(e.getSalaryItemId(), pre.getSalaryItemId())); if (!isExist) { current.getPreList().add(pre); } } } /** * 检查薪资项目之间是否存在相互引用 * * @param current */ private static void checkLoop(SalaryItemIdWithPriority current, SalaryItemIdWithPriority pre, Map salaryItemPOMap, User user) { if (Objects.equals(pre.getSalaryItemId(), current.getSalaryItemId())) { SalaryItemPO preSalaryItemPO = salaryItemPOMap.get(pre.getSalaryItemId()); SalaryItemPO currentSalaryItemPO = salaryItemPOMap.get(current.getSalaryItemId()); String errMsg = (SalaryI18nUtil.getI18nLabel(user.getLanguage(),542636, "以下项目的公式中存在相互引用") + ":{0}、{1}") .replace("{0}", Optional.ofNullable(preSalaryItemPO).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY)) .replace("{1}", Optional.ofNullable(currentSalaryItemPO).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY)); // String errMsg = SalaryI18nUtil.getI18nLabel(user.getLanguage(),101426, "{0}和{1}的公式中存在相互引用") // .replace("{0}", Optional.ofNullable(preSalaryItemPO).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY)) // .replace("{1}", Optional.ofNullable(currentSalaryItemPO).map(SalaryItemPO::getName).orElse(StringUtils.EMPTY)); throw new SalaryRunTimeException(errMsg); } if (CollectionUtils.isEmpty(pre.getPreList())) { return; } for (SalaryItemIdWithPriority salaryItemIdWithPriority : pre.getPreList()) { checkLoop(current, salaryItemIdWithPriority, salaryItemPOMap, user); } } /** * 更新薪资账套中的薪资项目的计算优先级 * * @param current */ private static void updatePriority(SalaryItemIdWithPriority current) { List preList = current.getPreList(); if (CollectionUtils.isEmpty(preList)) { return; } preList.stream() .filter(e -> e.getPriority() <= current.getPriority()) .forEach(e -> e.setPriority(current.getPriority() + 1)); } @Data @Builder @NoArgsConstructor @AllArgsConstructor private static class SalaryItemIdWithPriority { /** * 薪资账套中的薪资项目的计算优先级(数字越小,计算优先级越高,从0开始计算) */ private Integer priority; /** * 薪资项目的id */ private Long salaryItemId; /** * 当前层级中的上层 */ private List preList; } }