当前位置: 首页 > article >正文

springboot整合flowable工作流

1、工作流介绍

1.Flowable起源于Activiti工作流引擎,由Activiti的主要开发者在2016年创建。它继承了Activiti的众多优点,并在此基础上进行了优化和改进,以提供更加稳定、高效的工作流管理解决方案。Flowable与Activiti有着共同的祖先,即jbpm,并随着技术的发展和需求的变化,逐渐发展成为独立且功能强大的工作流引擎。

2.应用场景:Flowable广泛应用于各种需要流程管理的场景,如人力资源管理(如员工入职、离职、请假、绩效评估等)、自动化业务流程(如财务审批、采购流程、销售订单处理等)、任务管理和分配等。在由流程驱动的各种系统中,如OA、CRM、ERP、ECM、BI等,Flowable都能发挥重要作用。
优势:

3.轻量级与高效:Flowable是一个轻量级的引擎,启动快,内存占用小,非常适合在微服务架构中使用。

4.全面支持BPMN 2.0标准:允许使用标准化的方式来定义和执行流程,提高了流程的兼容性和可移植性。

5.丰富的API和可视化设计工具:降低了与其他系统的集成难度,提高了业务流程的建模和编辑效率。

6.良好的社区支持和文档:作为一个活跃的开源项目,Flowable拥有良好的社区支持和不断更新的文档,用户可以在社区中获取帮助和分享经验。

2、flowable相关概念

Flowable是一个功能强大的业务流程管理引擎,支持BPMN 2.0标准。以下是对Flowable相关概念的具体介绍:

2.1 流程定义
流程定义(Process Definition)是使用BPMN 2.0标准的XML格式描述的,它包含了流程中的节点、连接线和事件等元素。
对应的类:org.flowable.engine.repository.ProcessDefinition

2.2 流程实例
流程实例(Process Instance)当一个流程定义被启动时,会创建一个流程实例,这个实例将按照定义的流程节点顺序执行。
对应的类:org.flowable.engine.runtime.ProcessInstance

2.3 任务
任务(Task)是流程实例中的一个执行单元,代表需要由用户或系统自动完成的操作。在Flowable中,任务可以是用户任务、服务任务、脚本任务等多种类型。
对应的类:org.flowable.engine.task.Task

2.4 网关
网关(Gateway)用于控制流程的执行方向,Flowable支持多种类型的网关,如排他网关和并行网关等。排他网关用于在多个分支中选择一个分支进行执行,而并行网关则用于将流程拆分为多个并行执行的分支。
对应的类:
org.flowable.bpmn.model.ParallelGateway(并行网关)
org.flowable.bpmn.model.ExclusiveGateway(排他网关)

2.5 条件表达式
条件表达式(Conditional Expression):Flowable支持使用条件表达式来控制流程的执行方向。条件表达式使用Java的语法,可以在排他网关等地方使用,根据条件表达式的值来决定流程应该走向哪个分支。

2.6 边界事件
边界事件(Boundary Event):边界事件可以捕获流程中的错误或异常事件,并在事件发生时执行相应的处理逻辑。例如,可以在用户任务中添加一个错误边界事件,当用户任务执行失败时,触发错误边界事件进行错误处理。
对应的类:
org.flowable.bpmn.model.StartEvent(开始事件)
org.flowable.bpmn.model.EndEvent(结束事件)
org.flowable.bpmn.model.BoundaryEvent(边界事件)

总的来说,Flowable作为一个轻量级的业务流程引擎,提供了丰富的流程控制元素和灵活的扩展机制,通过掌握其核心概念和常见问题的解决方案,开发者可以更加高效地构建工作流应用,提高业务流程的自动化水平和执行效率。

3、配置工作流模型

3.1 请假工作流示意图

3.2 使用idea插件Flowable BPMN visualizer

Flowable BPMN visualizer是一款为IntelliJ IDEA系列IDE设计的插件,它提供了一个强大的BPMN(Business Process Model and Notation,即业务流程模型和表示法)模型编辑工具.

1、安装idea插件Flowable BPMN visualizer

2、在resources文件夹下创建文件夹processes

3、然后创建文件leave.bpmn20.xml

4、选中leave.bpmn20.xml文件右键选择View BPMN (Flowable) Diagram

 

5、然后就可以开始制作流程图了,右键会有各种工具 

3.3 插件里面的工具介绍

1、Start Events(开始事件)
Start Event: 流程的起点,手动触发。(重要)
Start Conditional Event: 条件满足时自动触发。
Start Message Event: 接收到特定消息时自动触发。
Start Error Event: 发生指定错误时自动触发。
Start Escalation Event: 需要升级或转交给更高级别人员时自动触发。
Start Signal Event: 接收到特定信号时自动触发。
Start Timer Event: 经过特定时间后自动触发。

2、Activities(活动)
Task: 用户执行的任务,如审批、填写表单等。
Service Task: 系统自动执行的任务,如调用外部服务。
User Task: 分配给用户执行的任务。(重要)
Script Task: 使用脚本语言(如Groovy、JavaScript)编写的自定义任务。
Business Rule Task: 基于业务规则引擎执行的任务。
Manual Task: 需要人工干预的任务。

3、Structural(结构)
Subprocess: 子流程,表示一个嵌套的流程。
Transaction: 事务,确保一组操作要么全部成功,要么全部失败。
Ad-hoc Subprocess: 临时子流程,可以在运行时动态创建。
Event Subprocess: 事件子流程,与特定事件相关联。

4、Gateways(网关)
Exclusive Gateway: 排他网关,根据条件选择一条路径执行。(重要)
Parallel Gateway: 并行网关,将流程分成多个并行分支。
Inclusive Gateway: 包容网关,允许多条路径同时执行。
Complex Gateway: 复杂网关,结合了排他网关和并行网关的特性。

5、Boundary Events(边界事件)
Boundary Timer Event: 定时边界事件,当到达指定时间点时触发。
Boundary Error Event: 错误边界事件,当发生指定错误时触发。
Boundary Signal Event: 信号边界事件,当接收到指定信号时触发。
Boundary Message Event: 消息边界事件,当接收到指定消息时触发。
Boundary Cancel Event: 取消边界事件,当流程被取消时触发。
Boundary Compensation Event: 补偿边界事件,用于处理补偿逻辑。

6、Intermediate Catching Events(中间捕获事件)
Intermediate Message Event: 中间消息事件,等待接收到特定消息时触发。
Intermediate Timer Event: 中间定时事件,等待到达指定时间点时触发。
Intermediate Signal Event: 中间信号事件,等待接收到特定信号时触发。
Intermediate Conditional Event: 中间条件事件,等待满足特定条件时触发。

7、Intermediate Throwing Events(中间抛出事件)
Intermediate Message Throw Event: 中间消息抛出事件,发送消息给其他流程或系统。
Intermediate Signal Throw Event: 中间信号抛出事件,发送信号给其他流程或系统。
Intermediate Escalation Event: 中间升级事件,将问题升级或转交给更高级别的人员或系统。
Intermediate Link Event: 中间链接事件,用于跨流程的通信。

8、End Events(结束事件)
End Event: 流程的正常结束点。(重要)
Error End Event: 流程的错误结束点。
Escalation End Event: 流程的升级结束点。
Message End Event: 流程的消息结束点,发送消息给其他流程或系统。
Signal End Event: 流程的信号结束点,发送信号给其他流程或系统。
Terminate End Event: 流程的终止结束点,强制终止流程。

9、Save to PNG
Save to PNG: 将当前BPMN模型保存为PNG格式的图片文件,便于分享和展示。

4、Flowable基础表结构

工作流程的相关操作都是操作存储在对应的表结构中,为了能更好的弄清楚Flowable的实现原理和细节,我们有必要先弄清楚Flowable的相关表结构及其作用。在Flowable中的表结构在初始化的时候会创建五类表结构,具体如下:

ACT_RE :'RE'表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU:'RU'表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Flowable只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
ACT_HI:'HI'表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE: GE 表示 general。 通用数据, 用于不同场景下
ACT_ID: ’ID’表示identity(组织机构)。这些表包含标识的信息,如用户,用户组,等等。

 

 

 

Flowable在项目启动的时候会自动创建表,以下是主要几张表介绍

ACT_RU_TASK:每次启动的流程都会在这张表中,表示代办项,流程结束会删除该流程数据
ACT_RU_EXECUTION:流程执行过程表,会存该流程正在执行的过程数据,流程结束会删除该流程数据
ACT_RU_VARIABLE:流程变量表,流程中传的参数都会在该表存储,流程结束会删除该流程数据
ACT_HI_PROCINST:历史运行流程,当流程处理完了, 在ACT_RU_* 表中就不会有数据, 可以在该表中查询历史
ACT_HI_TASKINST:历史运行的task信息,
ACT_RE_PROCDEF:流程模板记录,同一个key多次发布version_字段会递增
ACT_RE_DEPLOYMENT:部署的流程模板,可以启动流程使用的

5、springboot集成flowable

5.1、pom依赖文件

<!-- 工作流flowable jar包 -->
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.7.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>

5.2、application.properties

 

server.port=8090

spring.datasource.username=root
spring.datasource.password=root3306
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0


# 工作流 Flowable 配置
# 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程
flowable.check-process-definitions=true 
# full 保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
flowable.history-level=full 
#关闭定时任务JOB
flowable.async-executor-activate=false

5.3、flowable 配置文件 

import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: BestLiu
 * @CreateTime: 2024-12-02 09:08
 * @Descripton: flowable配置
 * @Version:
 */
@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: BestLiu
 * @CreateTime: 2024-12-02 09:10
 * @Descripton: TODO
 * @Version:
 */
@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&lt;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&gt;=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、核心功能类

实现功能如下

  1. 查询流程定义列表
  2. 创建请假审批流程
  3. 审批流程列表
  4. 提交审批流程
  5. 历史流程列表
  6. 删除流程
  7. 领导待办任务
  8. 领导已办任务
  9. 领导批准
  10. 领导拒绝
  11. 老板待办任务
  12. 老板批准
  13. 老板拒绝
  14. 员工再次申请请假
  15. 生成流程图
import com.zkaw.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: BestLiu
 * @CreateTime: 2024-12-02 09:33
 * @Descripton: TODO
 * @Version:
 */
@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();
            }
        }
    }

}


http://www.kler.cn/a/420712.html

相关文章:

  • Qt几何数据类型:QLine类型详解(基础向)
  • 动态规划-----路径问题
  • 一种多功能调试工具设计方案开源
  • Windows系统搭建Docker
  • Spire.PDF for .NET【页面设置】演示:旋放大 PDF 边距而不改变页面大小
  • 详解Rust异步编程
  • 入门算法 二 递归
  • 用postgresql实现数组中的模糊字符串查询
  • 【C++】程序流程控制(中)
  • Linux系统 进程
  • 大模型开发和微调工具Llama-Factory-->安装
  • Unity下载文件断点续下
  • K8S疑难概念理解——Pod,应该以哪种Kind来部署应用,为什么不直接Pod这种kind?
  • 【Elasticsearch】04-RestAPI
  • 深度学习常用训练命令解释
  • 在线家具商城基于 SpringBoot:设计模式与实现方法探究
  • vue中v-for的细节
  • 02appdesigner学习记录
  • Kafka怎么发送JAVA对象并在消费者端解析出JAVA对象--示例
  • Flutter的文字高度及行高简单计算
  • 智能探针技术:实现可视、可知、可诊的主动网络运维策略
  • 基于SSM超市商品管理系统JAVA|VUE|Springboot计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解
  • 如何运用Python爬虫快速获得1688商品详情数据
  • Spring MVC接收前台信息,并在页面返回
  • 人工智能-深度学习-BP算法
  • 【计算机网络】实验3:集线器和交换器的区别及交换器的自学习算法