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

301 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.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.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.mapper.formula.FormulaMapper;
import com.engine.salary.mapper.formula.FormulaVarMapper;
import com.engine.salary.service.FormulaRunService;
import com.engine.salary.service.SalaryFormulaService;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.valid.RuntimeTypeEnum;
import com.engine.salary.util.valid.ValidUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
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.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 static final ObjectMapper objectMapper = new ObjectMapper();
private FormulaMapper getFormulaMapper() {
return MapperProxyFactory.getProxy(FormulaMapper.class);
}
private FormulaVarMapper getFormulaVarMapper() {
return MapperProxyFactory.getProxy(FormulaVarMapper.class);
}
private FormulaRunService getFormulaRunService(User user) {
return (FormulaRunService) ServiceUtil.getService(FormulaRunServiceImpl.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("校验类型异常");
}
//todo 公式参数与公式内容是否相符
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 {
JsonNode jsonNode = objectMapper.readTree(extendParam);
JsonNode sqlReturnKeyNode = jsonNode.get("sqlReturnKey");
if (sqlReturnKeyNode != null) {
sqlReturnKey = sqlReturnKeyNode.asText();
}
} catch (JsonProcessingException e) {
log.error("express execute fail, sql extendParam parse fail", e);
}
if (StringUtils.isBlank(sqlReturnKey)) {
throw new SalaryRunTimeException("未设置SQL返回值");
}
//将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("", "or"));
param.setFormula(param.getFormula().replaceAll("", "in"));
param.setFormula(param.getFormula().replaceAll("", "like"));
param.setFormula(param.getFormula().replaceAll(" ", "like"));
}
//试运行公式
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;
}
/**
* 模拟运行公式
*
* @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();
Object run = null;
try {
run = getFormulaRunService(user).run(test, parameters, DataCollectionEmployee.builder().employeeId((long) user.getUID()).build());
} catch (Exception e) {
log.error("express execute fail ", e);
throw new SalaryRunTimeException("公式模拟运行出错,请检查公式配置!", e);
}
if (run != null && StringUtils.isNotBlank(String.valueOf(run)) && returnTypeEnum == ReturnTypeEnum.NUMBER) {
//返回结果不是数字
if (!NumberUtils.isCreatable(String.valueOf(run))) {
throw new SalaryRunTimeException("返回结果不是数值");
}
}
}
if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.SQL) {
if (formulaRunScript.contains(";") || formulaRunScript.contains("--")) {
throw new SalaryRunTimeException("SQL配置异常,请去除';'或者'--'");
}
}
}
@Override
public FormulaPO update(SalaryFormulaSaveParam param) {
ValidUtil.doValidator(param, RuntimeTypeEnum.UPDATE);
Long id = param.getId();
FormulaPO formulaPO = getFormulaMapper().getById(id);
if (formulaPO == null) {
throw new SalaryRunTimeException("公式不存在或已删除");
}
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(param.getExtendParam());
formulaPO.setFormula(param.getFormula());
formulaPO.setDeleteType(NumberUtils.INTEGER_ZERO);
Date now = new Date();
formulaPO.setUpdateTime(now);
formulaPO.setCreator((long) user.getUID());
getFormulaMapper().updateIgnoreNull(formulaPO);
List<FormulaVar> parameters = param.getParameters();
//删除公式下的变量
getFormulaVarMapper().deleteByFormulaId(id);
parameters.forEach(po -> {
po.setCreator((long) user.getUID());
po.setCreateTime(now);
po.setUpdateTime(now);
getFormulaVarMapper().insertIgnoreNull(po);
});
return formulaPO;
}
@Override
public void initFunction() {
}
}