422 lines
18 KiB
Java
422 lines
18 KiB
Java
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", "select"));
|
||
param.setFormula(param.getFormula().replaceAll("SELECT", "SELECT"));
|
||
param.setFormula(param.getFormula().replaceAll("join", "join"));
|
||
param.setFormula(param.getFormula().replaceAll("JOIN", "JOIN"));
|
||
param.setFormula(param.getFormula().replaceAll("and", "and"));
|
||
param.setFormula(param.getFormula().replaceAll("AND", "AND"));
|
||
param.setFormula(param.getFormula().replaceAll("or", "or"));
|
||
param.setFormula(param.getFormula().replaceAll("OR", "OR"));
|
||
param.setFormula(param.getFormula().replaceAll("in", "in"));
|
||
param.setFormula(param.getFormula().replaceAll("IN", "IN"));
|
||
param.setFormula(param.getFormula().replaceAll("like", "like"));
|
||
param.setFormula(param.getFormula().replaceAll("LIKE", "like"));
|
||
param.setFormula(param.getFormula().replaceAll("exists", "exists"));
|
||
param.setFormula(param.getFormula().replaceAll("EXISTS", "EXISTS"));
|
||
param.setFormula(param.getFormula().replaceAll("between", "between"));
|
||
param.setFormula(param.getFormula().replaceAll("BETWEEN", "BETWEEN"));
|
||
param.setFormula(param.getFormula().replaceAll("union", "union"));
|
||
param.setFormula(param.getFormula().replaceAll("UNION", "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);
|
||
}
|
||
|
||
}
|