aviator 轻量级表达式引擎
需求: 每个检查项目都可以维护自己的指标,分为异常和正常指标,指标分为两种一种是数字形式的比较,一种是字符串形式的包含
com.googlecode.aviator
aviator
NormTypeEnum: 为指标枚举
- 值不是固定的,有可能是数字、浮点、字符串等,为了统一,计算相关的都去转为了BigDecimal
- 如果是包含,需要取出list对应的变量,所以需要正则去做拆分
@Getter
@AllArgsConstructor
public enum NormTypeEnum {
COMPUTE("0", "计算","value", (pel) -> {
Asserts.notBlank(pel, "计算表达式不能为空");
Matcher matcher = RegexConstants.COMPUTE_REGEX_PATTERN.matcher(pel);
return matcher.find();
}),
INCLUDE("1", "选择/匹配","list", (pel) -> {
Asserts.notBlank(pel, "选择/匹配表达式不能为空");
Matcher matcher = RegexConstants.INCLUDE_REGEX_PATTERN.matcher(pel);
return matcher.find();
});
private final String code;
private final String des;
private final String var;
private final Function<String, Boolean> beforeProcess;
public static NormTypeEnum getByCode(String code) {
NormTypeEnum[] values = values();
for (NormTypeEnum item : values) {
if (item.getCode().equals(code)) {
return item;
}
}
throw new UtilException(String.format("NormTypeEnum 获取枚举值失败,code: %s", code));
}
public static void main(String[] args) {
// Matcher matcher = RegexConstants.INCLUDE_REGEX_PATTERN.matcher("contains(value, 20572537289314304@20572121830920192)");
// NormTypeEnum.INCLUDE.getBeforeProcess().apply("");
// System.err.println(matcher.find());
Matcher matcher = Pattern.compile("\\d+").matcher("100<=value && value <= 300");
while (matcher.find()){
System.err.println(matcher.group());
}
System.err.println(matcher.groupCount());
}
}
package com.qdp.health.utils;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import com.qdp.common.core.exception.UtilException;
import com.qdp.health.enums.NormTypeEnum;
import com.qdp.health.enums.ResSignEnum;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 表达式解析工具类
* @author : Betsy.
* 2024-12-16 11:42
*/
@Slf4j
public class ExpressionUtils {
private static final AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
public static Boolean validateMetrics(String normType, String evalStr, String value){
log.info("validateMetrics ===> normType:{}, evalStr:{}", normType, evalStr);
NormTypeEnum normTypeEnum = verify(normType, evalStr);
String list = "";
// 如果是匹配,处理参数
if(normTypeEnum.getCode().equals(NormTypeEnum.INCLUDE.getCode())){
// 需要将参数中的匹配值,处理出来
Pattern pattern = Pattern.compile("\\((.*?)\\)"); // 匹配括号内的内容
Matcher matcher = pattern.matcher(evalStr);
if (matcher.find()) {
String content = matcher.group(1);
String[] split = content.split(",");
list = split[0];
evalStr= evalStr.replaceAll(list, "list");
}
}
System.err.println(evalStr);
return eval(evalStr, getVariables(normTypeEnum, value, list));
}
public static String validateHighOrLow(String evalStr, String normType, String res){
try{
if(normType.equals(NormTypeEnum.COMPUTE.getCode())){
BigDecimal resBg = new BigDecimal(res);
Matcher matcher = Pattern.compile("\\d+").matcher(evalStr);
int i = 0;
String code = null;
String low = "0";
String high = "0";
while (matcher.find()){
if(i==0){
low = matcher.group();
}else {
high = matcher.group();
}
i++;
}
if(resBg.compareTo(new BigDecimal(low)) < 0){
code = ResSignEnum.LOW.getCode();
}else if(resBg.compareTo(new BigDecimal(high)) > 0){
code = ResSignEnum.HIGH.getCode();
}
return code;
}else {
log.info("当前指标不存在高低,不是计算属性: normType:{} , evalStr{}",normType,evalStr);
}
}catch (Exception e){
log.info("验证指标高低异常, validateHighOrLow: {}", e.getMessage());
}
return null;
}
/**
* 验证指标是否有效
* @param normType {@link String}
* @param evalStr {@link String}
* @return {@link NormTypeEnum}
*/
public static NormTypeEnum verify(String normType, String evalStr){
log.info("verify ===> normType:{}, evalStr:{}", normType, evalStr);
NormTypeEnum normTypeEnum = NormTypeEnum.getByCode(normType);
Boolean isEffectiveNorm = normTypeEnum.getBeforeProcess().apply(evalStr);
if(!isEffectiveNorm){
throw new UtilException(String.format("当前指标( %s ) 无效", evalStr));
}
return normTypeEnum;
}
/**
* 指标运算
* @param evalStr {@link String}
* @param variables {@link Map}
* @return {@link Boolean}
*/
public static Boolean eval(String evalStr, Map<String, Object> variables){
log.info("variables:{}",variables);
Expression compile = instance.compile(evalStr);
return (Boolean) compile.execute(variables);
}
/***
* 指标变量
* @param normTypeEnum {@link NormTypeEnum}
* @param value {@link String}
* @param list {@link String}
* @return {@link Map}
*/
public static Map<String, Object> getVariables(NormTypeEnum normTypeEnum, String value, String list){
Map<String, ? super Object> variables = new HashMap<>();
if(normTypeEnum.getCode().equals(NormTypeEnum.INCLUDE.getCode())){
// todo: 测试
variables.put(normTypeEnum.getVar(), list.split(","));
// todo: 值类型转换--优化 3 > val
// variables.put(NormTypeEnum.COMPUTE.getVar(), new BigDecimal(value));
variables.put(NormTypeEnum.COMPUTE.getVar(), value);
}else {
variables.put(normTypeEnum.getVar(), new BigDecimal(value));
}
return variables;
}
public static void main(String[] args) {
// String str = "include(list,value)";
// String strss = "3 <= value && value <= 5";
// System.err.println(NormTypeEnum.COMPUTE.getBeforeProcess().apply(strss));
// System.err.println(NormTypeEnum.INCLUDE.getBeforeProcess().apply(strss));
// System.err.println("=========");
// String[] split = str.split("\\(");
// String[] split1 = split[1].split(",");
// System.err.println(split[1]);
// System.err.println(split1[0]);
// List res = new ArrayList<>();
// List params = new ArrayList<>();
// res.add("admin");
// res.add("test");
// res.add("ruoyi");
// params.add("admin");
// params.add("ruoyi");
// Pattern pattern = Pattern.compile("\\((.*?)\\)"); // 匹配括号内的内容
// Matcher matcher = pattern.matcher("contains(value, 20572537289314304@20572121830920192)");
// if (matcher.find()) {
// String content = matcher.group(1); // 获取第一个匹配到的括号内的内容
// System.out.println(content);
// }
// String str = "value >= 100 && value <= 300";
String str = "include(value, 20572537289314304@20572121830920192)";
Pattern pattern = Pattern.compile("\\((.*?)\\)"); // 匹配括号内的内容
Matcher matcher = pattern.matcher(str);
String list= "";
if (matcher.find()) {
String content = matcher.group(1);
String[] split = content.split(",");
list = split[split.length - 1];
str = str.replaceAll(list, "list");
}
System.err.println(str);
Expression expression = instance.compile(str);
List<String> res = new ArrayList<>();
res.add("20572121830920192");
res.add("20572537289314304");
Map<String, ? super Object> variables = new HashMap<>();
variables.put("list", "20572537289314304");
variables.put("value", res);
// variables.put("list", new BigDecimal("80"));
Object result1 = expression.execute(variables);
System.out.println("Result 1: " + result1);
}
}