在电商、支付类系统中,价格计算、订单创建、优惠叠加等业务流程往往步骤多、规则变动频繁。传统硬编码会出现大量if-else嵌套、长方法臃肿、改流程就要改代码重部署等问题,维护成本极高。
LiteFlow 是一款轻量级、高性能的 Java 流程编排规则引擎,主打组件化拆分、规则化编排、支持热更新 ,非常适合支付、订单、营销这类流程多变的业务场景。本文结合商品价格计算实战案例,从原理、核心语法、代码实现到运行演示,完整讲解 LiteFlow 在支付场景下的落地使用。
一、LiteFlow 核心介绍
1. 什么是 LiteFlow
LiteFlow 是面向业务步骤编排 的规则引擎,区别于 Flowable、Activiti 这类偏重人工审批的工作流框架。它的核心思想是将业务步骤拆分为独立组件,通过 EL 表达式编排执行流程,规则与代码解耦。
核心优势:
-
组件解耦:每个业务步骤独立成组件,单一职责,可复用、易测试;
-
规则驱动:流程顺序、分支、并行全部由配置文件定义,调整流程无需改动业务代码;
-
秒级热更新:支持本地文件、配置中心、数据库存储规则,线上修改规则即时生效,无需重启服务;
-
语法简洁:内置串行、并行、条件分支、循环、异常捕获等丰富 EL 语法,覆盖绝大多数业务场景。
2. 核心设计思想:工作台模式
理解 LiteFlow 只需抓住三个核心概念:
-
组件(Component):对应每一个独立业务逻辑,如同 "工人",只负责自身逻辑;
-
上下文(Context):全局唯一的数据载体,相当于 "工作台",所有组件的数据交互都通过上下文完成,组件之间互不直接调用;
-
规则(Rule):定义组件的执行顺序、逻辑关系,相当于 "工序流程"。
这套模式彻底实现逻辑解耦,新增、删除、调整步骤都不会影响原有组件。
3. 主流框架对比
|-------------------|----------|-------------------------|
| 框架 | 定位 | 适用场景 |
| LiteFlow | 业务流程编排引擎 | 价格计算、订单链路、风控、营销规则等步骤型流程 |
| Flowable/Activiti | 工作流引擎 | 人工审批、任务流转、多级工单 |
| Drools | 规则推理引擎 | 复杂条件判断、策略推理,语法较重 |
| 纯硬编码 / 策略模式 | 原生代码实现 | 简单固定流程,流程极少变更 |
总结 :支付、订单这类步骤多、变动频繁的流程,优先选择 LiteFlow。
4. 核心 EL 关键字(常用)
LiteFlow 通过极简 EL 表达式编排流程,掌握以下关键字即可覆盖 90% 业务场景:
-
THEN:串行执行,组件按顺序依次运行; -
WHEN:并行执行,多个组件同时运行; -
IF:条件分支,满足条件走第一个分支,否则走第二个分支; -
SWITCH:路由分支,根据结果路由到不同组件; -
FOR/ITERATOR:循环执行; -
CATCH:异常捕获、重试、兜底降级。
二、组件生命周期四大法宝(你要补充的重点)
在实际支付、订单类业务中,我们经常需要:
-
组件执行成功后做一些收尾工作
-
组件异常时统一日志、告警
-
根据业务条件跳过某些组件
-
组件报错后是否继续执行后续流程
LiteFlow 提供 4 个非常实用的方法:
1. onSuccess()
组件执行成功后自动调用,适合:记录日志、埋点、数据后置处理。
2. onError(Exception e)
组件抛出异常时自动调用,适合:异常日志、告警、上报、数据回滚。
3. isAccess()
控制组件是否执行:
-
return true → 执行
-
return false → 直接跳过该组件
非常适合优惠活动开关、会员等级判断、功能灰度。
4. isContinueOnError()
组件报错后流程是否继续往下走:
-
return true(默认):报错后继续执行后续组件
-
return false:报错后立即终止整个流程
支付、风控场景必须用它保证数据安全。
三、业务场景说明
本文以电商支付价格计算为实战场景,完整流程如下:
-
calcBase:计算商品基础价格; -
isVip:判断用户是否为 VIP,走不同优惠逻辑;-
VIP 用户:执行
vipDiscount(VIP 折扣); -
普通用户:执行
couponDiscount(优惠券抵扣);
-
-
并行执行两个优惠计算:
fullReduction(满减)、pointsDeduct(积分抵扣); -
calcFinal:汇总所有优惠,计算最终支付价格。
对应 LiteFlow 规则表达式:
THEN(
calcBase,
IF(isVip, vipDiscount, couponDiscount),
WHEN(fullReduction, pointsDeduct),
calcFinal
)
四、完整代码实现
1. 项目环境与依赖
java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/>
</parent>
<groupId>com.pay</groupId>
<artifactId>liteflow-price-demo</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2. 配置 application.yml
java
liteflow:
rule-source: config/flow.el.xml
print-banner: false
3. 流程上下文 OrderContext
java
import com.yomahub.liteflow.context.ContextBean;
import lombok.Data;
@Data
@ContextBean("orderCtx")
public class OrderContext {
private Integer originalPrice;
private boolean isVip;
private Integer basePrice;
private Integer discountPrice;
private Integer fullReduce;
private Integer pointsDeduct;
private Integer finalPrice;
}
五、带生命周期的完整组件(你要的 4 个方法全部加上)
我以 calcBase 组件为例,把 4 个方法完整写进去,其他组件同理可复用。
1. calcBase 计算基础价格(完整版生命周期)
java
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("calcBase")
public class CalcBaseCmp extends NodeComponent {
@Override
public void process() {
OrderContext ctx = getContextBean(OrderContext.class);
ctx.setBasePrice(ctx.getOriginalPrice());
System.out.println("1. 计算基础价格:" + ctx.getBasePrice());
}
// ==================== 生命周期 4 大方法 ====================
/**
* 组件执行成功后回调
*/
@Override
public void onSuccess() {
System.out.println("[生命周期] calcBase 执行成功");
}
/**
* 组件异常时回调
*/
@Override
public void onError(Exception e) {
System.err.println("[生命周期] calcBase 执行失败:" + e.getMessage());
}
/**
* 组件是否执行:返回 false 则跳过
*/
@Override
public boolean isAccess() {
OrderContext ctx = getContextBean(OrderContext.class);
// 示例:原价不为空才执行
boolean canExec = ctx.getOriginalPrice() != null && ctx.getOriginalPrice() > 0;
System.out.println("[生命周期] calcBase 是否允许执行:" + canExec);
return canExec;
}
/**
* 报错后是否继续执行整个流程
* true:继续(默认)
* false:立即终止流程(支付场景常用)
*/
@Override
public boolean isContinueOnError() {
// 基础价格计算失败,整个支付必须终止
return false;
}
}
2. isVip 判断组件
java
import com.yomahub.liteflow.core.NodeIfComponent;
import org.springframework.stereotype.Component;
@Component("isVip")
public class IsVipCmp extends NodeIfComponent {
@Override
public boolean processIf() {
OrderContext ctx = getContextBean(OrderContext.class);
System.out.println("2. 判断是否VIP:" + ctx.isVip());
return ctx.isVip();
}
}
3. vipDiscount VIP 折扣
java
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("vipDiscount")
public class VipDiscountCmp extends NodeComponent {
@Override
public void process() {
OrderContext ctx = getContextBean(OrderContext.class);
int price = (int) (ctx.getBasePrice() * 0.8);
ctx.setDiscountPrice(price);
System.out.println("3. VIP折扣后价格:" + price);
}
}
4. couponDiscount 优惠券
java
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("couponDiscount")
public class CouponDiscountCmp extends NodeComponent {
@Override
public void process() {
OrderContext ctx = getContextBean(OrderContext.class);
int price = ctx.getBasePrice() - 50;
ctx.setDiscountPrice(price);
System.out.println("3. 优惠券抵扣后价格:" + price);
}
}
5. fullReduction 满减
java
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("fullReduction")
public class FullReduceCmp extends NodeComponent {
@Override
public void process() {
OrderContext ctx = getContextBean(OrderContext.class);
int reduce = ctx.getDiscountPrice() >= 200 ? 30 : 0;
ctx.setFullReduce(reduce);
System.out.println("4. 满减金额:" + reduce);
}
}
6. pointsDeduct 积分抵扣
java
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("pointsDeduct")
public class PointsDeductCmp extends NodeComponent {
@Override
public void process() {
OrderContext ctx = getContextBean(OrderContext.class);
ctx.setPointsDeduct(20);
System.out.println("4. 积分抵扣:20");
}
}
7. calcFinal 最终价格
java
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("calcFinal")
public class CalcFinalCmp extends NodeComponent {
@Override
public void process() {
OrderContext ctx = getContextBean(OrderContext.class);
int finalPrice = ctx.getDiscountPrice() - ctx.getFullReduce() - ctx.getPointsDeduct();
ctx.setFinalPrice(finalPrice);
System.out.println("5. 最终支付价格:" + finalPrice);
}
}
六、规则文件 flow.el.xml
java
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="priceChain">
THEN(
calcBase,
IF(isVip, vipDiscount, couponDiscount),
WHEN(fullReduction, pointsDeduct),
calcFinal
)
</chain>
</flow>
七、启动测试类
java
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class LiteFlowPriceApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(LiteFlowPriceApplication.class, args);
FlowExecutor executor = context.getBean(FlowExecutor.class);
OrderContext ctx = new OrderContext();
ctx.setOriginalPrice(300);
ctx.setVip(true);
LiteflowResponse response = executor.execute2Resp("priceChain", null, ctx);
OrderContext result = response.getContextBean(OrderContext.class);
System.out.println("\n===== 支付结果 =====");
System.out.println("最终支付:" + result.getFinalPrice());
}
}
八、运行效果(包含生命周期日志)
java
[生命周期] calcBase 是否允许执行:true
1. 计算基础价格:300
[生命周期] calcBase 执行成功
2. 判断是否VIP:true
3. VIP折扣后价格:240
4. 满减金额:30
4. 积分抵扣:20
5. 最终支付价格:190
===== 支付结果 =====
最终支付:190
九、生命周期 4 个方法总结(博客必备)
1. onSuccess()
组件执行成功后自动调用,用于日志、埋点、数据收尾。
2. onError(Exception e)
组件抛出异常时自动调用,用于异常日志、告警、回滚。
3. isAccess()
返回 true 执行,返回 false 跳过。 适合:活动开关、会员权限、功能灰度、条件执行。
4. isContinueOnError()
-
false :组件报错(异常) → 整个流程立即终止(支付、风控、订单必须用)
-
true :组件报错(异常) → 继续执行后续流程(日志、通知类可用)