好文推荐:
jdk8之stream流式编程
2.5万字讲解DDD领域驱动设计(史上最全DDD)
springboot 实现延时队列(超级实用)
2.5万字详解23种设计模式
目录
1. 前言
目前工作流框架最火的就是Activiti和Flowable,该文章介绍如何使用flowable,如果不想看理论的概念,可以直接跳到第五部分springboot集成flowable,或者文末直接获取代码!!!
2. 背景
- Flowable起源于Activiti工作流引擎,由Activiti的主要开发者在2016年创建。它继承了Activiti的众多优点,并在此基础上进行了优化和改进,以提供更加稳定、高效的工作流管理解决方案。Flowable与Activiti有着共同的祖先,即jbpm,并随着技术的发展和需求的变化,逐渐发展成为独立且功能强大的工作流引擎。
- 应用场景:Flowable广泛应用于各种需要流程管理的场景,如人力资源管理(如员工入职、离职、请假、绩效评估等)、自动化业务流程(如财务审批、采购流程、销售订单处理等)、任务管理和分配等。在由流程驱动的各种系统中,如OA、CRM、ERP、ECM、BI等,Flowable都能发挥重要作用。 优势:
- 轻量级与高效:Flowable是一个轻量级的引擎,启动快,内存占用小,非常适合在微服务架构中使用。
- 全面支持BPMN 2.0标准:允许使用标准化的方式来定义和执行流程,提高了流程的兼容性和可移植性。
- 丰富的API和可视化设计工具:降低了与其他系统的集成难度,提高了业务流程的建模和编辑效率。
- 良好的社区支持和文档:作为一个活跃的开源项目,Flowable拥有良好的社区支持和不断更新的文档,用户可以在社区中获取帮助和分享经验。
3. flowable相关概念
Flowable是一个功能强大的业务流程管理引擎,支持BPMN 2.0标准。以下是对Flowable相关概念的具体介绍:
3.1 流程定义:
流程定义(Process Definition)是使用BPMN 2.0标准的XML格式描述的,它包含了流程中的节点、连接线和事件等元素。
对应的类:org.flowable.engine.repository.ProcessDefinition
3.2 流程实例:
流程实例(Process Instance)当一个流程定义被启动时,会创建一个流程实例,这个实例将按照定义的流程节点顺序执行。
对应的类:org.flowable.engine.runtime.ProcessInstance
3.3 任务:
任务(Task)是流程实例中的一个执行单元,代表需要由用户或系统自动完成的操作。在Flowable中,任务可以是用户任务、服务任务、脚本任务等多种类型。
对应的类:org.flowable.engine.task.Task
3.4 网关:
网关(Gateway)用于控制流程的执行方向,Flowable支持多种类型的网关,如排他网关和并行网关等。排他网关用于在多个分支中选择一个分支进行执行,而并行网关则用于将流程拆分为多个并行执行的分支。
对应的类:
org.flowable.bpmn.model.ParallelGateway(并行网关)
org.flowable.bpmn.model.ExclusiveGateway(排他网关)
3.5 条件表达式
条件表达式(Conditional Expression):Flowable支持使用条件表达式来控制流程的执行方向。条件表达式使用Java的语法,可以在排他网关等地方使用,根据条件表达式的值来决定流程应该走向哪个分支。
3.6 边界事件
边界事件(Boundary Event):边界事件可以捕获流程中的错误或异常事件,并在事件发生时执行相应的处理逻辑。例如,可以在用户任务中添加一个错误边界事件,当用户任务执行失败时,触发错误边界事件进行错误处理。
对应的类:
org.flowable.bpmn.model.StartEvent(开始事件)
org.flowable.bpmn.model.EndEvent(结束事件)
org.flowable.bpmn.model.BoundaryEvent(边界事件)
总的来说,Flowable作为一个轻量级的业务流程引擎,提供了丰富的流程控制元素和灵活的扩展机制,通过掌握其核心概念和常见问题的解决方案,开发者可以更加高效地构建工作流应用,提高业务流程的自动化水平和执行效率。
4. 配置工作流模型
4.1 请假工作流示意图
员工请假案例:
4.2 使用idea插件Flowable BPMN visualizer
Flowable BPMN visualizer是一款为IntelliJ IDEA系列IDE设计的插件,它提供了一个强大的BPMN(Business Process Model and Notation,即业务流程模型和表示法)模型编辑工具.
- 安装idea插件Flowable BPMN visualizer
- 在resources文件夹下创建文件夹processes
- 然后创建文件leave.bpmn20.xml
- 选中leave.bpmn20.xml文件右键选择View BPMN (Flowable) Diagram
- 然后就可以开始制作流程图了,右键会有各种工具 重要的工具已标出
4.3 插件里面的工具介绍
右键会出现各种工具
- Start Events(开始事件) Start Event: 流程的起点,手动触发。(重要) Start Conditional Event: 条件满足时自动触发。 Start Message Event: 接收到特定消息时自动触发。 Start Error Event: 发生指定错误时自动触发。 Start Escalation Event: 需要升级或转交给更高级别人员时自动触发。 Start Signal Event: 接收到特定信号时自动触发。 Start Timer Event: 经过特定时间后自动触发。
- Activities(活动) Task: 用户执行的任务,如审批、填写表单等。 Service Task: 系统自动执行的任务,如调用外部服务。 User Task: 分配给用户执行的任务。(重要) Script Task: 使用脚本语言(如Groovy、JavaScript)编写的自定义任务。 Business Rule Task: 基于业务规则引擎执行的任务。 Manual Task: 需要人工干预的任务。
- Structural(结构) Subprocess: 子流程,表示一个嵌套的流程。 Transaction: 事务,确保一组操作要么全部成功,要么全部失败。 Ad-hoc Subprocess: 临时子流程,可以在运行时动态创建。 Event Subprocess: 事件子流程,与特定事件相关联。
- Gateways(网关) Exclusive Gateway: 排他网关,根据条件选择一条路径执行。(重要) Parallel Gateway: 并行网关,将流程分成多个并行分支。 Inclusive Gateway: 包容网关,允许多条路径同时执行。 Complex Gateway: 复杂网关,结合了排他网关和并行网关的特性。
- Boundary Events(边界事件) Boundary Timer Event: 定时边界事件,当到达指定时间点时触发。 Boundary Error Event: 错误边界事件,当发生指定错误时触发。 Boundary Signal Event: 信号边界事件,当接收到指定信号时触发。 Boundary Message Event: 消息边界事件,当接收到指定消息时触发。 Boundary Cancel Event: 取消边界事件,当流程被取消时触发。 Boundary Compensation Event: 补偿边界事件,用于处理补偿逻辑。
- Intermediate Catching Events(中间捕获事件) Intermediate Message Event: 中间消息事件,等待接收到特定消息时触发。 Intermediate Timer Event: 中间定时事件,等待到达指定时间点时触发。 Intermediate Signal Event: 中间信号事件,等待接收到特定信号时触发。 Intermediate Conditional Event: 中间条件事件,等待满足特定条件时触发。
- Intermediate Throwing Events(中间抛出事件) Intermediate Message Throw Event: 中间消息抛出事件,发送消息给其他流程或系统。 Intermediate Signal Throw Event: 中间信号抛出事件,发送信号给其他流程或系统。 Intermediate Escalation Event: 中间升级事件,将问题升级或转交给更高级别的人员或系统。 Intermediate Link Event: 中间链接事件,用于跨流程的通信。
- End Events(结束事件) End Event: 流程的正常结束点。(重要) Error End Event: 流程的错误结束点。 Escalation End Event: 流程的升级结束点。 Message End Event: 流程的消息结束点,发送消息给其他流程或系统。 Signal End Event: 流程的信号结束点,发送信号给其他流程或系统。 Terminate End Event: 流程的终止结束点,强制终止流程。
- Save to PNG Save to PNG: 将当前BPMN模型保存为PNG格式的图片文件,便于分享和展示。
5. springboot集成flowable
5.1 pom依赖文件
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.2</version><relativePath/></parent><dependencies><!-- 工作流flowable jar包 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--Mybatis-Plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>3.5.2</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency></dependencies>
3.2 application.yml
# 工作流 Flowable 配置
flowable:
check-process-definitions:true # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程
history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
#关闭定时任务JOB
async-executor-activate:false
5.3 flowable 配置文件
importorg.flowable.spring.SpringProcessEngineConfiguration;importorg.flowable.spring.boot.EngineConfigurationConfigurer;importorg.springframework.context.annotation.Configuration;/**
* flowable配置
*/@ConfigurationpublicclassFlowableConfigimplementsEngineConfigurationConfigurer<SpringProcessEngineConfiguration>{@Overridepublicvoidconfigure(SpringProcessEngineConfiguration engineConfiguration){
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");}}
5.4 流程任务实体
importlombok.Data;importjava.util.Map;/**
* @author wdyin
* @date 2024/11/15
**/@DatapublicclassTaskVo{/**
* 任务id
*/privateString taskId;/**
* 任务名称
*/privateString taskName;/**
* 流程实例id
*/privateString processInstanceId;/**
* 流程变量
*/Map<String,Object> processVariables;}
5.5 bpmn20.xml文件
leave.bpmn20.xml,工作流模型配置请看第四部分使用idea插件可视化制作工作流模型
<?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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"typeLanguage="http://www.w3.org/2001/XMLSchema"expressionLanguage="http://www.w3.org/1999/XPath"targetNamespace="http://www.flowable.org/processdef"><process id="leave"name="leave"isExecutable="true"><startEvent id="sid-7271f156-c78c-403a-9d19-73ccbfdd9881"name="开始请假流程"><documentation>员工开始请假流程</documentation></startEvent><userTask id="sid-bd605c32-03cb-4e04-86cb-fa93d7e5ef83"name="请假申请" flowable:assignee="${assignee}"><documentation>员工请假申请</documentation></userTask><sequenceFlow id="sid-ae7fd113-b1b7-4dc8-a276-f7355ef22d5c"sourceRef="sid-7271f156-c78c-403a-9d19-73ccbfdd9881"targetRef="sid-bd605c32-03cb-4e04-86cb-fa93d7e5ef83"name="流程开始"><documentation>流程开始</documentation></sequenceFlow><userTask id="sid-617c05f7-8e8d-4734-b831-f601443701df"name="领导审批" flowable:assignee="${assignee}"><documentation>领导审批</documentation></userTask><sequenceFlow id="sid-53772e79-4146-49d9-a04e-fefefccfe21d"sourceRef="sid-bd605c32-03cb-4e04-86cb-fa93d7e5ef83"targetRef="sid-617c05f7-8e8d-4734-b831-f601443701df"name="申请流程"/><sequenceFlow id="sid-5b9c0f8c-594b-4dac-a07d-0ef68f5b0b54"sourceRef="sid-617c05f7-8e8d-4734-b831-f601443701df"targetRef="sid-bd605c32-03cb-4e04-86cb-fa93d7e5ef83"name="领导审批驳回"><conditionExpression xsi:type="tFormalExpression">${result==false}</conditionExpression></sequenceFlow><exclusiveGateway id="sid-cb1bce6f-ecef-48aa-b4cb-7dee1f76fdb6"/><sequenceFlow id="sid-f433236a-6e6f-46ba-9799-b240cf151d76"sourceRef="sid-617c05f7-8e8d-4734-b831-f601443701df"targetRef="sid-cb1bce6f-ecef-48aa-b4cb-7dee1f76fdb6"name="领导审批通过"><conditionExpression xsi:type="tFormalExpression">${result==true}</conditionExpression></sequenceFlow><endEvent id="sid-68b72d73-e005-4403-b454-5c8f5d99745d"/><sequenceFlow id="sid-575778a8-b18b-4542-b11a-dd2965b09a30"sourceRef="sid-cb1bce6f-ecef-48aa-b4cb-7dee1f76fdb6"targetRef="sid-68b72d73-e005-4403-b454-5c8f5d99745d"name="请假小于两天"><conditionExpression xsi:type="tFormalExpression">${day<2}</conditionExpression></sequenceFlow><userTask id="sid-afa9dbd8-3aaf-44a5-8ae0-c2035cb2a698"name="老板审批" flowable:assignee="${assignee}"><documentation>老板审批</documentation></userTask><sequenceFlow id="sid-30725bf0-64b7-45b5-a14b-914c27462e2d"sourceRef="sid-cb1bce6f-ecef-48aa-b4cb-7dee1f76fdb6"targetRef="sid-afa9dbd8-3aaf-44a5-8ae0-c2035cb2a698"name="请假大于等于两天"><conditionExpression xsi:type="tFormalExpression">${day>=2}</conditionExpression></sequenceFlow><sequenceFlow id="sid-9b841925-57c1-4e6b-8ec8-ac4a58d2b80c"sourceRef="sid-afa9dbd8-3aaf-44a5-8ae0-c2035cb2a698"targetRef="sid-68b72d73-e005-4403-b454-5c8f5d99745d"name="老板审批通过"><documentation>老板不同意</documentation><conditionExpression xsi:type="tFormalExpression">${result==true}</conditionExpression></sequenceFlow><sequenceFlow id="sid-0d2f0355-dded-45a9-8474-84893afae40d"sourceRef="sid-afa9dbd8-3aaf-44a5-8ae0-c2035cb2a698"targetRef="sid-7271f156-c78c-403a-9d19-73ccbfdd9881"name="老板审批驳回"><conditionExpression xsi:type="tFormalExpression">${result==false}</conditionExpression></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_leave"><bpmndi:BPMNPlane bpmnElement="leave"id="BPMNPlane_leave"><bpmndi:BPMNShape id="shape-bc3b6696-4835-4f6e-9458-e77ba9666cac"bpmnElement="sid-7271f156-c78c-403a-9d19-73ccbfdd9881"><omgdc:Bounds x="-330.0"y="-170.0"width="30.0"height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-2ccaef2e-ca0c-43d9-9965-d0eda82b2633"bpmnElement="sid-bd605c32-03cb-4e04-86cb-fa93d7e5ef83"><omgdc:Bounds x="-275.0"y="-180.0"width="55.0"height="50.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-9f7f9b61-5efe-4203-9010-822a74a103ce"bpmnElement="sid-ae7fd113-b1b7-4dc8-a276-f7355ef22d5c"><omgdi:waypoint x="-300.0"y="-155.0"/><omgdi:waypoint x="-275.0"y="-155.0"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="sid-d4255997-664d-4c6d-903b-38c48a453d09"bpmnElement="sid-617c05f7-8e8d-4734-b831-f601443701df"><omgdc:Bounds x="-178.5"y="-180.0"width="55.0"height="50.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-a0b9bc01-a881-4f1f-87e0-363c0d904811"bpmnElement="sid-53772e79-4146-49d9-a04e-fefefccfe21d"><omgdi:waypoint x="-220.0"y="-155.0"/><omgdi:waypoint x="-178.5"y="-155.0"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-1cd38e63-1d71-4aea-b340-5ca4886333b3"bpmnElement="sid-5b9c0f8c-594b-4dac-a07d-0ef68f5b0b54"><omgdi:waypoint x="-151.0"y="-180.0"/><omgdi:waypoint x="-151.0"y="-222.5"/><omgdi:waypoint x="-247.5"y="-222.5"/><omgdi:waypoint x="-247.5"y="-180.0"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-8f3a8a11-80b2-43cc-b754-ee73faf7c91c"bpmnElement="sid-cb1bce6f-ecef-48aa-b4cb-7dee1f76fdb6"><omgdc:Bounds x="-75.0"y="-175.0"width="40.0"height="40.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-2459964a-c3e5-49a9-b182-0a1a9f1576b2"bpmnElement="sid-f433236a-6e6f-46ba-9799-b240cf151d76"><omgdi:waypoint x="-123.5"y="-155.0"/><omgdi:waypoint x="-75.0"y="-155.0"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-b4874158-c2df-4d2a-8467-21cc5488e5f6"bpmnElement="sid-68b72d73-e005-4403-b454-5c8f5d99745d"><omgdc:Bounds x="10.0"y="-170.0"width="30.0"height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-793e0ef5-d447-41b5-8aa7-ebbfd9ea0ce8"bpmnElement="sid-575778a8-b18b-4542-b11a-dd2965b09a30"><omgdi:waypoint x="-35.0"y="-155.0"/><omgdi:waypoint x="10.0"y="-155.0"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="sid-1a7cb41a-8eb2-4db9-9237-7ab7d1d802ce"bpmnElement="sid-afa9dbd8-3aaf-44a5-8ae0-c2035cb2a698"><omgdc:Bounds x="-82.5"y="-82.25"width="55.0"height="50.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-781d3fc5-dea4-4ec3-8741-1adcc1a03995"bpmnElement="sid-30725bf0-64b7-45b5-a14b-914c27462e2d"><omgdi:waypoint x="-55.0"y="-135.0"/><omgdi:waypoint x="-55.0"y="-108.625"/><omgdi:waypoint x="-55.0"y="-82.25"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-2dbcc4cc-2d70-41f1-9afe-b0e45bf598f4"bpmnElement="sid-9b841925-57c1-4e6b-8ec8-ac4a58d2b80c"><omgdi:waypoint x="-27.5"y="-57.25"/><omgdi:waypoint x="25.0"y="-57.25"/><omgdi:waypoint x="25.0"y="-140.0"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-18d2af23-bed9-4368-8f47-021d2fa60d01"bpmnElement="sid-0d2f0355-dded-45a9-8474-84893afae40d"><omgdi:waypoint x="-82.5"y="-57.25"/><omgdi:waypoint x="-315.0"y="-57.25"/><omgdi:waypoint x="-315.0"y="-140.0"/></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram></definitions>
5.6 核心功能类
实现功能如下
- 查询流程定义列表
- 创建请假审批流程
- 审批流程列表
- 提交审批流程
- 历史流程列表
- 删除流程
- 领导待办任务
- 领导已办任务
- 领导批准
- 领导拒绝
- 老板待办任务
- 老板批准
- 老板拒绝
- 员工再次申请请假
- 生成流程图 代码如下
importcom.wander.flowable.vo.TaskVo;importlombok.extern.slf4j.Slf4j;importorg.flowable.bpmn.model.BpmnModel;importorg.flowable.engine.*;importorg.flowable.engine.history.HistoricProcessInstance;importorg.flowable.engine.history.HistoricProcessInstanceQuery;importorg.flowable.engine.repository.ProcessDefinition;importorg.flowable.engine.runtime.Execution;importorg.flowable.engine.runtime.ProcessInstance;importorg.flowable.image.ProcessDiagramGenerator;importorg.flowable.task.api.Task;importorg.flowable.task.api.history.HistoricTaskInstance;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.*;importjavax.imageio.ImageIO;importjavax.servlet.http.HttpServletResponse;importjava.awt.image.BufferedImage;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.util.*;importjava.util.stream.Collectors;/**
* @author wdyin
* @date 2024/11/13
**/@RestController@RequestMapping("/flowable/test")@Slf4jpublicclassFlowableTestController{@AutowiredprivateRuntimeService runtimeService;@AutowiredprivateTaskService taskService;@AutowiredprivateRepositoryService repositoryService;@AutowiredprivateProcessEngine processEngine;/**
* 对应leave.bpmn20.xml文件中process标签的id属性
*/privateString processKey ="leave";/**
* 1. 查询流程定义列表
*/@GetMapping("/processDefinitionList")publicvoidprocessDefinitionList(){List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().list();for(ProcessDefinition processDefinition : processDefinitions){
log.info("部署id:{},流程定义id:{},流程定义名称:{}", processDefinition.getDeploymentId(), processDefinition.getId(), processDefinition.getName());}}/**
* 2. 创建请假审批流程
*
* @param day 请假天数
* @param employeeId 员工id
* @return
*/@PostMapping("/start/{day}/{employeeId}")publicvoidstart(@PathVariable("day")Integer day,@PathVariable("employeeId")String employeeId){Map<String,Object> variables =newHashMap<>();
variables.put("day", day);
variables.put("employeeId", employeeId);//assignee表示流程的办理人
variables.put("assignee", employeeId);ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, variables);
runtimeService.updateBusinessStatus(processInstance.getId(),"未审批");
log.info("流程实例ID:{}", processInstance.getId());}/**
* 3. 审批流程列表
*
* @return
*/@DeleteMapping("/processList")publicvoidprocessList(){List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().list();for(ProcessInstance processInstance : processInstances){
log.info("流程实例ID:{}, 流程状态:{}", processInstance.getId(), processInstance.getBusinessStatus());}}/**
* 4. 提交审批流程
*
* @param processInstanceId 流程实例ID
* @param employeeId 员工ID
* @param leaderId 领导ID
*/@PostMapping("/submit/{processInstanceId}/{employeeId}/{leaderId}")publicvoidsubmit(@PathVariable("processInstanceId")String processInstanceId,@PathVariable("employeeId")String employeeId,@PathVariable("leaderId")String leaderId){
log.info("流程实例ID:"+ processInstanceId);//查询自己的审批流程Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(employeeId).singleResult();Map<String,Object> variables =newHashMap<>();//交给哪位领导审批
variables.put("assignee", leaderId);//提交请假审批流程
taskService.complete(task.getId(), variables);//修改请假流程的状态
runtimeService.updateBusinessStatus(processInstanceId,"审批中");
log.info("流程id:{},任务id:{},", processInstanceId, task.getId(),"审批中");}/**
* 5. 历史流程列表
*
* @return
*/@DeleteMapping("/historicProcessInstanceList")publicvoidhistoricProcessInstanceList(){HistoricProcessInstanceQuery query = processEngine.getHistoryService().createHistoricProcessInstanceQuery();List<HistoricProcessInstance> historicProcessInstances = query.list();
historicProcessInstances.forEach(historicProcessInstance ->{
log.info("历史流程列表ID:{},流程状态:{}", historicProcessInstance.getId(), historicProcessInstance.getBusinessStatus());});}/**
* 6. 删除流程
*
* @param id 流程id
* @return
*/@DeleteMapping("/process/delete/{id}")publicvoidprocessDelete(@PathVariable("id")String id){
runtimeService.deleteProcessInstance(id,"删除流程");
log.info("删除该流程{}", id);}/**
* 7. 领导待办任务
*
* @return
*/@GetMapping("/leaderTodoList/{leaderId}")publicvoidleaderTodoList(@PathVariable("leaderId")String leaderId){List<Task> tasks = taskService.createTaskQuery().taskAssignee(leaderId).list();List<TaskVo> taskVoList = tasks.stream().map(task ->{TaskVo taskVo =newTaskVo();
taskVo.setTaskId(task.getId());
taskVo.setTaskName(task.getName());
taskVo.setProcessInstanceId(task.getProcessInstanceId());Map<String,Object> processVariables = processEngine.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(task.getProcessInstanceId()).list().stream().collect(Collectors.toMap(var->var.getVariableName(),var->var.getValue()));
taskVo.setProcessVariables(processVariables);return taskVo;}).collect(Collectors.toList());
log.info("领导任务列表:"+ taskVoList);}/**
* 8. 领导已办任务
*/@GetMapping("/leaderDoneList/{leaderId}")publicvoidleaderDoneList(@PathVariable("leaderId")String leaderId){List<HistoricTaskInstance> historicTaskInstanceList = processEngine.getHistoryService().createHistoricTaskInstanceQuery().taskAssignee(leaderId).list();List<TaskVo> taskVoList = historicTaskInstanceList.stream().map(task ->{TaskVo taskVo =newTaskVo();
taskVo.setTaskId(task.getId());
taskVo.setTaskName(task.getName());Map<String,Object> processVariables = processEngine.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(task.getProcessInstanceId()).list().stream().collect(Collectors.toMap(var->var.getVariableName(),var->var.getValue()));
taskVo.setProcessVariables(processVariables);return taskVo;}).collect(Collectors.toList());
log.info("已办任务:"+ taskVoList);}/**
* 9. 领导批准
*
* @param taskId 任务ID,非流程id
*/@GetMapping("/leaderApply/{taskId}/{bossId}")publicvoidleaderApply(@PathVariable("taskId")String taskId,@PathVariable("bossId")String bossId){Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee("2000").singleResult();Map<String,Object> variables =newHashMap<>();
variables.put("assignee", bossId);
variables.put("result",true);
taskService.complete(task.getId(), variables);
runtimeService.updateBusinessStatus(task.getProcessInstanceId(),"领导审批完成");
log.info("领导审批成功任务{}", taskId);}/**
* 10. 领导拒绝
*
* @param taskId 任务ID
*/@GetMapping("/leaderRefuse/{taskId}")publicvoidleaderRefuse(@PathVariable("taskId")String taskId){Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee("2000").singleResult();//通过审核HashMap<String,Object> variables =newHashMap<>();
variables.put("result",false);
taskService.complete(task.getId(), variables);
runtimeService.updateBusinessStatus(task.getProcessInstanceId(),"领导审批拒绝");
log.info("领导审批拒绝任务{}", taskId);}/**
* 11. 老板待办任务
*/@GetMapping("/bossTodoList")publicvoidbossTodoList(){List<Task> tasks = taskService.createTaskQuery().taskAssignee("3000").list();List<TaskVo> taskVoList = tasks.stream().map(task ->{Map<String,Object> variables = taskService.getVariables(task.getId());TaskVo taskVO =newTaskVo();
taskVO.setTaskId(task.getId());
taskVO.setTaskName(task.getName());
taskVO.setProcessVariables(variables);return taskVO;}).collect(Collectors.toList());
log.info("老板任务列表:"+ taskVoList);}/**
* 12. 老板批准
*
* @param taskId 任务ID,非流程id
* @return
*/@GetMapping("/bossApply/{taskId}")publicvoidapply(@PathVariable("taskId")String taskId){Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee("3000").singleResult();if(task ==null){
log.info("老板没有任务");}//通过审核HashMap<String,Object> map =newHashMap<>();
map.put("result",true);String processInstanceId = task.getProcessInstanceId();
runtimeService.updateBusinessStatus(processInstanceId,"老板审批完成");
taskService.complete(task.getId(), map);}/**
* 13. 老板拒绝
*
* @param taskId 任务ID
* @return
*/@GetMapping("/bossRefuse/{taskId}")publicvoidbossRefuse(@PathVariable("taskId")String taskId){Task task = taskService.createTaskQuery().taskId(taskId).taskCandidateGroupIn(Arrays.asList("boss")).singleResult();if(task ==null){
log.info("老板没有任务");}//通过审核HashMap<String,Object> map =newHashMap<>();
map.put("result","驳回");
taskService.complete(task.getId(), map);
runtimeService.updateBusinessStatus(task.getProcessInstanceId(),"老板审批拒绝");
log.info("领导审批拒绝任务{}", taskId);}/**
* 14. 员工再次申请请假
*
* @param processId 流程id
* @param day
* @return
*/@GetMapping("/applyAgain/{processId}/{day}/{employeeId}/{leaderId}")publicvoidapplyAgain(@PathVariable("processId")String processId,@PathVariable("day")Integer day,@PathVariable("employeeId")String employeeId,@PathVariable("leaderId")String leaderId){Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();if(task ==null){
log.info("员工没有任务");}// 提交请假申请Map<String,Object> map =newHashMap<>();
map.put("day", day);
map.put("employeeId", employeeId);
map.put("leaderId", leaderId);
map.put("groups",Arrays.asList("leader"));
taskService.complete(task.getId(), map);
runtimeService.updateBusinessStatus(task.getProcessInstanceId(),"审批中");
log.info("员工再次请假申请,任务{}", task.getId());}/**
* 15. 生成流程图
*
* @param processId 流程ID
*/@GetMapping("/processDiagram/{processId}")publicvoidgenProcessDiagram(HttpServletResponse httpServletResponse,@PathVariable("processId")String processId)throwsException{ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();if(pi ==null){return;}Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();StringInstanceId= task.getProcessInstanceId();List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(InstanceId).list();List<String> activityIds =newArrayList<>();List<String> flows =newArrayList<>();for(Execution exe : executions){List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
activityIds.addAll(ids);}BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel,"png", activityIds, flows, engconf.getActivityFontName(),
engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(),1.0,true);OutputStream os =null;try{BufferedImage image =ImageIO.read(inputStream);
httpServletResponse.setContentType("image/png");
os = httpServletResponse.getOutputStream();if(image !=null){ImageIO.write(image,"png", os);}}catch(Exception e){
e.printStackTrace();}finally{try{if(os !=null){
os.flush();
os.close();}}catch(IOException e){
e.printStackTrace();}}}}
6. 多租户flowable数据隔离
多租户Flowable是指在一个单一的应用实例中支持多个租户(客户)的业务流程管理。每个租户拥有独立的数据和流程定义,但共享同一个应用实例。这种架构在SaaS(软件即服务)应用中非常常见,因为它能够有效地利用资源并降低运营成本。有关flowable多租户的完整代码在文末获取!
7. 总结
flowable工作流对于提高效率、优化流程、降低成本、提高质量等方面都有重要作用,是现代企业管理中不可或缺的工具,老铁们用起来吧!
如果看到这里,说明你喜欢这篇文章,请转发,点赞。
- 关注公众号老板来一杯java
- 加群即可获取flowable多租户代码,并赠送DDD领域驱动设计实战落地解惑PDF一份!
- 公众号回复java即可获取java基础经典面试一份!
好文推荐:
推荐一款基于AI编程的代码自动生成工具Cursor,替代VSCode
2.5万字讲解DDD领域驱动设计(史上最全DDD)
python vs java,从java转python一键简简单单入门,轻轻松松上手,抓紧收藏起来吧
版权归原作者 王德印 所有, 如有侵权,请联系我们删除。