当前位置: 首页 > news >正文

springboot + easyRules 搭建规则引擎服务

依赖
        <dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-core</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-mvel</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-spel</artifactId><version>4.0.0</version></dependency>
输入

为更灵活的发挥规则引擎的功能,输入可以使用JSONObject,这样增删改字段无需修改代码。

输出
@Data
public class RuleResult {/*** 规则主键*/Long ruleId;/*** 规则名* 默认false*/boolean isMatch = false;/*** 规则名*/String value;/*** 满足规则后执行*/public void setValue(String msg){this.value = msg;this.isMatch = true;}
}

规则引擎本身的目的是为了业务与开发解耦,因此只输出是否满足规则,至于满足规则后要干什么业务,这个就是调用方的事。

简单的测试
    @Testvoid jsonExpressionTest() {// 输入项JSONObject dataSource = new JSONObject();dataSource.put("name","大帅比");dataSource.put("age",18);// 结果RuleResult result = new RuleResult();// 规则实例Facts facts = new Facts();facts.put("fact",dataSource);facts.put("result",result);// whenString when = "fact.get(\"name\")==\"大帅比\"";// thenString then = "result.setValue(\"确实帅\");";// 规则内容Rule testModeRule = new MVELRule().name("test rule").description("rule for test").when(when).then(then);// 规则集合Rules rules = new Rules();// 将规则添加到集合rules.register(testModeRule);//创建规则执行引擎,并执行规则RulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);log.info("result:{}",result.toString());}

完善下,把规则抽象出来,最终的目的就是通过调用方传入的参数,生成when和then表达式;

规则
@Data
@ToString(callSuper = true)
@TableName("EXPRESSION_RULE")
public class ExpressionRule {/*** 主键*/Long id;/*** 规则分组*/String ruleGroup;/*** 规则标识(英文,且唯一)*/String ruleCode;/*** 规则名*/String ruleName;/*** 规则描述*/String ruleDescription;/*** 满足规则后的消息*/String message;/*** 规则表达式,通过conditionList生成*/String whenExpression;/*** 匹配规则操作表达式*/String thenExpression;/*** 创建人工号*/String creatUser;/*** 创建时间*/Date createTime;/*** 最近更新时间*/Date lastUpdateTime;/*** 最近更新人*/String lastUpdateUser;/*** 是否启用*/boolean isEnable;/*** 条件列表*/@TableField(exist = false)List<ExpressionRuleCondition> conditionList;
}
规则下的条件
@Data
@ToString(callSuper = true)
@TableName("EXPRESSION_RULE_CONDITION")
public class ExpressionRuleCondition {/*** 主键*/Long id;/*** 主表ID*/Long masterId;/*** 左括号* ( ~ (((((*/String leftBrackets;/*** 字段*/String field;/*** 操作* 大于,小于,等于,不等于,包含,不包含,前缀,后缀* >  , <  , ==, !=  , contains , !contains, startWith, endWith**/String operator;/*** 值字段类型* 自定义输入 或 实体字段*  input , field*/String valueType;/*** 字段规则命中值*/String value;/*** 右括号* ) ~ )))))*/String rightBrackets;/*** 多条件连接符号* && ~ ||*/String link;
}
RuleUtils
@Slf4j
public class RuleUtils {public static RuleResult match(JSONObject dataSource, ExpressionRule rule){// 结果RuleResult result = new RuleResult();// 规则实例Facts facts = new Facts();facts.put("fact",dataSource);facts.put("result",result);// 规则内容Rule mvelrule = new MVELRule().name(rule.getRuleName()).description(rule.getRuleDescription()).when(rule.getWhenExpression()).then(rule.getThenExpression());// 规则集合Rules rules = new Rules();// 将规则添加到集合rules.register(mvelrule);//创建规则执行引擎,并执行规则RulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);result.setRuleId(rule.getId());return result;}}
新建规则

controller

    @PostMapping("/addRule")@ApiOperation("新增表达式规则")public ResponseResult<Long> addRule(@RequestBody BaseRuleRo ruleRo){return ruleService.addRule(ruleRo);}

serviceImpl

    /*** 新增表达式规则*/@Override@Transactional(rollbackFor = Exception.class)public ResponseResult<Long> addRule(BaseRuleRo rule) {ExpressionRule expressionRule = new ExpressionRule();expressionRule.setId(ruleMapper.nextSque());expressionRule.setCreateTime(new Date());expressionRule.setEnable(true);expressionRule.setRuleGroup(rule.getRuleGroup());expressionRule.setRuleCode(rule.getRuleCode());expressionRule.setRuleDescription(rule.getRuleDescription());expressionRule.setRuleName(rule.getRuleName());expressionRule.setMessage(rule.getMessage());// 核心代码段expressionRule.setWhenExpression(rule.getWhenExpression("fact"));expressionRule.setThenExpression(rule.getThenExpression("result"));// TODO 获取用户信息// expressionRule.setCreatUser(userHandler.getUserName());ruleMapper.insert(expressionRule);// 条件表List<ExpressionRuleCondition> conditionList = new ArrayList<>();rule.getConditionRoList().forEach(condition->{ExpressionRuleCondition expressionRuleCondition = new ExpressionRuleCondition();expressionRuleCondition.setId(conditionMapper.nextSque());expressionRuleCondition.setMasterId(expressionRule.getId());expressionRuleCondition.setLeftBrackets(condition.getLeftBrackets());expressionRuleCondition.setField(condition.getField());expressionRuleCondition.setOperator(condition.getOperator());expressionRuleCondition.setValueType(condition.getValueType());expressionRuleCondition.setValue(condition.getValue());expressionRuleCondition.setRightBrackets(condition.getRightBrackets());expressionRuleCondition.setLink(condition.getLink());conditionList.add(expressionRuleCondition);});conditionMapper.insertBatch(conditionList);return ResponseResult.buildSucc(expressionRule.getId());}

RO

@Slf4j
@Data
@ToString
public class BaseRuleRo {/*** 主键*/@ApiModelProperty(value = "id")Long id;/*** 规则组*/@ApiModelProperty(value = "ruleGroup",required = true)String ruleGroup;/*** 规则标识(英文,且唯一)*/@ApiModelProperty(value = "ruleCode",required = true)String ruleCode;/*** 规则名*/@ApiModelProperty(value = "ruleName",required = true)String ruleName;/*** 规则描述*/@ApiModelProperty(value = "ruleDescription",required = true)String ruleDescription;/*** 条件*/@ApiModelProperty(value = "conditionRoList",required = true)List<ConditionRo> conditionRoList;/*** 满足规则后可以带这个值*/@ApiModelProperty(value = "message",required = true)String message;/*** 是否启用*/@ApiModelProperty(value = "isEnable")boolean isEnable = true;/*** 条件表达式*/@ApiModelProperty(hidden=true)@JsonIgnoreprivate String whenExpression;/*** 结果表达式*/@ApiModelProperty(hidden=true)@JsonIgnoreprivate String thenExpression;/*** 组装when表达式*/public String getWhenExpression(String factAlias) throws UnSupportException {StringBuilder expression = new StringBuilder();conditionRoList.forEach(conditionRo -> {expression.append(conditionRo.leftBrackets).append(factAlias)// 从JSON中取出字段值.append(".get(\"").append(conditionRo.field).append("\")").append(conditionRo.getOperatorExpress(factAlias)).append(conditionRo.rightBrackets).append(conditionRo.getLinkExpress());});log.info("whenExpression: {}",expression.toString());return expression.toString();}/*** 组装then表达式* 不做太过灵活的操作,值返回是否命中规则,至于命中后改干什么业务代码自己实现*/public String getThenExpression(String resultAlias){StringBuilder expression = new StringBuilder();expression.append(resultAlias).append(".setValue(\"").append(message).append("\");");log.info("thenExpression: {}",expression.toString());return expression.toString();}
}
@Data
@ToString(callSuper = true)
public class ConditionRo {/*** 左括号* ( ~ (((((*/@ApiModelProperty(value = "leftBrackets",required = true)String leftBrackets;/*** 字段*/@ApiModelProperty(value = "field",required = true)String field;/*** 操作* 大于,小于,等于,不等于,包含,不包含,前缀,后缀* >  , <  , ==, !=  , contains , !contains, startWith, endWith**/@ApiModelProperty(value = "operator",required = true)String operator;/*** 值字段类型* 自定义输入 或 实体字段*  input , field*/@ApiModelProperty(value = "valueType",required = true)String valueType;/*** 字段规则命中值*/@ApiModelProperty(value = "value",required = true)String value;/*** 右括号* ) ~ )))))*/@ApiModelProperty(value = "rightBrackets",required = true)String rightBrackets;/*** 多条件连接符号* && ~ ||*/@ApiModelProperty(value = "link",required = true)String link;/*** 运算符表达式*/String getOperatorExpress(String factAlias) throws UnSupportException {if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_GREATER_THEN)){return ">"+getValueExpress(factAlias);}if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_LESS_THEN)){return "<"+getValueExpress(factAlias);}if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_QEUAL)){return "=="+getValueExpress(factAlias);}if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_UNQEUAL)){return "!="+getValueExpress(factAlias);}if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_CONTAIN)){return ".contains("+getValueExpress(factAlias)+")";}if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_PREFIX)){return ".startsWith("+getValueExpress(factAlias)+")";}if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_SUFFIX)){return ".endsWith("+getValueExpress(factAlias)+")";}throw new UnSupportException(operator+" is not support ");}/*** 比较值表达式*/private String getValueExpress(String factAlias) throws UnSupportException{if(!valueType.equalsIgnoreCase(RuleOperation.INPUT_VALUE) && !valueType.equalsIgnoreCase(RuleOperation.FIELD_VALUE)){throw new UnSupportException(valueType+" is not support ");}return valueType.equalsIgnoreCase(RuleOperation.INPUT_VALUE)? String.format("\"%s\"", value) : String.format("%s.get(\"%s\")", factAlias, value);}/*** 连接符表达式*/String getLinkExpress() throws UnSupportException{if(StringUtils.isEmpty(link)){return "";}if(link.equalsIgnoreCase(RuleOperation.LINK_AND)){return "&&";}if(link.equalsIgnoreCase(RuleOperation.LINK_OR)){return "||";}throw new UnSupportException(link+" is not support ");}
}
匹配规则

controller

    /*** 匹配规则*/@PostMapping("/match")@ApiOperation("匹配规则")public ResponseResult<List<RuleResult>> match(@RequestBody RuleMatchRo matchRo){return ruleService.match(matchRo);}

serviceImpl

    /*** 匹配规则*/@Overridepublic ResponseResult<List<RuleResult>> match(RuleMatchRo ruleRo) {try{if(CollectionUtils.isEmpty(ruleRo.getRuleIds())) {return ResponseResult.buildFail("ruleIds为空");}// 查询规则,先查redis,再查DBList<ExpressionRule> rules = getRuleList(ruleRo.getRuleIds());// 进行规则匹配List<RuleResult> results = new ArrayList<>();rules.forEach(rule -> {RuleResult matchResult = RuleUtils.match(ruleRo.getFact(),rule);results.add(matchResult);});return ResponseResult.buildSucc(results);}catch (Exception e){log.error("匹配规则异常",e);return ResponseResult.buildFail("匹配规则异常!");}}

RO

@Data
public class RuleMatchRo {/*** 需要匹配的规则ID列表*/@ApiModelProperty(value = "ruleIds")List<Long> ruleIds;/*** 数据源*/@ApiModelProperty(value = "fact")JSONObject fact;
}

相关文章:

springboot + easyRules 搭建规则引擎服务

依赖 <dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-core</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules…...

Mac电脑配置环境变量

1.打开配置文件bash_profile open -e .bash_profile 2.如果没有创建过.bash_profile&#xff0c;则先需要创建 touch .bash_profile 3.输入你要配置的环境变量 #Setting PATH for Android ADB Tools export ANDROID_HOME/Users/xxx/android export PATH${PATH}:${ANDROID_HOME}…...

Windows系统x86机器安装(麒麟、统信)ARM系统详细教程

本次介绍在window系统x86机器上安装国产系统 arm 系统的详细教程。 注&#xff1a;ubuntu 的arm系统安装是一样的流程。 1.安装环境准备。 首先&#xff0c;你得有台电脑&#xff0c;配置别太差&#xff0c;至少4核8G内存&#xff0c;安装window10或者11都行&#xff08;为啥…...

消息中间件篇之RabbitMQ-高可用机制

一、怎么保证高可用性 在生产环境下&#xff0c;使用集群来保证高可用性&#xff0c;一般我们采用普通集群、镜像集群、仲裁队列。 二、普通集群 普通集群&#xff0c;或者叫标准集群&#xff08;classic cluster&#xff09;&#xff0c;具备下列特征&#xff1a; 1. 会在集…...

express+mysql+vue,从零搭建一个商城管理系统5--用户注册

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、新建user表二、安装bcryptjs、MD5、body-parser三、修改config/db.js四、新建config/bcrypt.js五、新建models文件夹和models/user.js五、index.js引入使用body-parser六、修改routes/user.js七、启动项…...

canvas水波纹效果,jquery鼠标水波纹插件

canvas水波纹效果&#xff0c;jquery鼠标水波纹插件 效果展示 jQuery水波纹效果&#xff0c;canvas水波纹插件 HTML代码片段 <div class"scroll04wrap"><h3>发展历程</h3><div class"scroll04"><p>不要回头&#xff0c;一…...

Zookeeper客户端命令、JAVA API、监听原理、写数据原理以及案例

1. Zookeeper节点信息 指定服务端&#xff0c;启动客户端命令&#xff1a; bin/zkCli.sh -server 服务端主机名:端口号 1&#xff09;ls / 查看根节点下面的子节点 ls -s / 查看根节点下面的子节点以及根节点详细信息 其中&#xff0c;cZxid是创建节点的事务id&#xff0c…...

[嵌入式系统-34]:RT-Thread -19- 新手指南:RT-Thread标准版系统架构

目录 一、RT-Thread 简介 二、RT-Thread 概述 三、许可协议 四、RT-Thread 的架构 4.1 内核层&#xff1a; 4.2 组件与服务层&#xff1a; 4.3 RT-Thread 软件包&#xff1a; 一、RT-Thread 简介 作为一名 RTOS 的初学者&#xff0c;也许你对 RT-Thread 还比较陌生。然…...

postman访问k8s api

第一种方式&#xff1a; kubectl -n kubesphere-system get sa kubesphere -oyaml apiVersion: v1 kind: ServiceAccount metadata:annotations:meta.helm.sh/release-name: ks-coremeta.helm.sh/release-namespace: kubesphere-systemcreationTimestamp: "2023-07-24T07…...

UE4c++ ConvertActorsToStaticMesh

UE4c ConvertActorsToStaticMesh ConvertActorsToStaticMesh UE4c ConvertActorsToStaticMesh创建Edior模块&#xff08;最好是放Editor模块毕竟是编辑器代码&#xff09;创建UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量…...

Qt中tableView控件的使用

tableView使用注意事项 tableView在使用时&#xff0c;从工具栏拖动到底层页面后&#xff0c;右键进行选择如下图所示&#xff1a; 此处需要注意的是&#xff0c;需要去修改属性&#xff0c;从UI上修改属性如下所示&#xff1a; 也可以通过代码修改属性&#xff1a; //将其设…...

【医学影像】LIDC-IDRI数据集的无痛制作

LIDC-IDRI数据集制作 0.下载0.0 链接汇总0.1 步骤 1.合成CT图reference 0.下载 0.0 链接汇总 LIDC-IDRI官方网址&#xff1a;https://www.cancerimagingarchive.net/nbia-search/?CollectionCriteriaLIDC-IDRINBIA Data Retriever 下载链接&#xff1a;https://wiki.canceri…...

MacOS开发环境搭建详解

搭建MacOS开发环境需要准备相应的软硬件&#xff0c;并遵循一系列步骤。以下是详细的步骤&#xff1a; 软硬件准备&#xff1a; MacOS电脑&#xff1a;确保你的电脑运行的是MacOS操作系统。Xcode软件&#xff1a;打开AppStore&#xff0c;搜索并安装Xcode。安装过程可能较长&…...

全量知识系统问题及SmartChat给出的答复 之2

Q6. 根据DDD的思想( 也就是借助 DDD的某个或某些实现)&#xff0c;是否能按照这个想法给出程序设计和代码结构&#xff1f; 当使用领域驱动设计&#xff08;DDD&#xff09;的思想来设计程序和代码结构时&#xff0c;可以根据领域模型、领域服务、值对象、实体等概念来进行设计…...

嵌入式驱动学习第一周——vim的使用

前言 本篇博客学习使用vim&#xff0c;vim作为linux下的编辑器&#xff0c;学linux肯定是绕不开vim的&#xff0c;因为不确定对方环境中是否安装了编译器&#xff0c;但一定会有vim。 对于基本的使用只需要会打开文件&#xff0c;保存文件&#xff0c;编辑文件即可。 嵌入式驱动…...

loop_list单向循环列表

#include "loop_list.h" //创建单向循环链表 loop_p create_head() { loop_p L(loop_p)malloc(sizeof(loop_list)); if(LNULL) { printf("create fail\n"); return NULL; } L->len 0; L->nextL; retur…...

Python爬虫实战第二例【二】

零.前言&#xff1a; 本文章借鉴&#xff1a;Python爬虫实战&#xff08;五&#xff09;&#xff1a;根据关键字爬取某度图片批量下载到本地&#xff08;附上完整源码&#xff09;_python爬虫下载图片-CSDN博客 大佬的文章里面有API的获取&#xff0c;在这里我就不赘述了。 一…...

Eclipse是如何创建web project项目的?

前面几篇描述先后描述了tomcat的目录结构和访问机制&#xff0c;以及Eclipse的项目类型和怎么调用jar包&#xff0c;还有java的main函数等&#xff0c;这些是一些基础问题&#xff0c;基础高清出来才更容易搞清楚后面要说的东西&#xff0c;也就是需求带动学习&#xff0c;后面…...

Excel的中高级用法

单元格格式&#xff0c;根据数值的正负分配不同的颜色和↑ ↓ 根据数值正负分配颜色 2-7 [蓝色]#,##0;[红色]-#,##0 分配颜色的基础上&#xff0c;根据正负加↑和↓ 2↑-7↓ 其实就是在上面颜色的代码基础上加个 向上的符号↑&#xff0c;或向下的符号↓ [蓝色]#,##0↑;[红色…...

【ArcGIS】基本概念-空间参考与变换

ArcGIS基本概念-空间参考与变换 1 空间参考与地图投影1.1 空间参考1.2 大地坐标系&#xff08;地理坐标系&#xff09;1.3 投影坐标系总结 2 投影变换预处理2.1 定义投影2.2 转换自定义地理&#xff08;坐标&#xff09;变换2.3 转换坐标记法 3 投影变换3.1 矢量数据的投影变换…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...