公式解析javax.script中 ScriptEngine

应用的场景:

客户要实现在web页面上填入一套参数,根据计算公式计算n结果,并用输入和中间计算结果再计算得到m个最终结果。输入项和计算的项加起来总计60个,支持新增,导入和导出

设计:

方案1 :一个一个公式的分析,设计页面进行输入,缺点是繁琐,需要一个一个的分析设计,比较麻烦,而且如果计算公式换了需要改代码

方案2:对所有的指标统一考虑,一套代码一网打尽,直接在数据库中配置好计算公式,然后在代码中对字符串进行解析和值替换。

设计思想:
(1)根据需求提供的原始计算excel文件入库作为元数据
(2)将分析项分类为填写和公式计算,公式计算的需要写出code公式和汉字公式,公式中使用的分析项code串
(3)赋予分析项计算顺序,便于确保在分析计算的时候,必要的分析项已经处理完成
(4) 计算:工具ExtendComputeUtil.compute 用javax.script中 ScriptEngine,使用ScriptEngine.eval进行计算,替换公式字符串中的变量后得到计算结果,替换时按照字符串长的先替换,保证param中的key不会互相覆盖,比如 c12 被c1覆盖

(5)完全依赖后端,在新增时的初始内容也由后端接口提供,计算通过后端接口计算

优点:实现快,如果公式变动或者要添加计算项目修改容易,直接改数据库表即可。

缺点:页面比较死板,需要保证基本的计算工具是正确的,初始化的公式正确,

完全依赖后端,如果并发高,性能不高

注意:计算和替换的顺序

代码片段:

计算的工具:

import cn.hutool.core.util.NumberUtil;
import com.troy.keeper.common.exception.KeeperException;
import com.xkcoding.http.util.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@Component
public class ExtendComputeUtil {
    /**
     * 拓展计算的公式计算工具
     * 需要保证param中的key不会互相覆盖,比如 c12 被c1覆盖
     * @param param 有序的计算因子键值对
     * @param formula 计算公式
     * @return
     */
    public  static  String  compute(LinkedHashMap param, String formula){
        if(StringUtils.isEmpty(formula) || MapUtil.isEmpty(param)){
            return null;
        }
        try {
            ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
            String str = formula;
            for(Map.Entry e: param.entrySet()){
                str = str.replace(e.getKey(), e.getValue());
            }
            String result = jse.eval(str).toString();
            if(result.equals("NaN") || result.equals("Infinity")){
                return "0.00";
            }
            return  result;
        }catch (Exception e){
            log.error("计算出错,详情:",e);

            throw new KeeperException("计算错误,请检查分析项原始值是否正确");
        }
    }


    public static void main(String[] args) {
        LinkedHashMap param = new LinkedHashMap<>();

        String f = "C1*(C2-C3)/(C7-C10)";
        param.put("C10","1");
        param.put("C7","1");
        param.put("C1","1111113333333113.10");
        param.put("C2","11111113333333113.10");
        param.put("C3","111113333333113.10");


        System.out.println(  NumberUtil.round(NumberUtil.parseDouble(compute(param,f)), 2).toString());
    }
}

自己的评价:

可能直接通过前端进行计算性能更高一些

你可能感兴趣的:(设计)