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,则先需要创建 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 系统的详细教程。 注:ubuntu 的arm系统安装是一样的流程。 1.安装环境准备。 首先,你得有台电脑,配置别太差,至少4核8G内存,安装window10或者11都行(为啥…...
消息中间件篇之RabbitMQ-高可用机制
一、怎么保证高可用性 在生产环境下,使用集群来保证高可用性,一般我们采用普通集群、镜像集群、仲裁队列。 二、普通集群 普通集群,或者叫标准集群(classic cluster),具备下列特征: 1. 会在集…...
express+mysql+vue,从零搭建一个商城管理系统5--用户注册
提示:学习express,搭建管理系统 文章目录 前言一、新建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水波纹效果,jquery鼠标水波纹插件 效果展示 jQuery水波纹效果,canvas水波纹插件 HTML代码片段 <div class"scroll04wrap"><h3>发展历程</h3><div class"scroll04"><p>不要回头,一…...
Zookeeper客户端命令、JAVA API、监听原理、写数据原理以及案例
1. Zookeeper节点信息 指定服务端,启动客户端命令: bin/zkCli.sh -server 服务端主机名:端口号 1)ls / 查看根节点下面的子节点 ls -s / 查看根节点下面的子节点以及根节点详细信息 其中,cZxid是创建节点的事务id,…...
[嵌入式系统-34]:RT-Thread -19- 新手指南:RT-Thread标准版系统架构
目录 一、RT-Thread 简介 二、RT-Thread 概述 三、许可协议 四、RT-Thread 的架构 4.1 内核层: 4.2 组件与服务层: 4.3 RT-Thread 软件包: 一、RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然…...
postman访问k8s api
第一种方式: 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模块(最好是放Editor模块毕竟是编辑器代码)创建UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量…...
Qt中tableView控件的使用
tableView使用注意事项 tableView在使用时,从工具栏拖动到底层页面后,右键进行选择如下图所示: 此处需要注意的是,需要去修改属性,从UI上修改属性如下所示: 也可以通过代码修改属性: //将其设…...
【医学影像】LIDC-IDRI数据集的无痛制作
LIDC-IDRI数据集制作 0.下载0.0 链接汇总0.1 步骤 1.合成CT图reference 0.下载 0.0 链接汇总 LIDC-IDRI官方网址:https://www.cancerimagingarchive.net/nbia-search/?CollectionCriteriaLIDC-IDRINBIA Data Retriever 下载链接:https://wiki.canceri…...
MacOS开发环境搭建详解
搭建MacOS开发环境需要准备相应的软硬件,并遵循一系列步骤。以下是详细的步骤: 软硬件准备: MacOS电脑:确保你的电脑运行的是MacOS操作系统。Xcode软件:打开AppStore,搜索并安装Xcode。安装过程可能较长&…...
全量知识系统问题及SmartChat给出的答复 之2
Q6. 根据DDD的思想( 也就是借助 DDD的某个或某些实现),是否能按照这个想法给出程序设计和代码结构? 当使用领域驱动设计(DDD)的思想来设计程序和代码结构时,可以根据领域模型、领域服务、值对象、实体等概念来进行设计…...
嵌入式驱动学习第一周——vim的使用
前言 本篇博客学习使用vim,vim作为linux下的编辑器,学linux肯定是绕不开vim的,因为不确定对方环境中是否安装了编译器,但一定会有vim。 对于基本的使用只需要会打开文件,保存文件,编辑文件即可。 嵌入式驱动…...
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爬虫实战第二例【二】
零.前言: 本文章借鉴:Python爬虫实战(五):根据关键字爬取某度图片批量下载到本地(附上完整源码)_python爬虫下载图片-CSDN博客 大佬的文章里面有API的获取,在这里我就不赘述了。 一…...
Eclipse是如何创建web project项目的?
前面几篇描述先后描述了tomcat的目录结构和访问机制,以及Eclipse的项目类型和怎么调用jar包,还有java的main函数等,这些是一些基础问题,基础高清出来才更容易搞清楚后面要说的东西,也就是需求带动学习,后面…...
Excel的中高级用法
单元格格式,根据数值的正负分配不同的颜色和↑ ↓ 根据数值正负分配颜色 2-7 [蓝色]#,##0;[红色]-#,##0 分配颜色的基础上,根据正负加↑和↓ 2↑-7↓ 其实就是在上面颜色的代码基础上加个 向上的符号↑,或向下的符号↓ [蓝色]#,##0↑;[红色…...
【ArcGIS】基本概念-空间参考与变换
ArcGIS基本概念-空间参考与变换 1 空间参考与地图投影1.1 空间参考1.2 大地坐标系(地理坐标系)1.3 投影坐标系总结 2 投影变换预处理2.1 定义投影2.2 转换自定义地理(坐标)变换2.3 转换坐标记法 3 投影变换3.1 矢量数据的投影变换…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
