weaver-hrm-salary/src/com/engine/salary/service/impl/SalaryFormulaServiceImpl.java

422 lines
18 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.service.impl;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.salaryformula.ExpressFormula;
import com.engine.salary.entity.salaryformula.param.SalaryFormulaMockParam;
import com.engine.salary.entity.salaryformula.param.SalaryFormulaSaveParam;
import com.engine.salary.entity.salaryformula.po.FormulaPO;
import com.engine.salary.entity.salaryformula.po.FormulaVar;
import com.engine.salary.enums.salaryformula.ReferenceTypeEnum;
import com.engine.salary.enums.salaryformula.ReturnTypeEnum;
import com.engine.salary.enums.salaryformula.ValidateTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.formlua.entity.standard.ExcelResult;
import com.engine.salary.mapper.formula.FormulaMapper;
import com.engine.salary.mapper.formula.FormulaVarMapper;
import com.engine.salary.service.FormulaRunService;
import com.engine.salary.service.RemoteExcelService;
import com.engine.salary.service.SalaryFormulaService;
import com.engine.salary.util.JsonUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.valid.ValidUtil;
import com.google.common.collect.Lists;
import dm.jdbc.util.IdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeanUtils;
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
**/
@Slf4j
public class SalaryFormulaServiceImpl extends Service implements SalaryFormulaService {
private FormulaMapper getFormulaMapper() {
return MapperProxyFactory.getProxy(FormulaMapper.class);
}
private FormulaVarMapper getFormulaVarMapper() {
return MapperProxyFactory.getProxy(FormulaVarMapper.class);
}
private FormulaRunService getFormulaRunService(User user) {
return ServiceUtil.getService(FormulaRunServiceImpl.class, user);
}
private RemoteExcelService getRemoteExcelService(User user) {
return ServiceUtil.getService(RemoteExcelServiceImpl.class, user);
}
@Override
public List<ExpressFormula> listExpressFormula(Collection<Long> formulaIds) {
formulaIds = formulaIds.stream().filter(id -> id != null && id > 0).collect(Collectors.toList());
if (CollectionUtils.isEmpty(formulaIds)) {
return Collections.emptyList();
}
try {
// 当前租户自己新建的公式
List<FormulaPO> expressFormulas = getFormulaMapper().listByIds(Lists.newArrayList(formulaIds));
return expressFormulas.stream().filter(Objects::nonNull).map(m -> {
ExpressFormula expressFormula = new ExpressFormula();
BeanUtils.copyProperties(m, expressFormula);
List<FormulaVar> formulaVarPOS = getFormulaVarMapper().listSome(FormulaVar.builder().formulaId(m.getId()).build());
expressFormula.setParameters(formulaVarPOS);
return expressFormula;
}).collect(Collectors.toList());
} catch (Exception e) {
log.info("获取公示详情失败", e);
throw new SalaryRunTimeException("获取公示详情失败");
}
}
@Override
public ExpressFormula getExpressFormula(Long formulaId) {
if (formulaId == null || formulaId <= 0) {
return null;
}
try {
// 当前租户自己新建的公式
FormulaPO formulaPO = getFormulaMapper().getById(formulaId);
ExpressFormula expressFormula = new ExpressFormula();
BeanUtils.copyProperties(formulaPO, expressFormula);
List<FormulaVar> formulaVarPOS = getFormulaVarMapper().listSome(FormulaVar.builder().formulaId(formulaId).build());
expressFormula.setParameters(formulaVarPOS);
return expressFormula;
} catch (Exception e) {
log.info("获取公示详情失败", e);
throw new SalaryRunTimeException("获取公示详情失败");
}
}
@Override
public FormulaPO save(SalaryFormulaSaveParam param) {
ValidUtil.doValidator(param);
if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == null) {
throw new SalaryRunTimeException("引用类型异常");
}
if (ReturnTypeEnum.parseByValue(param.getReturnType()) == null) {
throw new SalaryRunTimeException("返回类型异常");
}
if (ValidateTypeEnum.parseByValue(param.getValidateType()) == null) {
throw new SalaryRunTimeException("校验类型异常");
}
//将select因XSS过滤造成的异常字符转换回来
param.setFormula(param.getFormula().replaceAll("", "select"));
param.setFormula(param.getFormula().replaceAll("", "SELECT"));
param.setFormula(param.getFormula().replaceAll("", "join"));
param.setFormula(param.getFormula().replaceAll("", "JOIN"));
param.setFormula(param.getFormula().replaceAll("", "and"));
param.setFormula(param.getFormula().replaceAll("", "AND"));
param.setFormula(param.getFormula().replaceAll("", "or"));
param.setFormula(param.getFormula().replaceAll("", "OR"));
param.setFormula(param.getFormula().replaceAll("", "in"));
param.setFormula(param.getFormula().replaceAll("", "IN"));
param.setFormula(param.getFormula().replaceAll("", "like"));
param.setFormula(param.getFormula().replaceAll("", "like"));
param.setFormula(param.getFormula().replaceAll("", "exists"));
param.setFormula(param.getFormula().replaceAll("", "EXISTS"));
param.setFormula(param.getFormula().replaceAll("", "between"));
param.setFormula(param.getFormula().replaceAll("", "BETWEEN"));
param.setFormula(param.getFormula().replaceAll("", "union"));
param.setFormula(param.getFormula().replaceAll("", "UNION"));
// 解析公式中的参数
if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.FORMULA) {
List<FormulaVar> parameterList = parseFormulaParameters(param.getFormula(), ReferenceTypeEnum.FORMULA);
param.setParameters(parameterList);
} else if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.SQL) {
List<FormulaVar> parameterList = parseFormulaParameters(param.getFormula(), ReferenceTypeEnum.SQL);
param.setParameters(parameterList);
}
int orderIndex = 0;
for (FormulaVar parameter : param.getParameters()) {
parameter.setOrderIndex(orderIndex++);
}
List<FormulaVar> parameters = param.getParameters();
//防止参数名和字段名呈现一对多的问题
if (CollectionUtils.isNotEmpty(parameters)) {
List<String> notRepeatingNameSize = parameters.stream().map(FormulaVar::getFieldName).distinct().collect(Collectors.toList());
if (notRepeatingNameSize.size() < parameters.size()) {
throw new SalaryRunTimeException("公式参数配置异常!参数名称重复,请清空公式内容后,再重新打开编辑");
}
}
String extendParam = param.getExtendParam();
if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.SQL) {
if (extendParam == null) {
throw new SalaryRunTimeException("未设置SQL返回值");
}
String sqlReturnKey = "";
try {
Map<String, String> map = JsonUtil.parseMap(extendParam, String.class);
sqlReturnKey = map.getOrDefault("sqlReturnKey", "");
} catch (Exception e) {
log.error("express execute fail, sql extendParam parse fail", e);
}
if (StringUtils.isBlank(sqlReturnKey)) {
throw new SalaryRunTimeException("未设置SQL返回值");
}
}
//试运行公式
checkRun(param);
FormulaPO formulaPO = new FormulaPO();
String formula = param.getFormula();
long formulaId = IdGenerator.generate();
formulaPO.setId(formulaId);
formulaPO.setName(param.getName());
formulaPO.setDescription(param.getDescription());
formulaPO.setModule(param.getModule());
formulaPO.setUseFor(param.getUseFor());
formulaPO.setReferenceType(param.getReferenceType());
formulaPO.setReturnType(param.getReturnType());
formulaPO.setValidateType(param.getValidateType());
formulaPO.setExtendParam(extendParam);
formulaPO.setFormula(formula);
formulaPO.setDeleteType(NumberUtils.INTEGER_ZERO);
Date now = new Date();
formulaPO.setCreateTime(now);
formulaPO.setUpdateTime(now);
formulaPO.setCreator((long) user.getUID());
/*
公式内容以如下显示方式存储
{薪资项目.输入项1}+{薪资项目.输入项2}
转换为实际的运行脚本
*/
String formulaRunScript = formula;
for (FormulaVar po : parameters) {
formulaRunScript = formulaRunScript.replace(po.getFieldName(), po.getFieldId());
}
formulaPO.setFormulaRunScript(formulaRunScript);
getFormulaMapper().insertIgnoreNull(formulaPO);
for (FormulaVar po : parameters) {
po.setId(IdGenerator.generate());
po.setFormulaId(formulaId);
po.setDeleteType(NumberUtils.INTEGER_ZERO);
po.setCreator((long) user.getUID());
po.setCreateTime(now);
po.setUpdateTime(now);
getFormulaVarMapper().insertIgnoreNull(po);
}
return formulaPO;
}
public List<FormulaVar> parseFormulaParameters(String formula, ReferenceTypeEnum referenceTypeEnum) {
List<FormulaVar> parameters = new ArrayList<>();
// 获取公式中所有可选的变量项目
Map<String, List<FormulaVar>> allParameters = getRemoteExcelService(user).allFieldList(referenceTypeEnum);
List<String> groups = new ArrayList<>(allParameters.keySet());
StringBuilder reg = new StringBuilder("(");
for (int i = 0; i < groups.size(); i++) {
reg.append("\\{" + groups.get(i) + "\\.");
if (i + 1 != groups.size()) {
reg.append("|");
}
}
reg.append(").*?}");
// 提取公式中所有的变量
Pattern pattern = Pattern.compile(reg.toString());
Matcher matcher = pattern.matcher(formula);
List<String> vars = new ArrayList<>();
while (matcher.find()) {
String var = matcher.group(0).replaceAll("\\{", "").replaceAll("}", "");
if (StringUtils.isNotBlank(var)) {
vars.add(var);
}
}
// 去重
vars = vars.stream().distinct().collect(Collectors.toList());
for (String var : vars) {
String[] split = var.split("\\.");
if (split.length == 2) {
List<FormulaVar> formulaVars = allParameters.get(split[0]);
if (formulaVars == null) {
throw new SalaryRunTimeException("保存失败,公式变量" + split[0] + "输入有误!");
}
Optional<FormulaVar> field = formulaVars.stream().filter(v -> Objects.equals(v.getName(), split[1])).findFirst();
if (field.isPresent()) {
FormulaVar formulaVar = field.get();
String fieldName = "{" + split[0] + "." + split[1] + "}";
formulaVar.setFieldName(fieldName);
parameters.add(formulaVar);
} else {
throw new SalaryRunTimeException("保存失败,公式变量" + split[0] + split[1] + "输入有误!");
}
} else if (split.length > 2) {
// 变量名称中包含.
List<FormulaVar> formulaVars = allParameters.get(split[0]);
if (formulaVars == null) {
throw new SalaryRunTimeException("保存失败,公式变量" + split[0] + "输入有误!");
}
StringBuilder field = new StringBuilder("");
for (int i = 1; i < split.length; i++) {
if (i != 1) {
field.append(".");
}
field = field.append(split[i]);
}
String finalField = field.toString();
Optional<FormulaVar> optional = formulaVars.stream().filter(v -> Objects.equals(v.getName(), finalField)).findFirst();
if (optional.isPresent()) {
FormulaVar formulaVar = optional.get();
String fieldName = "{" + split[0] + "." + field + "}";
formulaVar.setFieldName(fieldName);
parameters.add(optional.get());
} else {
throw new SalaryRunTimeException("保存失败,公式变量" + split[0] + split[1] + "输入有误!");
}
}
}
return parameters;
}
/**
* 模拟运行公式
*
* @param param
*/
private void checkRun(SalaryFormulaSaveParam param) {
//返回类型
String returnType = param.getReturnType();
ReturnTypeEnum returnTypeEnum = ReturnTypeEnum.parseByValue(returnType);
/*
公式内容以如下显示方式存储
{薪资项目.输入项1}+{薪资项目.输入项2}
转换为实际的运行脚本
*/
String formulaRunScript = param.getFormula();
//
// List<FormulaVar> parameters = param.getParameters();
// for (int i = 0; i < parameters.size(); i++) {
// FormulaVar po = parameters.get(i);
// formulaRunScript = formulaRunScript.replace(po.getFieldName(), po.getFieldId());
// po.setContent(String.valueOf(i + 1));
// }
// //验证公式是否可运行
// if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.FORMULA) {
// ExpressFormula test = ExpressFormula.builder().formulaRunScript(formulaRunScript).extendParam(param.getExtendParam()).referenceType(param.getReferenceType()).build();
// ExcelResult run = getFormulaRunService(user).run(test, parameters, DataCollectionEmployee.builder().employeeId((long) user.getUID()).build());
//
// if (run.isStatus()) {
// //返回结果不是数字
// if (returnTypeEnum == ReturnTypeEnum.NUMBER && !NumberUtils.isCreatable(String.valueOf(run))) {
// throw new SalaryRunTimeException("返回结果不是数值");
// }
// }else {
// throw new SalaryRunTimeException("公式配置异常");
// }
// }
if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.SQL) {
if (formulaRunScript.contains(";") || formulaRunScript.contains("--")) {
throw new SalaryRunTimeException("SQL配置异常,请去除';'或者'--'");
}
}
}
@Override
public Object mock(SalaryFormulaMockParam param) {
ValidUtil.doValidator(param);
FormulaPO po = getFormulaMapper().getById(param.getId());
//验证公式是否可运行
ExpressFormula test = ExpressFormula.builder().formulaRunScript(po.getFormulaRunScript()).extendParam(po.getExtendParam()).referenceType(po.getReferenceType()).build();
ExcelResult run = getFormulaRunService(user).run(test, param.getParameters(), DataCollectionEmployee.builder().employeeId((long) user.getUID()).build());
if (run.isStatus()) {
return run.getData();
} else {
return run.getErrorMsg();
}
}
@Override
public void initFunction() {
}
@Override
public void deleteNotIn(List<Long> formulaIds) {
Date today = new Date();
Calendar c = Calendar.getInstance();
c.setTime(today);
c.add(Calendar.DAY_OF_MONTH, -1);
Date yesterday = c.getTime();
if (CollectionUtils.isNotEmpty(formulaIds)) {
List<FormulaPO> allFormula = getFormulaMapper().listAll();
//待删除的公式
List<Long> needDeleteIds = allFormula.stream().map(FormulaPO::getId).filter(id -> !formulaIds.contains(id)).collect(Collectors.toList());
List<List<Long>> partition = Lists.partition(needDeleteIds, 1000);
partition.forEach(list -> {
getFormulaMapper().deleteIn(list, yesterday);
getFormulaVarMapper().deleteInFormulaIds(list, yesterday);
});
}
}
@Override
public List<FormulaVar> listVarByFormulaIds(List<Long> effectiveFormulaIds) {
List<FormulaVar> vars = new ArrayList<>();
if (CollectionUtils.isNotEmpty(effectiveFormulaIds)) {
List<List<Long>> partition = Lists.partition(effectiveFormulaIds, 999);
partition.forEach(list -> {
vars.addAll(getFormulaVarMapper().listSome(FormulaVar.builder().formulaIds(list).build()));
});
}
return vars;
}
@Override
public List<FormulaVar> listByCode(String code) {
return getFormulaVarMapper().listSome(FormulaVar.builder().fieldId(code).build());
}
@Override
public void updateVar(FormulaVar formulaVar) {
getFormulaVarMapper().updateIgnoreNull(formulaVar);
}
@Override
public List<FormulaPO> listByIds(List<Long> formulaIds) {
if (CollectionUtils.isEmpty(formulaIds)) {
return new ArrayList<>();
}
return getFormulaMapper().listByIds(formulaIds);
}
@Override
public void update(FormulaPO formulaPO) {
getFormulaMapper().updateIgnoreNull(formulaPO);
}
}