package cn.iocoder.yudao.module.qcreport.engine.rule; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 判定规则引擎入口。 *
* 规则文本来自用户,全程只用自建的词法/语法分析与白名单函数求值, * 不碰 eval、不碰脚本引擎——这是硬约束,不是风格偏好。 *
* 与前端 {@code engine/rule-engine.ts} 一一对应,同一份规则两端必须算出同一结果。
*/
public final class RuleEngine {
/** 已解析的语法树缓存:同一条规则要在多行检验项上重复求值,不必反复解析 */
private static final Map
* 规则来自保存下来的模板,条数天然有限;但校验接口会被反复调用,
* 没有上限的静态缓存等于把用户输入攒在堆里,这里到量就不再往里放。
*/
private static final int MAX_CACHE_SIZE = 512;
private RuleEngine() {
}
/** 解析规则文本,语法错误抛 {@link RuleSyntaxException} */
public static RuleNode parse(String expression) {
return RuleParser.parse(expression);
}
/** 解析并缓存,供渲染时反复求值使用 */
public static RuleNode compiled(String expression) {
if (expression == null) {
throw new RuleSyntaxException("规则表达式为空", 0);
}
RuleNode cached = CACHE.get(expression);
if (cached != null) {
return cached;
}
RuleNode node = RuleParser.parse(expression);
if (CACHE.size() < MAX_CACHE_SIZE) {
CACHE.put(expression, node);
}
return node;
}
/**
* 校验规则文本的语法,返回错误清单,空列表表示通过。
*
* 这里只查语法;函数白名单交给 {@link RuleEvaluator#findUnknownFunction},
* 由 {@link cn.iocoder.yudao.module.qcreport.engine.QualityReportEngine#validateRules}
* 把两者拼成完整的保存前校验。
* 前端把这两步合在了 {@code validateRule} 一个函数里,但报错文案两端逐字一致——
* 口径不一致会变成「设计器能存、后端存不了」这种互相打架的场面。
*/
public static List