Files
flow-center-service/src/main/java/com/yunhai/flowcenter/engine/ExpressionEvaluator.java
T
chenxing 92619508f0 feat(condition): CONDITION 判断方式二选一——EXPRESSION 内置 Aviator 表达式(沙箱)+ SERVICE 原口径;JudgeBody 补 submitterId
- ExpressionEvaluator:Aviator 5.4.3 沙箱(FEATURE_SET 只留 If、ALLOWED_CLASS_SET 空=禁类访问/反射)、编译缓存、
  顶层变量缺失抛错挂起(Aviator 缺失变量按 nil 且 nil 视作最小值,<= 兜底出口会静默放行——宁挂起不误放行)
- ConditionRouteService.decide 分流:EXPRESSION 按出边顺序取首个 true(逐边结果摘要落 raw_response),
  全 false/异常挂起沿用 SERVICE 纪律(route_log outlet=NULL + EXPRESSION 常量 + 告警 + 管理端重跑)
- DefValidator 发布期分层校验:EXPRESSION 出口缺条件/语法错/违禁表达式硬拦;表单变量放行(运行时核对);
  内置变量疑似拼错 warn;judgeMode 非法值拦
- /admin/router/eval 调试端点(同一求值核心,不落库不走流程)
- 单测 22 新增 + 全量 170 绿;E2E 18/18(发布/分支选路/挂起留痕/重跑/调试)见 local-debug/four-topics-a-e2e.py
2026-10-10 18:52:40 +08:00

73 lines
3.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.yunhai.flowcenter.engine;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Feature;
import com.googlecode.aviator.Options;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 条件路由表达式求值核心(2026-10-10 四议题 Phase 4):CONDITION 节点 judgeMode=EXPRESSION 时,
* 出边 condition 即 AviatorScript 表达式,按出边定义顺序取首个求值为 true 的出口。
* 发布期校验(DefValidator)、运行时(ConditionRouteService)、调试端点(/admin/router/eval)
* 共用本类——三处口径天然一致。
*
* <p><b>沙箱纪律(勿放宽)</b>:FEATURE_SET 只留 If(三元/条件),NewInstance/Module/Use/
* Static* 全部不在集合内即禁用;ALLOWED_CLASS_SET=空不可变集 = 禁一切 Java 类访问(反射/静态
* 方法/类字面量);表达式只能引用变量与 Aviator 内置纯函数(math/string/seq)。
*
* <p><b>缺失变量口径(宁挂起不误放行)</b>:顶层变量缺失(拼写错/表单未传)直接抛错由调用方
* 挂起——Aviator 的 nil 参与大小比较时视作最小值,{@code x <= n} 这类兜底出口会对缺失字段
* <i>静默放行</i>,必须拦住。判空请显式写 {@code x == nil}(嵌套属性缺失走 nil 语义,
* {@code form.amount == nil} 可用)。env 中显式为 null 的值仍按 nil 语义求值。
*
* <p>线程安全:{@link AviatorEvaluatorInstance} 本身线程安全,编译结果带缓存(key=表达式串)。
*/
public final class ExpressionEvaluator {
private ExpressionEvaluator() {
}
private static final AviatorEvaluatorInstance INSTANCE = createSandboxed();
private static AviatorEvaluatorInstance createSandboxed() {
AviatorEvaluatorInstance instance = AviatorEvaluator.newInstance();
instance.setOption(Options.FEATURE_SET, Feature.asSet(Feature.If));
instance.setOption(Options.ALLOWED_CLASS_SET, Collections.emptySet());
instance.setOption(Options.NIL_WHEN_PROPERTY_NOT_FOUND, Boolean.TRUE);
return instance;
}
/**
* 求值(编译缓存复用)。顶层变量缺失直接抛错(调用方挂起)——Aviator 对缺失顶层变量一律
* 按 nil 求值且不可配置,而 nil 视作最小值会让 {@code x <= n} 这类兜底出口静默放行。
* 嵌套属性(form.amount)缺失走 nil 语义,判空请显式 {@code form.amount == nil}。
*
* @return 求值结果;调用方仅把 Boolean.TRUE 视为命中(null/非布尔一律不命中——宁挂起不误放行)
*/
public static Object evaluate(String expression, Map<String, Object> env) {
Expression compiled = compile(expression);
for (String name : compiled.getVariableNames()) {
if (!env.containsKey(name)) {
throw new IllegalStateException("表达式变量未提供:" + name
+ "(拼写错误或表单未传;宁挂起不误放行,不按 nil 求值)");
}
}
return compiled.execute(env);
}
/** 语法校验/预编译(发布期拦语法错;与运行时走同一编译路径) */
public static Expression compile(String expression) {
return INSTANCE.compile(expression, true);
}
/** 表达式引用的顶层变量名(发布期"内置变量疑似拼错"提示用) */
public static List<String> variableNames(String expression) {
return compile(expression).getVariableNames();
}
}