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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/706701.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里巴巴淘口令API的安全保障措施在电商领域的应用与实践

在电商领域&#xff0c;阿里巴巴淘口令API的安全保障措施是非常重要的&#xff0c;因为它们确保了数据的机密性、完整性和可用性。以下是一些关于阿里巴巴淘口令API安全保障措施在电商领域的应用与实践&#xff0c;并附带相关代码示例&#xff1a; 1. 身份验证和授权 应用示例…

VoIP之AVPF(Audio-Visual Profile with Feedback)

在VoIP音视频通话中&#xff0c;接收者可以依赖rtcp机制向发送者报告RTP数据接收的统计情况&#xff0c;以便发送者根据接收情况&#xff08;丢包数量等&#xff09;调整传输行为&#xff08;发送速率等&#xff09;。由于基本的RTCP统计报告是定期发送的&#xff0c;通过该机制…

安全运营中心(SOC)综合指南

什么是安全运营中心&#xff08;SOC&#xff09; 安全运营中心&#xff0c;也称为信息安全运营中心 &#xff08;ISOC&#xff09;&#xff0c;是结构良好的网络安全战略的核心。安全运营中心是一个集中式枢纽&#xff0c;无论是在组织内部还是外包&#xff0c;都致力于对整个…

云计算时代的运维: 职业发展方向与岗位选择

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

Nginx网络服务五-----rewrite和反向代理

1.rewrite 1.1rewrite指令 通过正则表达式的匹配来改变URI&#xff0c;可以同时存在一个或多个指令&#xff0c;按照顺序依次对URI进行匹配&#xff0c;rewrite主要是针对用户请求的URL或者是URI做具体处理 官方文档&#xff1a; https://nginx.org/en/docs/http/ngx_http_r…

numpy数组索引

文章目录 简介索引方法 简介 numpy是python中最常用的科学计算包&#xff0c;而数组则是是numpy的核心类型&#xff0c;也是Python称为科学计算领域首选语言的关键类型&#xff0c;可以不夸张地说&#xff0c;正是数组类型的强大与易用性&#xff0c;筑就了Python帝国。 和Py…

自动驾驶消息传输机制-LCM

需要用到LCM消息通讯&#xff0c;遂研究下。 这里写目录标题 1 LCM简介2. LCM源码分析3 LCM C教程与实例3.1 安装配置及介绍3.2 创建类型定义3.3 初始化LCM3.4 发布publish一个消息3.5 订阅和接收一个消息3.6 LCM进程间通讯3.7 注意事项&#xff1f;3.7.1 当数据结构定义的是数…

代码随想录算法训练营29期|day64 任务以及具体安排

第十章 单调栈part03 有了之前单调栈的铺垫&#xff0c;这道题目就不难了。 84.柱状图中最大的矩形class Solution {int largestRectangleArea(int[] heights) {Stack<Integer> st new Stack<Integer>();// 数组扩容&#xff0c;在头和尾各加入一个元素int [] ne…

斯元Z-ONE-China Cybersecurity Tech Landscape·中国网络安全全景图-百度网盘下载

面向全球&#xff0c;斯元Z-ONE正式发布首版「China Cybersecurity Tech Landscape中国网络安全全景图」。 为了提升海外市场对中国网络安全行业的全局认识&#xff0c;方便国际客户及合作伙伴了解中国网络安全科技的赛道分布和国内外厂商对标&#xff0c;助力中国网安厂商出海…

uni-app之android原生插件开发

官网 uni小程序SDK 一 插件简介 1.1 当HBuilderX中提供的能力无法满足App功能需求&#xff0c;需要通过使用Andorid/iOS原生开发实现时&#xff0c;可使用App离线SDK开发原生插件来扩展原生能力。 1.2 插件类型有两种&#xff0c;Module模式和Component模式 Module模式&…

【PyTorch笔记 04】F.cross_entropy的使用

torch.nn.functional.cross_entropy是PyTorch中用于计算交叉熵损失的函数&#xff0c;非常适合用于多分类问题。这个函数结合了log_softmax操作和nll_loss&#xff08;负对数似然损失&#xff09;的计算&#xff0c;因此输入得分&#xff08;即模型输出&#xff09;不需要事先经…

【架构笔记1】剃刀思维-如无必要,勿增实体

欢迎来到文思源想的架构空间&#xff0c;前段时间博主做了一个工作经历复盘&#xff0c;10年开发路&#xff0c;走了不少弯路&#xff0c;也算积累了不少软件开发、架构设计的经验和心得&#xff0c;确实有必要好好盘一盘&#xff0c;作为个人的总结&#xff0c;同时也留给有缘…

对抗生成网络

作用 手底有一堆数据&#xff0c;告诉它要生成什么东西&#xff0c;它就这么去做。 超分辨率重构 告诉模型什么样的东西是一个低分辨率图像&#xff0c;什么样的东西是高分辨率图像&#xff0c;再设计一个损失函数。让计算机学习两者之间的关系&#xff0c;怎样由一个低分辨率…

2.6基本算法之动态规划1944:吃糖果

名名的妈妈从外地出差回来&#xff0c;带了一盒好吃又精美的巧克力给名名&#xff08;盒内共有 N 块巧克力&#xff0c;20 > N >0&#xff09;。妈妈告诉名名每天可以吃一块或者两块巧克力。假设名名每天都吃巧克力&#xff0c;问名名共有多少种不同的吃完巧克力的方案。…

Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、防盗链、SSL、性能优化

引言 一、性能怪兽-Nginx概念深入浅出 二、Nginx环境搭建 三、Nginx反向代理-负载均衡 四、Nginx动静分离 五、Nginx资源压缩 六、Nginx缓冲区 七、Nginx缓存机制 八、Nginx实现IP黑白名单 九、Nginx跨域配置 十、Nginx防盗链设计 十一、Nginx大文件传输配置 十二、Nginx配置SL…

redis真实使用

Spring Data Redis中提供了一个高度封装的类&#xff1a;RedisTemplate&#xff0c;对相关api进行了归类封装,将同一类型操作封装为operation接口&#xff0c;具体分类如下&#xff1a; ValueOperations&#xff1a;string数据操作 SetOperations&#xff1a;set类型数据操作 …

Flink SQL 中的流式概念:状态算子

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

动态规划-01背包问题新解(c)

动态规划-01背包问题新解 概述动态规划01背包问题传统思路算法官方递推关系算法2种算法比较 概述 本文将从一个新的角度来描述和实现01背包问题&#xff0c;以协助对01背包问题以及教材上的算法的彻底理解。 新的角度为&#xff1a;传统思路算法&#xff0c;“新”是新在与绝…

【Python】Ubuntu创建虚拟环境运行Python

虚拟环境让项目之间的依赖关系更加清晰&#xff0c;同时也可以避免全局安装的包的版本冲突问题。 在Ubuntu系统上&#xff0c;可以使用Python的内置工具venv来创建虚拟环境。以下是在Ubuntu上创建Python虚拟环境的步骤&#xff1a; 安装Python虚拟环境工具&#xff1a;首先确保…

Maven配置文件忘记更新阿里云的Mavne镜像的最新地址

最近更新最新版的IDEA&#xff0c;打开旧的工作空间&#xff0c;发现以前Spring相关项目全部报错并无法运行。 单元测试发现无法识别XML配置文件路径&#xff0c;同时提示各种异常 异常一 org.springframework.beans.factory.BeanDefinitionStoreException: IOException par…