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

Copyright: Copyright (c) 2022

*

Company: 泛微软件

* * @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 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("校验类型异常"); } //将select因XSS过滤造成的异常字符转换回来 ValidUtil.modify(param); // 解析公式中的参数 if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.FORMULA) { List parameterList = parseFormulaParameters(param.getFormula(), ReferenceTypeEnum.FORMULA); param.setParameters(parameterList); } else if (ReferenceTypeEnum.parseByValue(param.getReferenceType()) == ReferenceTypeEnum.SQL) { List parameterList = parseFormulaParameters(param.getFormula(), ReferenceTypeEnum.SQL); param.setParameters(parameterList); } int orderIndex = 0; for (FormulaVar parameter : param.getParameters()) { parameter.setOrderIndex(orderIndex++); } 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 { Map 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 parseFormulaParameters(String formula, ReferenceTypeEnum referenceTypeEnum) { List parameters = new ArrayList<>(); // 获取公式中所有可选的变量项目 Map> allParameters = getRemoteExcelService(user).allFieldList(referenceTypeEnum); List 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 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 formulaVars = allParameters.get(split[0]); if (formulaVars == null) { throw new SalaryRunTimeException("保存失败,公式变量" + split[0] + "输入有误!"); } Optional 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 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 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 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 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 allFormula = getFormulaMapper().listAll(); //待删除的公式 List needDeleteIds = allFormula.stream().map(FormulaPO::getId).filter(id -> !formulaIds.contains(id)).collect(Collectors.toList()); List> partition = Lists.partition(needDeleteIds, 1000); partition.forEach(list -> { getFormulaMapper().deleteIn(list, yesterday); getFormulaVarMapper().deleteInFormulaIds(list, yesterday); }); } } @Override public List listVarByFormulaIds(List effectiveFormulaIds) { List vars = new ArrayList<>(); if (CollectionUtils.isNotEmpty(effectiveFormulaIds)) { List> partition = Lists.partition(effectiveFormulaIds, 999); partition.forEach(list -> { vars.addAll(getFormulaVarMapper().listSome(FormulaVar.builder().formulaIds(list).build())); }); } return vars; } @Override public List listByCode(String code) { return getFormulaVarMapper().listSome(FormulaVar.builder().fieldId(code).build()); } @Override public void updateVar(FormulaVar formulaVar) { getFormulaVarMapper().updateIgnoreNull(formulaVar); } @Override public List listByIds(List formulaIds) { if (CollectionUtils.isEmpty(formulaIds)) { return new ArrayList<>(); } return getFormulaMapper().listByIds(formulaIds); } @Override public void update(FormulaPO formulaPO) { getFormulaMapper().updateIgnoreNull(formulaPO); } }