规则引擎aviatorEvaluator注意点
官方文档:AviatorScript 编程指南(5.0) · 语雀
1、依赖
XML
<!--规则引擎-->
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.4.3</version>
</dependency>
2、自定义函数
java
package com.jayce.boot.route.common.aviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDecimal;
import com.googlecode.aviator.runtime.type.AviatorObject;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
public class RoundFunction extends AbstractFunction {
private static final long serialVersionUID = 1;
@Override
public AviatorObject call(final Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
Number number = FunctionUtils.getNumberValue(arg1, env);
Number number2 = FunctionUtils.getNumberValue(arg2, env);
BigDecimal numberData = new BigDecimal(number.toString());
// 四舍五入
BigDecimal roundedHalfUp = numberData.setScale(number2.intValue(), RoundingMode.HALF_UP);
return AviatorDecimal.valueOf(roundedHalfUp);
}
@Override
public String getName() {
return "ROUND";
}
}
使用时:
String expression = "ROUND((task_a + task_b) - (task_c/task_d) + task_a*task_c,2)";
Expression compiledExp = AviatorEvaluator.getInstance().compile(expression);
Expression compiledExp = instance.compile(expression);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("task_a", 3.666666666666666);
paramMap.put("task_b", 7);
paramMap.put("task_c", 1);
paramMap.put("task_d", 2);
Object result = compiledExp.execute(paramMap);
3、关于计算精度问题
// -- 1. 解析浮点数为 Decimal 类型
AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
instance.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
// -- 2. 解析整数为 Decimal 类型
instance.setOption(Options.ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, true);
4、除法变量替换表达式存在bug
如:String expression = "1/2"; result = 0.5;
a=1,b=2;
String expression = "a/b"; result = 0;
得出结果不一样,底层处理可能存在计算漏洞。
临时解决办法,先自行定义规则进行替换,再直接数字直接运算
替换方法:
java
/**
* 替换字符串表达式中的变量为map中对应的值。
*
* @param expression 包含变量的数学表达式
* @param values 存储变量及其值的映射
* @return 替换后的数学表达式
*/
public static String replaceVariables(String expression, Map<String, Object> values) {
// 正则表达式用于匹配变量名
//正则表达式:\\b[a-zA-Z_][a-zA-Z0-9_]*\\b 用于匹配变量名。这里的 \b 是单词边界,确保我们只匹配完整的变量名,而不是部分字符串
Pattern pattern = Pattern.compile("\\b[a-zA-Z_][a-zA-Z0-9_]*\\b");
Matcher matcher = pattern.matcher(expression);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String variable = matcher.group(); // 获取匹配到的变量名
if (values.containsKey(variable)) {
String replacement = String.valueOf(values.get(variable));
// 使用找到的值替换变量
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
} else {
// 如果变量不在map中,保留原样
matcher.appendReplacement(sb, Matcher.quoteReplacement(variable));
}
}
matcher.appendTail(sb);
return sb.toString();
}
使用示例:
String expression = "ROUND((task_a + task_b) - (task_c/task_d) + task_a*task_c,2)";
Map<String, Object> paramMap = new HashMap<>();
变量赋值............
//计算逻辑手动替换,自动替换有bug,除法丢精度
expression = replaceVariables(expression, paramMap);
//参数无需传入
Object result = xxx.execute();