应用的场景:
客户要实现在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<String,String> 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<String,String> 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<String,String> 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());
}
}
自己的评价:
可能直接通过前端进行计算性能更高一些