在企业应用中,业务流程管理(BPM) 是核心基础设施之一:请假审批、订单审核、采购流程、财务核销等都需要灵活可变的流程支持。硬编码流程的方式在需求变更时成本极高,而 BPMN 2.0 工作流引擎通过可视化建模 + 运行时解释执行,实现了业务逻辑与流程编排的分离。
1. BPMN 2.0 核心概念
1.1 基本元素
| 元素类型 | 图标 | 用途 | XML 标签 |
|---|---|---|---|
| 开始事件 | ○ | 流程起点 | <startEvent> |
| 结束事件 | ◉ | 流程终点 | <endEvent> |
| 用户任务 | ▭ | 需要人工处理 | <userTask> |
| 服务任务 | ▭+齿轮 | 自动执行的服务 | <serviceTask> |
| 网关 | ◇ | 分支/汇聚 | <exclusiveGateway> |
| 顺序流 | → | 流转方向 | <sequenceFlow> |
| 子流程 | ▭+框 | 嵌套流程 | <subProcess> |
1.2 网关类型
| 网关 | 行为 | 使用场景 |
|---|---|---|
| 排他网关 (XOR) | 只走一条满足条件的分支 | 条件审批:金额>1万走高级审批 |
| 并行网关 (AND) | 所有分支同时执行 | 会签:需要多部门同时审批 |
| 包容网关 (OR) | 走所有满足条件的分支 | 可选审批:满足的条件都走 |
| 事件网关 | 基于事件选择分支 | 等待多个外部事件之一 |
2. Flowable 架构
2.1 核心引擎
ProcessEngine TaskService
├── RepositoryService ← 部署流程定义 ├── 查询任务
├── RuntimeService ← 启动/控制流程实例 ├── 完成任务
├── TaskService ← 用户任务管理 └── 委派/转办
├── HistoryService ← 历史数据查询
├── ManagementService ← 引擎管理
└── IdentityService ← 用户/组管理
2.2 数据模型
| 表前缀 | 用途 |
|---|---|
ACT_RE_* | Repository: 流程定义、部署 |
ACT_RU_* | Runtime: 运行时实例、任务、变量 |
ACT_HI_* | History: 历史流程、活动、变量 |
ACT_ID_* | Identity: 用户、组、成员关系 |
ACT_EVT_LOG | 事件日志 |
2.3 Spring Boot Starter 整合
<dependencies>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.8.0</version>
</dependency>
</dependencies>
spring:
flowable:
database-schema-update: true # 自动建表
async-executor-activate: true # 启用异步执行器
history-level: full # 完整历史记录
db-history-used: true
3. 流程定义与部署
3.1 请假流程 BPMN 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://mycompany.com/leave">
<process id="leaveApproval" name="请假审批流程">
<!-- 开始 -->
<startEvent id="start" />
<sequenceFlow sourceRef="start" targetRef="applyTask" />
<!-- 申请人填写 -->
<userTask id="applyTask" name="填写请假申请"
flowable:assignee="${applicant}"
flowable:formKey="leaveForm">
<extensionElements>
<flowable:taskListener event="complete"
class="com.myapp.listener.CalcDurationListener" />
</extensionElements>
</userTask>
<sequenceFlow sourceRef="applyTask" targetRef="decisionGateway" />
<!-- 排他网关:根据天数判断 -->
<exclusiveGateway id="decisionGateway" />
<sequenceFlow sourceRef="decisionGateway" targetRef="managerTask">
<conditionExpression xsi:type="tFormalExpression">
${days <= 3}
</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decisionGateway" targetRef="directorTask">
<conditionExpression xsi:type="tFormalExpression">
${days > 3}
</conditionExpression>
</sequenceFlow>
<!-- 经理审批(<=3天) -->
<userTask id="managerTask" name="部门经理审批"
flowable:candidateGroups="managers">
<extensionElements>
<flowable:taskListener event="create"
class="com.myapp.listener.NotifyManagerListener" />
</extensionElements>
</userTask>
<sequenceFlow sourceRef="managerTask" targetRef="exclusiveGateway2" />
<!-- 总监审批(>3天) -->
<userTask id="directorTask" name="总监审批"
flowable:candidateGroups="directors" />
<sequenceFlow sourceRef="directorTask" targetRef="exclusiveGateway2" />
<!-- 汇聚网关 -->
<exclusiveGateway id="exclusiveGateway2" />
<sequenceFlow sourceRef="exclusiveGateway2" targetRef="hrTask" />
<!-- HR 备案 -->
<userTask id="hrTask" name="HR 备案"
flowable:candidateGroups="hr" />
<sequenceFlow sourceRef="hrTask" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
3.2 部署流程
@Service
public class ProcessDeploymentService {
@Autowired private RepositoryService repositoryService;
public void deploy() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave-approval.bpmn20.xml")
.addClasspathResource("processes/leave-form.form") // 可选:表单定义
.name("请假审批流程v1.0")
.category("hr")
.deploy();
System.out.println("部署ID: " + deployment.getId());
System.out.println("部署时间: " + deployment.getDeploymentTime());
}
}
4. 流程运行时API
4.1 启动流程实例
@Service
public class LeaveService {
@Autowired private RuntimeService runtimeService;
@Autowired private TaskService taskService;
public String startLeaveProcess(String applicant, int days, String reason) {
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", applicant);
variables.put("days", days);
variables.put("reason", reason);
variables.put("startTime", new Date());
ProcessInstance instance = runtimeService
.startProcessInstanceByKey("leaveApproval", variables);
return instance.getId(); // 流程实例ID,后续查询用
}
}
4.2 查询与完成任务
@Service
public class TaskQueryService {
@Autowired private TaskService taskService;
// 查询待办任务
public List<Task> getTodoList(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee) // 指定执行人
.orderByTaskCreateTime().desc()
.list();
}
// 查询组任务
public List<Task> getGroupTasks(String groupId) {
return taskService.createTaskQuery()
.taskCandidateGroup(groupId)
.orderByTaskCreateTime().desc()
.list();
}
// 认领任务
public void claimTask(String taskId, String userId) {
taskService.claim(taskId, userId);
}
// 完成任务(带审批意见)
public void completeTask(String taskId, String userId, boolean approved, String comment) {
// 添加审批意见
taskService.addComment(taskId, null, approved ? "同意" : "驳回", comment);
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
variables.put("approver", userId);
variables.put("approveTime", new Date());
taskService.complete(taskId, variables);
}
// 转办任务
public void delegateTask(String taskId, String delegateTo) {
taskService.delegateTask(taskId, delegateTo);
}
// 驳回/退回上一步
public void rejectTask(String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
String processInstanceId = task.getProcessInstanceId();
// 获取历史节点,回退到申请节点
List<HistoricActivityInstance> list = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.activityType("userTask")
.orderByHistoricActivityInstanceStartTime().desc()
.list();
String targetNodeId = list.size() > 1 ? list.get(1).getActivityId() : null;
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(processInstanceId)
.moveActivityIdTo(task.getTaskDefinitionKey(), targetNodeId)
.changeState();
}
}
4.3 流程变量与监听
// 全局变量监听
@Component
public class ProcessVariableListener implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
String eventName = execution.getEventName(); // start, end, take
String processInstanceId = execution.getProcessInstanceId();
Map<String, Object> variables = execution.getVariables();
log.info("流程[{}]事件[{}],变量: {}", processInstanceId, eventName, variables);
}
}
// 任务监听
@Component
public class NotifyManagerListener implements TaskListener {
@Autowired private NotificationService notificationService;
@Override
public void notify(DelegateTask delegateTask) {
String event = delegateTask.getEventName(); // create, assignment, complete
if ("create".equals(event)) {
String assignee = delegateTask.getAssignee();
String taskName = delegateTask.getName();
notificationService.send(assignee, "您有新的审批任务: " + taskName);
}
}
}
5. 事件驱动与异步处理
5.1 消息边界事件
<!-- 超时自动处理 -->
<userTask id="managerTask" name="经理审批">
<boundaryEvent id="timer" attachedToRef="managerTask" cancelActivity="true">
<timerEventDefinition>
<timeDuration>PT24H</timeDuration> <!-- 24小时超时 -->
</timerEventDefinition>
</boundaryEvent>
<sequenceFlow sourceRef="timer" targetRef="autoApproveTask" />
</userTask>
<serviceTask id="autoApproveTask" name="自动通过"
flowable:class="com.myapp.delegate.AutoApproveDelegate" />
5.2 信号与消息事件
// 发送信号(全局广播)
runtimeService.signalEventReceived("policyUpdated");
// 发送消息(定向)
runtimeService.messageEventReceived("paymentReceived",
runtimeService.createExecutionQuery()
.processInstanceBusinessKey(orderId)
.singleResult().getId());
<!-- 接收消息继续流程 -->
<intermediateCatchEvent id="messageCatch">
<messageEventDefinition messageRef="paymentReceived" />
</intermediateCatchEvent>
6. 动态表单与流程设计器
6.1 Flowable Modeler
Flowable 提供开源的 Web 流程设计器(基于 BPNM.js),支持:
- 拖拽式流程建模
- 表单设计器
- 决策表(DMN)设计
# 启用 Flowable UI
spring:
flowable:
modeler:
enabled: true
rest:
app:
admin:
user-id: admin
password: admin
first-name: Admin
6.2 表单引擎
{
"key": "leaveForm",
"name": "请假申请表单",
"fields": [
{
"id": "startDate",
"name": "开始日期",
"type": "date",
"required": true
},
{
"id": "endDate",
"name": "结束日期",
"type": "date",
"required": true
},
{
"id": "reason",
"name": "请假原因",
"type": "multi-line-text",
"required": true
},
{
"id": "type",
"name": "请假类型",
"type": "dropdown",
"options": [
{ "id": "annual", "name": "年假" },
{ "id": "sick", "name": "病假" },
{ "id": "personal", "name": "事假" }
]
}
]
}
7. 企业级扩展实践
7.1 多租户流程隔离
// 启动时设置租户ID
identityService.setAuthenticatedUserId(userId);
ProcessInstance instance = runtimeService
.createProcessInstanceBuilder()
.processDefinitionKey("leaveApproval")
.tenantId("tenant_" + companyId) // 租户隔离
.variables(variables)
.start();
7.2 流程版本控制
// 查询最新版本
ProcessDefinition latest = repositoryService
.createProcessDefinitionQuery()
.processDefinitionKey("leaveApproval")
.latestVersion()
.singleResult();
// 启动时指定版本
ProcessInstance instance = runtimeService
.startProcessInstanceById(latest.getId(), variables);
7.3 流程性能优化
| 优化点 | 建议 |
|---|---|
| 历史级别 | 非必要时 history-level: audit 而非 full |
| 异步执行 | 服务任务使用 flowable:async="true" |
| 批量部署 | 一次部署多个流程定义 |
| 定时清理 | 定期删除 6 个月前的历史数据 |
| 索引优化 | ACT_RU_TASK 的 ASSIGNEE_、PROC_INST_ID_ |
延伸阅读
- Java 异步与响应式编程 — 与事件驱动架构的对比
- Spring Cloud 微服务架构全家桶 — 分布式流程编排
- 监控诊断与可观测性 — 流程引擎监控
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。