告别if else,试试这款轻量级流程引擎吧,自带IDEA插件真香

news/2024/5/5 15:49:31/文章来源:https://blog.csdn.net/Q54665642ljf/article/details/126968910

在我们平时做项目的时候,经常会遇到复杂的业务逻辑,如果使用if else来实现的话,往往会很冗长,维护成本也很高。今天给大家推荐一个轻量级流程引擎 LiteFlow ,可以优雅地实现复杂的业务逻辑,本文将以电商项目中的订单价格计算为例来聊聊它的使用。

LiteFlow简介

LiteFlow是一个轻量且强大的国产流程引擎框架,可用于复杂的组件化业务的编排工作。通过它我们可以把业务逻辑都定义到不同组件之中,然后使用简洁的规则文件来串联整个流程,从而实现复杂的业务逻辑。

LiteFlow主要特性如下:

  • 组件定义统一:所有的逻辑都是组件,直接使用Spring原生注解 @Component 定义即可。

  • 规则轻量:基于规则文件来编排流程,学习规则表达式入门仅需5分钟。

  • 规则多样化:规则支持xml、json、yml三种规则文件写法,喜欢哪种用哪个。

  • 任意编排:同步异步混编,再复杂的逻辑过程,都能轻易实现。

  • 规则能从任意地方加载:框架中提供本地文件配置源和zk配置源的实现,也提供了扩展接口。

  • 优雅热刷新机制:规则变化,无需重启应用,即时改变应用的规则。

  • 支持广泛:同事支持SpringBoot,Spring或其他Java项目。

下面是使用LiteFlow来实现订单价格计算的展示页面,实现起来确实比较优雅!

IDEA插件

LiteFlow还拥有自己的IDEA插件 LiteFlowX ,通过该插件能支持规则文件的智能提示、语法高亮、组件与规则文件之间的跳转及LiteFlow工具箱等功能,强烈建议大家安装下。

  • 首先我们在IDEA的插件市场中安装该插件;

  • 安装好LiteFlowX插件后,我们代码中所定义的组件和规则文件都会显示特定的图标;

  • 当我们编辑规则文件时,会提示我们已经定义好的组件,并支持从规则文件中跳转到组件;

  • 还支持从右侧打开工具箱,快捷查看组件和规则文件。

规则表达式

接下来我们学习下规则表达式,也就是规则文件的编写,入门表达式非常简单,但这对使用LiteFlow非常有帮助!

串行编排

当我们想要依次执行a、b、c、d四个组件时,直接使用 THEN 关键字即可。

<chain name="chain1">THEN(a, b, c, d);
</chain>

并行编排

如果想并行执行a、b、c三个组件的话,可以使用 WHEN 关键字。

<chain name="chain1">WHEN(a, b, c);
</chain>

选择编排

如果想实现代码中的switch逻辑的话,例如通过a组件的返回结果进行判断,如果返回的是组件名称b的话则执行b组件,可以使用 SWITCH 关键字。

<chain name="chain1">SWITCH(a).to(b, c, d);
</chain>

条件编排

如果想实现代码中的if逻辑的话,例如当x组件返回为true时执行a,可以使用 IF 关键字。

<chain name="chain1">IF(x, a);
</chain>

如果想实现if的三元运算符逻辑的话,例如x组件返回为true时执行a组件,返回为false时执行b组件,可以编写如下规则文件。

<chain name="chain1">IF(x, a, b);
</chain>

如果想实现if else逻辑的话,可以使用 ELSE 关键字,和上面实现效果等价。

<chain name="chain1">IF(x, a).ELSE(b);
</chain>

如果想实现else if逻辑的话,可以使用 ELIF 关键字。

<chain name="chain1">IF(x1, a).ELIF(x2, b).ELSE(c);
</chain>

使用子流程

当某些流程比较复杂时,我们可以定义子流程,然后在主流程中引用,这样逻辑会比较清晰。

例如我们有如下子流程,执行C、D组件。

<chain name="subChain">THEN(C, D);
</chain>

然后我们直接在主流程中引用子流程即可。

<chain name="mainChain">THEN(A, B,subChain,E);
</chain>

使用

学习完规则表达式后,我们发现LiteFlow寥寥几个关键字,就可以实现复杂的流程了。下面我们将以订单价格计算为例,实践下LiteFlow这个流程引擎框架。

  • 首先我们需要在项目中集成LiteFlow,这里以SpringBoot应用为例,在 pom.xml 中添加如下依赖即可;

<dependency><groupId>com.yomahub</groupId><artifactId>liteflow-spring-boot-starter</artifactId><version>2.8.5</version>
</dependency>
  • 接下来修改配置文件 application.yml ,配置好LiteFlow的规则文件路径即可;

server:port: 8580
liteflow:#规则文件路径rule-source: liteflow/*.el.xml
  • 这里直接使用LiteFlow官方的Demo,该案例为一个价格计算引擎,模拟了电商中对订单价格的计算,并提供了简单的界面,下载地址如下:

https://gitee.com/bryan31/liteflow-example
  • 下载完成后,直接运行Demo,通过如下地址可以访问测试页面:http://localhost:8580

  • 这个案例通过传入的订单数据,能计算出订单的最终价格,这里涉及到会员折扣、促销优惠、优惠券抵扣、运费计算等操作,多达十几步,如果不使用流程引擎的话实现起来是非常复杂的,下面是订单价格计算中各组件执行流程图;

  • NodeComponent
    process()
    @Component
    isAccess
    
/*** 优惠券抵扣计算组件*/
@Component("couponCmp")
public class CouponCmp extends NodeComponent {@Overridepublic void process() throws Exception {PriceContext context = this.getContextBean(PriceContext.class);/**这里Mock下根据couponId取到的优惠卷面值为15元**/Long couponId = context.getCouponId();BigDecimal couponPrice = new BigDecimal(15);BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();BigDecimal currPrice = prePrice.subtract(couponPrice);context.addPriceStep(new PriceStepVO(PriceTypeEnum.COUPON_DISCOUNT,couponId.toString(),prePrice,currPrice.subtract(prePrice),currPrice,PriceTypeEnum.COUPON_DISCOUNT.getName()));}@Overridepublic boolean isAccess() {PriceContext context = this.getContextBean(PriceContext.class);if(context.getCouponId() != null){return true;}else{return false;}}
}
  • 还有一些比较特殊的组件,比如用于判断是按国内运费计算规则来计算还是境外规则的条件组件,需要继承 NodeSwitchComponent 并实现 processSwitch() 方法;

/*** 运费条件组件*/
@Component("postageCondCmp")
public class PostageCondCmp extends NodeSwitchComponent {@Overridepublic String processSwitch() throws Exception {PriceContext context = this.getContextBean(PriceContext.class);//根据参数oversea来判断是否境外购,转到相应的组件boolean oversea = context.isOversea();if(oversea){return "overseaPostageCmp";}else{return "postageCmp";}}
}
  • 其他组件逻辑具体可以参考demo源码,定义好组件之后就可以通过规则文件将所有流程连接起来了,首先是促销优惠计算子流程;

<?xml version="1.0" encoding="UTF-8"?>
<flow><chain name="promotionChain">THEN(fullCutCmp, fullDiscountCmp, rushBuyCmp);</chain>
</flow>
  • 然后是整个流程,大家可以对比下上面的流程图,基本能画出流程图的都可以用LiteFlow来实现;

<?xml version="1.0" encoding="UTF-8"?>
<flow><chain name="mainChain">THEN(checkCmp, slotInitCmp, priceStepInitCmp,promotionConvertCmp, memberDiscountCmp,promotionChain, couponCmp,SWITCH(postageCondCmp).to(postageCmp, overseaPostageCmp),priceResultCmp, stepPrintCmp);</chain>
</flow>
  • 最后在Controller中添加接口,获取传入的订单数据,然后调用 FlowExecutor 类的执行方法即可;

@Controller
public class PriceExampleController {@Resourceprivate FlowExecutor flowExecutor;@RequestMapping(value = "/submit", method = RequestMethod.POST)@ResponseBodypublic String submit(@Nullable @RequestBody String reqData) {try {PriceCalcReqVO req = JSON.parseObject(reqData, PriceCalcReqVO.class);LiteflowResponse response = flowExecutor.execute2Resp("mainChain", req, PriceContext.class);return response.getContextBean(PriceContext.class).getPrintLog();} catch (Throwable t) {t.printStackTrace();return "error";}}
}
  • 我们平时在写复杂代码时,后面一步经常会用到前面一步的结果,然而使用LiteFlow之后,组件里并没有参数传递,那么各个流程中参数是这么处理的?其实是LiteFlow中有个上下文的概念,流程中的所有数据都统一存放在此,比如上面的 PriceContext 类;

public class PriceContext {/*** 订单号*/private String orderNo;/*** 是否境外购*/private boolean oversea;/*** 商品包*/private List<ProductPackVO> productPackList;/*** 订单渠道*/private OrderChannelEnum orderChannel;/*** 会员CODE*/private String memberCode;/*** 优惠券*/private Long couponId;/*** 优惠信息*/private List<PromotionPackVO> promotionPackList;/*** 价格步骤*/private List<PriceStepVO> priceStepList = new ArrayList<>();/*** 订单原始价格*/private BigDecimal originalOrderPrice;/*** 订单最终价格*/private BigDecimal finalOrderPrice;/*** 步骤日志*/private String printLog;
}
  • slotInitCmp
    getRequestData()
    PriceContext
    
/*** Slot初始化组件*/
@Component("slotInitCmp")
public class SlotInitCmp extends NodeComponent {@Overridepublic void process() throws Exception {//把主要参数冗余到slot里PriceCalcReqVO req = this.getRequestData();PriceContext context = this.getContextBean(PriceContext.class);context.setOrderNo(req.getOrderNo());context.setOversea(req.isOversea());context.setMemberCode(req.getMemberCode());context.setOrderChannel(req.getOrderChannel());context.setProductPackList(req.getProductPackList());context.setCouponId(req.getCouponId());}@Overridepublic boolean isAccess() {PriceCalcReqVO req = this.getSlot().getRequestData();if(req != null){return true;}else{return false;}}
}

总结

LiteFlow确实是一款好用的轻量级流程引擎,可以让复杂的业务逻辑变得清晰起来,便于代码维护。它的规则文件比起其他流程引擎来说,编写简单太多了,几分钟就能上手,感兴趣的朋友可以尝试下它!

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

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

相关文章

HashSet的存储机制

文章目录HashSet类内部存储机制实验1.不重写hashcode equals方法2.重写&#xff0c;但改写equals3.重写参考&#xff1a;HashSet类 HashSet是Set接口的典型实现&#xff0c;大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素&#xff0c;因此具…

PID控制算法

闭环控制(反馈回路close loop): 闭环控制系统需要目标量,执行器,传感器 通过偏差量获得执行量是最为重要的 目标量和传感器获得的执行器数据都需要是连续的; 偏差量来自于传感器和目标量数据和执行量不是同一个单位,需要一个比例P系数进行规整; 偏差量=目标量-当前位置量…

Java项目:JSP宠物店管理系统

作者主页&#xff1a;夜未央5788 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;主要分为管理员与用户两种角色&#xff0c;管理员登录后台&#xff0c;普通用户登录前台&#xff1b; 管理员角色包含…

【LeetCode每日一题】——面试题17.16.按摩师

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 动态规划 二【题目难度】 简单 三【题目编号】 面试题17.16.按摩师 四【题目描述】 一个有名的按摩师会收到…

App移动端测试(10)—— Monkey自定义脚本案例

01、前言 Monkey自定义脚本案例&#xff1a;QQ的操作 02、Monkey API LaunchActivity(pkg_name, cl_name)启动应用的Activity。参数&#xff1a;包名和启动的 Tap(x, y, tapDuration)模拟一次手指单击事件。参数&#xff1a;x,y为控件坐标&#xff0c;tapDuration为点击的持续…

你到底是前端人还是搬砖人?推荐一款国产摸鱼神器!

☀️ 前言 大家好我是小卢&#xff0c;前几天在群里见到有群友抱怨一周内要完成这么一个大概20&#xff5e;30页的小程序。 群友: 这20多个页面一个星期让我开发完&#xff0c;我是不相信&#x1f62e;‍&#x1f4a8;。群友1: 跑吧&#xff0c;这公司留着没用了&#xff0c;不…

【Python 技能树共建】Beautiful Soup

Beautiful Soup 模块是什么 初学 Python 爬虫&#xff0c;十之八九你采集的目标是网页&#xff0c;因此快速定位到网页内容&#xff0c;就成为你面临的第一道障碍&#xff0c;本篇博客就为你详细说明最易上手的网页元素定位术&#xff0c;学完就会系列。 本文核心使用到的是 …

Spring Security 中的RBAC角色和权限

在这篇文章中&#xff0c;我们将看看使用 Spring boot的R ole B ased A ccess Control ( RBAC )。 了解 RBAC 在 RBAC 模型中存在三个关键实体。他们是&#xff0c; 用户或主题 ——执行操作的系统参与者。它可以代表一个自然人、一个自动帐户&#xff0c;甚至是另一个应用程…

专业思维导图软件 Mindjet MindManager 2021下载

Mindjet MindManager 2021 是一款专业的思维导图软件&#xff0c;美国Mindjet公司开发&#xff0c;一款视觉工作管理的思维导图软件&#xff0c;界面友好功能强大&#xff0c;头脑风暴、会议管理及项目管理工具帮您轻松创建思维导图&#xff0c;有序组织思维、资源和项目进程。…

win10+cuda+cudnn+anconda+pytorch+pycharm全家桶安装

1、下载安装cuda&#xff1a; 网址&#xff1a;CUDA Toolkit 11.7 Update 1 Downloads | NVIDIA Developer 网址下方可以找到以前版本 安装完后&#xff0c;可以在命令行窗口输入nvcc --version查看cuda版本是否正确 显卡驱动版本与cuda版本对应关系&#xff1a; 2、安装cud…

操作系统实验四 进程间通信

★观前提示&#xff1a;本篇内容为操作系统实验内容&#xff0c;代码等内容经测试没有问题&#xff0c;但是可能会不符合每个人实验的要求&#xff0c;因此以下内容建议仅做思路参考。 目录一、实验目的二、实验内容三、具体实现四、实验总结一、实验目的 多道程序设计中&…

【前端面试】-- 必知必会的promise题

Promise 想必大家都十分熟悉&#xff0c;想想就那么几个 api&#xff0c;可是你真的了解 Promise 吗&#xff1f; 请迎接测试: 以下 promise 均指代 Promise 实例&#xff0c;环境是 Node.js 题目一&#xff1a; const promise new Promise((resolve, reject) > {conso…

ES8JC-ASEMI快恢复二极管ES8JC

编辑:ll ES8JC-ASEMI快恢复二极管ES8JC 型号:ES8JC 品牌:ASEMI 封装:SMC 特性:快恢复二极管 正向电流:8A 反向耐压:600V 恢复时间:35ns 引脚数量:2 芯片个数:1 芯片尺寸:84MIL 浪涌电流:125A 漏电流:<5ua 工作温度:-40℃~150℃ 包装方式:30/管;3000/箱 备受…

华为云各Region网络延迟实测

一、测试综述 测试内容&#xff1a; 序号 评测内容 测试日期 1 华为云各大区公网接入网络延迟 2022-09-20 2 华为云各大区之间网络延迟&#xff08;通过公网&#xff09; 2022-09-20 3 华为云各大区之间网络延迟&#xff08;通过云连接&#xff09; 2022-09-20 测…

【Linux】聊聊删文件的那些破事

聊聊删文件的那些破事前言正文rm命令find命令perl方式10w文件删除对比50w文件删除对比100w文件删除对比结语前言 在操作系统的日常运维中&#xff0c;我们经常会做文件的创建、删除、修改操作&#xff0c;尤其是删除&#xff0c;无论是定期清理日志文件&#xff0c;还是做完一…

传统光流方法汇总

又搬运了一个3d视觉相关的~~ 还是先道歉 就是学习用 还是公交上回家看那种 ~~ 这次分享传统光流方法汇总及其在深度学习中的应用&#xff01;&#xff08;基于相位/能量/匹配/变分&#xff09; 回望传统光流估计方法 近年来&#xff0c;随着深度学习技术的快速发展&#xff…

嵌入式分享合集63

一、PCB为什么一定要做阻抗 在具有电阻、电感和电容的电路里&#xff0c;对交流电所起的阻碍作用叫做阻抗。阻抗常用Z表示&#xff0c;是一个复数&#xff0c;实部称为电阻&#xff0c;虚部称为电抗。 其中电容在电路中对交流电所起的阻碍作用称为容抗&#xff0c;电感在电路…

Pr:多机位编辑

很多时候一个机位满足不了影视创作的需求。比如拍摄人物动作&#xff0c;如果能使远景、近景、特写等一些镜头相互衔接&#xff0c;将会使得角色显得更加丰富饱满。不同的景别传达着不同的信息&#xff0c;更容易交待环境和表达角色的情绪。早期人们在拍摄的同时完成多机位切换…

应用层 HTTP 代理服务器转发消息时的相关头部 请求头 X-Forwarded-For

在http消息传递过程当中会经过很多正向代理服务器和反向代理服务器&#xff0c;这些代理服务器在转发消息的时候会涉及到http的头部&#xff0c;下面将会介绍这些头部&#xff0c;包括由于存在这些代理服务器所以客户端和源服务器之前有许多的tcp连接&#xff0c;有一些http头部…

Flutter快学快用15 服务通信:Flutter 中常见的网络协议

上一课时之前&#xff0c;我们的接口都是在代码中模拟假数据&#xff0c;并没有从服务端获取数据&#xff0c;但是在实际开发中&#xff0c;必须与服务端进行交互。本课时主要介绍在 Flutter 中常见的网络传输协议序列化方式&#xff0c;并对其中比较常用的协议进行简单实践&am…