Flowable 流程引擎从入门到精通:基础概念与实践

601 阅读7分钟

Flowable 流程引擎从入门到精通:基础概念与实践

一、引言

在企业级应用开发中,我们常常会遇到这样的场景:需要为报销系统实现多级审批流程,为订单系统设计状态流转逻辑,或者为人力资源系统构建复杂的入职流程。传统的实现方式往往是在业务代码中硬编码流程逻辑,随着业务复杂度的提升,这种方式会导致代码变得冗长、难以维护,并且缺乏灵活性。

让我们看一个典型的问题场景:某企业的报销流程需要根据金额不同,分别由部门经理、财务总监和总经理审批。用传统方式实现可能会写出这样的代码:

// 传统方式实现报销审批流程(简化示例)
public void processReimbursement(ReimbursementForm form) {
    // 1. 提交申请
    saveForm(form);
    
    // 2. 根据金额判断审批人
    User approver = null;
    if (form.getAmount() < 1000) {
        approver = userService.getDeptManager(form.getApplicant().getDeptId());
    } else if (form.getAmount() < 5000) {
        approver = userService.getCfo();
    } else {
        approver = userService.getCeo();
    }
    
    // 3. 发送审批通知
    notificationService.sendApprovalRequest(approver, form);
    
    // 4. 等待审批结果
    ApprovalResult result = waitForApproval(approver.getId(), form.getId());
    
    // 5. 根据审批结果处理
    if (result.isApproved()) {
        if (form.getAmount() >= 5000) {
            // 总经理审批后还需财务确认
            User finance = userService.getFinanceStaff();
            notificationService.sendApprovalRequest(finance, form);
            result = waitForApproval(finance.getId(), form.getId());
            
            if (result.isApproved()) {
                processPayment(form);
            } else {
                rejectForm(form);
            }
        } else {
            processPayment(form);
        }
    } else {
        rejectForm(form);
    }
}

这种实现方式存在明显的问题:

  1. 流程逻辑与业务代码高度耦合,难以修改和扩展

  2. 复杂的条件判断使代码变得臃肿,可读性差

  3. 无法跟踪流程执行状态,难以进行监控和优化

  4. 当业务规则变化时,需要修改核心代码,风险高

而使用 Flowable 流程引擎,我们可以用更优雅的方式实现相同的功能:

// 使用Flowable实现报销审批流程
public class ReimbursementProcessService {

    @Autowired
    private RuntimeService runtimeService;
    
    @Autowired
    private TaskService taskService;
    
    public String startReimbursementProcess(ReimbursementForm form) {
        // 准备流程变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("applicantId", form.getApplicantId());
        variables.put("amount", form.getAmount());
        variables.put("reason", form.getReason());
        
        // 启动流程实例
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(
            "reimbursementProcess", variables);
            
        return instance.getId();
    }
    
    public void approveTask(String taskId, String userId, boolean approved, String comments) {
        // 验证任务归属
        Task task = taskService.createTaskQuery()
           .taskId(taskId)
           .taskAssignee(userId)
           .singleResult();
           
        if (task == null) {
            throw new IllegalArgumentException("任务不存在或无权处理");
        }
        
        // 设置审批结果
        Map<String, Object> variables = new HashMap<>();
        variables.put("approved", approved);
        variables.put("comments", comments);
        
        // 完成任务
        taskService.complete(taskId, variables);
    }
    
    // 其他流程操作方法...
}

对应的 BPMN 2.0 流程定义文件(简化):

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
    <process id="reimbursementProcess" name="报销审批流程">
        <startEvent id="startEvent"/>
        
        <!-- 提交申请 -->
        <userTask id="submitTask" name="提交报销申请">
            <extensionElements>
                <flowable:formProperty id="amount" type="double" required="true"/>
                <flowable:formProperty id="reason" type="string" required="true"/>
            </extensionElements>
        </userTask>
        
        <!-- 根据金额判断审批级别 -->
        <exclusiveGateway id="amountGateway" name="金额判断"/>
        
        <!-- 部门经理审批 -->
        <userTask id="deptManagerTask" name="部门经理审批">
            <extensionElements>
                <flowable:assignee>${deptManagerId}</flowable:assignee>
            </extensionElements>
        </userTask>
        
        <!-- 财务总监审批 -->
        <userTask id="cfoTask" name="财务总监审批">
            <extensionElements>
                <flowable:assignee>${cfoId}</flowable:assignee>
            </extensionElements>
        </userTask>
        
        <!-- 总经理审批 -->
        <userTask id="ceoTask" name="总经理审批">
            <extensionElements>
                <flowable:assignee>${ceoId}</flowable:assignee>
            </extensionElements>
        </userTask>
        
        <!-- 财务确认 -->
        <userTask id="financeTask" name="财务确认">
            <extensionElements>
                <flowable:assignee>${financeId}</flowable:assignee>
            </extensionElements>
        </userTask>
        
        <!-- 结束事件 -->
        <endEvent id="approveEnd" name="审批通过"/>
        <endEvent id="rejectEnd" name="审批拒绝"/>
        
        <!-- 连线定义 -->
        <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitTask"/>
        <sequenceFlow id="flow2" sourceRef="submitTask" targetRef="amountGateway"/>
        
        <!-- 金额条件分支 -->
        <sequenceFlow id="flow3" sourceRef="amountGateway" targetRef="deptManagerTask">
            <conditionExpression xsi:type="tFormalExpression">${amount &lt; 1000}</conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow4" sourceRef="amountGateway" targetRef="cfoTask">
            <conditionExpression xsi:type="tFormalExpression">${amount &lt; 5000}</conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow5" sourceRef="amountGateway" targetRef="ceoTask">
            <conditionExpression xsi:type="tFormalExpression">${amount &gt;= 5000}</conditionExpression>
        </sequenceFlow>
        
        <!-- 其他连线... -->
    </process>
</definitions>

通过对比可以看出,使用 Flowable 后:

  1. 流程逻辑与业务代码分离,流程变更只需修改 BPMN 文件

  2. 代码结构更清晰,核心业务逻辑得以简化

  3. 支持可视化流程设计,非技术人员也能参与流程优化

  4. 提供完整的流程监控和历史记录功能

  5. 便于实现复杂的流程模式,如并行处理、子流程等

作为一个在多个企业级项目中重度使用 Flowable 的开发者,我深知其强大之处。在接下来的内容中,我将深入讲解 Flowable 的各个方面,从基础概念到高级应用,帮助你快速掌握这一利器。

二、Flowable 技术架构解析

Flowable 是一个轻量级、高性能的开源流程引擎,其技术架构主要包含以下组件:

  1. 核心引擎:基于 Apache License 2.0 的开源实现,提供流程执行的核心能力

  2. Modeler:可视化流程设计工具,支持 BPMN 2.0 标准

  3. REST API:提供标准的 REST 接口,便于与其他系统集成

  4. 多种服务组件:包括 RepositoryService、RuntimeService、TaskService 等

  5. 扩展模块:支持 DMN 决策表、CMMN 案例管理等高级特性

与其他主流流程引擎相比,Flowable 具有更好的扩展性和社区活跃度,特别适合需要深度定制的企业级应用场景。

三、快速搭建开发环境

以下是基于 Spring Boot 的 Flowable 开发环境配置:

<dependencies>
    <!-- Flowable核心依赖 -->
    <dependency>
        <groupId>org.flowable</groupId>
        <artifactId>flowable-spring-boot-starter</artifactId>
        <version>6.8.0</version>
    </dependency>
    
    <!-- Web支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- 数据库支持 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

配置 application.properties:

# Flowable配置
flowable.database-schema-update=true
flowable.async-executor-activate=true

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/flowable?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

四、流程定义详解

下面通过一个完整的请假流程示例,展示 Flowable 的流程定义方式:

@Configuration
public class ProcessEngineConfig {

    @Autowired
    private RepositoryService repositoryService;

    @PostConstruct
    public void deployProcess() {
        repositoryService.createDeployment()
            .addClasspathResource("processes/employee-leave.bpmn20.xml")
            .name("员工请假流程")
            .deploy();
    }
}

BPMN 2.0 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"
             targetNamespace="http://flowable.org/processdef">
             
    <process id="employeeLeaveProcess" name="员工请假流程" isExecutable="true">
    
        <!-- 开始事件 -->
        <startEvent id="startEvent" name="开始"/>
        
        <!-- 提交请假申请任务 -->
        <userTask id="submitLeaveRequest" name="提交请假申请">
            <extensionElements>
                <flowable:formProperty id="employeeId" name="员工ID" type="string" required="true"/>
                <flowable:formProperty id="leaveType" name="请假类型" type="enum" required="true">
                    <flowable:value id="annual" name="年假"/>
                    <flowable:value id="sick" name="病假"/>
                    <flowable:value id="personal" name="事假"/>
                </flowable:formProperty>
                <flowable:formProperty id="startDate" name="开始日期" type="date" required="true"/>
                <flowable:formProperty id="endDate" name="结束日期" type="date" required="true"/>
                <flowable:formProperty id="reason" name="请假原因" type="string" required="true"/>
            </extensionElements>
        </userTask>
        
        <!-- 连线 -->
        <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitLeaveRequest"/>
        
        <!-- 部门经理审批 -->
        <userTask id="deptManagerApproval" name="部门经理审批">
            <extensionElements>
                <flowable:formProperty id="approvalResult" name="审批结果" type="enum" required="true">
                    <flowable:value id="approved" name="同意"/>
                    <flowable:value id="rejected" name="拒绝"/>
                </flowable:formProperty>
                <flowable:formProperty id="comments" name="审批意见" type="string"/>
            </extensionElements>
        </userTask>
        
        <sequenceFlow id="flow2" sourceRef="submitLeaveRequest" targetRef="deptManagerApproval"/>
        
        <!-- 结束事件 -->
        <endEvent id="endEvent" name="结束"/>
        <sequenceFlow id="flow3" sourceRef="deptManagerApproval" targetRef="endEvent"/>
        
    </process>
    
</definitions>

五、流程实例操作

以下是操作流程实例的核心 API 示例:

@Service
public class LeaveProcessService {

    @Autowired
    private RuntimeService runtimeService;
    
    @Autowired
    private TaskService taskService;
    
    @Autowired
    private HistoryService historyService;
    
    /**
     * 启动请假流程
     */
    public String startLeaveProcess(String employeeId, String leaveType, 
                                  Date startDate, Date endDate, String reason) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("employeeId", employeeId);
        variables.put("leaveType", leaveType);
        variables.put("startDate", startDate);
        variables.put("endDate", endDate);
        variables.put("reason", reason);
        
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(
            "employeeLeaveProcess", variables);
            
        return instance.getId();
    }
    
    /**
     * 获取用户待办任务
     */
    public List<Task> getPendingTasks(String assignee) {
        return taskService.createTaskQuery()
            .taskAssignee(assignee)
            .active()
            .list();
    }
    
    /**
     * 完成任务
     */
    public void completeTask(String taskId, Map<String, Object> variables) {
        taskService.complete(taskId, variables);
    }
    
    /**
     * 获取历史流程实例
     */
    public List<HistoricProcessInstance> getHistoricProcessInstances() {
        return historyService.createHistoricProcessInstanceQuery()
            .processDefinitionKey("employeeLeaveProcess")
            .finished()
            .orderByProcessInstanceEndTime()
            .desc()
            .list();
    }
}

六、Flowable 数据模型

Flowable 使用以下核心数据表存储流程相关信息:

  1. ACT_RE_*:存储流程定义和资源信息

    • ACT_RE_DEPLOYMENT:部署信息表
    • ACT_RE_PROCDEF:流程定义表
    • ACT_RE_MODEL:模型信息表
  2. ACT_RU_*:存储流程运行时数据

    • ACT_RU_EXECUTION:执行实例表
    • ACT_RU_TASK:任务实例表
    • ACT_RU_VARIABLE:变量表
    • ACT_RU_EVENT_SUBSCR:事件订阅表
  3. ACT_HI_*:存储历史数据

    • ACT_HI_PROCINST:历史流程实例表
    • ACT_HI_TASKINST:历史任务实例表
    • ACT_HI_VARINST:历史变量表
    • ACT_HI_DETAIL:历史详情表
  4. ACT_GE_*:存储通用数据

    • ACT_GE_BYTEARRAY:二进制资源表
    • ACT_GE_PROPERTY:属性表

七、流程监控与管理

Flowable 提供多种方式监控和管理流程:

@Service
public class ProcessMonitorService {

    @Autowired
    private ManagementService managementService;
    
    @Autowired
    private RepositoryService repositoryService;
    
    /**
     * 获取流程引擎统计信息
     */
    public Map<String, Object> getEngineStatistics() {
        Map<String, Object> stats = new HashMap<>();
        
        // 获取流程定义数量
        long processDefinitionCount = repositoryService.createProcessDefinitionQuery().count();
        
        // 获取正在运行的流程实例数量
        long runningInstancesCount = runtimeService.createProcessInstanceQuery().active().count();
        
        // 获取已完成的流程实例数量
        long completedInstancesCount = historyService.createHistoricProcessInstanceQuery()
            .finished()
            .count();
            
        stats.put("processDefinitionCount", processDefinitionCount);
        stats.put("runningInstancesCount", runningInstancesCount);
        stats.put("completedInstancesCount", completedInstancesCount);
        
        return stats;
    }
    
    /**
     * 暂停流程定义
     */
    public void suspendProcessDefinition(String processDefinitionKey) {
        repositoryService.suspendProcessDefinitionByKey(processDefinitionKey);
    }
    
    /**
     * 激活流程定义
     */
    public void activateProcessDefinition(String processDefinitionKey) {
        repositoryService.activateProcessDefinitionByKey(processDefinitionKey);
    }
}

八、最佳实践与常见问题

最佳实践

  1. 使用业务键 (Business Key) 关联业务数据
  2. 合理设置流程变量,避免存储大对象
  3. 使用事件监听器处理流程中的业务逻辑
  4. 定期清理历史数据,保持数据库性能
  5. 对复杂流程进行子流程拆分

常见问题

  1. 流程定义部署失败:检查 BPMN XML 格式是否正确
  2. 任务无法分配:确保 assignee 或 candidateUsers 设置正确
  3. 流程卡在某个节点:检查网关条件和连线设置
  4. 性能问题:优化数据库查询,避免频繁获取流程变量
  5. 历史数据丢失:检查数据库配置,确保历史级别设置正确

九、总结与展望

通过本文的学习,我们掌握了 Flowable 流程引擎的基本概念、环境搭建、流程定义和实例操作等核心技能。Flowable 不仅提供了强大的流程执行能力,还通过丰富的 API 和工具支持,使开发者能够轻松集成到各种企业应用中。

在下一篇文章中,我们将深入探讨 Flowable 的高级特性,包括动态流程变更、DMN 决策表、多实例任务等,进一步提升流程设计的灵活性和效率。