最近在重构公司内部的OA系统需要引入工作流引擎来处理请假、报销等审批流程。一开始以为工作流开发会很复杂但实际用SpringBoot集成Flowable后发现上手比想象中简单很多。不过在流程设计环节手动编写BPMN 2.0 XML文件确实是个体力活不仅容易出错而且维护起来也不直观。为了解决这个问题我开始寻找可视化的流程设计器最终锁定了功能强大且开源免费的bpmn-js。本文将手把手带你完成SpringBoot与工作流引擎Flowable的集成并重点介绍如何将bpmn-js流程编辑器整合到你的项目中实现流程的可视化设计与部署。无论你是刚接触工作流的新手还是想为现有系统添加流程设计能力的老手都能从这篇实战指南中找到清晰的路径。1. 工作流引擎与可视化设计器为什么需要它们在深入代码之前我们先理清几个核心概念这有助于理解我们为什么要做这些集成。工作流引擎比如我们这里要用的Flowable它的核心职责是执行。它负责解析我们定义好的业务流程通常是BPMN 2.0格式管理流程实例的生命周期启动、流转、挂起、终止处理任务分配、条件判断、并行网关等复杂逻辑并将所有状态持久化到数据库中。简单说它是业务流程的“运行时”和“执行者”。BPMN 2.0(Business Process Model and Notation) 是一种国际标准的业务流程建模语言。它用一套统一的图形符号如圆角矩形代表任务、菱形代表网关、圆圈代表事件来描述业务流程使得业务人员和技术人员可以用同一种“语言”沟通。Flowable、Activiti等引擎都原生支持BPMN 2.0 XML作为流程定义文件。流程设计器/编辑器例如bpmn-js它的核心职责是设计。它提供了一个图形化界面让用户可以通过拖拽的方式绘制流程图并最终生成符合BPMN 2.0标准的XML文件。这解决了手动编写复杂XML易错、不直观的痛点极大地提升了流程设计和维护的效率。所以一个完整的工作流解决方案通常是使用可视化设计器bpmn-js设计流程图 - 生成BPMN XML - 部署到工作流引擎Flowable - 引擎解析并执行业务流程。接下来我们就从零开始搭建这个完整的解决方案。2. 环境准备与项目初始化在开始编码前请确保你的开发环境已就绪。本文示例基于以下环境但核心思路适用于其他相近版本。JDK: 1.8 或 11 (推荐11注意Flowable 6.x对JDK版本有要求)Maven: 3.6IDE: IntelliJ IDEA 或 Eclipse数据库: MySQL 5.7 (Flowable支持多种数据库如H2, MySQL, PostgreSQL, Oracle等)首先我们使用Spring Initializrhttps://start.spring.io或IDE自带的创建向导初始化一个Spring Boot项目。项目基本信息Project: MavenLanguage: JavaSpring Boot: 2.7.x (本文使用2.7.18与Flowable 6.7.0兼容性好)Group:com.exampleArtifact:springboot-flowable-bpmnjsPackaging: JarJava: 11依赖选择在Dependencies中添加Spring Web(构建Web应用)Spring Data JPA(可选用于业务数据操作)MySQL Driver(数据库驱动)Lombok(简化代码可选)创建完成后我们需要手动在pom.xml中添加Flowable和bpmn-js前端的核心依赖。3. 核心依赖与基础配置打开项目中的pom.xml文件在dependencies节点内添加以下依赖。!-- Flowable Spring Boot Starter - 核心工作流引擎 -- dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter/artifactId version6.7.0/version /dependency !-- Spring Boot JDBC Starter (Flowable依赖JDBC操作数据库) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency !-- MySQL 连接驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Spring Boot Web Starter (提供REST API) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Lombok (简化Getter/Setter等代码) -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency版本说明flowable-spring-boot-starter的版本需要与Spring Boot版本匹配。6.7.0版本与Spring Boot 2.7.x配合良好。你可以在Flowable官方文档或Maven中央仓库查看版本兼容性矩阵。接下来配置数据库连接。编辑src/main/resources/application.yml或application.properties文件。server: port: 8080 spring: datasource: # 请替换为你自己的MySQL连接信息 url: jdbc:mysql://localhost:3306/flowable_db?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver # JPA相关配置如果使用了JPA jpa: show-sql: true hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect # Flowable 配置 flowable: # 禁用异步执行器适用于演示和简单应用生产环境需根据情况配置 async-executor-activate: false # 启动时检查流程定义如果版本有变化则自动更新 check-process-definitions: true # 数据库 schema 更新策略: true-启动时创建表false-不处理表已存在时使用 database-schema-update: true重要提示database-schema-update: true意味着应用首次启动时Flowable会自动在指定的数据库本例为flowable_db中创建它所需的所有表大约60多张。请确保数据库已存在且连接信息正确。生产环境建议在首次初始化后将此值改为false并建立规范的数据库版本管理流程。4. Flowable 基础集成与表结构初探完成配置后启动Spring Boot应用。如果控制台没有报错并且看到类似ProcessEngine configuration initialized的日志说明Flowable集成成功。此时检查你的MySQL数据库会发现Flowable自动创建了大量的表它们都以ACT_为前缀大致分为以下几类ACT_RE_*:RE代表repository。存储静态信息如流程定义、模型、部署内容等。ACT_RU_*:RU代表runtime。存储运行时的流程实例、任务、变量等动态数据。这些表在流程实例结束后会被清理。ACT_HI_*:HI代表history。存储历史数据如已完成的流程实例、任务、变量变更等用于审计和报表。ACT_GE_*:GE代表general。通用数据如二进制资源流程图片、BPMN XML文件。了解这些表的前缀有助于你在排查问题时快速定位数据。现在我们来创建一个最简单的流程定义并启动它验证引擎是否正常工作。首先在src/main/resources目录下创建processes文件夹这是Flowable默认扫描BPMN XML文件的路径。在该文件夹内创建一个简单的请假流程文件leave-process.bpmn20.xml。?xml version1.0 encodingUTF-8? definitions xmlnshttp://www.omg.org/spec/BPMN/20100524/MODEL xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:flowablehttp://flowable.org/bpmn targetNamespacehttp://www.flowable.org/processdef !-- 定义一个流程id是流程的唯一标识在代码中引用 -- process idleaveProcess name请假流程 isExecutabletrue !-- 开始事件 -- startEvent idstart name开始申请/ !-- 用户任务员工提交请假单 -- userTask idsubmitLeave name提交请假申请 flowable:assignee${applicant}/ !-- 用户任务经理审批 -- userTask idmanagerAudit name经理审批 flowable:candidateGroupsmanagers/ !-- 排他网关根据审批结果决定流向 -- exclusiveGateway iddecisionGateway/ !-- 结束事件审批通过 -- endEvent idendApproved name审批通过结束/ !-- 结束事件审批驳回 -- endEvent idendRejected name审批驳回结束/ !-- 连接线定义节点间的流转顺序 -- sequenceFlow idflow1 sourceRefstart targetRefsubmitLeave/ sequenceFlow idflow2 sourceRefsubmitLeave targetRefmanagerAudit/ sequenceFlow idflow3 sourceRefmanagerAudit targetRefdecisionGateway/ !-- 条件流审批结果为‘通过’时流向‘endApproved’ -- sequenceFlow idflow4 sourceRefdecisionGateway targetRefendApproved conditionExpression xsi:typetFormalExpression ${auditResult 通过} /conditionExpression /sequenceFlow !-- 条件流审批结果为‘驳回’时流向‘endRejected’ -- sequenceFlow idflow5 sourceRefdecisionGateway targetRefendRejected conditionExpression xsi:typetFormalExpression ${auditResult 驳回} /conditionExpression /sequenceFlow /process /definitions这个XML定义了一个简单的两级审批流程开始 - 员工提交 - 经理审批 - 根据结果结束。重启应用Flowable会自动部署这个流程定义。我们可以写一个简单的Controller来测试流程的启动和任务查询。package com.example.springbootflowablebpmnjs.controller; import lombok.RequiredArgsConstructor; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; RestController RequestMapping(/api/process) RequiredArgsConstructor public class ProcessController { // Flowable 核心服务运行时服务 private final RuntimeService runtimeService; // Flowable 核心服务任务服务 private final TaskService taskService; /** * 启动一个请假流程实例 * param applicant 申请人ID * return 流程实例ID */ PostMapping(/startLeave) public String startLeaveProcess(RequestParam String applicant) { // 设置流程变量${applicant} 将会被替换为这里的值 MapString, Object variables new HashMap(); variables.put(applicant, applicant); // 通过流程定义的Key来启动流程实例 ProcessInstance processInstance runtimeService.startProcessInstanceByKey(leaveProcess, variables); return 流程启动成功流程实例ID: processInstance.getId(); } /** * 查询某个用户的任务列表 * param userId 用户ID * return 任务列表信息 */ GetMapping(/myTasks) public ListMapString, String getMyTasks(RequestParam String userId) { // 查询分配给指定用户的任务 ListTask tasks taskService.createTaskQuery().taskAssignee(userId).list(); return tasks.stream().map(task - { MapString, String taskInfo new HashMap(); taskInfo.put(taskId, task.getId()); taskInfo.put(taskName, task.getName()); taskInfo.put(processInstanceId, task.getProcessInstanceId()); taskInfo.put(createTime, task.getCreateTime().toString()); return taskInfo; }).toList(); } /** * 完成一个任务例如经理进行审批 * param taskId 任务ID * param auditResult 审批结果“通过” 或 “驳回” * return 操作结果 */ PostMapping(/completeTask) public String completeTask(RequestParam String taskId, RequestParam String auditResult) { MapString, Object variables new HashMap(); variables.put(auditResult, auditResult); // 设置流程变量用于条件判断 // 完成任务并传递变量 taskService.complete(taskId, variables); return 任务[ taskId ]已完成审批结果: auditResult; } }启动应用使用Postman或curl测试启动流程POST http://localhost:8080/api/process/startLeave?applicantzhangsan返回流程启动成功流程实例ID: 25001查询任务GET http://localhost:8080/api/process/myTasks?userIdzhangsan返回可以看到zhangsan有一个“提交请假申请”的任务。完成任务模拟提交POST http://localhost:8080/api/process/completeTask?taskId任务IDauditResult提交(注意这里auditResult变量在第一个任务中未使用但接口需要)此时任务会流转到“经理审批”环节该任务候选组是managers。需要查询候选组任务或直接指定办理人。为了简化我们可以修改查询查询候选组任务taskService.createTaskQuery().taskCandidateGroup(managers).list()然后通过taskService.claim(taskId, lisi)拾取任务再完成。至此Flowable的基础集成和API调用就完成了。但整个过程我们都在和代码、XML打交道流程设计并不直观。接下来我们将引入bpmn-js让流程设计变得可视化。5. 集成 bpmn-js 流程设计器bpmn-js是一个基于BPMN 2.0标准的Web建模工具包。我们将它作为前端资源集成到Spring Boot项目中并通过后端API提供流程定义的保存和部署功能。5.1 前端资源准备由于Spring Boot项目通常前后端分离这里我们采用一种简单的方式将bpmn-js的库文件作为静态资源引入。你可以通过npm安装也可以直接使用CDN。为了项目完整性我们选择下载相关库到src/main/resources/static目录下。访问 bpmn-js 的发布页面如 unpkg: https://unpkg.com/browse/bpmn-jslatest/dist/或使用npm命令下载。将以下文件放入src/main/resources/static/bpmnjs/目录bpmn-modeler.development.js(或bpmn-modeler.production.min.js)bpmn-js.cssdiagram-js.css(如果bpmn-js.css未包含)实际上更简单的方式是直接在HTML中引用CDN。我们创建一个简单的设计器页面src/main/resources/static/index.html。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleBPMN 2.0 流程设计器/title !-- 引入 bpmn-js 样式和脚本 (使用CDN) -- link relstylesheet hrefhttps://unpkg.com/bpmn-js14.0.0/dist/assets/bpmn-js.css link relstylesheet hrefhttps://unpkg.com/bpmn-js14.0.0/dist/assets/diagram-js.css link relstylesheet hrefhttps://unpkg.com/bpmn-js14.0.0/dist/assets/bpmn-font/css/bpmn.css style html, body, #canvas { height: 100%; margin: 0; padding: 0; overflow: hidden; font-family: Arial, sans-serif; } #canvas { width: 100%; } #toolbar { position: absolute; top: 10px; left: 10px; z-index: 100; background: rgba(255, 255, 255, 0.9); padding: 10px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); } button { margin: 5px; padding: 8px 15px; border: 1px solid #ccc; background: #f8f9fa; border-radius: 4px; cursor: pointer; } button:hover { background: #e9ecef; } .hidden { display: none; } /style /head body div idtoolbar button onclickcreateNewDiagram()新建/button button onclickopenDiagram()打开XML/button button onclicksaveDiagram()保存XML/button button onclickdeployDiagram()部署流程/button button onclickexportDiagram()导出SVG/button input typefile idfile-input classhidden accept.bpmn,.xml/ /div div idcanvas/div !-- 使用CDN引入 bpmn-js -- script srchttps://unpkg.com/bpmn-js14.0.0/dist/bpmn-modeler.development.js/script script // 初始化 BPMN 建模器 const bpmnModeler new BpmnJS({ container: #canvas }); // 创建一个新的空流程图 async function createNewDiagram() { try { const { xml } await bpmnModeler.createDiagram(); console.log(创建了新流程图); } catch (err) { console.error(创建失败:, err); } } // 打开本地BPMN XML文件 function openDiagram() { document.getElementById(file-input).click(); } document.getElementById(file-input).addEventListener(change, function(event) { const file event.target.files[0]; if (!file) { return; } const reader new FileReader(); reader.onload function(e) { const xml e.target.result; openDiagramFromXml(xml); }; reader.readAsText(file); }); // 通过XML字符串打开流程图 async function openDiagramFromXml(xml) { try { await bpmnModeler.importXML(xml); console.log(流程图导入成功); } catch (err) { console.error(导入失败:, err); alert(导入失败请检查XML格式是否正确。错误: err.message); } } // 保存当前流程图为XML async function saveDiagram() { try { const { xml } await bpmnModeler.saveXML({ format: true }); downloadFile(xml, diagram.bpmn); } catch (err) { console.error(保存失败:, err); } } // 部署流程图到后端服务器 async function deployDiagram() { try { const { xml } await bpmnModeler.saveXML({ format: true }); const response await fetch(/api/model/deploy, { method: POST, headers: { Content-Type: application/xml, }, body: xml }); const result await response.text(); alert(部署结果: result); } catch (err) { console.error(部署失败:, err); alert(部署失败: err.message); } } // 导出为SVG图片 async function exportDiagram() { try { const { svg } await bpmnModeler.saveSVG(); downloadFile(svg, diagram.svg); } catch (err) { console.error(导出失败:, err); } } // 通用文件下载函数 function downloadFile(content, fileName) { const blob new Blob([content], { type: application/xml }); const link document.createElement(a); link.href URL.createObjectURL(blob); link.download fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // 页面加载完成后创建一个默认流程图 window.onload function() { createNewDiagram(); }; /script /body /html这个HTML页面创建了一个完整的BPMN设计器。它提供了新建、打开本地XML文件、保存XML、部署到后端、导出SVG等基本功能。5.2 后端API接收与部署BPMN XML现在我们需要在后端创建API来接收前端发送的BPMN XML并将其部署到Flowable引擎中。package com.example.springbootflowablebpmnjs.controller; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.RepositoryService; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.DeploymentBuilder; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.nio.charset.StandardCharsets; RestController RequestMapping(/api/model) RequiredArgsConstructor Slf4j public class ModelDeployController { private final RepositoryService repositoryService; /** * 部署BPMN XML字符串 * param bpmnXml BPMN 2.0 XML 字符串 * return 部署结果信息 */ PostMapping(value /deploy, consumes MediaType.APPLICATION_XML_VALUE) public String deployProcessDefinition(RequestBody String bpmnXml) { try { log.info(接收到部署请求XML长度: {}, bpmnXml.length()); // 1. 构建部署 DeploymentBuilder deploymentBuilder repositoryService.createDeployment(); deploymentBuilder.name(BPMNJS_Deployment_ System.currentTimeMillis()) .addString(process.bpmn20.xml, bpmnXml) // 资源名称必须符合规范 .enableDuplicateFiltering(true); // 启用重复过滤相同key和版本的流程不会重复部署 // 2. 执行部署 Deployment deployment deploymentBuilder.deploy(); // 3. 记录部署信息 log.info(流程部署成功部署ID: {}, 部署名称: {}, 部署时间: {}, deployment.getId(), deployment.getName(), deployment.getDeploymentTime()); return String.format(流程部署成功部署ID: %s, deployment.getId()); } catch (Exception e) { log.error(流程部署失败, e); return 流程部署失败: e.getMessage(); } } /** * 获取已部署的流程定义列表 */ GetMapping(/definitions) public Object getProcessDefinitions() { return repositoryService.createProcessDefinitionQuery() .orderByProcessDefinitionKey().asc() .list() .stream() .map(pd - { // 构造一个简单的DTO返回 java.util.MapString, Object map new java.util.HashMap(); map.put(id, pd.getId()); map.put(key, pd.getKey()); map.put(name, pd.getName()); map.put(version, pd.getVersion()); map.put(deploymentId, pd.getDeploymentId()); return map; }).toList(); } }这个控制器提供了两个关键接口POST /api/model/deploy: 接收前端发送的BPMN XML内容并通过Flowable的RepositoryService将其部署到引擎中。addString方法将XML字符串作为部署资源。enableDuplicateFiltering可以防止重复部署相同版本的流程。GET /api/model/definitions: 获取所有已部署的流程定义列表方便前端展示和管理。5.3 运行与测试确保Spring Boot应用正在运行。在浏览器中访问http://localhost:8080/index.html。你将看到一个可视化的BPMN设计器界面。你可以从左侧面板拖拽元素开始事件、用户任务、网关、结束事件等到画布连接它们并设置属性如任务办理人、条件表达式。设计一个简单的流程例如我们之前XML定义的请假流程。点击“保存XML”按钮可以将当前设计的流程图保存为.bpmn文件到本地。点击“部署流程”按钮设计器会将当前的XML通过POST请求发送到后端的/api/model/deploy接口。如果部署成功页面会弹出提示。此时你可以通过之前写的ProcessController中的/api/process/startLeave接口来启动这个新部署的流程。关键点在bpmn-js设计器中设置用户任务的办理人Assignee或候选组Candidate Groups时需要使用属性面板。在属性面板中找到“主要”选项卡在“办理人”或“候选组”字段中你可以直接填写固定值如zhangsan或者填写表达式如${applicant}这与我们在XML中手动编写的方式一致。6. 常见问题与排查思路在实际集成过程中你可能会遇到一些问题。下面是一些常见问题及其解决方案。问题现象可能原因排查思路与解决方案应用启动失败报数据库连接错误1. 数据库地址/用户名/密码错误。2. 数据库flowable_db不存在。3. 网络或防火墙问题。1. 检查application.yml中的spring.datasource配置。2. 登录MySQL执行CREATE DATABASE flowable_db;创建数据库。3. 检查MySQL服务是否启动端口是否开放。访问http://localhost:8080/index.html4041. 静态资源路径不对。2.index.html文件未放在static目录下。3. 控制器路径冲突。1. 确保index.html在src/main/resources/static/目录下。2. 检查是否有其他控制器拦截了/或/index.html路径。3. 尝试访问http://localhost:8080/看是否有默认页面。bpmn-js 设计器页面空白或JS报错1. CDN链接失效或网络问题。2. 浏览器控制台有CORS错误。3. bpmn-js版本不兼容。1. 打开浏览器开发者工具(F12)的Console和Network面板查看具体错误。2. 将CDN资源下载到本地static目录并修改HTML中的引用路径。3. 尝试使用更稳定或更早的bpmn-js版本。部署流程时报错“Error parsing XML”1. 前端生成的XML格式错误。2. XML不符合BPMN 2.0规范。3. 包含Flowable不支持的扩展元素或属性。1. 使用“保存XML”功能将XML保存到本地用文本编辑器或XML验证工具检查。2. 对比一个能正常工作的简单BPMN文件检查根节点definitions的命名空间等。3. 简化流程逐步添加元素测试。启动流程实例时任务没有分配给指定人1. 流程定义XML中的flowable:assignee表达式未正确解析。2. 启动流程时未传入对应的流程变量。3. 任务查询条件错误。1. 检查XML确保表达式语法正确如${applicant}。2. 在runtimeService.startProcessInstanceByKey方法中确保传入了名为applicant的变量。3. 使用taskService.createTaskQuery().taskAssignee(userId).list()查询时userId需与变量值匹配。Flowable自动创建的表非常多影响业务数据库Flowable默认将所有表创建在配置的数据库中。最佳实践为Flowable单独使用一个数据库实例或Schema。修改配置spring.datasource.url指向专门的数据库。生产环境初始化后将flowable.database-schema-update设为false。7. 最佳实践与工程建议将bpmn-js与SpringBoot、Flowable集成只是第一步要将其用于生产环境还需要考虑更多工程化问题。前后端分离部署本文为了简化将bpmn-js页面放在了Spring Boot的static目录。在生产环境中更推荐将前端Vue/React bpmn-js和后端Spring Boot API完全分离部署通过Nginx进行路由或使用跨域配置。流程定义版本管理Flowable支持流程定义的版本控制。当你部署一个与已存在流程定义Key相同但内容不同的BPMN文件时Flowable会为其创建新版本Version递增。在查询和启动流程时默认使用最高版本。你需要考虑如何管理这些版本例如提供界面让用户选择特定版本启动。模型与部署分离上述例子是直接部署BPMN XML。更复杂的系统可能会引入“模型”概念。即先通过bpmn-js设计并保存一个“模型”包含JSON格式的元数据到业务数据库在需要发布时再将模型转换为BPMN XML进行部署。这提供了更多的灵活性和审计能力。属性扩展与表单bpmn-js允许通过“属性面板”扩展自定义属性。你可以集成一个自定义的属性面板让用户配置业务相关的字段如“表单Key”、“审批规则”等。后端在流程运行时读取这些属性动态渲染表单或决定路由逻辑。安全性API安全部署、启动流程等API接口必须加入权限校验如Spring Security防止未授权访问。数据安全流程定义可能包含敏感信息。确保只有有权限的用户才能设计和部署流程。SQL注入防护虽然Flowable使用预编译语句但在自定义查询时仍需注意。性能与监控数据库优化Flowable的ACT_RU_*运行时和ACT_HI_*历史表会随着使用快速增长。需要制定历史数据归档或清理策略。异步执行器对于耗时操作如邮件任务、服务调用启用Flowable的异步执行器Async Executor避免阻塞流程引擎。监控利用Flowable提供的ManagementService和HistoryService监控流程实例数量、任务积压等情况或集成Spring Boot Actuator。错误处理与事务流程引擎的操作通常涉及数据库事务。确保你的业务操作与Flowable的API调用在同一个事务管理器中或在异常时进行合理的回滚和补偿。通过本文的步骤你已经成功搭建了一个集成了可视化流程设计器bpmn-js和工作流引擎Flowable的Spring Boot应用。这为你构建OA、审批流、工单系统等需要流程驱动的应用打下了坚实的基础。接下来你可以深入探索Flowable更高级的特性如并行网关、子流程、调用活动、事件监听器以及如何将业务数据与流程任务更紧密地结合构建出更强大、更灵活的业务流程管理系统。