1. 为什么选择SpringBoot+Flowable组合
在开始构建订单审批工作流之前,我们需要先理解为什么这个技术组合如此适合此类场景。SpringBoot作为Java领域最流行的微服务框架,其自动配置和快速启动特性能够极大简化项目搭建过程。而Flowable作为Activiti的分支项目,是一个轻量级、高性能的BPMN 2.0流程引擎,特别适合嵌入到Java应用中。
我曾在多个电商项目中采用这个组合实现审批流程,实测下来发现几个显著优势:
- 开发效率极高 - SpringBoot的starter机制与Flowable的自动表创建配合,10分钟就能跑通第一个流程
- 资源占用低 - 相比完整BPM套件,嵌入式Flowable引擎内存占用通常不超过50MB
- 扩展性强 - 通过Spring的依赖注入可以轻松定制各个环节的行为
注意:虽然Activiti和Flowable系出同源,但Flowable的社区活跃度和文档质量目前更胜一筹,这也是我推荐它的主要原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础配置
2.1 项目初始化
使用IDEA创建SpringBoot项目时,需要勾选以下依赖:
- Spring Web (用于提供REST接口)
- Lombok (简化实体类编写)
- H2 Database (嵌入式数据库,适合演示)
- Flowable Starter
或者通过Maven手动添加:
xml复制<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.7.2</version>
</dependency>
2.2 数据库配置
虽然H2适合演示,但生产环境建议使用MySQL。在application.yml中配置:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/flowable-demo
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
flowable:
database-schema-update: true
关键点:
database-schema-update设置为true会让Flowable自动创建所需的23张表,首次启动时会看到大量建表日志。
3. 订单审批流程设计
3.1 BPMN流程图设计
使用Flowable Eclipse Designer或在线工具(如https://bpmn.io)设计审批流程。典型的订单审批包含以下节点:
- 开始事件(Start Event)
- 用户任务(User Task)-提交申请
- 排他网关(Exclusive Gateway)-金额判断
- 用户任务-经理审批(金额<5000)
- 用户任务-总监审批(金额≥5000)
- 结束事件(End Event)
将设计好的bpmn文件放在resources/processes目录下,SpringBoot会自动部署。
3.2 流程变量设计
审批流程中需要传递的关键变量:
- orderId (订单ID)
- applicant (申请人)
- amount (订单金额)
- approvalComment (审批意见)
在流程定义中可以这样使用:
xml复制<userTask id="managerApproval" name="经理审批"
flowable:assignee="${applicant.department.manager}">
<extensionElements>
<flowable:formProperty id="comment"
name="审批意见" type="string"/>
</extensionElements>
</userTask>
4. 核心业务实现
4.1 服务层开发
创建ProcessService处理流程操作:
java复制@Service
@RequiredArgsConstructor
public class OrderApprovalService {
private final RuntimeService runtimeService;
private final TaskService taskService;
public String startProcess(Order order) {
Map<String, Object> variables = new HashMap<>();
variables.put("orderId", order.getId());
variables.put("applicant", order.getApplicant());
variables.put("amount", order.getAmount());
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"orderApproval",
"ORDER_" + order.getId(),
variables
);
return instance.getId();
}
public void completeTask(String taskId, ApprovalVO vo) {
Map<String, Object> variables = new HashMap<>();
variables.put("approved", vo.isApproved());
variables.put("comment", vo.getComment());
taskService.complete(taskId, variables);
}
}
4.2 控制器开发
提供REST接口供前端调用:
java复制@RestController
@RequestMapping("/api/approval")
@RequiredArgsConstructor
public class ApprovalController {
private final OrderApprovalService approvalService;
@PostMapping("/start")
public String startApproval(@RequestBody Order order) {
return approvalService.startProcess(order);
}
@PostMapping("/complete/{taskId}")
public void completeTask(@PathVariable String taskId,
@RequestBody ApprovalVO vo) {
approvalService.completeTask(taskId, vo);
}
}
5. 进阶功能实现
5.1 动态任务分配
实际项目中,审批人往往需要动态确定。可以通过实现Flowable的TaskListener来实现:
java复制@Component
public class ManagerAssignmentListener implements TaskListener {
@Override
public void notify(DelegateTask task) {
String applicant = (String) task.getVariable("applicant");
User manager = userService.findManagerByUser(applicant);
task.setAssignee(manager.getUsername());
}
}
在bpmn中配置:
xml复制<userTask id="managerApproval" name="经理审批"
flowable:assignee="${initiator}">
<extensionElements>
<flowable:taskListener event="create"
class="com.example.listener.ManagerAssignmentListener"/>
</extensionElements>
</userTask>
5.2 审批历史查询
添加历史记录查询功能:
java复制public List<HistoricActivityInstance> getApprovalHistory(String processInstanceId) {
return historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceStartTime().asc()
.list();
}
6. 生产环境注意事项
6.1 性能优化建议
- 异步执行器配置:
yaml复制flowable:
async-executor-activate: true
async-executor-thread-pool-size: 10
- 历史级别设置:对于审批系统,建议使用
audit级别:
yaml复制flowable:
history-level: audit
6.2 常见问题排查
- 流程定义未部署:
- 检查bpmn文件是否在resources/processes目录下
- 检查文件名是否以.bpmn20.xml结尾
- 任务分配失败:
- 确认TaskListener是否被Spring管理
- 检查assignee表达式是否正确
- 变量获取为null:
- 确保在startProcessInstance时设置了所有必需变量
- 变量名区分大小写
7. 前端集成建议
虽然本文主要关注后端实现,但前端集成时需要注意:
- 任务列表接口:
java复制@GetMapping("/tasks")
public List<TaskRepresentation> getTasks(@RequestParam String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.list()
.stream()
.map(this::convertToRepresentation)
.collect(Collectors.toList());
}
- 流程图高亮显示:
java复制@GetMapping("/diagram/{processInstanceId}")
public void getDiagram(HttpServletResponse response,
@PathVariable String processInstanceId) throws IOException {
BpmnModel model = repositoryService.getBpmnModel(
runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult()
.getProcessDefinitionId()
);
ProcessDiagramGenerator diagramGenerator = processEngineConfiguration
.getProcessDiagramGenerator();
InputStream is = diagramGenerator.generateDiagram(model, "png",
runtimeService.getActiveActivityIds(processInstanceId));
IOUtils.copy(is, response.getOutputStream());
}
在实际项目中,我发现将Flowable Admin嵌入系统非常有用,可以通过添加以下依赖实现:
xml复制<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-rest</artifactId>
<version>${flowable.version}</version>
</dependency>
8. 测试策略
8.1 单元测试
使用Flowable的测试支持:
java复制@SpringBootTest
public class ApprovalProcessTest {
@Autowired
private RuntimeService runtimeService;
@Test
@Deployment(resources = "processes/order-approval.bpmn20.xml")
public void testApprovalProcess() {
Map<String, Object> vars = new HashMap<>();
vars.put("amount", 1000);
ProcessInstance instance = runtimeService
.startProcessInstanceByKey("orderApproval", vars);
assertThat(instance).isActive();
Task task = taskService.createTaskQuery()
.processInstanceId(instance.getId())
.singleResult();
assertThat(task.getName()).isEqualTo("经理审批");
}
}
8.2 集成测试
使用Testcontainers进行真实数据库测试:
java复制@Testcontainers
@SpringBootTest
public class ApprovalIntegrationTest {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0");
@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
registry.add("spring.datasource.username", mysql::getUsername);
registry.add("spring.datasource.password", mysql::getPassword);
}
// 测试方法
}
9. 监控与运维
9.1 监控端点
SpringBoot Actuator提供了Flowable监控端点:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,flowable
访问/actuator/flowable可以获取:
- 流程定义数量
- 运行中的流程实例
- 作业数量等关键指标
9.2 日志配置
建议为Flowable单独配置日志级别:
yaml复制logging:
level:
org.flowable: DEBUG
org.flowable.common.engine.impl.persistence.entity: WARN
10. 扩展思考
在实际项目中,我们还可以考虑以下扩展方向:
- 多级审批配置化:将审批规则存储在数据库,实现动态审批流
- 会签功能:需要多个审批人全部同意才能进入下一步
- 撤回功能:在特定条件下允许申请人撤回审批
- 超时自动处理:设置审批超时时间,超时后自动通过或拒绝
我曾在一个跨境电商项目中实现过动态多级审批,核心思路是:
- 将审批规则存储在mongodb
- 使用Flowable的ExecutionListener在流程运行时动态创建后续节点
- 通过历史记录实现完整的审批轨迹追踪
这种方案的优点是审批规则可以随时调整而无需重新部署流程定义,但实现复杂度较高,需要谨慎处理并发问题。
