一、工作流引擎简介
工作流引擎的核心是将业务逻辑与流程控制逻辑分离。开发者无需在代码中硬编码复杂的流程跳转和状态判断,而是通过流程定义文件(如 BPMN 2.0 标准)来描述业务流程,由引擎来解析和执行这个流程。
1.1 核心价值:
- 可维护性:业务流程变更时,通常只需修改流程定义文件,无需改动代码。
- 可视化:使用 BPMN 2.0 等标准图表来描述流程,业务人员和技术人员可以更好地协作。
- 可追溯性:引擎会记录流程实例的完整执行路径,便于审计和问题排查。
- 灵活性:支持动态调整流程、跳转、回退等复杂场景。
1.2 主流 Java 工作流引擎
Activiti:
- 背景:由 Alfresco 公司开发,是 jBPM 的一个分支。
- 版本:目前主要有 Activiti 5/6/7 三个大版本。Activiti 7 与 Spring Boot 深度集成,提供了更云原生的部署方式。
- 特点:严格遵循 BPMN 2.0 标准,与 Spring 生态集成度极高,社区庞大,文档丰富。
Flowable:
- 背景:由 Activiti 的核心贡献者从 Activiti 项目 fork 出来,旨在提供一个更轻量、更快速、功能更丰富的版本。
- 特点:
- 性能优于 Activiti。
- 提供了更多开箱即用的高级功能,如历史数据归档、动态表格等。
- 同样严格遵循 BPMN 2.0 标准,并支持 CMMN(案例管理)和 DMN(决策规则)。
- 社区非常活跃,迭代速度快。
1.3 核心概念
无论选择哪个引擎,你都会接触到以下核心概念:
- 流程定义:业务流程的蓝图,通常是一个 .bpmn20.xml 文件,用 BPMN 2.0 XML 描述。
- 流程实例:一个正在运行的流程定义的实例。例如,一个请假流程定义,每个员工的每一次请假申请就是一个流程实例。
- 活动:流程中的一个步骤,如"用户任务"、"服务任务"、"网关"等。
- 用户任务:需要人工参与的任务,例如"经理审批"。引擎会将该任务分配给指定的用户或组。
- 服务任务:自动执行的任务,例如调用一个 Java 类或 HTTP API。
- 网关:用于控制流程的走向,如"排他网关"(类似 if-else)、"并行网关"(同时执行多个分支)。
1.4 工作流程
- 建模:使用图形化工具(如 Flowable Modeler, Camunda Modeler)或直接编写 BPMN 2.0 XML 来设计流程。
- 部署:将流程定义文件部署到引擎中。
- 启动实例:通过 API 启动一个流程实例。
- 查询任务:用户登录系统后,通过 API 查询分配给自己的任务。
- 处理任务:用户完成任务(如审批通过),引擎会自动推动流程到下一个节点。
- 监控:通过 API 或管理后台监控所有流程实例的状态。
二、Flowable 工作流引擎
2.1 Flowable 核心特性
- BPMN 2.0 标准支持:完整的业务流程建模与执行
- DMN 1.3 支持:决策规则引擎
- CMMN 1.1 支持:案例管理
- 轻量级设计:可嵌入到应用中,也可独立部署
- REST API:提供完整的 RESTful 接口
- Spring 集成:与 Spring 框架深度集成
- 多数据库支持:MySQL、PostgreSQL、Oracle 等
- 高性能:优化的流程执行引擎
2.2 Flowable 核心引擎组件
- Flowable Process Engine:核心流程引擎
- Flowable DMN Engine:决策规则引擎
- Flowable CMMN Engine:案例管理引擎
- Flowable Content Engine:内容管理引擎
- Flowable IDM Engine:身份管理引擎
2.3 Flowable 核心服务
java
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService(); // 流程部署
RuntimeService runtimeService = processEngine.getRuntimeService(); // 流程运行
TaskService taskService = processEngine.getTaskService(); // 任务管理
HistoryService historyService = processEngine.getHistoryService(); // 历史数据
IdentityService identityService = processEngine.getIdentityService(); // 用户管理
2.4 Flowable 核心表结构
- ACT_RE_*:资源存储(流程定义、图片等)
- ACT_RU_*:运行时数据(任务、变量等)
- ACT_HI_*:历史数据(已完成流程实例)
- ACT_GE_*:通用数据(变量、字节数组等)
三、Flowable 使用示例
3.1 环境配置
-
引入依赖
xml<dependency> <groupId>org.flowable</groupId> <artifactId>flowable-engine</artifactId> <version>6.7.2</version> </dependency> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring</artifactId> <version>6.7.2</version> </dependency> -
Spring Boot application.yml 配置:
yml# application.yml flowable: database-schema-update: true async-executor-activate: false history-level: audit
3.2 定义 BPMN 流程
创建一个请假审批流程 leave-request.bpmn20.xml:
xml
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.flowable.org/processdef">
<process id="leaveRequest" name="请假审批流程" isExecutable="true">
<!-- 开始事件 -->
<startEvent id="startEvent" name="开始请假">
<extensionElements>
<flowable:formProperty id="leaveType" name="请假类型"
type="enum" required="true">
<flowable:value id="sick" name="病假" />
<flowable:value id="annual" name="年假" />
</flowable:formProperty>
<flowable:formProperty id="days" name="请假天数"
type="long" required="true" />
<flowable:formProperty id="reason" name="请假事由"
type="string" />
</extensionElements>
</startEvent>
<!-- 用户任务 - 员工提交申请 -->
<userTask id="submitRequest" name="提交请假申请"
flowable:assignee="${applicant}">
<documentation>员工提交请假申请</documentation>
</userTask>
<!-- 排他网关 - 路由判断 -->
<exclusiveGateway id="decisionGateway" name="审批路由" />
<!-- 用户任务 - 经理审批 -->
<userTask id="managerApprove" name="经理审批"
flowable:candidateGroups="managers">
<extensionElements>
<flowable:formProperty id="managerApproved" name="是否批准"
type="enum" required="true">
<flowable:value id="true" name="批准" />
<flowable:value id="false" name="拒绝" />
</flowable:formProperty>
<flowable:formProperty id="managerComment" name="审批意见"
type="string" />
</extensionElements>
</userTask>
<!-- 用户任务 - HR备案(仅3天以上需要) -->
<userTask id="hrRecord" name="HR备案"
flowable:candidateGroups="hr">
<extensionElements>
<flowable:formProperty id="hrRecorded" name="是否已备案"
type="enum" required="true">
<flowable:value id="true" name="已备案" />
</flowable:formProperty>
</extensionElements>
</userTask>
<!-- 结束事件 -->
<endEvent id="endEvent" name="流程结束" />
<!-- 顺序流 -->
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitRequest" />
<sequenceFlow id="flow2" sourceRef="submitRequest" targetRef="decisionGateway" />
<!-- 条件顺序流 -->
<sequenceFlow id="flowToManager" sourceRef="decisionGateway" targetRef="managerApprove">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days <= 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flowToHR" sourceRef="decisionGateway" targetRef="hrRecord">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days > 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="hrRecord" targetRef="managerApprove" />
<!-- 审批结果流向 -->
<sequenceFlow id="flowApprove" sourceRef="managerApprove" targetRef="endEvent">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${managerApproved == 'true'}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flowReject" sourceRef="managerApprove" targetRef="endEvent">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${managerApproved == 'false'}]]>
</conditionExpression>
</sequenceFlow>
</process>
</definitions>
3.3 Java 代码实现
流程部署服务:
java
@Service
public class ProcessDeploymentService {
@Autowired
private RepositoryService repositoryService;
/**
* 部署请假流程
*/
public Deployment deployLeaveProcess() {
return repositoryService.createDeployment()
.addClasspathResource("processes/leave-request.bpmn20.xml")
.name("请假审批流程")
.category("HR")
.deploy();
}
}
流程启动服务:
java
@Service
@Transactional
public class LeaveProcessService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private IdentityService identityService;
/**
* 启动请假流程
*/
public ProcessInstance startLeaveProcess(String applicant,
String leaveType,
Long days,
String reason) {
// 设置流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", applicant);
variables.put("leaveType", leaveType);
variables.put("days", days);
variables.put("reason", reason);
variables.put("startTime", new Date());
// 启动流程实例
return runtimeService.startProcessInstanceByKey("leaveRequest", variables);
}
/**
* 提交请假申请
*/
public void submitLeaveRequest(String taskId, String applicant) {
// 认领任务
taskService.claim(taskId, applicant);
// 完成任务
taskService.complete(taskId);
}
/**
* 经理审批
*/
public void managerApprove(String taskId, Boolean approved, String comment) {
Map<String, Object> variables = new HashMap<>();
variables.put("managerApproved", approved.toString());
variables.put("managerComment", comment);
variables.put("approveTime", new Date());
taskService.complete(taskId, variables);
}
/**
* HR备案
*/
public void hrRecord(String taskId) {
Map<String, Object> variables = new HashMap<>();
variables.put("hrRecorded", "true");
variables.put("recordTime", new Date());
taskService.complete(taskId, variables);
}
}
任务查询服务:
java
@Service
public class TaskQueryService {
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 查询用户待办任务
*/
public List<Task> getUserTasks(String userId) {
return taskService.createTaskQuery()
.taskAssignee(userId)
.orderByTaskCreateTime().desc()
.list();
}
/**
* 查询组任务
*/
public List<Task> getGroupTasks(String group) {
return taskService.createTaskQuery()
.taskCandidateGroup(group)
.orderByTaskCreateTime().desc()
.list();
}
/**
* 查询流程历史
*/
public List<HistoricProcessInstance> getProcessHistory(String applicant) {
return historyService.createHistoricProcessInstanceQuery()
.variableValueEquals("applicant", applicant)
.orderByProcessInstanceStartTime().desc()
.list();
}
}
3.4 Spring Boot 控制器
java
@RestController
@RequestMapping("/api/leave")
public class LeaveProcessController {
@Autowired
private LeaveProcessService leaveProcessService;
@Autowired
private TaskQueryService taskQueryService;
@Autowired
private ProcessDeploymentService deploymentService;
/**
* 部署流程
*/
@PostMapping("/deploy")
public ResponseEntity<String> deployProcess() {
Deployment deployment = deploymentService.deployLeaveProcess();
return ResponseEntity.ok("流程部署成功,ID: " + deployment.getId());
}
/**
* 启动请假流程
*/
@PostMapping("/start")
public ResponseEntity<Map<String, Object>> startProcess(
@RequestParam String applicant,
@RequestParam String leaveType,
@RequestParam Long days,
@RequestParam String reason) {
ProcessInstance instance = leaveProcessService
.startLeaveProcess(applicant, leaveType, days, reason);
Map<String, Object> result = new HashMap<>();
result.put("processInstanceId", instance.getId());
result.put("processDefinitionId", instance.getProcessDefinitionId());
result.put("status", "流程已启动");
return ResponseEntity.ok(result);
}
/**
* 查询待办任务
*/
@GetMapping("/tasks")
public ResponseEntity<List<Map<String, Object>>> getTasks(
@RequestParam(required = false) String userId,
@RequestParam(required = false) String group) {
List<Map<String, Object>> tasks = new ArrayList<>();
if (userId != null) {
taskQueryService.getUserTasks(userId).forEach(task -> {
tasks.add(convertTaskToMap(task));
});
}
if (group != null) {
taskQueryService.getGroupTasks(group).forEach(task -> {
tasks.add(convertTaskToMap(task));
});
}
return ResponseEntity.ok(tasks);
}
/**
* 完成任务
*/
@PostMapping("/complete")
public ResponseEntity<String> completeTask(
@RequestParam String taskId,
@RequestParam(required = false) String userId,
@RequestParam(required = false) Boolean approved,
@RequestParam(required = false) String comment) {
if (userId != null) {
// 提交申请
leaveProcessService.submitLeaveRequest(taskId, userId);
} else if (approved != null) {
// 经理审批
leaveProcessService.managerApprove(taskId, approved, comment);
} else {
// HR备案
leaveProcessService.hrRecord(taskId);
}
return ResponseEntity.ok("任务完成");
}
private Map<String, Object> convertTaskToMap(Task task) {
Map<String, Object> taskMap = new HashMap<>();
taskMap.put("id", task.getId());
taskMap.put("name", task.getName());
taskMap.put("assignee", task.getAssignee());
taskMap.put("createTime", task.getCreateTime());
taskMap.put("processInstanceId", task.getProcessInstanceId());
return taskMap;
}
}
3.5 配置类
java
@Configuration
@EnableTransactionManagement
public class FlowableConfig {
@Bean
public ProcessEngineConfiguration processEngineConfiguration(DataSource dataSource) {
SpringProcessEngineConfiguration configuration =
new SpringProcessEngineConfiguration();
configuration.setDataSource(dataSource);
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
configuration.setAsyncExecutorActivate(false);
configuration.setHistoryLevel(HistoryLevel.AUDIT);
return configuration;
}
@Bean
public ProcessEngineFactoryBean processEngine(ProcessEngineConfiguration configuration) {
ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean();
factoryBean.setProcessEngineConfiguration(configuration);
return factoryBean;
}
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
@Bean
public IdentityService identityService(ProcessEngine processEngine) {
return processEngine.getIdentityService();
}
}
3.6 错误处理
java
@Service
public class ProcessErrorHandler {
@Autowired
private ManagementService managementService;
/**
* 处理作业失败
*/
public void handleFailedJob(String jobId) {
managementService.moveTimerToExecutableJob(jobId);
managementService.executeJob(jobId);
}
/**
* 流程异常处理
*/
public void handleProcessException(String processInstanceId, Exception e) {
// 记录错误日志
// 发送通知
// 执行补偿操作
}
}
四、流程监控和管理
4.1 使用 Flowable UI
Flowable 提供了基于 Spring Boot 的管理控制台:
xml
<!-- 添加 UI 依赖 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-admin</artifactId>
<version>6.7.2</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler</artifactId>
<version>6.7.2</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-task</artifactId>
<version>6.7.2</version>
</dependency>
4.2 自定义监控
java
@Service
public class ProcessMonitorService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private HistoryService historyService;
/**
* 获取流程实例状态
*/
public Map<String, Object> getProcessInstanceStatus(String processInstanceId) {
ProcessInstance instance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
HistoricProcessInstance historicInstance = historyService
.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
Map<String, Object> status = new HashMap<>();
if (instance != null) {
status.put("status", "RUNNING");
status.put("currentActivities", runtimeService.getActiveActivityIds(processInstanceId));
} else if (historicInstance != null) {
status.put("status", historicInstance.getEndTime() != null ? "COMPLETED" : "SUSPENDED");
status.put("endTime", historicInstance.getEndTime());
}
return status;
}
}