Spring Boot + Flowable 工作流入门
一、整体架构与技术栈
技术栈
- 后端框架: Spring Boot 2.7+
- 工作流引擎: Flowable 6.8.0
- 数据库: MySQL 8.0
- 持久层: Spring Data JPA
- 接口规范: RESTful API
系统架构
┌─────────────┐ ┌───────────────┐ ┌─────────────┐
│ 客户端 │──HTTP─▶│ Spring Boot │───┬──▶│ Flowable │
│(Web/App) │◀──────│ 应用服务 │ │ │ 工作流引擎 │
└─────────────┘ └───────────────┘ │ └─────────────┘
│ │
│ ├──▶│ MySQL │
│ │ │ 数据库 │
│ │ └─────────────┘
└──────────┘
二、核心实现步骤
1. 项目依赖配置 (pom.xml)
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Flowable -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.8.0</version>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
2. 配置文件 (application.yml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/flowable_demo?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
flowable:
database-schema-update: true
async-executor-activate: true
history-level: audit
mail-server-host: smtp.example.com
mail-server-port: 587
mail-server-username: admin@example.com
mail-server-password: password
3. 流程定义 (leave-process.bpmn20.xml)
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="http://flowable.org/bpmn">
<process id="leaveApproval" name="请假审批流程">
<startEvent id="startEvent"/>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="applyTask"/>
<!-- 申请任务 -->
<userTask id="applyTask" name="请假申请" flowable:assignee="${applicant}">
<extensionElements>
<flowable:formProperty id="leaveDays" name="请假天数" type="long" required="true"/>
<flowable:formProperty id="reason" name="请假原因" type="string" required="true"/>
</extensionElements>
</userTask>
<sequenceFlow id="flow2" sourceRef="applyTask" targetRef="decision"/>
<!-- 决策网关 -->
<exclusiveGateway id="decision"/>
<sequenceFlow id="flowToManager" sourceRef="decision" targetRef="managerTask">
<conditionExpression xsi:type="tFormalExpression">${leaveDays > 3}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flowToHr" sourceRef="decision" targetRef="hrTask">
<conditionExpression xsi:type="tFormalExpression">${leaveDays <= 3}</conditionExpression>
</sequenceFlow>
<!-- 经理审批 -->
<userTask id="managerTask" name="经理审批" flowable:candidateGroups="managers">
<extensionElements>
<flowable:formProperty id="managerApproved" name="是否批准" type="boolean" required="true"/>
<flowable:formProperty id="managerComment" name="审批意见" type="string"/>
</extensionElements>
</userTask>
<sequenceFlow id="flow3" sourceRef="managerTask" targetRef="hrTask"/>
<!-- HR备案 -->
<userTask id="hrTask" name="HR备案" flowable:candidateGroups="hr">
<extensionElements>
<flowable:formProperty id="hrApproved" name="是否备案" type="boolean" required="true"/>
</extensionElements>
</userTask>
<sequenceFlow id="flow4" sourceRef="hrTask" targetRef="endEvent"/>
<endEvent id="endEvent"/>
</process>
</definitions>
三、核心功能实现
1. 流程服务类
@Service
public class WorkflowService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private RepositoryService repositoryService;
// 启动流程
public ProcessInstance startProcess(String processKey, Map<String, Object> variables) {
return runtimeService.startProcessInstanceByKey(processKey, variables);
}
// 查询用户任务
public List<Task> getUserTasks(String userId) {
return taskService.createTaskQuery()
.taskCandidateOrAssigned(userId)
.orderByTaskCreateTime().desc()
.list();
}
// 完成任务
public void completeTask(String taskId, Map<String, Object> variables) {
taskService.complete(taskId, variables);
}
// 查询历史流程
public List<HistoricProcessInstance> getHistoricProcesses(String userId) {
return historyService.createHistoricProcessInstanceQuery()
.involvedUser(userId)
.finished()
.orderByProcessInstanceEndTime().desc()
.list();
}
// 部署流程定义
public void deployProcess(String resourceName, InputStream inputStream) {
Deployment deployment = repositoryService.createDeployment()
.addInputStream(resourceName, inputStream)
.deploy();
}
}
2. 业务实体与关联
@Entity
@Table(name = "leave_application")
public class LeaveApplication {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String applicant;
private Integer leaveDays;
private String reason;
private LocalDateTime applyTime;
@Column(name = "process_instance_id")
private String processInstanceId;
// 省略 getter/setter
}
@Entity
@Table(name = "workflow_comment")
public class WorkflowComment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String taskId;
private String processInstanceId;
private String userId;
private String comment;
private LocalDateTime createTime;
// 省略 getter/setter
}
3. REST 控制器
@RestController
@RequestMapping("/api/workflow")
public class WorkflowController {
@Autowired
private WorkflowService workflowService;
@Autowired
private LeaveApplicationRepository leaveAppRepo;
// 启动请假流程
@PostMapping("/start-leave")
public ResponseEntity<?> startLeaveProcess(@RequestBody LeaveRequest request) {
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", request.getApplicant());
variables.put("leaveDays", request.getLeaveDays());
variables.put("reason", request.getReason());
ProcessInstance instance = workflowService.startProcess("leaveApproval", variables);
// 保存业务数据
LeaveApplication application = new LeaveApplication();
application.setApplicant(request.getApplicant());
application.setLeaveDays(request.getLeaveDays());
application.setReason(request.getReason());
application.setApplyTime(LocalDateTime.now());
application.setProcessInstanceId(instance.getId());
leaveAppRepo.save(application);
return ResponseEntity.ok(Collections.singletonMap("processInstanceId", instance.getId()));
}
// 获取用户任务
@GetMapping("/tasks")
public ResponseEntity<List<TaskRepresentation>> getUserTasks(@RequestParam String userId) {
List<Task> tasks = workflowService.getUserTasks(userId);
return ResponseEntity.ok(tasks.stream()
.map(this::convertToRepresentation)
.collect(Collectors.toList()));
}
// 完成任务
@PostMapping("/complete-task/{taskId}")
public ResponseEntity<?> completeTask(@PathVariable String taskId,
@RequestBody Map<String, Object> variables) {
workflowService.completeTask(taskId, variables);
return ResponseEntity.ok().build();
}
// 添加审批意见
@PostMapping("/add-comment")
public ResponseEntity<?> addComment(@RequestBody CommentRequest request) {
workflowService.addComment(request.getTaskId(), request.getUserId(), request.getComment());
return ResponseEntity.ok().build();
}
private TaskRepresentation convertToRepresentation(Task task) {
TaskRepresentation rep = new TaskRepresentation();
rep.setId(task.getId());
rep.setName(task.getName());
rep.setAssignee(task.getAssignee());
rep.setCreateTime(task.getCreateTime());
rep.setProcessInstanceId(task.getProcessInstanceId());
return rep;
}
// 内部DTO类
@Data
public static class TaskRepresentation {
private String id;
private String name;
private String assignee;
private Date createTime;
private String processInstanceId;
}
}
4. 监听器实现(任务分配、事件通知等)
@Component
public class TaskAssignmentListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
if ("create".equals(eventName)) {
String taskDefinitionKey = delegateTask.getTaskDefinitionKey();
// 自动分配任务
if ("managerTask".equals(taskDefinitionKey)) {
// 从数据库中获取经理
String manager = findManager(delegateTask.getProcessInstanceId());
delegateTask.setAssignee(manager);
}
// 发送任务通知
sendTaskNotification(delegateTask);
}
}
private String findManager(String processInstanceId) {
// 实现查找经理的逻辑
return "manager1";
}
private void sendTaskNotification(DelegateTask task) {
// 实现邮件或消息通知
System.out.println("新任务通知: " + task.getName() + " - 分配给: " + task.getAssignee());
}
}
四、Flowable 核心数据表说明
Flowable 使用约60张表,主要分为以下几类:
1. 通用数据表(ACT_GE_*)
ACT_GE_BYTEARRAY
:存储流程定义、流程资源等二进制数据ACT_GE_PROPERTY
:系统配置属性表
2. 流程定义表(ACT_RE_*)
ACT_RE_DEPLOYMENT
:部署信息表ACT_RE_PROCDEF
:流程定义表(存储BPMN流程定义)ACT_RE_MODEL
:模型表(存储流程设计器设计的模型)
3. 运行时表(ACT_RU_*)
ACT_RU_EXECUTION
:流程执行实例表ACT_RU_TASK
:任务表(当前任务)ACT_RU_VARIABLE
:流程变量表ACT_RU_IDENTITYLINK
:任务参与者表
4. 历史表(ACT_HI_*)
ACT_HI_PROCINST
:历史流程实例表ACT_HI_TASKINST
:历史任务表ACT_HI_ACTINST
:历史活动表(每个步骤)ACT_HI_VARINST
:历史变量表ACT_HI_DETAIL
:历史详情表
5. 身份表(ACT_ID_*)
ACT_ID_USER
:用户表ACT_ID_GROUP
:用户组表ACT_ID_MEMBERSHIP
:用户-组关系表
五、常见功能实现方案
1. 会签(多实例任务)
BPMN 配置:
<userTask id="multiInstanceTask" name="会签审批">
<multiInstanceLoopCharacteristics
isSequential="false"
flowable:collection="${participants}"
flowable:elementVariable="participant">
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
Java 代码:
// 启动流程时设置参与者
List<String> participants = Arrays.asList("user1", "user2", "user3", "user4");
variables.put("participants", participants);
runtimeService.startProcessInstanceByKey("multiInstanceProcess", variables);
2. 动态分配任务处理人
方案1:使用任务监听器
public class DynamicAssignmentListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
String manager = findManagerBasedOnRules(delegateTask);
delegateTask.setAssignee(manager);
}
}
方案2:使用表达式
<userTask id="dynamicTask" name="动态分配任务"
flowable:assignee="${taskAssignmentService.findAssignee(execution)}"/>
3. 流程跳转(自由跳转)
public void jumpToActivity(String processInstanceId, String targetActivityId) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
Execution execution = runtimeService.createExecutionQuery()
.processInstanceId(processInstanceId)
.activityId(processInstance.getActivityId())
.singleResult();
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(processInstanceId)
.moveExecutionToActivityId(execution.getId(), targetActivityId)
.changeState();
}
4. 流程版本控制
// 部署新版本
repositoryService.createDeployment()
.addClasspathResource("new-process.bpmn20.xml")
.deploy();
// 查询流程定义
List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myProcess")
.orderByProcessDefinitionVersion().desc()
.list();
5. 流程监控与统计
// 获取活跃流程实例
List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
.processDefinitionKey("leaveApproval")
.active()
.list();
// 获取任务统计
List<TaskCount> taskCounts = taskService.createTaskQuery()
.taskCandidateGroup("managers")
.list()
.stream()
.collect(Collectors.groupingBy(Task::getName, Collectors.counting()))
.entrySet().stream()
.map(entry -> new TaskCount(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
六、最佳实践与优化建议
-
数据库优化:
- 定期清理历史数据
- 为常用查询字段添加索引(如PROC_INST_ID_, TASK_DEF_KEY_)
- 分离业务数据与流程数据存储
-
流程设计原则:
- 保持流程简单,避免过度复杂的分支
- 使用子流程封装可重用逻辑
- 为关键节点添加超时处理
-
性能优化:
- 控制流程变量大小,避免存储大对象
- 使用异步服务任务处理耗时操作
- 合理配置历史级别(audit通常足够)
-
安全实践:
- 使用Spring Security保护Flowable REST API
- 实现细粒度的数据权限控制
- 定期审计流程操作日志
-
异常处理:
- 实现全局异常处理器
- 使用边界错误事件捕获流程异常
- 记录详细的错误日志
七、总结
Spring Boot与Flowable的结合为构建企业级工作流应用提供了强大而灵活的解决方案。本文详细介绍了从环境搭建到核心功能实现的完整流程,包括:
- Spring Boot集成Flowable的配置方法
- BPMN流程设计与部署
- 核心API的使用与封装
- Flowable数据表结构与功能
- 常见工作流场景的实现方案
- 性能优化与最佳实践
通过合理设计流程、优化数据库访问以及实现必要的监控机制,可以构建出高性能、高可靠性的工作流系统,满足企业复杂的业务流程管理需求。