在咱们平时做我的项目的时候,常常会遇到简单的业务逻辑,如果应用if else来实现的话,往往会很简短,保护老本也很高。明天给大家举荐一个轻量级流程引擎LiteFlow,能够优雅地实现简单的业务逻辑,本文将以电商我的项目中的订单价格计算为例来聊聊它的应用。

SpringBoot实战电商我的项目mall(50k+star)地址:https://github.com/macrozheng/mall

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: 8580liteflow:  #规定文件门路  rule-source: liteflow/*.el.xml
  • 这里间接应用LiteFlow官网的Demo,该案例为一个价格计算引擎,模仿了电商中对订单价格的计算,并提供了简略的界面,下载地址如下:
https://gitee.com/bryan31/lit...
  • 下载实现后,间接运行Demo,通过如下地址能够拜访测试页面:http://localhost:8580

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

  • 接下来咱们来聊聊如何应用LiteFlow来实现这个性能,首先咱们须要定义好各个组件,一般组件须要继承NodeComponent并实现process()办法,比方这里的优惠券抵扣组件,还需设置@Component注解的名称,能够通过重写isAccess办法来决定是否执行该组件;
/** * 优惠券抵扣计算组件 */@Component("couponCmp")public class CouponCmp extends NodeComponent {    @Override    public 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()));    }    @Override    public 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 {    @Override    public 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类的执行办法即可;
@Controllerpublic class PriceExampleController {    @Resource    private FlowExecutor flowExecutor;    @RequestMapping(value = "/submit", method = RequestMethod.POST)    @ResponseBody    public 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 {    @Override    public 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());    }    @Override    public boolean isAccess() {        PriceCalcReqVO req = this.getSlot().getRequestData();        if(req != null){            return true;        }else{            return false;        }    }}

总结

LiteFlow的确是一款好用的轻量级流程引擎,能够让简单的业务逻辑变得清晰起来,便于代码保护。它的规定文件比起其余流程引擎来说,编写简略太多了,几分钟就能上手,感兴趣的敌人能够尝试下它!

参考资料

官网文档:https://liteflow.yomahub.com/

我的项目源码地址

https://gitee.com/dromara/lit...