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.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; /** * 薪酬管理公式编辑器 *

Copyright: Copyright (c) 2022

*

Company: 泛微软件

* * @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 listExpressFormula(Collection formulaIds) { formulaIds = formulaIds.stream().filter(id -> id != null && id > 0).collect(Collectors.toList()); if (CollectionUtils.isEmpty(formulaIds)) { return Collections.emptyList(); } try { // 当前租户自己新建的公式 List expressFormulas = getFormulaMapper().listByIds(Lists.newArrayList(formulaIds)); return expressFormulas.stream().filter(Objects::nonNull).map(m -> { ExpressFormula expressFormula = new ExpressFormula(); BeanUtils.copyProperties(m, expressFormula); List 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 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("校验类型异常"); } List parameters = param.getParameters(); //防止参数名和字段名呈现一对多的问题 if (CollectionUtils.isNotEmpty(parameters)) { List 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", "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")); } //试运行公式 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 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 Object mock(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("校验类型异常"); } List parameters = param.getParameters(); //防止参数名和字段名呈现一对多的问题 if (CollectionUtils.isNotEmpty(parameters)) { List 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", "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")); } String formulaRunScript = param.getFormula(); for (FormulaVar po : parameters) { formulaRunScript = formulaRunScript.replace(po.getFieldName(), po.getFieldId()); } if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.SQL) { if (formulaRunScript.contains(";") || formulaRunScript.contains("--")) { throw new SalaryRunTimeException("SQL配置异常,请去除';'或者'--'"); } } //返回类型 String returnType = param.getReturnType(); ReturnTypeEnum returnTypeEnum = ReturnTypeEnum.parseByValue(returnType); //验证公式是否可运行 ExpressFormula test = ExpressFormula.builder().formulaRunScript(formulaRunScript).extendParam(extendParam).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("返回结果不是数值"); } } return run; } @Override public void initFunction() { } @Override public void deleteNotIn(List formulaIds) { if (CollectionUtils.isNotEmpty(formulaIds)) { List> partition = Lists.partition(formulaIds, 1000); partition.forEach(list -> { getFormulaMapper().deleteNotIn(list); getFormulaVarMapper().deleteNotInFormulaIds(list); }); } } }