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

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

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

LiteFlow简介

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

LiteFlow主要特性如下:

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

IDEA插件

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

规则表达式

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

串行编排

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


    THEN(a, b, c, d);

复制代码

并行编排

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


    WHEN(a, b, c);

复制代码

选择编排

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


    SWITCH(a).to(b, c, d);

复制代码

条件编排

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


    IF(x, a);

复制代码

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


    IF(x, a, b);

复制代码

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


    IF(x, a).ELSE(b);

复制代码

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


    IF(x1, a).ELIF(x2, b).ELSE(c);

复制代码

使用子流程

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

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


  	THEN(C, D);

复制代码

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


    THEN(
    	A, B,
    	subChain,
    	E
    );

复制代码

使用

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


    com.yomahub
    liteflow-spring-boot-starter
    2.8.5

复制代码
server:
  port: 8580
liteflow:
  #规则文件路径
  rule-source: liteflow/*.el.xml
复制代码

gitee.com/bryan31/lit…

/**
 * 优惠券抵扣计算组件
 */
@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;
        }
    }
}
复制代码
/**
 * 运费条件组件
 */
@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";
        }
    }
}
复制代码
<?xml version="1.0" encoding="UTF-8"?>

    
        THEN(fullCutCmp, fullDiscountCmp, rushBuyCmp);
    

复制代码
<?xml version="1.0" encoding="UTF-8"?>

    
        THEN(
            checkCmp, slotInitCmp, priceStepInitCmp,
            promotionConvertCmp, memberDiscountCmp,
            promotionChain, couponCmp,
            SWITCH(postageCondCmp).to(postageCmp, overseaPostageCmp),
            priceResultCmp, stepPrintCmp
        );
    

复制代码
@Controller
public 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";
        }
    }
}
复制代码
public class PriceContext {

    /**
     * 订单号
     */
    private String orderNo;

    /**
     * 是否境外购
     */
    private boolean oversea;

    /**
     * 商品包
     */
    private List productPackList;

    /**
     * 订单渠道
     */
    private OrderChannelEnum orderChannel;

    /**
     * 会员CODE
     */
    private String memberCode;

    /**
     * 优惠券
     */
    private Long couponId;

    /**
     * 优惠信息
     */
    private List promotionPackList;

    /**
     * 价格步骤
     */
    private List priceStepList = new ArrayList<>();

    /**
     * 订单原始价格
     */
    private BigDecimal originalOrderPrice;

    /**
     * 订单最终价格
     */
    private BigDecimal finalOrderPrice;

    /**
     * 步骤日志
     */
    private String printLog;
}
复制代码
/**
 * 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确实是一款好用的轻量级流程引擎,可以让复杂的业务逻辑变得清晰起来,便于代码维护。它的规则文件比起其他流程引擎来说,编写简单太多了,几分钟就能上手,感兴趣的朋友可以尝试下它!

展开阅读全文

页面更新:2024-05-12

标签:流程   表达式   组件   插件   逻辑   订单   定义   规则   代码   引擎   文件   价格

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top