weaver-hrm-salary/src/com/engine/salary/entity/salaryacct/bo/SalaryAcctCalculatePriority...

255 lines
12 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/**
* 薪资核算-薪资项目运算优先级
* <p>Copyright: Copyright (c) 2022</p>
* <p>Company: 泛微软件</p>
*
* @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<List<Long>> calculatePriority(List<SalarySobItemPO> salarySobItems,
List<SalaryItemPO> salaryItems,
List<ExpressFormula> expressFormulas,
List<SalarySobBackItemPO> salarySobBackItems,
Set<String> issuedFieldIds,
User user) {
// 公式详情
Map<Long, List<FormulaVar>> formulaIdKeyMap = ExpressFormulaBO.buildFormulaVar(expressFormulas);
// key薪资项目的idvalue薪资项目的po
Map<Long, SalaryItemPO> salaryItemPOMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getId);
// key薪资项目的codevalue薪资项目的po
Map<String, SalaryItemPO> codeKeySalaryItemPOMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getCode);
// key薪资项目的idvalue薪资账套下的薪资项目副本的po
Map<Long, SalarySobItemPO> salaryItemIdKeySalarySobItemPOMap = SalaryEntityUtil.convert2Map(salarySobItems, SalarySobItemPO::getSalaryItemId);
// 薪资账套项目+薪资回算项目
Set<Long> salarySobItemsAndBackItems = SalaryEntityUtil.properties(salarySobItems, SalarySobItemPO::getSalaryItemId);
salarySobItemsAndBackItems.addAll(SalaryEntityUtil.properties(salarySobBackItems, SalarySobBackItemPO::getSalaryItemId));
Map<Long, SalaryItemIdWithPriority> salaryItemIdWithPriorityMap = Maps.newHashMapWithExpectedSize(salarySobItemsAndBackItems.size());
// key薪资回算项目idvalue薪资回算项目副本PO
Map<Long, SalarySobBackItemPO> 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<Long, SalaryItemPO> salaryItemPOMap,
Map<String, SalaryItemPO> codeKeySalaryItemPOMap,
Map<Long, SalarySobItemPO> salaryItemIdKeySalarySobItemPOMap,
Map<Long, List<FormulaVar>> formulaIdKeyMap,
Map<Long, SalaryItemIdWithPriority> salaryItemIdWithPriorityMap,
SalaryItemIdWithPriority pre,
Map<Long, SalarySobBackItemPO> salarySobBackItemPOMap,
Set<String> issuedFieldIds,
User user) {
List<Long> salaryItemIds = Lists.newArrayList();
// 获取公式详情
List<FormulaVar> 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<String> issuedFieldIds, String fieldId) {
if (referenceEnum == SalaryFormulaReferenceEnum.ISSUED) {
issuedFieldIds.add(fieldId);
}
}
/**
* 薪资项目被哪些薪资项目引用
*
* @param current
* @param pre
*/
private static void addPre(SalaryItemIdWithPriority current, SalaryItemIdWithPriority pre, Map<Long, SalaryItemPO> 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<Long, SalaryItemPO> 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<SalaryItemIdWithPriority> 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<SalaryItemIdWithPriority> preList;
}
}