当前位置: 首页 > 后端技术 > Java

告别ifelse!试试这个轻量级的流程引擎,内置的IDEA插件真的很好吃!

时间:2023-04-02 00:13:20 Java

我们平时做项目的时候,经常会遇到复杂的业务逻辑。如果我们用ifelse来实现,往往很冗长,维护成本也很高。今天给大家推荐LiteFlow,一个轻量级的流程引擎,可以优雅的实现复杂的业务逻辑。本文将以电子商务项目中订单价格的计算为例,谈谈其使用。SpringBoot实战电商项目商城(50k+star)地址:https://github.com/macrozheng/mallLiteFlow简介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关键字。THEN(a,b,c,d);并行安排如果想并行执行a,b,c三个组件,可以使用WHEN关键字。WHEN(a,b,c);选择布局如果想在代码中实现切换逻辑,比如通过组件a的返回结果判断,如果组件nameb返回如果执行b组件,可以使用SWITCH关键字。SWITCH(a).to(b,c,d);条件安排如果想在代码中实现if逻辑,比如x组件返回时执行atrue,您可以使用IF关键字。IF(x,a);如果要实现if的三元运算符逻辑,比如x组件返回true时执行a组件,执行b组件当它返回false时。一个规则文件可以写成如下。IF(x,a,b);如果要实现ifelse逻辑,可以使用ELSE关键字,相当于上面的实现。IF(x,a).ELSE(b);如果要实现elseif逻辑,可以使用ELIF关键字。IF(x1,a).ELIF(x2,b).ELSE(c);使用子流程当一些流程比较复杂的时候,我们可以定义子流程,并且那么在主流程中引用,逻辑会更清晰。比如我们有如下子流程来执行C和D组件。THEN(C,D);那么我们就可以在主进程中直接引用子进程了。THEN(A,B,subChain,E);在使用和学习正则表达式后,我们发现LiteFlow只需几个关键字就可以实现复杂的流程。下面我们以订单价格的计算为例来实践一下LiteFlow的流程引擎框架。首先,我们需要在项目中集成LiteFlow。这里我们以SpringBoot应用为例,只需要在pom.xml中添加如下依赖即可;com.yomahubliteflow-spring-boot-starter2.8.5接下来修改配置文件application.yml并配置LiteFlow规则文件路径;server:port:8580liteflow:#rule文件路径rule-source:liteflow/*.el.xml这里直接使用官方的LiteFlowdemo。本案例是一个价格计算引擎,模拟电子商务中订单价格的计算,提供简单的接口。下载地址如下:https://gitee.com/bryan31/lit...下载完成后直接运行demo,通过以下地址访问测试页面:http://localhost:8580中这种情况下,可以通过传入的订单数据计算出订单的最终价格。这涉及会员折扣、促销折扣、优惠券抵扣、运费计算等操作。有十几步之多。不使用流程引擎实现起来非常复杂。以下是订单价格计算中各组成部分的执行流程图;下面我们就来说说如何使用LiteFlow来实现这个功能。首先,我们需要定义每个组件。普通组件需要继承NodeComponent并实现process()方法。比如这里的优惠券扣费组件需要设置@Component注解Name,可以通过重写isAccess方法来决定是否执行该组件;/***优惠券扣减计算组件*/@Component("couponCmp")publicclassCouponCmpextendsNodeComponent{@Overridepublicvoidprocess()throwsException{PriceContextcontext=this.getContextBean(PriceContext.class);/**这里Mock下根据couponId获取的优惠券面值为15元**/longcouponId=context.getCouponId();BigDecimal优惠券价格=newBigDecimal(15);BigDecimalprePrice=context.getLastestPriceStep().getCurrPrice();BigDecimalcurrPrice=prePrice.subtract(couponPrice);context.addPriceStep(newPriceStepVO(PriceTypeEnum.COUPON_DISCOUNT,couponId.toString(),prePrice,currPrice.subtract(prePrice),currNamegetONDISCOUNTISCOUP()));}@OverridepublicbooleanisAccess(){PriceContextcontext=this.getContextBean(PriceContext.class);if(context.getCouponId()!=null){返回真;}else{返回错误;还有一些比较特殊的组件,比如用于判断是按照国内运费计算规则还是按照海外规则计算的condition组件,需要继承NodeSwitchComponent并实现processSwitch()方法;/***货运条件组件*/@Component("postageCondCmp")publicclassPostageCondCmp扩展NodeSwitchComponent{@OverridepublicStringprocessSwitch()throwsException{PriceContextcontext=this.getContextBean(PriceContext.class);//根据参数overseas判断是否海外购买,去对应组件booleanoverseas=context.isOversea();如果(海外){返回“overseaPostageCmp”;}else{返回“postageCmp”;}}}其他组件逻辑请参考demo源码。促销计算子流程;THEN(fullCutCmp,fullDiscountCmp,rushBuyCmp);然后就是整个流程了,大家可以对比上面的流程图,基本上可以用LiteFlow来画流程图;THEN(checkCmp,slotInitCmp,priceStepInitCmp,promotionConvertCmp,memberDiscountCmp,promotionChain,couponCmp,SWITCH(postageCondCmp).to(postageCmp,overseasPostageCmp),priceResultCmp,stepPrintCmp);最后在Controller中添加一个接口获取传入的订单数据,然后调用FlowExecutor类的execution方法即可;@ControllerpublicclassPriceExampleController{@ResourceprivateFlowExecutorflowExecutor;@RequestMapping(value="/submit",method=RequestMethod.POST)@ResponseBodypublicStringsubmit(@Nullable@RequestBodyStringreqData){尝试{PriceCalcReqVOreq=JSON.parseObject(reqData,PriceCalcReqVO.class);LiteflowResponseresponse=flowExecutor.execute2Resp("mainChain",req,PriceContext.class);返回response.getContextBean(PriceContext.class).getPrintLog();}catch(Throwablet){t.printStackTrace();返回“错误”;}}}我们平时写复杂代码的时候,往往会在下一步中用到上一步的结果。但是使用LiteFlow后,组件中没有传参,所以每个进程中的参数都是这么处理的?其实在LiteFlow中有一个context的概念,流程中的所有数据都统一存储在这里,比如上面的PriceContext类;publicclassPriceContext{/***订单号*/privateStringorderNo;/***是否海外采购*/privatebooleanoverseas;/***产品包*/privateListproductPackList;/***下单通道*/privateOrderChannelEnumorderChannel;/***会员代码*/privateStringmemberCode;/***优惠券*/privateLongcouponId;/***促销信息*/privateListpromotionPackList;/***价格步长*/privateListpriceStepList=newArrayList<>();/***原始订单价格*/privateBigDecimaloriginalOrderPrice;/***最终订单价格*/privateBigDecimalfinalOrderPrice;/***steplog*/privateStringprintLog;}在初始化上下文的slotInitCmp组件中,我们已经从getRequestData()方法中获取到了请求的订单参数,然后将它们设置到PriceContext上下文中,还存储过程中的其他参数和结果的位置/***插槽初始化组件*/@Component("slotInitCmp")publicclassSlotInitCmpextendsNodeComponent{@Overridepublicvoidprocess()throwsException{//插槽的冗余主要参数PriceCalcReqVOreq=this.getRequestData();PriceContextcontext=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());}@OverridepublicbooleanisAccess(){PriceCalcReqVOreq=this.getSlot().getRequestData();如果(要求!=null){返回真;}else{返回错误;}}}总结LiteFlow确实是一个简单易用的轻量级流程引擎,可以让复杂的业务逻辑变得清晰,方便代码维护。与其他流程引擎相比,它的规则文件编写起来要容易得多,几分钟就可以上手。有兴趣的朋友可以试试!参考官网??文档:https://liteflow.yomahub.com/项目源码地址https://gitee.com/dromara/lit...