一文精通flowable并使用springboot整合flowable工作流(最完整版本)
springboot整合flowable并实现功能如下1. 查询流程定义列表2. 创建请假审批流程3. 审批流程列表4. 提交审批流程5. 历史流程列表6. 删除流程7. 领导待办任务8. 领导已办任务9. 领导批准10. 领导拒绝11. 老板待办任务12. 老板批准13. 老板拒绝14. 员工再次申请请假15. 生成流程图
好文推荐:
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 配置文件
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
/**
* flowable配置
*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
}
}
5.4 流程任务实体
import lombok.Data;
import java.util.Map;
/**
* @author wdyin
* @date 2024/11/15
**/
@Data
public class TaskVo {
/**
* 任务id
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 流程实例id
*/
private String 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 核心功能类
实现功能如下
- 查询流程定义列表
- 创建请假审批流程
- 审批流程列表
- 提交审批流程
- 历史流程列表
- 删除流程
- 领导待办任务
- 领导已办任务
- 领导批准
- 领导拒绝
- 老板待办任务
- 老板批准
- 老板拒绝
- 员工再次申请请假
- 生成流程图
代码如下
import com.wander.flowable.vo.TaskVo;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author wdyin
* @date 2024/11/13
**/
@RestController
@RequestMapping("/flowable/test")
@Slf4j
public class FlowableTestController {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessEngine processEngine;
/**
* 对应leave.bpmn20.xml文件中process标签的id属性
*/
private String processKey = "leave";
/**
* 1. 查询流程定义列表
*/
@GetMapping("/processDefinitionList")
public void processDefinitionList() {
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}")
public void start(@PathVariable("day") Integer day, @PathVariable("employeeId") String employeeId) {
Map<String, Object> variables = new HashMap<>();
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")
public void processList() {
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}")
public void submit(@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 = new HashMap<>();
//交给哪位领导审批
variables.put("assignee", leaderId);
//提交请假审批流程
taskService.complete(task.getId(), variables);
//修改请假流程的状态
runtimeService.updateBusinessStatus(processInstanceId, "审批中");
log.info("流程id:{},任务id:{},", processInstanceId, task.getId(), "审批中");
}
/**
* 5. 历史流程列表
*
* @return
*/
@DeleteMapping("/historicProcessInstanceList")
public void historicProcessInstanceList() {
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}")
public void processDelete(@PathVariable("id") String id) {
runtimeService.deleteProcessInstance(id, "删除流程");
log.info("删除该流程{}", id);
}
/**
* 7. 领导待办任务
*
* @return
*/
@GetMapping("/leaderTodoList/{leaderId}")
public void leaderTodoList(@PathVariable("leaderId") String leaderId) {
List<Task> tasks = taskService.createTaskQuery().taskAssignee(leaderId).list();
List<TaskVo> taskVoList = tasks.stream().map(task -> {
TaskVo taskVo = new TaskVo();
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}")
public void leaderDoneList(@PathVariable("leaderId") String leaderId) {
List<HistoricTaskInstance> historicTaskInstanceList = processEngine.getHistoryService().createHistoricTaskInstanceQuery().taskAssignee(leaderId).list();
List<TaskVo> taskVoList = historicTaskInstanceList.stream().map(task -> {
TaskVo taskVo = new TaskVo();
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}")
public void leaderApply(@PathVariable("taskId") String taskId, @PathVariable("bossId") String bossId) {
Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee("2000").singleResult();
Map<String, Object> variables = new HashMap<>();
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}")
public void leaderRefuse(@PathVariable("taskId") String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee("2000").singleResult();
//通过审核
HashMap<String, Object> variables = new HashMap<>();
variables.put("result", false);
taskService.complete(task.getId(), variables);
runtimeService.updateBusinessStatus(task.getProcessInstanceId(), "领导审批拒绝");
log.info("领导审批拒绝任务{}", taskId);
}
/**
* 11. 老板待办任务
*/
@GetMapping("/bossTodoList")
public void bossTodoList() {
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 = new TaskVo();
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}")
public void apply(@PathVariable("taskId") String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee("3000").singleResult();
if (task == null) {
log.info("老板没有任务");
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
map.put("result", true);
String processInstanceId = task.getProcessInstanceId();
runtimeService.updateBusinessStatus(processInstanceId, "老板审批完成");
taskService.complete(task.getId(), map);
}
/**
* 13. 老板拒绝
*
* @param taskId 任务ID
* @return
*/
@GetMapping("/bossRefuse/{taskId}")
public void bossRefuse(@PathVariable("taskId") String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).taskCandidateGroupIn(Arrays.asList("boss")).singleResult();
if (task == null) {
log.info("老板没有任务");
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
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}")
public void applyAgain(@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 = new HashMap<>();
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}")
public void genProcessDiagram(HttpServletResponse httpServletResponse, @PathVariable("processId") String processId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
if (pi == null) {
return;
}
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
String InstanceId = task.getProcessInstanceId();
List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(InstanceId).list();
List<String> activityIds = new ArrayList<>();
List<String> flows = new ArrayList<>();
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一键简简单单入门,轻轻松松上手,抓紧收藏起来吧
更多推荐
所有评论(0)