前言 Flowable 项目提供了一套核心的开源业务流程引擎这些引擎紧凑且高效。它们为开发人员、系统管理员和业务用户提供工作流和业务流程管理 (BPM) 平台。 它的核心是一个闪电般快速、久经考验的动态 BPMN 流程引擎伴随着 DMN 决策表和 CMMN 案例管理引擎所有这些引擎都是用 Java 编写的。它们是 Apache 2.0 许可的开放源代码拥有一个承诺社区。 所有引擎都可以嵌入在 Java 应用程序中运行也可以作为服务器、集群和云中的服务运行。它们可以作为独立的引擎或服务运行也可以集成在一起为业务流程管理提供丰富的套件。它们与 Spring 完美集成。 凭借丰富的 Java 和 REST API它们是编排人工或机器人自动化处理的理想基础。它们启用微服务编排或软连接微服务本身。一、pom中引入Flowable相关框架本Demo使用的SpringBoot版本是2.7.5parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.5/version relativePath/ !-- lookup parent from repository -- /parentdependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 工作流flowable架包 -- dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter/artifactId version6.4.0/version /dependency !-- mysql数据库连接架包 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.11/version /dependency !-- mybatis ORM 架包 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.2.2/version /dependency !-- thymeleaf架包 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency二、相关配置文件1.application.properties配置文件server.port8081 #数据库配置 spring.datasource.urljdbc:mysql://localhost:3306/flowable01?autoReconnecttrueuseUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneGMT%2B8 spring.datasource.usernameroot spring.datasource.passwordsong1234 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver #开启调试信息 logging.level.org.flowableDEBUG #业务流程涉及的表自动生成 flowable.database-schema-updatetrue flowable.async-executor-activatefalse2.审批流程xml文件默认放置在resources下的processess文件夹下3.vacationRequest.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 typeLanguagehttp://www.w3.org/2001/XMLSchema expressionLanguagehttp://www.w3.org/1999/XPath targetNamespacehttp://www.flowable.org/processdef !-- -请假条流程图 -- process idvacationRequest name请假条流程 isExecutabletrue !-- -流程的开始 -- startEvent idstartEvent/ sequenceFlow sourceRefstartEvent targetRefapproveTask/ !-- -流程的节点 -- userTask idapproveTask name开始请假 flowable:candidateGroupsmanagers/ !-- -流程节点间的线条上一个节点和下一个节点-- sequenceFlow sourceRefapproveTask targetRefdecision/ !-- -排他性网关 -- exclusiveGateway iddecision/ !-- -同意时 -- sequenceFlow sourceRefdecision targetRefholidayApprovedTask conditionExpression xsi:typetFormalExpression ![CDATA[${approved}]] /conditionExpression /sequenceFlow !-- -拒绝时 -- sequenceFlow sourceRefdecision targetRefrejectEnd conditionExpression xsi:typetFormalExpression ![CDATA[${!approved}]] /conditionExpression /sequenceFlow !-- -外部服务 -- !-- serviceTask idexternalSystemCall nameEnter holidays in external system flowable:classorg.javaboy.flowable02.flowable.Approve/ sequenceFlow sourceRefexternalSystemCall targetRefholidayApprovedTask/ -- userTask idholidayApprovedTask flowable:assignee${employee} name同意请假/ sequenceFlow sourceRefholidayApprovedTask targetRefapproveEnd/ !-- serviceTask idrejectLeave nameSend out rejection email flowable:classorg.javaboy.flowable02.flowable.Reject/ sequenceFlow sourceRefrejectLeave targetRefrejectEnd/ -- endEvent idapproveEnd/ endEvent idrejectEnd/ !-- -流程的结束 -- /process /definitions三、控制层代码块package com.sxjg.controller; import com.sxjg.pojo.ResponseBean; import com.sxjg.pojo.VacationApproveVo; import com.sxjg.pojo.VacationRequestVo; import com.sxjg.service.VacationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; /** * project 请假流程测试 * Description * Author songwp * Date 2023/2/13 20:06 * Version 1.0.0 **/ RequestMapping(vacation) RestController public class VacationController { Autowired VacationService vacationService; /** * 请假条新增页面 * return */ GetMapping(/add) public ModelAndView add(){ return new ModelAndView(vacation); } /** * 请假条审批列表 * return */ GetMapping(/aList) public ModelAndView aList(){ return new ModelAndView(list); } /** * 请假条查询列表 * return */ GetMapping(/sList) public ModelAndView sList(){ return new ModelAndView(search); } /** * 请假请求方法 * param vacationRequestVO * return */ PostMapping public ResponseBean askForLeave(RequestBody VacationRequestVo vacationRequestVO) { return vacationService.askForLeave(vacationRequestVO); } /** * 获取待审批列表 * param identity * return */ GetMapping(/list) public ResponseBean leaveList(String identity) { return vacationService.leaveList(identity); } /** * 拒绝或同意请假 * param vacationVO * return */ PostMapping(/handler) public ResponseBean askForLeaveHandler(RequestBody VacationApproveVo vacationVO) { return vacationService.askForLeaveHandler(vacationVO); } /** * 请假查询 * param name * return */ GetMapping(/search) public ResponseBean searchResult(String name) { return vacationService.searchResult(name); } }四、Service层请假条新增、审批、查询的业务处理package com.sxjg.service; import com.sxjg.pojo.ResponseBean; import com.sxjg.pojo.VacationApproveVo; import com.sxjg.pojo.VacationInfo; import com.sxjg.pojo.VacationRequestVo; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.task.api.Task; import org.flowable.variable.api.history.HistoricVariableInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * project 请假流程测试 * Description * Author songwp * Date 2023/2/13 20:08 * Version 1.0.0 **/ Service public class VacationService { Autowired RuntimeService runtimeService; Autowired TaskService taskService; Autowired HistoryService historyService; /** * 申请请假 * param vacationRequestVO * return */ Transactional public ResponseBean askForLeave(VacationRequestVo vacationRequestVO) { MapString, Object variables new HashMap(); variables.put(name, vacationRequestVO.getName()); variables.put(days, vacationRequestVO.getDays()); variables.put(reason, vacationRequestVO.getReason()); try { //指定业务流程 runtimeService.startProcessInstanceByKey(vacationRequest, vacationRequestVO.getName(), variables); return ResponseBean.ok(已提交请假申请); } catch (Exception e) { e.printStackTrace(); } return ResponseBean.error(提交申请失败); } /** * 审批列表 * param identity * return */ public ResponseBean leaveList(String identity) { ListTask tasks taskService.createTaskQuery().taskCandidateGroup(identity).list(); ListMapString, Object list new ArrayList(); for (int i 0; i tasks.size(); i) { Task task tasks.get(i); MapString, Object variables taskService.getVariables(task.getId()); variables.put(id, task.getId()); list.add(variables); } return ResponseBean.ok(加载成功, list); } /** * 操作审批 * param vacationVO * return */ public ResponseBean askForLeaveHandler(VacationApproveVo vacationVO) { try { boolean approved vacationVO.getApprove(); MapString, Object variables new HashMapString, Object(); variables.put(approved, approved); variables.put(employee, vacationVO.getName()); Task task taskService.createTaskQuery().taskId(vacationVO.getTaskId()).singleResult(); taskService.complete(task.getId(), variables); if (approved) { //如果是同意还需要继续走一步 Task t taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); taskService.complete(t.getId()); } return ResponseBean.ok(操作成功); } catch (Exception e) { e.printStackTrace(); } return ResponseBean.error(操作失败); } /** * 请假列表 * param name * return */ public ResponseBean searchResult(String name) { ListVacationInfo vacationInfos new ArrayList(); ListHistoricProcessInstance historicProcessInstances historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).finished().orderByProcessInstanceEndTime().desc().list(); for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) { VacationInfo vacationInfo new VacationInfo(); Date startTime historicProcessInstance.getStartTime(); Date endTime historicProcessInstance.getEndTime(); ListHistoricVariableInstance historicVariableInstances historyService.createHistoricVariableInstanceQuery() .processInstanceId(historicProcessInstance.getId()) .list(); for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) { String variableName historicVariableInstance.getVariableName(); Object value historicVariableInstance.getValue(); if (reason.equals(variableName)) { vacationInfo.setReason((String) value); } else if (days.equals(variableName)) { vacationInfo.setDays(Integer.parseInt(value.toString())); } else if (approved.equals(variableName)) { vacationInfo.setStatus((Boolean) value); } else if (name.equals(variableName)) { vacationInfo.setName((String) value); } } vacationInfo.setStartTime(startTime); vacationInfo.setEndTime(endTime); vacationInfos.add(vacationInfo); } return ResponseBean.ok(ok, vacationInfos); } }五、POJO相关类import lombok.Data; /** * 请假条审批 * Date */ Data public class VacationApproveVo { private String taskId; private Boolean approve; private String name; } import lombok.Data; /** * 请假条申请 * Date */ Data public class VacationRequestVo { private String name; private Integer days; private String reason; } import lombok.Data; /** * 响应类 * Date */ Data public class ResponseBean { private Integer status; private String msg; private Object data; public static ResponseBean ok(String msg, Object data) { return new ResponseBean(200, msg, data); } public static ResponseBean ok(String msg) { return new ResponseBean(200, msg, null); } public static ResponseBean error(String msg, Object data) { return new ResponseBean(500, msg, data); } public static ResponseBean error(String msg) { return new ResponseBean(500, msg, null); } private ResponseBean() { } private ResponseBean(Integer status, String msg, Object data) { this.status status; this.msg msg; this.data data; } } import java.util.Date; import lombok.Data; /** * 请假条DO * Date */ Data public class VacationInfo { private String name; private Date startTime; private Date endTime; private String reason; private Integer days; private Boolean status; }六、页面代码页面文件放在resources的templates文件夹下1.提交请假条申请页面vacation.html!DOCTYPE html html langen head meta charsetUTF-8 title提交请假条申请页面/title script srchttps://unpkg.com/axios/dist/axios.min.js/script !-- Import style -- link relstylesheet hrefhttps://unpkg.com/element-plus/dist/index.css/ script srchttps://unpkg.com/vue3/script !-- Import component library -- script src//unpkg.com/element-plus/script /head body div idapp h1开始一个请假流程/h1 table tr td请输入姓名/td td el-input typetext v-modelafl.name/ /td /tr tr td请输入请假天数/td td el-input typetext v-modelafl.days/ /td /tr tr td请输入请假理由/td td el-input typetext v-modelafl.reason/ /td /tr /table el-button typeprimary clicksubmit提交请假申请/el-button /div script Vue.createApp( { data() { return { afl: { name: test, days: 3, reason: 测试 } } }, methods: { submit() { let _this this; axios.post(/vacation, this.afl) .then(function (response) { if (response.data.status 200) { //提交成功 _this.$message.success(response.data.msg); } else { //提交失败 _this.$message.error(response.data.msg); } }) .catch(function (error) { console.log(error); }); } } } ).use(ElementPlus).mount(#app) /script /body /html2.审批请假条页面list.html!DOCTYPE html html langen head meta charsetUTF-8 title审批请假条页面/title script srchttps://unpkg.com/axios/dist/axios.min.js/script !-- Import style -- link relstylesheet hrefhttps://unpkg.com/element-plus/dist/index.css/ script srchttps://unpkg.com/vue3/script !-- Import component library -- script src//unpkg.com/element-plus/script /head body div idapp div div请选择你的身份/div div el-select name id v-modelidentity changeinitTasks el-option :valueiden v-for(iden,index) in identities :keyindex :labeliden/el-option /el-select el-button typeprimary clickinitTasks刷新一下/el-button /div /div el-table border strip :datatasks el-table-column propname label姓名/el-table-column el-table-column propdays label请假天数/el-table-column el-table-column propreason label请假原因/el-table-column el-table-column lable操作 template #defaultscope el-button typeprimary clickapproveOrReject(scope.row.id,true,scope.row.name)批准/el-button el-button typedanger clickapproveOrReject(scope.row.id,false,scope.row.name)拒绝/el-button /template /el-table-column /el-table /div script Vue.createApp( { data() { return { tasks: [], identities: [ managers ], identity: } }, methods: { initTasks() { let _this this; axios.get(/vacation/list?identity this.identity) .then(function (response) { _this.tasks response.data.data; }) .catch(function (error) { console.log(error); }); }, approveOrReject(taskId, approve,name) { let _this this; axios.post(/vacation/handler, {taskId: taskId, approve: approve,name:name}) .then(function (response) { _this.$message.success(审批成功); _this.initTasks(); }) .catch(function (error) { _this.$message.error(操作失败); console.log(error); }); } } } ).use(ElementPlus).mount(#app) /script /body /html3.已审批请假条查询页面search.html!DOCTYPE html html langen head meta charsetUTF-8 title已审批请假条查询页面/title script srchttps://unpkg.com/axios/dist/axios.min.js/script !-- Import style -- link relstylesheet hrefhttps://unpkg.com/element-plus/dist/index.css/ script srchttps://unpkg.com/vue3/script !-- Import component library -- script src//unpkg.com/element-plus/script /head body div idapp div stylemargin-top: 50px el-input v-modelname stylewidth: 300px placeholder请输入用户名/el-input el-button typeprimary clicksearch查询/el-button /div div el-table border strip :datahistoryInfos el-table-column propname label姓名/el-table-column el-table-column propstartTime label提交时间/el-table-column el-table-column propendTime label审批时间/el-table-column el-table-column propreason label事由/el-table-column el-table-column propdays label天数/el-table-column el-table-column label状态 template #defaultscope el-tag typesuccess v-ifscope.row.status已通过/el-tag el-tag typedanger v-else已拒绝/el-tag /template /el-table-column /el-table /div /div script Vue.createApp( { data() { return { historyInfos: [], name: zhangsan } }, methods: { search() { let _this this; axios.get(/vacation/search?name this.name) .then(function (response) { if (response.data.status 200) { _this.historyInfosresponse.data.data; } else { _this.$message.error(response.data.msg); } }) .catch(function (error) { console.log(error); }); } } } ).use(ElementPlus).mount(#app) /script /body /html七、启动并测试1.第一次运行系统会自动创建flowable需要数据表结构2.输入url地址localhost:8081/vacation/add建立几个请假条3.请假条建立好了审批处理一下注意第一次运行这个demo权限暂且不管角色也先写死先把demo跑起来再说。四个请假条两个通过两个拒绝操作完成后在待审批列表不在出现4.作为请假人查询一下自己提交的假条审批了.通过查询结果得知两个通过两个拒绝。至此一个简单的请假条审批流程走完了八、API梳理说明1.FormService表单数据的管理 是可选服务也就是说Flowable没有它也能很好地运行而不必牺牲任何功能。这个服务引入了开始表单(start form)与任务表单(task form)的概念。 开始表单是在流程实例启动前显示的表单而任务表单是用户完成任务时显示的表单。Flowable可以在BPMN 2.0流程定义中定义这些表单。表单服务通过简单的方式暴露这些数据。再次重申表单不一定要嵌入流程定义因此这个服务是可选的formService.getStartFormKey() // 获取表单key formService.getRenderedStartForm() // 查询表单json无数据2.RepositiryService提供了在编辑和发布审批流程的api。主要是模型管理和流程定义的业务api这个服务提供了管理与控制部署(deployments)与流程定义(process definitions)的操作查询引擎现有的部署与流程定义。 暂停或激活部署中的某些流程或整个部署。暂停意味着不能再对它进行操作激活刚好相反重新使它可以操作。 获取各种资源比如部署中保存的文件或者引擎自动生成的流程图。 获取POJO版本的流程定义。它可以用Java而不是XML的方式查看流程。1.提供了带条件的查询模型流程定义的api repositoryService.createXXXQuery() 例如 repositoryService.createModelQuery().list() 模型查询 repositoryService.createProcessDefinitionQuery().list() 流程定义查询 repositoryService.createXXXXQuery().XXXKey(XXX) 查询该key是否存在 2.提供一大波模型与流程定义的通用方法 模型相关 repositoryService.getModel() 获取模型 repositoryService.saveModel() 保存模型 repositoryService.deleteModel() 删除模型 repositoryService.createDeployment().deploy(); 部署模型 repositoryService.getModelEditorSource() 获得模型JSON数据的UTF8字符串 repositoryService.getModelEditorSourceExtra() 获取PNG格式图像 3.流程定义相关 repositoryService.getProcessDefinition(ProcessDefinitionId); 获取流程定义具体信息 repositoryService.activateProcessDefinitionById() 激活流程定义 repositoryService.suspendProcessDefinitionById() 挂起流程定义 repositoryService.deleteDeployment() 删除流程定义 repositoryService.getProcessDiagram()获取流程定义图片流 repositoryService.getResourceAsStream()获取流程定义xml流 repositoryService.getBpmnModel(pde.getId()) 获取bpmn对象当前进行到的那个节点的流程图使用 4.流程定义授权相关 repositoryService.getIdentityLinksForProcessDefinition() 流程定义授权列表 repositoryService.addCandidateStarterGroup()新增组流程授权 repositoryService.addCandidateStarterUser()新增用户流程授权 repositoryService.deleteCandidateStarterGroup() 删除组流程授权 repositoryService.deleteCandidateStarterUser() 删除用户流程授权3.RuntimeService处理正在运行的流程runtimeService.createProcessInstanceBuilder().start() 发起流程 runtimeService.deleteProcessInstance() 删除正在运行的流程 runtimeService.suspendProcessInstanceById() 挂起流程定义 runtimeService.activateProcessInstanceById() 激活流程实例 runtimeService.getVariables(processInstanceId); 获取表单中填写的值 runtimeService.getActiveActivityIds(processInstanceId)获取以进行的流程图节点 当前进行到的那个节点的流程图使用 runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState(); 终止流程4.HistoryService在用户发起审批后会生成流程实例。historyService为处理流程实例的api但是其中包括了已完成的和未完成的流程实例 如果是处理正在运行的流程实例请使用runtimeService暴露Flowable引擎收集的所有历史数据。当执行流程时引擎会保存许多数据可配置例如流程实例启动时间、谁在执行哪个任务、完成任务花费的事件、每个流程实例的执行路径等等。这个服务主要提供查询这些数据的能力historyService.createHistoricProcessInstanceQuery().list() 查询流程实例列表历史流程,包括未完成的 historyService.createHistoricProcessInstanceQuery().list().foreach().getValue() 可以获取历史中表单的信息 historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 根绝id查询流程实例 historyService.deleteHistoricProcessInstance() 删除历史流程 historyService.deleteHistoricTaskInstance(taskid); 删除任务实例 historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list() 流程实例节点列表 当前进行到的那个节点的流程图使用 flowable 有api查看act_hi_varinst里面的数据吗 HistoricVariableInstanceQuery query historyService.createHistoricVariableInstanceQuery().processInstanceId(instance.getId()); HistoricVariableInstance operate query.variableName(operate).singleResult(); if (operate.getValue().toString().equals(1)) { // 表示流程同意 }5.TaskService对流程实例的各个节点的审批处理流转的节点审批taskService.createTaskQuery().list() 待办任务列表 taskService.createTaskQuery().taskId(taskId).singleResult(); 待办任务详情 taskService.saveTask(task); 修改任务 taskService.setAssignee() 设置审批人 taskService.addComment() 设置审批备注 taskService.complete() 完成当前审批 taskService.getProcessInstanceComments(processInstanceId); 查看任务详情也就是都经过哪些人的审批意见是什么 taskService.delegateTask(taskId, delegater); 委派任务 taskService.claim(taskId, userId);认领任务 taskService.unclaim(taskId); 取消认领 taskService.complete(taskId, completeVariables); 完成任务 任务授权 taskService.addGroupIdentityLink()新增组任务授权 taskService.addUserIdentityLink() 新增人员任务授权 taskService.deleteGroupIdentityLink() 删除组任务授权 taskService.deleteUserIdentityLink() 删除人员任务授权6.ManagementService主要是执行自定义命令 managementService.executeCommand(new classA()) 执行classA的内部方法 在自定义的方法中可以使用以下方法获取repositoryService ProcessEngineConfiguration processEngineConfiguration CommandContextUtil.getProcessEngineConfiguration(commandContext); RepositoryService repositoryService processEngineConfiguration.getRepositoryService(); 也可以获得流程定义方法集合 ProcessEngineConfigurationImpl processEngineConfiguration CommandContextUtil.getProcessEngineConfiguration(commandContext); ProcessDefinitionEntityManager processDefinitionEntityManager processEngineConfiguration.getProcessDefinitionEntityManager(); 如 findById/findLatestProcessDefinitionByKey/findLatestProcessDefinitionByKeyAndTenantId 等。7.IdentityService用于身份信息获取和保存这里主要是获取身份信息用于管理创建更新删除查询……组与用户。请注意Flowable实际上在运行时并不做任何用户检查。例如任务可以分派给任何用户而引擎并不会验证系统中是否存在该用户。这是因为Flowable有时要与LDAP、Active Directory等服务结合使用identityService.createUserQuery().userId(userId).singleResult(); 获取审批用户的具体信息 identityService.createGroupQuery().groupId(groupId).singleResult(); 获取审批组的具体信息8.DynamicBpmnService动态流程变更服务可用于修改流程定义中的部分内容而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置或者修改一个服务任务中的类名。1. 获取动态流程模型、实例相关// 获取流程定义的动态Bpmn模型可编辑 DynamicBpmnModel getDynamicBpmnModel(String processDefinitionId); // 获取单个流程实例的动态模型仅该实例专属改动 DynamicBpmnModel getDynamicBpmnModel(String processInstanceId, boolean fetchAllElements);2. 动态修改流程定义全局生效新实例使用// 保存对流程定义的动态修改全局 void saveDynamicBpmnModel(String processDefinitionId, DynamicBpmnModel dynamicBpmnModel); // 删除流程定义的所有动态变更恢复原始BPMN void removeDynamicBpmnModel(String processDefinitionId);3. 动态修改单个流程实例仅当前流程生效// 保存单个流程实例的专属动态修改 void saveDynamicBpmnModel(String processInstanceId, DynamicBpmnModel dynamicBpmnModel); // 清除某流程实例的动态变更恢复原始流程结构 void removeDynamicBpmnModel(String processInstanceId);4. 流程实例动态跳转、节点迁移高频业务方法// 将运行中的流程实例从当前节点迁移到目标节点动态跳转 void moveExecutionToNode(String executionId, String targetElementId); void moveExecutionsToNode(ListString executionIds, String targetElementId); // 带条件迁移支持跳过、回退、加签场景 void moveExecutionToNode(String executionId, String targetElementId, MapString, Object variables);5. 动态新增 / 删除任务节点、调整连线依托DynamicBpmnModel对象操作配合服务完成持久化addUserTask动态新增人工审批节点removeFlowElement删除原有节点addSequenceFlow新增流转连线、设置流转条件removeSequenceFlow删除分支连线updateFlowElementProperty修改节点属性负责人、表单、监听器等6. 查询动态变更记录// 判断流程定义是否存在动态修改 boolean hasDynamicBpmnModel(String processDefinitionId); // 判断流程实例是否有专属动态变更 boolean hasDynamicBpmnModel(String processInstanceId);九、数据库表说明(34张表)表分类表名表说明一般数据(2)ACT_GE_BYTEARRAY通用的流程定义和流程资源ACT_GE_PROPERTY系统相关属性流程历史记录(8)ACT_HI_ACTINST历史的流程实例ACT_HI_ATTACHMENT历史的流程附件ACT_HI_COMMENT历史的说明性信息ACT_HI_DETAIL历史的流程运行中的细节信息ACT_HI_IDENTITYLINK历史的流程运行过程中用户关系ACT_HI_PROCINST历史的流程实例ACT_HI_TASKINST历史的任务实例ACT_HI_VARINST历史的流程运行中的变量信息用户用户组表(9)ACT_ID_BYTEARRAY二进制数据表ACT_ID_GROUP用户组信息表ACT_ID_INFO用户信息详情表ACT_ID_MEMBERSHIP人与组关系表ACT_ID_PRIV权限表ACT_ID_PRIV_MAPPING用户或组权限关系表ACT_ID_PROPERTY属性表ACT_ID_TOKEN系统登录日志表ACT_ID_USER用户表流程定义表(3)ACT_RE_MODEL模型信息ACT_RE_DEPLOYMENT部署单元信息ACT_RE_PROCDEF已部署的流程定义运行实例表(10)ACT_RU_DEADLETTER_JOB正在运行的任务表ACT_RU_EVENT_SUBSCR运行时事件ACT_RU_EXECUTION运行时流程执行实例ACT_RU_HISTORY_JOB历史作业表ACT_RU_IDENTITYLINK运行时用户关系信息ACT_RU_JOB运行时作业表ACT_RU_SUSPENDED_JOB暂停作业表ACT_RU_TASK运行时任务表ACT_RU_TIMER_JOB定时作业表ACT_RU_VARIABLE运行时变量表其他表(2)ACT_EVT_LOG事件日志表ACT_PROCDEF_INFO流程定义信息
SpingBoot集成Flowable小试牛刀
发布时间:2026/7/13 14:43:16
前言 Flowable 项目提供了一套核心的开源业务流程引擎这些引擎紧凑且高效。它们为开发人员、系统管理员和业务用户提供工作流和业务流程管理 (BPM) 平台。 它的核心是一个闪电般快速、久经考验的动态 BPMN 流程引擎伴随着 DMN 决策表和 CMMN 案例管理引擎所有这些引擎都是用 Java 编写的。它们是 Apache 2.0 许可的开放源代码拥有一个承诺社区。 所有引擎都可以嵌入在 Java 应用程序中运行也可以作为服务器、集群和云中的服务运行。它们可以作为独立的引擎或服务运行也可以集成在一起为业务流程管理提供丰富的套件。它们与 Spring 完美集成。 凭借丰富的 Java 和 REST API它们是编排人工或机器人自动化处理的理想基础。它们启用微服务编排或软连接微服务本身。一、pom中引入Flowable相关框架本Demo使用的SpringBoot版本是2.7.5parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.5/version relativePath/ !-- lookup parent from repository -- /parentdependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 工作流flowable架包 -- dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter/artifactId version6.4.0/version /dependency !-- mysql数据库连接架包 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.11/version /dependency !-- mybatis ORM 架包 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.2.2/version /dependency !-- thymeleaf架包 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency二、相关配置文件1.application.properties配置文件server.port8081 #数据库配置 spring.datasource.urljdbc:mysql://localhost:3306/flowable01?autoReconnecttrueuseUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneGMT%2B8 spring.datasource.usernameroot spring.datasource.passwordsong1234 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver #开启调试信息 logging.level.org.flowableDEBUG #业务流程涉及的表自动生成 flowable.database-schema-updatetrue flowable.async-executor-activatefalse2.审批流程xml文件默认放置在resources下的processess文件夹下3.vacationRequest.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 typeLanguagehttp://www.w3.org/2001/XMLSchema expressionLanguagehttp://www.w3.org/1999/XPath targetNamespacehttp://www.flowable.org/processdef !-- -请假条流程图 -- process idvacationRequest name请假条流程 isExecutabletrue !-- -流程的开始 -- startEvent idstartEvent/ sequenceFlow sourceRefstartEvent targetRefapproveTask/ !-- -流程的节点 -- userTask idapproveTask name开始请假 flowable:candidateGroupsmanagers/ !-- -流程节点间的线条上一个节点和下一个节点-- sequenceFlow sourceRefapproveTask targetRefdecision/ !-- -排他性网关 -- exclusiveGateway iddecision/ !-- -同意时 -- sequenceFlow sourceRefdecision targetRefholidayApprovedTask conditionExpression xsi:typetFormalExpression ![CDATA[${approved}]] /conditionExpression /sequenceFlow !-- -拒绝时 -- sequenceFlow sourceRefdecision targetRefrejectEnd conditionExpression xsi:typetFormalExpression ![CDATA[${!approved}]] /conditionExpression /sequenceFlow !-- -外部服务 -- !-- serviceTask idexternalSystemCall nameEnter holidays in external system flowable:classorg.javaboy.flowable02.flowable.Approve/ sequenceFlow sourceRefexternalSystemCall targetRefholidayApprovedTask/ -- userTask idholidayApprovedTask flowable:assignee${employee} name同意请假/ sequenceFlow sourceRefholidayApprovedTask targetRefapproveEnd/ !-- serviceTask idrejectLeave nameSend out rejection email flowable:classorg.javaboy.flowable02.flowable.Reject/ sequenceFlow sourceRefrejectLeave targetRefrejectEnd/ -- endEvent idapproveEnd/ endEvent idrejectEnd/ !-- -流程的结束 -- /process /definitions三、控制层代码块package com.sxjg.controller; import com.sxjg.pojo.ResponseBean; import com.sxjg.pojo.VacationApproveVo; import com.sxjg.pojo.VacationRequestVo; import com.sxjg.service.VacationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; /** * project 请假流程测试 * Description * Author songwp * Date 2023/2/13 20:06 * Version 1.0.0 **/ RequestMapping(vacation) RestController public class VacationController { Autowired VacationService vacationService; /** * 请假条新增页面 * return */ GetMapping(/add) public ModelAndView add(){ return new ModelAndView(vacation); } /** * 请假条审批列表 * return */ GetMapping(/aList) public ModelAndView aList(){ return new ModelAndView(list); } /** * 请假条查询列表 * return */ GetMapping(/sList) public ModelAndView sList(){ return new ModelAndView(search); } /** * 请假请求方法 * param vacationRequestVO * return */ PostMapping public ResponseBean askForLeave(RequestBody VacationRequestVo vacationRequestVO) { return vacationService.askForLeave(vacationRequestVO); } /** * 获取待审批列表 * param identity * return */ GetMapping(/list) public ResponseBean leaveList(String identity) { return vacationService.leaveList(identity); } /** * 拒绝或同意请假 * param vacationVO * return */ PostMapping(/handler) public ResponseBean askForLeaveHandler(RequestBody VacationApproveVo vacationVO) { return vacationService.askForLeaveHandler(vacationVO); } /** * 请假查询 * param name * return */ GetMapping(/search) public ResponseBean searchResult(String name) { return vacationService.searchResult(name); } }四、Service层请假条新增、审批、查询的业务处理package com.sxjg.service; import com.sxjg.pojo.ResponseBean; import com.sxjg.pojo.VacationApproveVo; import com.sxjg.pojo.VacationInfo; import com.sxjg.pojo.VacationRequestVo; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.task.api.Task; import org.flowable.variable.api.history.HistoricVariableInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * project 请假流程测试 * Description * Author songwp * Date 2023/2/13 20:08 * Version 1.0.0 **/ Service public class VacationService { Autowired RuntimeService runtimeService; Autowired TaskService taskService; Autowired HistoryService historyService; /** * 申请请假 * param vacationRequestVO * return */ Transactional public ResponseBean askForLeave(VacationRequestVo vacationRequestVO) { MapString, Object variables new HashMap(); variables.put(name, vacationRequestVO.getName()); variables.put(days, vacationRequestVO.getDays()); variables.put(reason, vacationRequestVO.getReason()); try { //指定业务流程 runtimeService.startProcessInstanceByKey(vacationRequest, vacationRequestVO.getName(), variables); return ResponseBean.ok(已提交请假申请); } catch (Exception e) { e.printStackTrace(); } return ResponseBean.error(提交申请失败); } /** * 审批列表 * param identity * return */ public ResponseBean leaveList(String identity) { ListTask tasks taskService.createTaskQuery().taskCandidateGroup(identity).list(); ListMapString, Object list new ArrayList(); for (int i 0; i tasks.size(); i) { Task task tasks.get(i); MapString, Object variables taskService.getVariables(task.getId()); variables.put(id, task.getId()); list.add(variables); } return ResponseBean.ok(加载成功, list); } /** * 操作审批 * param vacationVO * return */ public ResponseBean askForLeaveHandler(VacationApproveVo vacationVO) { try { boolean approved vacationVO.getApprove(); MapString, Object variables new HashMapString, Object(); variables.put(approved, approved); variables.put(employee, vacationVO.getName()); Task task taskService.createTaskQuery().taskId(vacationVO.getTaskId()).singleResult(); taskService.complete(task.getId(), variables); if (approved) { //如果是同意还需要继续走一步 Task t taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); taskService.complete(t.getId()); } return ResponseBean.ok(操作成功); } catch (Exception e) { e.printStackTrace(); } return ResponseBean.error(操作失败); } /** * 请假列表 * param name * return */ public ResponseBean searchResult(String name) { ListVacationInfo vacationInfos new ArrayList(); ListHistoricProcessInstance historicProcessInstances historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).finished().orderByProcessInstanceEndTime().desc().list(); for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) { VacationInfo vacationInfo new VacationInfo(); Date startTime historicProcessInstance.getStartTime(); Date endTime historicProcessInstance.getEndTime(); ListHistoricVariableInstance historicVariableInstances historyService.createHistoricVariableInstanceQuery() .processInstanceId(historicProcessInstance.getId()) .list(); for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) { String variableName historicVariableInstance.getVariableName(); Object value historicVariableInstance.getValue(); if (reason.equals(variableName)) { vacationInfo.setReason((String) value); } else if (days.equals(variableName)) { vacationInfo.setDays(Integer.parseInt(value.toString())); } else if (approved.equals(variableName)) { vacationInfo.setStatus((Boolean) value); } else if (name.equals(variableName)) { vacationInfo.setName((String) value); } } vacationInfo.setStartTime(startTime); vacationInfo.setEndTime(endTime); vacationInfos.add(vacationInfo); } return ResponseBean.ok(ok, vacationInfos); } }五、POJO相关类import lombok.Data; /** * 请假条审批 * Date */ Data public class VacationApproveVo { private String taskId; private Boolean approve; private String name; } import lombok.Data; /** * 请假条申请 * Date */ Data public class VacationRequestVo { private String name; private Integer days; private String reason; } import lombok.Data; /** * 响应类 * Date */ Data public class ResponseBean { private Integer status; private String msg; private Object data; public static ResponseBean ok(String msg, Object data) { return new ResponseBean(200, msg, data); } public static ResponseBean ok(String msg) { return new ResponseBean(200, msg, null); } public static ResponseBean error(String msg, Object data) { return new ResponseBean(500, msg, data); } public static ResponseBean error(String msg) { return new ResponseBean(500, msg, null); } private ResponseBean() { } private ResponseBean(Integer status, String msg, Object data) { this.status status; this.msg msg; this.data data; } } import java.util.Date; import lombok.Data; /** * 请假条DO * Date */ Data public class VacationInfo { private String name; private Date startTime; private Date endTime; private String reason; private Integer days; private Boolean status; }六、页面代码页面文件放在resources的templates文件夹下1.提交请假条申请页面vacation.html!DOCTYPE html html langen head meta charsetUTF-8 title提交请假条申请页面/title script srchttps://unpkg.com/axios/dist/axios.min.js/script !-- Import style -- link relstylesheet hrefhttps://unpkg.com/element-plus/dist/index.css/ script srchttps://unpkg.com/vue3/script !-- Import component library -- script src//unpkg.com/element-plus/script /head body div idapp h1开始一个请假流程/h1 table tr td请输入姓名/td td el-input typetext v-modelafl.name/ /td /tr tr td请输入请假天数/td td el-input typetext v-modelafl.days/ /td /tr tr td请输入请假理由/td td el-input typetext v-modelafl.reason/ /td /tr /table el-button typeprimary clicksubmit提交请假申请/el-button /div script Vue.createApp( { data() { return { afl: { name: test, days: 3, reason: 测试 } } }, methods: { submit() { let _this this; axios.post(/vacation, this.afl) .then(function (response) { if (response.data.status 200) { //提交成功 _this.$message.success(response.data.msg); } else { //提交失败 _this.$message.error(response.data.msg); } }) .catch(function (error) { console.log(error); }); } } } ).use(ElementPlus).mount(#app) /script /body /html2.审批请假条页面list.html!DOCTYPE html html langen head meta charsetUTF-8 title审批请假条页面/title script srchttps://unpkg.com/axios/dist/axios.min.js/script !-- Import style -- link relstylesheet hrefhttps://unpkg.com/element-plus/dist/index.css/ script srchttps://unpkg.com/vue3/script !-- Import component library -- script src//unpkg.com/element-plus/script /head body div idapp div div请选择你的身份/div div el-select name id v-modelidentity changeinitTasks el-option :valueiden v-for(iden,index) in identities :keyindex :labeliden/el-option /el-select el-button typeprimary clickinitTasks刷新一下/el-button /div /div el-table border strip :datatasks el-table-column propname label姓名/el-table-column el-table-column propdays label请假天数/el-table-column el-table-column propreason label请假原因/el-table-column el-table-column lable操作 template #defaultscope el-button typeprimary clickapproveOrReject(scope.row.id,true,scope.row.name)批准/el-button el-button typedanger clickapproveOrReject(scope.row.id,false,scope.row.name)拒绝/el-button /template /el-table-column /el-table /div script Vue.createApp( { data() { return { tasks: [], identities: [ managers ], identity: } }, methods: { initTasks() { let _this this; axios.get(/vacation/list?identity this.identity) .then(function (response) { _this.tasks response.data.data; }) .catch(function (error) { console.log(error); }); }, approveOrReject(taskId, approve,name) { let _this this; axios.post(/vacation/handler, {taskId: taskId, approve: approve,name:name}) .then(function (response) { _this.$message.success(审批成功); _this.initTasks(); }) .catch(function (error) { _this.$message.error(操作失败); console.log(error); }); } } } ).use(ElementPlus).mount(#app) /script /body /html3.已审批请假条查询页面search.html!DOCTYPE html html langen head meta charsetUTF-8 title已审批请假条查询页面/title script srchttps://unpkg.com/axios/dist/axios.min.js/script !-- Import style -- link relstylesheet hrefhttps://unpkg.com/element-plus/dist/index.css/ script srchttps://unpkg.com/vue3/script !-- Import component library -- script src//unpkg.com/element-plus/script /head body div idapp div stylemargin-top: 50px el-input v-modelname stylewidth: 300px placeholder请输入用户名/el-input el-button typeprimary clicksearch查询/el-button /div div el-table border strip :datahistoryInfos el-table-column propname label姓名/el-table-column el-table-column propstartTime label提交时间/el-table-column el-table-column propendTime label审批时间/el-table-column el-table-column propreason label事由/el-table-column el-table-column propdays label天数/el-table-column el-table-column label状态 template #defaultscope el-tag typesuccess v-ifscope.row.status已通过/el-tag el-tag typedanger v-else已拒绝/el-tag /template /el-table-column /el-table /div /div script Vue.createApp( { data() { return { historyInfos: [], name: zhangsan } }, methods: { search() { let _this this; axios.get(/vacation/search?name this.name) .then(function (response) { if (response.data.status 200) { _this.historyInfosresponse.data.data; } else { _this.$message.error(response.data.msg); } }) .catch(function (error) { console.log(error); }); } } } ).use(ElementPlus).mount(#app) /script /body /html七、启动并测试1.第一次运行系统会自动创建flowable需要数据表结构2.输入url地址localhost:8081/vacation/add建立几个请假条3.请假条建立好了审批处理一下注意第一次运行这个demo权限暂且不管角色也先写死先把demo跑起来再说。四个请假条两个通过两个拒绝操作完成后在待审批列表不在出现4.作为请假人查询一下自己提交的假条审批了.通过查询结果得知两个通过两个拒绝。至此一个简单的请假条审批流程走完了八、API梳理说明1.FormService表单数据的管理 是可选服务也就是说Flowable没有它也能很好地运行而不必牺牲任何功能。这个服务引入了开始表单(start form)与任务表单(task form)的概念。 开始表单是在流程实例启动前显示的表单而任务表单是用户完成任务时显示的表单。Flowable可以在BPMN 2.0流程定义中定义这些表单。表单服务通过简单的方式暴露这些数据。再次重申表单不一定要嵌入流程定义因此这个服务是可选的formService.getStartFormKey() // 获取表单key formService.getRenderedStartForm() // 查询表单json无数据2.RepositiryService提供了在编辑和发布审批流程的api。主要是模型管理和流程定义的业务api这个服务提供了管理与控制部署(deployments)与流程定义(process definitions)的操作查询引擎现有的部署与流程定义。 暂停或激活部署中的某些流程或整个部署。暂停意味着不能再对它进行操作激活刚好相反重新使它可以操作。 获取各种资源比如部署中保存的文件或者引擎自动生成的流程图。 获取POJO版本的流程定义。它可以用Java而不是XML的方式查看流程。1.提供了带条件的查询模型流程定义的api repositoryService.createXXXQuery() 例如 repositoryService.createModelQuery().list() 模型查询 repositoryService.createProcessDefinitionQuery().list() 流程定义查询 repositoryService.createXXXXQuery().XXXKey(XXX) 查询该key是否存在 2.提供一大波模型与流程定义的通用方法 模型相关 repositoryService.getModel() 获取模型 repositoryService.saveModel() 保存模型 repositoryService.deleteModel() 删除模型 repositoryService.createDeployment().deploy(); 部署模型 repositoryService.getModelEditorSource() 获得模型JSON数据的UTF8字符串 repositoryService.getModelEditorSourceExtra() 获取PNG格式图像 3.流程定义相关 repositoryService.getProcessDefinition(ProcessDefinitionId); 获取流程定义具体信息 repositoryService.activateProcessDefinitionById() 激活流程定义 repositoryService.suspendProcessDefinitionById() 挂起流程定义 repositoryService.deleteDeployment() 删除流程定义 repositoryService.getProcessDiagram()获取流程定义图片流 repositoryService.getResourceAsStream()获取流程定义xml流 repositoryService.getBpmnModel(pde.getId()) 获取bpmn对象当前进行到的那个节点的流程图使用 4.流程定义授权相关 repositoryService.getIdentityLinksForProcessDefinition() 流程定义授权列表 repositoryService.addCandidateStarterGroup()新增组流程授权 repositoryService.addCandidateStarterUser()新增用户流程授权 repositoryService.deleteCandidateStarterGroup() 删除组流程授权 repositoryService.deleteCandidateStarterUser() 删除用户流程授权3.RuntimeService处理正在运行的流程runtimeService.createProcessInstanceBuilder().start() 发起流程 runtimeService.deleteProcessInstance() 删除正在运行的流程 runtimeService.suspendProcessInstanceById() 挂起流程定义 runtimeService.activateProcessInstanceById() 激活流程实例 runtimeService.getVariables(processInstanceId); 获取表单中填写的值 runtimeService.getActiveActivityIds(processInstanceId)获取以进行的流程图节点 当前进行到的那个节点的流程图使用 runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState(); 终止流程4.HistoryService在用户发起审批后会生成流程实例。historyService为处理流程实例的api但是其中包括了已完成的和未完成的流程实例 如果是处理正在运行的流程实例请使用runtimeService暴露Flowable引擎收集的所有历史数据。当执行流程时引擎会保存许多数据可配置例如流程实例启动时间、谁在执行哪个任务、完成任务花费的事件、每个流程实例的执行路径等等。这个服务主要提供查询这些数据的能力historyService.createHistoricProcessInstanceQuery().list() 查询流程实例列表历史流程,包括未完成的 historyService.createHistoricProcessInstanceQuery().list().foreach().getValue() 可以获取历史中表单的信息 historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 根绝id查询流程实例 historyService.deleteHistoricProcessInstance() 删除历史流程 historyService.deleteHistoricTaskInstance(taskid); 删除任务实例 historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list() 流程实例节点列表 当前进行到的那个节点的流程图使用 flowable 有api查看act_hi_varinst里面的数据吗 HistoricVariableInstanceQuery query historyService.createHistoricVariableInstanceQuery().processInstanceId(instance.getId()); HistoricVariableInstance operate query.variableName(operate).singleResult(); if (operate.getValue().toString().equals(1)) { // 表示流程同意 }5.TaskService对流程实例的各个节点的审批处理流转的节点审批taskService.createTaskQuery().list() 待办任务列表 taskService.createTaskQuery().taskId(taskId).singleResult(); 待办任务详情 taskService.saveTask(task); 修改任务 taskService.setAssignee() 设置审批人 taskService.addComment() 设置审批备注 taskService.complete() 完成当前审批 taskService.getProcessInstanceComments(processInstanceId); 查看任务详情也就是都经过哪些人的审批意见是什么 taskService.delegateTask(taskId, delegater); 委派任务 taskService.claim(taskId, userId);认领任务 taskService.unclaim(taskId); 取消认领 taskService.complete(taskId, completeVariables); 完成任务 任务授权 taskService.addGroupIdentityLink()新增组任务授权 taskService.addUserIdentityLink() 新增人员任务授权 taskService.deleteGroupIdentityLink() 删除组任务授权 taskService.deleteUserIdentityLink() 删除人员任务授权6.ManagementService主要是执行自定义命令 managementService.executeCommand(new classA()) 执行classA的内部方法 在自定义的方法中可以使用以下方法获取repositoryService ProcessEngineConfiguration processEngineConfiguration CommandContextUtil.getProcessEngineConfiguration(commandContext); RepositoryService repositoryService processEngineConfiguration.getRepositoryService(); 也可以获得流程定义方法集合 ProcessEngineConfigurationImpl processEngineConfiguration CommandContextUtil.getProcessEngineConfiguration(commandContext); ProcessDefinitionEntityManager processDefinitionEntityManager processEngineConfiguration.getProcessDefinitionEntityManager(); 如 findById/findLatestProcessDefinitionByKey/findLatestProcessDefinitionByKeyAndTenantId 等。7.IdentityService用于身份信息获取和保存这里主要是获取身份信息用于管理创建更新删除查询……组与用户。请注意Flowable实际上在运行时并不做任何用户检查。例如任务可以分派给任何用户而引擎并不会验证系统中是否存在该用户。这是因为Flowable有时要与LDAP、Active Directory等服务结合使用identityService.createUserQuery().userId(userId).singleResult(); 获取审批用户的具体信息 identityService.createGroupQuery().groupId(groupId).singleResult(); 获取审批组的具体信息8.DynamicBpmnService动态流程变更服务可用于修改流程定义中的部分内容而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置或者修改一个服务任务中的类名。1. 获取动态流程模型、实例相关// 获取流程定义的动态Bpmn模型可编辑 DynamicBpmnModel getDynamicBpmnModel(String processDefinitionId); // 获取单个流程实例的动态模型仅该实例专属改动 DynamicBpmnModel getDynamicBpmnModel(String processInstanceId, boolean fetchAllElements);2. 动态修改流程定义全局生效新实例使用// 保存对流程定义的动态修改全局 void saveDynamicBpmnModel(String processDefinitionId, DynamicBpmnModel dynamicBpmnModel); // 删除流程定义的所有动态变更恢复原始BPMN void removeDynamicBpmnModel(String processDefinitionId);3. 动态修改单个流程实例仅当前流程生效// 保存单个流程实例的专属动态修改 void saveDynamicBpmnModel(String processInstanceId, DynamicBpmnModel dynamicBpmnModel); // 清除某流程实例的动态变更恢复原始流程结构 void removeDynamicBpmnModel(String processInstanceId);4. 流程实例动态跳转、节点迁移高频业务方法// 将运行中的流程实例从当前节点迁移到目标节点动态跳转 void moveExecutionToNode(String executionId, String targetElementId); void moveExecutionsToNode(ListString executionIds, String targetElementId); // 带条件迁移支持跳过、回退、加签场景 void moveExecutionToNode(String executionId, String targetElementId, MapString, Object variables);5. 动态新增 / 删除任务节点、调整连线依托DynamicBpmnModel对象操作配合服务完成持久化addUserTask动态新增人工审批节点removeFlowElement删除原有节点addSequenceFlow新增流转连线、设置流转条件removeSequenceFlow删除分支连线updateFlowElementProperty修改节点属性负责人、表单、监听器等6. 查询动态变更记录// 判断流程定义是否存在动态修改 boolean hasDynamicBpmnModel(String processDefinitionId); // 判断流程实例是否有专属动态变更 boolean hasDynamicBpmnModel(String processInstanceId);九、数据库表说明(34张表)表分类表名表说明一般数据(2)ACT_GE_BYTEARRAY通用的流程定义和流程资源ACT_GE_PROPERTY系统相关属性流程历史记录(8)ACT_HI_ACTINST历史的流程实例ACT_HI_ATTACHMENT历史的流程附件ACT_HI_COMMENT历史的说明性信息ACT_HI_DETAIL历史的流程运行中的细节信息ACT_HI_IDENTITYLINK历史的流程运行过程中用户关系ACT_HI_PROCINST历史的流程实例ACT_HI_TASKINST历史的任务实例ACT_HI_VARINST历史的流程运行中的变量信息用户用户组表(9)ACT_ID_BYTEARRAY二进制数据表ACT_ID_GROUP用户组信息表ACT_ID_INFO用户信息详情表ACT_ID_MEMBERSHIP人与组关系表ACT_ID_PRIV权限表ACT_ID_PRIV_MAPPING用户或组权限关系表ACT_ID_PROPERTY属性表ACT_ID_TOKEN系统登录日志表ACT_ID_USER用户表流程定义表(3)ACT_RE_MODEL模型信息ACT_RE_DEPLOYMENT部署单元信息ACT_RE_PROCDEF已部署的流程定义运行实例表(10)ACT_RU_DEADLETTER_JOB正在运行的任务表ACT_RU_EVENT_SUBSCR运行时事件ACT_RU_EXECUTION运行时流程执行实例ACT_RU_HISTORY_JOB历史作业表ACT_RU_IDENTITYLINK运行时用户关系信息ACT_RU_JOB运行时作业表ACT_RU_SUSPENDED_JOB暂停作业表ACT_RU_TASK运行时任务表ACT_RU_TIMER_JOB定时作业表ACT_RU_VARIABLE运行时变量表其他表(2)ACT_EVT_LOG事件日志表ACT_PROCDEF_INFO流程定义信息