Aviator规则引擎组件开发

Aviator 规则引擎 是一款轻量级、高性能的 Java 表达式求值引擎,专注于解决动态规则执行和表达式计算问题,它可以运用在如电商促销规则(满减、折扣)、风控策略(用户评分模型)等场景。

困扰:

在过去的项目中,我多次使用规则引擎来处理不同的业务场景。每个项目的规则需求各不相同,前端传递的 JSON 数据格式也多种多样。为了应对这些差异,我不得不为每种规则单独编写解析方法,将前端的 JSON 数据转换为对应的规则公式。为了解决这一问题,我设计了一个通用组件,用于统一前端 JSON 数据的格式,并在后端实现了一套通用的解析逻辑。该组件具有高度的可扩展性和复用性,能够适应不同项目的规则需求,同时减少了重复开发的工作量。

PS: 组件代码处处都重要,篇幅过长,我主要介绍将规则实体解析成公式的部分。

规则的核心由条件动作两部分组成。首先,需要明确触发规则的条件,即满足何种逻辑时规则会被命中;其次,在规则命中后,需定义执行的具体动作。同时,还需考虑规则未命中时的处理逻辑,以确保规则引擎在不同场景下均能做出合理的响应。

组件整体架构

以下是我统一的规则实体

规则实体

js

代码解读

复制代码

@Data @AllArgsConstructor @NoArgsConstructor public class Rule { @Schema(description = "规则ID") private String id; @Schema(description = "规则表达式") private String expression; @Schema(description = "条件列表") private List<Condition> conditions; @Schema(description = "命中后的动作") private Action action; @Schema(description = "未命中的动作") private Action fallbackAction; // @Schema(description = "规则优先级") // private int priority; // // @Schema(description = "规则是否有效") // private boolean validity; }

条件实体

js

代码解读

复制代码

@Data @AllArgsConstructor @NoArgsConstructor public class Condition { @Schema(description = "字段名") private String field; @Schema(description = "操作符") // 可以是 '==', '>', '<' 等 private String operator; @Schema(description = "值") private List<String> value; @Schema(description = "自定义函数") private String customFunction; @Schema(description = "布尔逻辑") // 可以是 "&&" 或 "||" private String logic; @Schema(description = "前缀") private String prefix; @Schema(description = "值是否被引号包围") private Boolean isValueQuoted = false; @Schema(description = "子条件") private List<Condition> subConditions; @Schema(description = "算术表达式链") private List<ArithmeticExpression> arithmeticExpressions; @Schema(description = "是否需要括号包围") private boolean isBracketed; @Schema(description = "表达式,存在则直接运用,不解析") private String Expression; @Schema(description = "命中后的动作") private Action action;

动作实体

js

代码解读

复制代码

@Data @AllArgsConstructor @NoArgsConstructor public class Action { @Schema(description = "动作类型") private String type; @Schema(description = "动作目标") private Object target; @Schema(description = "目标类型") private Class<?> targetType; }

算术表达式链(为了满足复杂公式的解析)

java

代码解读

复制代码

@Data @AllArgsConstructor @NoArgsConstructor public class ArithmeticExpression { @Schema(description = "算术操作符,例如 + 或 -,可以为null表示第一个操作数") private String operator; @Schema(description = "操作数,例如 A 或 B") private String operand; public ArithmeticExpression(String operand) { this.operator = null; this.operand = operand; } }

规则构造测试

简单加减乘除,与或非

算术表达式链,括号的运用

自定义函数的使用

规则实体解析成公式的方法

java

代码解读

复制代码

/** * 获取完整表达式 * * @param rule * @return */ public static String buildStatement(Rule rule) { String expression; if (ObjUtil.isNotNull(rule.getExpression()) && !rule.getExpression().isEmpty()) { // 有传表达式,则直接使用,不解析 expression = rule.getExpression(); } else { // 解析 expression = buildExpression(rule.getConditions()); } if (Boolean.TRUE.equals(rule.getIsMontageAction())) { if (Boolean.TRUE.equals(rule.getIsDegree())) { // 计算匹配度 int i = calculateMinConditions(expression); return String.format("if (%s) { return %s; } else { return %s; }", expression, i, -1); } else { return String.format("if (%s) { return %s; } else { return %s; }", expression, rule.getAction().getTarget().toString(), rule.getFallbackAction().getTarget().toString()); } } else { return expression; } }

构建规则表达式

java

代码解读

复制代码

/** * 构建规则表达式 * * @param conditions 条件 * @return 表达式 */ public static String buildExpression(List<Condition> conditions) { StringBuilder expressionBuilder = new StringBuilder(); // 判断是否存在action有值的condition long actionNum = conditions.stream().filter(condition -> ObjUtil.isNotEmpty(condition.getAction())).count(); if (actionNum > 0) { expressionBuilder.append("if ("); } for (int i = 0; i < conditions.size(); i++) { Condition condition = conditions.get(i); if (ObjUtil.isNotEmpty(condition.getExpression())) { expressionBuilder.append("(").append(condition.getExpression()).append(")"); if (ObjUtil.isNotEmpty(condition.getLogic())) { expressionBuilder.append(" ").append(condition.getLogic()).append(" "); } } else { // 如果需要括号,添加左括号 if (condition.isBracketed()) { expressionBuilder.append("("); } // 处理子条件 if (condition.getSubConditions() != null && !condition.getSubConditions().isEmpty()) { expressionBuilder.append(buildExpression(condition.getSubConditions())); } // 动态拼接前缀 String prefixedField = condition.getPrefix() != null ? condition.getPrefix() + condition.getField() : condition.getField(); // 处理算术表达式或单一条件 if (condition.getArithmeticExpressions() != null && !condition.getArithmeticExpressions().isEmpty()) { for (int j = 0; j < condition.getArithmeticExpressions().size(); j++) { ArithmeticExpression arithmeticExpression = condition.getArithmeticExpressions().get(j); // 如果是第一个操作数,直接添加 if (j > 0 && arithmeticExpression.getOperator() != null) { expressionBuilder.append(" ").append(arithmeticExpression.getOperator()).append(" "); } expressionBuilder.append(arithmeticExpression.getOperand()); } // 添加比较运算符和值 expressionBuilder.append(" ") .append(condition.getOperator()) .append(" ") .append(condition.getIsValueQuoted() ? "'" + String.join(", ", condition.getValue()) + "'" : String.join(", ", condition.getValue())); } else if (condition.getField() != null) { if (condition.getCustomFunction() != null && !condition.getCustomFunction().isEmpty()) { // 使用自定义函数,确保格式为 field operator function(value) expressionBuilder.append(prefixedField) .append(" ") .append(condition.getOperator()) .append(" ") .append(condition.getCustomFunction()) .append("(") .append(condition.getIsValueQuoted() ? "'" + String.join(", ", condition.getValue()) + "'" : String.join(", ", condition.getValue())) .append(")"); } else { // 普通的操作符逻辑 expressionBuilder.append(prefixedField) .append(" ") .append(condition.getOperator()) .append(" ") .append(condition.getIsValueQuoted() ? "'" + String.join(", ", condition.getValue()) + "'" : String.join(", ", condition.getValue())); } } // 如果需要括号,添加右括号 if (condition.isBracketed()) { expressionBuilder.append(")"); } // 添加逻辑操作符 if (i < conditions.size() - 1 && condition.getLogic() != null) { expressionBuilder.append(" ").append(condition.getLogic()).append(" "); } } // 添加逻辑操作符 if (i < conditions.size() - 1) { Condition nextCondition = conditions.get(i + 1); if (nextCondition.getAction() != null) { expressionBuilder.append(")") .append(" { return ") .append(nextCondition.getAction().getTargetType() == Boolean.class ? nextCondition.getAction().getTarget() : "'" + nextCondition.getAction().getTarget() + "'") .append("; }") .append(" elsif ("); } } } String result; if (expressionBuilder.charAt(expressionBuilder.length() - 1) == '(' && expressionBuilder.length() > 8) { result = expressionBuilder.substring(0, expressionBuilder.length() - 8); } else { result = expressionBuilder.toString().trim(); } return result; }

实际运用页面图(并未开放复杂规则配置,前端优化一下即可,后台都可以解析)

前端传的规则实体:

js

代码解读

复制代码

{ "id": "rule", "expression": "", "conditions": [ { "field": "", "operator": "", "value": [ "aircraftType", "'NARROW;WIDE'" ], "customFunction": "intersect", "logic": "&&", "prefix": "", "isValueQuoted": false, "bracketed": false, "valueType": "dict", "optionalValue": "AircraftType" }, { "field": "", "operator": "", "value": [ "firstClassVipPaxNum", "'121;33'" ], "customFunction": "intersect", "logic": "&&", "prefix": "", "isValueQuoted": false, "bracketed": false, "valueType": "", "optionalValue": "" }, { "field": "firstClassVipPaxNum", "operator": ">", "value": [ "'1212'" ], "customFunction": "", "logic": "&&", "prefix": "", "isValueQuoted": false, "bracketed": false, "valueType": "", "optionalValue": "" }, { "field": "", "operator": "", "value": [ "airlineIcaoCode", "'A320'" ], "customFunction": "string.endsWith", "logic": "", "prefix": "", "isValueQuoted": false, "bracketed": false, "valueType": "baseAirport", "optionalValue": "airline" } ], "isMontageAction": true, "isDegree": true }

实际转换成的表达式:

js

代码解读

复制代码

if (intersect(aircraftType, 'NARROW;WIDE') && intersect(firstClassVipPaxNum, '121;33') && firstClassVipPaxNum > '1212' && string.endsWith(airlineIcaoCode, 'A320')) { return 3; } else { return -1; } 因为最少需要三个参数都命中才可为true,所以匹配度是3(组件里有计算的方法),否则返回 -1,我一个收费项可以配置多个规则,最后返回命中的匹配度最高的规则的计算公式的值。

计算结果(支持批量计算)

总结:

由于开发时间有限,组件的实现可能不够简洁和优雅,但其核心目的是为大家提供一个清晰的思路,帮助大家更好地理解规则引擎的原理及其应用场景。希望通过这个组件,能够启发大家在规则引擎的使用上探索更多可能性。同时,也欢迎大家在此基础上进一步完善和优化,共同打造更加高效、优雅的解决方案。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值