EasyRules规则引擎工具类

这篇具有很好参考价值的文章主要介绍了EasyRules规则引擎工具类。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

EasyRules是一款基于Java的开源的轻量级的规则引擎框架。它可以帮助开发人员快速开发并管理规则,实现应用程序的自动化决策。EasyRules框架非常易于使用,且可以与任何Java应用程序无缝集成。在本文中,我们将对其进行一个简单的封装,以实现复杂的规则表达式匹配。

一、EasyRules的基本概念


EasyRules是一个基于规则的引擎,它基于规则引擎的常见原则和概念。以下是一些EasyRules框架中的重要概念:

  • 规则(Rule):规则是EasyRules框架中的核心概念,它用于描述应用程序中需要遵循的规则。每个规则通常包含两个部分:规则名称和规则条件。
  • 规则条件(Condition):规则条件定义了规则的前提条件。如果规则条件为true,则规则将被触发执行。否则,规则将被忽略。
  • 规则动作(Action):规则动作是在规则被触发时执行的一段代码。它可以用于实现各种应用程序逻辑,例如更新数据、发送消息等。
  • 规则执行(Rule Engine):规则执行是EasyRules框架的核心功能之一,它负责解析规则条件,并根据条件执行相应的规则动作。

二、快速上手Demo


了解了easyRules的基本概念后,我们来写一个简单的demo:

假设我们有一个需求,根据用户的年龄来决定是否可以购买酒类产品,如果用户年龄小于18岁,则不能购买酒类产品。

首先,我们需要定义一个规则类,继承自org.jeasy.rules.annotation.Rule,如下所示:

import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;

@Rule(name = "age rule", description = "Check if user is of legal age to buy alcohol")
public class AgeRule {

    @Condition
    public boolean checkAge(@Fact("age") int age) {
        return age >= 18;
    }
}

在这个规则类中,我们定义了一个名为“checkAge”的条件方法,它接受一个名为“age”的事实参数,并返回一个布尔值表示用户是否满足购买酒类产品的年龄要求。

接下来,我们需要创建一个规则引擎实例,并将规则类添加到规则引擎中。代码如下所示:

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.core.RulesImpl;
import org.jeasy.rules.core.DefaultRulesEngine;

public class RuleEngineDemo {
    public static void main(String[] args) {
        AgeRule ageRule = new AgeRule();
        Rules rules = new RulesImpl();
        rules.register(ageRule);
        DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
        Facts facts = new Facts();
        facts.put("age", 20);
        rulesEngine.fire(rules, facts);
    }
}

在这个示例代码中,我们创建了一个名为“ageRule”的规则对象,并将其注册到名为“rules”的规则集合中。接着,我们创建了一个默认的规则引擎实例,并创建了一个名为“facts”的事实对象,并将“age”和“20”作为键值对添加到事实对象中。最后,我们通过调用规则引擎的fire方法来启动规则引擎并触发规则执行。

三、easyRules工具类


以上示例展示了如何快速的使用easyRule实现规则创建及调用的流程。但是如果我们有更复杂的场景,比如在数据质量规则校验中,我们有如下的规则判断:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJC0OufR-1685683113114)(/Users/casey/Library/Application Support/typora-user-images/image-20230329152414895.png)]

对于这种比较复杂且校验规则经常会发生变化的规则,通过上述代码就无法实现了,为此,我将对easyRule做进一步的封装,以达到此目的。

1. 定义常量类

首先定义一个常量类

package com.shsc.bigdata.indicator.monitor.common.constant;

public class EasyRulesConstants {

    // 事实别名
    public static final String FACT_ALIAS = "fact";
    // 结果别名
    public static final String RESULT_ALIAS = "result";
    // and关系
    public static final String RELATION_AND = "and";
    // or关系
    public static final String RELATION_OR = "or";
    // 匹配成功信息
    public static final String MATCH_SUCCESS_MESSAGE = "匹配成功";
    public static final String FIELD_TYPE = "type";
    public static final String FIELD_OPERATOR = "operator";
    public static final String FIELD_NAME = "metricName";
    public static final String FIELD_VALUE = "value";
    public static final String FIELD_CHILDREN = "children";
    public static final String EXPRESSION_TYPE = "EXPRESSION";
    public static final String RELATION_TYPE = "RELATION";
    public static final String LEFT_BRACKETS = "(";
    public static final String RIGHT_BRACKETS = ")";
    public static final String SYMBOL_SPACE = " ";
    public static final String SYMBOL_EMPTY = "";
    public static final String LOGICAL_AND = "&&";
    public static final String LOGICAL_OR = "||";
}

2. 定义枚举类

定义一个枚举类,罗列出常用的运算符

package com.shsc.bigdata.indicator.monitor.common.enums;

public enum EasyRulesOperation {
    GREATER_THAN("GREATER_THAN", "%s > %s", "大于"),
    GREATER_THAN_EQUAL("GREATER_THAN_EQUAL", "%s >= %s", "大于等于"),
    LESS_THAN("LESS_THAN", "%s < %s", "小于"),
    LESS_THAN_EQUAL("LESS_THAN_EQUAL", "%s <= %s", "小于等于"),
    EQUAL("EQUAL", "%s == %s", "等于"),
    UNEQUAL("UNEQUAL", "%s != %s", "不等于"),
    BETWEEN("BETWEEN", "%s >= %s && %s <= %s", "介于之间"),
    OUT_OF_RANGE("OUT_OF_RANGE", "%s >= %s || %s >= %s", "超出范围"),
    CONTAINS("CONTAINS", "%s.contains(\"%s\")", "包含"),
    STARTSWITH("STARTSWITH", "%s.startsWith(\"%s\")", "前缀"),
    ENDSWITH("ENDSWITH", "%s.endsWith(\"%s\")", "后缀"),
    ;

    public static EasyRulesOperation getOperationByOperator(String operator) {
        EasyRulesOperation[] list = EasyRulesOperation.values();
        for (EasyRulesOperation item : list) {
            String compareOperator = item.getOperator();
            if (compareOperator.equals(operator)) {
                return item;
            }
        }
        return null;
    }

    private final String operator;
    private final String expression;
    private final String remark;

    EasyRulesOperation(String operator, String expression, String remark) {
        this.operator = operator;
        this.expression = expression;
        this.remark = remark;
    }

    public String getOperator() {
        return operator;
    }

    public String getExpression() {
        return expression;
    }

    public String getRemark() {
        return remark;
    }
}

3. 工具类

package com.shsc.bigdata.indicator.monitor.common.utils.easyRules;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.shsc.bigdata.indicator.monitor.common.enums.EasyRulesOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.mvel.MVELRule;
import static com.shsc.bigdata.indicator.monitor.common.constant.EasyRulesConstants.*;

@Slf4j
public class EasyRulesUtil {

    /**
     * 执行规则匹配
     * @param fact 事实json
     * @param ruleModel 规则模型
     */
    public static RuleResult match(JSONObject fact, RuleModel ruleModel){
        // 结果
        RuleResult result = new RuleResult();
        result.setRuleId(ruleModel.getRuleId());
        // 规则实例
        Facts facts = new Facts();
        facts.put(FACT_ALIAS, fact);
        facts.put(RESULT_ALIAS, result);
        // 规则内容
        org.jeasy.rules.api.Rule mvelrule = new MVELRule()
                .name(ruleModel.getRuleName())
                .description(ruleModel.getDescription())
                .when(ruleModel.getWhenExpression())
                .then(ruleModel.getThenExpression());
        // 规则集合
        Rules rules = new Rules();
        // 将规则添加到集合
        rules.register(mvelrule);
        // 创建规则执行引擎,并执行规则
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);
        return result;
    }

    /**
     * 构建mvel条件表达式
     * @param json 节点json,例如:
     * {
     *     "type": "EXPRESSION",
     *     "operator": "LESS_THAN",
     *     "metricName": "NORMAL_NUMBER",
     *     "value": "11",
     *     "children": []
     * }
     */
    public static String buildWhenExpression(JSONObject json) {
        StringBuilder mvelExpression = new StringBuilder();
        String type = json.getString(FIELD_TYPE);
        String operator = json.getString(FIELD_OPERATOR);

        switch (type) {
            case EXPRESSION_TYPE:
                String fieldName = json.getString(FIELD_NAME);
                String fieldValue = json.getString(FIELD_VALUE);
                mvelExpression.append(buildOperatorExpress(operator, fieldName, fieldValue));
                break;
            case RELATION_TYPE:
                JSONArray children = json.getJSONArray(FIELD_CHILDREN);
                if (children.size() == 0) {
                    return SYMBOL_EMPTY;
                }
                operator = convertRelationExpress(operator);
                StringBuilder childrenExpression = new StringBuilder();
                for (int i = 0; i < children.size(); i++) {
                    JSONObject child = children.getJSONObject(i);
                    // 递归构建单个规则条件
                    String childExpression = buildWhenExpression(child);
                    if (!childExpression.isEmpty()) {
                        if (childrenExpression.length() > 0) {
                            childrenExpression.append(SYMBOL_SPACE).append(operator).append(SYMBOL_SPACE);
                        }
                        childrenExpression.append(LEFT_BRACKETS).append(childExpression).append(RIGHT_BRACKETS);
                    }
                }
                mvelExpression.append(childrenExpression);
                break;
            default:
                break;
        }
        return mvelExpression.toString();
    }

    /**
     * 构建mvel结果表达式
     */
    public static String buildThenExpression() {
        StringBuilder expression = new StringBuilder();
        expression.append(RESULT_ALIAS).append(".setValue(\"").append(MATCH_SUCCESS_MESSAGE).append("\");");
        log.info("thenExpression: {}", expression);
        return expression.toString();
    }

    /**
     * 转换条件连接符
     * @param relation 条件连接符
     */
    private static String convertRelationExpress(String relation) {
        if (StringUtils.isEmpty(relation)){
            return SYMBOL_EMPTY;
        } else if(relation.equalsIgnoreCase(RELATION_AND)){
            return LOGICAL_AND;
        } else if(relation.equalsIgnoreCase(RELATION_OR)){
            return LOGICAL_OR;
        }
        return relation;
    }

    /**
     * 构建mvel表达式
     * @param operator 操作符
     * @param fieldName 字段名称
     * @param value 字段值
     */
    private static String buildOperatorExpress(String operator, String fieldName, Object value) {
        EasyRulesOperation operation = EasyRulesOperation.getOperationByOperator(operator);
        if (ObjectUtils.isNotEmpty(operation)) {
            String expression = operation.getExpression();
            return String.format(expression, buildValueExpress(fieldName), value);
        }
        return SYMBOL_EMPTY;
    }

    /**
     * 构建mvel取值表达式
     * @param fieldName 字段名称
     */
    private static String buildValueExpress(String fieldName) {
        return String.format("%s.get(\"%s\")", FACT_ALIAS, fieldName);
    }

     @Data
     public static class RuleModel {
        private String ruleId;
        String ruleName;
        String description;
        String whenExpression;
        String thenExpression;
    }

    @Data
    public static class RuleResult {
        // 规则主键
        private String ruleId;

        // 是否匹配, 默认false
        boolean isMatch = false;

        // 匹配信息,默认为匹配失败
        String message = "匹配失败";

        /**
         * 匹配成功后设置成功信息
         */
        public void setValue(String message){
            this.message = message;
            this.isMatch = true;
        }
    }
}

4. 测试


package com.shsc.bigdata.indicator.monitor.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.shsc.bigdata.indicator.monitor.common.utils.easyRules.EasyRulesUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TestEasyRules {
    public static void main(String[] args) {
        // 1. 新增规则
        EasyRulesUtil.RuleModel ruleModel = new EasyRulesUtil.RuleModel();
        ruleModel.setRuleId("1");
        ruleModel.setRuleName("rule1");
        ruleModel.setDescription("测试规则");
        // 2. 设置规则条件
        String ruleJson = "{\n" +
                "    \"validateCondition\": {\n" +
                "        \"type\": \"RELATION\",\n" +
                "        \"operator\": \"OR\",\n" +
                "        \"children\": [\n" +
                "            {\n" +
                "                \"type\": \"EXPRESSION\",\n" +
                "                \"operator\": \"LESS_THAN\",\n" +
                "                \"metricName\": \"NORMAL_NUMBER\",\n" +
                "                \"value\": \"11\",\n" +
                "                \"children\": []\n" +
                "            },\n" +
                "            {\n" +
                "                \"type\": \"EXPRESSION\",\n" +
                "                \"operator\": \"LESS_THAN_EQUAL\",\n" +
                "                \"metricName\": \"ERROR_NUMBER\",\n" +
                "                \"value\": \"11\",\n" +
                "                \"children\": []\n" +
                "            },\n" +
                "            {\n" +
                "                \"type\": \"RELATION\",\n" +
                "                \"children\": [\n" +
                "                    {\n" +
                "                        \"type\": \"EXPRESSION\",\n" +
                "                        \"metricName\": \"NORMAL_NUMBER\",\n" +
                "                        \"operator\": \"GREATER_THAN\",\n" +
                "                        \"value\": 10,\n" +
                "                        \"children\": []\n" +
                "                    },\n" +
                "                    {\n" +
                "                        \"type\": \"EXPRESSION\",\n" +
                "                        \"metricName\": \"ERROR_NUMBER\",\n" +
                "                        \"operator\": \"GREATER_THAN\",\n" +
                "                        \"value\": 100,\n" +
                "                        \"children\": []\n" +
                "                    },\n" +
                "                    {\n" +
                "                        \"type\": \"RELATION\",\n" +
                "                        \"children\": [\n" +
                "                            {\n" +
                "                                \"type\": \"EXPRESSION\",\n" +
                "                                \"metricName\": \"NORMAL_NUMBER\",\n" +
                "                                \"operator\": \"EQUAL\",\n" +
                "                                \"value\": 1,\n" +
                "                                \"children\": []\n" +
                "                            },\n" +
                "                            {\n" +
                "                                \"type\": \"EXPRESSION\",\n" +
                "                                \"metricName\": \"ERROR_NUMBER\",\n" +
                "                                \"operator\": \"EQUAL\",\n" +
                "                                \"value\": 1,\n" +
                "                                \"children \": []\n" +
                "                            }\n" +
                "                        ],\n" +
                "                        \"operator\": \"OR\"\n" +
                "                    }\n" +
                "                ],\n" +
                "                \"operator\": \"OR\"\n" +
                "            }\n" +
                "        ]\n" +
                "    }\n" +
                "}";
        JSONObject conditionJson = JSON.parseObject(ruleJson);
        // 3. 设置fact
        String whenExpression = EasyRulesUtil.buildWhenExpression(conditionJson.getJSONObject("validateCondition"));
        log.info("whenExpression:{}", whenExpression);
        ruleModel.setWhenExpression(whenExpression);
        // 4. 设置结果表达式
        ruleModel.setThenExpression(EasyRulesUtil.buildThenExpression());

        // 5. 设置匹配条件
        JSONObject json = new JSONObject();
        json.put("NORMAL_NUMBER", "10");
        json.put("ERROR_NUMBER", 10);
        json.put("省=陕西;市=西安;", 100);
        // 6. 调用规则匹配
        EasyRulesUtil.RuleResult result = EasyRulesUtil.match(json, ruleModel);
        System.out.println(result);
    }
}

上面的例子中,我使用了MVEL表达式语言实现了动态规则,关于MVEL的知识请翻阅历史文章查看。然后传入了一个比较复杂的多层级的规则表达式,测试结果如下:

15:30:29.915 [main] INFO com.shsc.bigdata.indicator.monitor.common.EasyRules - whenExpression:(fact.get("NORMAL_NUMBER") < 11) || (fact.get("ERROR_NUMBER") <= 11) || ((fact.get("NORMAL_NUMBER") > 10) || (fact.get("ERROR_NUMBER") > 100) || ((fact.get("NORMAL_NUMBER") == 1) || (fact.get("ERROR_NUMBER") == 1)))
15:30:29.919 [main] INFO com.shsc.bigdata.indicator.monitor.common.utils.easyRules.EasyRulesUtil - thenExpression: result.setValue("匹配成功");
15:30:30.085 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
15:30:30.085 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Registered rules:
15:30:30.086 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule { name = 'rule1', description = '测试规则', priority = '2147483646'}
15:30:30.086 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Known facts:
15:30:30.086 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Fact{name='result', value=EasyRulesUtil.RuleResult(ruleId=1, isMatch=false, message=匹配失败)}
15:30:30.086 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Fact{name='fact', value={"ERROR_NUMBER":10,"NORMAL_NUMBER":"10","省=陕西;市=西安;":100}}
15:30:30.096 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rules evaluation started
15:30:30.178 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'rule1' triggered
15:30:30.187 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'rule1' performed successfully
EasyRulesUtil.RuleResult(ruleId=1, isMatch=true, message=匹配成功)

从上述日志可以看到,isMatch=true表示匹配成功:

EasyRulesUtil.RuleResult(ruleId=1, isMatch=true, message=匹配成功)

四、写在最后

通过以上对easyRules的封装,我们可以实现复杂的规则表达式校验,只需要定义好表达式的json结构以及匹配的条件即可。文章来源地址https://www.toymoban.com/news/detail-643233.html

到了这里,关于EasyRules规则引擎工具类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java规则引擎Drools急速入门

    1.Drools规则引擎简介 (1)什么是规则引擎 ​ 全称为业务规则管理系统,英⽂名为BRMS(即 Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理

    2024年02月04日
    浏览(61)
  • 【微服务】java 规则引擎使用详解

    目录 一、什么是规则引擎 1.1 规则引擎概述 1.2 规则引擎执行过程

    2024年02月05日
    浏览(48)
  • Java中轻量级规则引擎Groovy介绍

    假如在开发一个充值功能,需要支持经常变化的充值营销活动,产品提出了以下需求: 1、根据不同季节,不同节日做不同的充值送活动,以及不定期的优惠活动。 2、还需要根据用户的等级以及用户以往的充值历史做不同的营销活动。 3、能够灵活的配置营销活动 需求很简单

    2024年02月10日
    浏览(74)
  • Java源码规则引擎:jvs-rules 8月新增功能介绍

    JVS-rules是JAVA语言下开发的规则引擎,是jvs企业级数字化解决方案中的重要配置化工具,核心解决业务判断的配置化,常见的使用场景:金融信贷风控判断、商品优惠折扣计算、对员工考核评分等各种变化的规则判断情景。 8月是收获的季节,jvs-rules在这个季节到来之时做了大

    2024年02月14日
    浏览(47)
  • 规则引擎专题---2、开源规则引擎对比

    开源的规则引擎整体分为下面几类: 通过界面配置的成熟规则引擎,这种规则引擎相对来说就比较重,但功能全,比较出名的有:drools, urule。 基于jvm脚本语言,互联网公司会觉得drools太重了,然后会基于一些jvm的脚本语言自己开发一个轻量级的规则引擎,比较出名的有,gr

    2024年02月04日
    浏览(47)
  • 规则引擎调研及初步使用

    生产过程中,线上的业务规则内嵌在系统的各处代码中,每次策略的调整都需要更新线上系统,进行从需求-设计-编码-测试-上线这种长周期的流程,满足不了业务规则的快速变化以及低成本的更新试错迭代。 因此需要有一种解决方案将 商业决策逻辑 和应用开发者的 技术决

    2024年02月09日
    浏览(75)
  • 【规则引擎】Drools急速入门

    1.Drools规则引擎简介 (1)什么是规则引擎 ​ 全称为业务规则管理系统,英⽂名为BRMS(即 Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理

    2024年02月05日
    浏览(37)
  • Drools规则引擎

    Drools(Drools Rule Engine)是一个开源的规则引擎,它主要用于在Java应用程序中实现规则管理。Drools规则引擎将规则定义和管理从应用程序代码中分离出来,使得规则可以独立于应用程序运行。这样可以提高规则的可靠性和可维护性,同时也可以使得规则的更新和管理更加方便。

    2024年02月07日
    浏览(48)
  • 规则引擎----easy rules

    将复杂的if else判断剥离出来 2.1、引入POM 2.2、编写规则 2.2.1、注解 2.2.2、表达式 2.2.3 yml配置文件 2.2.4 组合规则 2.2.5 组合规则说明 类 说明 UnitRuleGroup 要么应用所有规则,要么不应用任何规则(AND逻辑) ActivationRuleGroup 它触发第一个适用规则,并忽略组中的其他规则(XOR逻辑

    2024年02月13日
    浏览(42)
  • flink规则引擎设计思路

    在日常工作中我们经常收到一些诸如此类需求:“用户给点击了开屏广告,给用户下发私信”、“用户进入了推荐线,但在60秒内没有任何点击操作,弹框引导用户选择感兴趣的内容”、“用户点赞了某位作者的两篇以上的内容,但并没有关注过此作者,则弹框引导用户关注

    2024年02月05日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包