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 CACHE = new ConcurrentHashMap<>(); /** * 缓存条数上限。 *

* 规则来自保存下来的模板,条数天然有限;但校验接口会被反复调用, * 没有上限的静态缓存等于把用户输入攒在堆里,这里到量就不再往里放。 */ 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 validate(QualityRuleDefinition rule) { List errors = new ArrayList<>(); String label = rule.label(); String expression = rule.expression(); if (expression == null || expression.trim().isEmpty()) { errors.add("规则「" + label + "」的表达式为空"); return errors; } try { RuleParser.parse(expression); } catch (RuleSyntaxException error) { errors.add("规则「" + label + "」语法错误:" + error.getMessage() + "(第 " + (error.position() + 1) + " 个字符)"); } catch (RuntimeException error) { errors.add("规则「" + label + "」无法解析:" + error.getMessage()); } return errors; } /** 求值,返回原始结果(可能是数字、字符串、布尔) */ public static Object evaluate(String expression, Object context) { return RuleEvaluator.evaluateNode(parse(expression), context); } /** 求值并转成布尔判定,用于「是否合格」这类条件 */ public static boolean test(String expression, Object context) { return RuleEvaluator.evaluateCondition(parse(expression), context); } /** 静态检查表达式里调用的函数是否在白名单内,不在则返回函数名 */ public static String findUnknownFunction(String expression) { return RuleEvaluator.findUnknownFunction(parse(expression)); } /** 表达式依赖的上下文路径,渲染前可据此检查数据是否齐备 */ public static List listPaths(String expression) { return RuleEvaluator.listPaths(parse(expression)); } }