SpringBoot集成Flowable

一、工作流介绍

1、概念

通过计算机对业务流程的自动化管理。工作流是建立在业务流程的基础上,一个软件的系统核心根本上还是系统的业务流程,工作流只是协助进行业务流程管理。

解决的是:在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标

2、工作流系统

概念:具有工作流功能的系统

比如,OA、ERP系统,可能涉及工作流,都可以叫工作流系统

3、具体应用

关键业务流程:订单、报价处理、合同审核、客户电话处理、供应链管理等

行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报周报等凡是原来手工流转处理的行政表单。

人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。

财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等。

客户服务类:客户信息管理、客户投诉、请求处理、售后服务管理等。

特殊服务类:SO系列对应流程、质量管理对应流程、产品数据信息管理、贸易公司报关处理、物流公司货物跟踪处理等各种通过表单逐步手工流转完成的任务均可应用工作流软件自动规范地实施。

4、实现方式对比

原始方式:就是采用状态值来跟踪流程的变化,通过这个值去决定不同用户是否展示,耦合度高

新的工作流引擎,可以灵活调整,实现简单

二、Flowable概述

具体参考官网:https://tkjohn.github.io/flowable-userguide/#_introduction

1、介绍

Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据,等等。

2、BPM

业务流程管理,一种规范化的构造端到端的业务流程,提高组织业务效率。

3、BPMN

业务流程模型和符号,由BPMI 开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

一般就是用来画我们需要的流程图。

常用符号

网关

互斥网关(Exclusive Gateway) 又称排他网关,他有且仅有一个有效出口,可以理解为if...else if... else if...else,就和我们平时写代码的一样。

并行网关(Parallel Gateway) 他的所有出口都会被执行,可以理解为开多线程同时执行多个任务。

**包容性网关(Inclusive Gateway)**只要满足条件的出口都会执行,可以理解为 if(...) do, if (...) do, if (...) do,所有的条件判断都是同级别的。

任务

人工任务(User Task)

它是使用的最多的一种任务类型,他自带有一些人工任务的变量,例如签收人(Assignee),签收人就代表该任务交由谁处理,我们也可以通过某个特定或一系列特定的签收人来查找待办任务。利用上面的行为解释便是,当到达User Task节点的时候,节点设置Assignee变量或等待设置Assignee变量,当任务被完成的时候,我们使用Trigger来要求流程引擎退出该任务,继续流转。

服务任务(Service Task)

该任务会在到达的时候执行一段自动的逻辑并自动流转。从"到达自动执行一段逻辑"这里我们就可以发现,服务任务的想象空间就可以非常大,我们可以执行一段计算,执行发送邮件,执行RPC调用,而使用最广泛的则为HTTP调用,因为HTTP是使用最广泛的协议之一,它可以解决大部分第三方调用问题,在我们的使用中,HTTP服务任务也被我们单独剥离出来作为一个特殊任务节点。

接受任务(Receive Task)

该任务的名字让人费解,但它又是最简单的一种任务,当该任务到达的时候,它不做任何逻辑,而是被动地等待Trigger,它的适用场景往往是一些不明确的阻塞,比如:一个复杂的计算需要等待很多条件,这些条件是需要人为来判断是否可以执行,而不是直接执行,这个时候,工作人员如果判断可以继续了,那么就Trigger一下使其流转。

结构

调用活动(Call Activity)

调用活动可以理解为函数调用,它会引用另外一个流程使之作为子流程运行,调用活动跟函数调用的功能一样,使流程模块化,增加复用的可能性。

一个流程必须包含一个事件(如:开始事件)和至少一个结束(事件)。其中网关的作用是流程流转逻辑的控制。任务则分很多类型,他们各司其职,所有节点均由连线联系起来。

4、为什么选择Flowable?

修复了activiti6很多的bug,可以实现零成本从activiti迁移到flowable。flowable目前已经支持加签、动态增加实例中的节点、支持cmmn、dmn规范。这些都是activiti6目前版本没有的。

flowable已经支持所有的历史数据使用mongdb存储,activiti没有。

flowable支持事务子流程,activiti没有。

flowable支持多实例加签、减签,activiti没有。

flowable支持httpTask等新的类型节点,activiti没有。

flowable支持在流程中动态添加任务节点,activiti没有。

flowable支持历史任务数据通过消息中间件发送,activiti没有。

flowable支持java11,activiti没有。

flowable支持动态脚本,,activiti没有。

flowable支持条件表达式中自定义juel函数,activiti没有。

flowable支持cmmn规范,activiti没有。

flowable修复了dmn规范设计器,activit用的dmn设计器还是旧的框架,bug太多。

flowable屏蔽了pvm,activiti6也屏蔽了pvm(因为6版本官方提供了加签功能,发现pvm设计的过于臃肿,索性直接移除,这样加签实现起来更简洁、事实确实如此,如果需要获取节点、连线等信息可以使用bpmnmodel替代)。

flowable与activiti提供了新的事务监听器。activiti5版本只有事件监听器、任务监听器、执行监听器。

flowable对activiti的代码大量的进行了重构。

activiti以及flowable支持的数据库有h2、hsql、mysql、oracle、postgres、mssql、db2。其他数据库不支持的。使用国产数据库的可能有点失望了,需要修改源码了。

flowable支持jms、rabbitmq、mongodb方式处理历史数据,activiti没有。

5、 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:部署的流程模板,可以启动流程使用的

三、创建项目

创建一个maven项目,引入springboot、mysql、flowable

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test</groupId>
    <artifactId>flowable-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <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>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!--flowable-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.4.1</version>
        </dependency>


        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.22</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
</project>

application.yml

注意mysql数据库驱动版本差异

  • com.mysql.jdbc.Driver:

这是 旧版 MySQL JDBC 驱动的类名。

该驱动属于 mysql-connector-java 5.x 或更早版本。

它是 MySQL 5.x 系列及之前的 JDBC 驱动程序的标准类名。

从 MySQL 5.1 到 MySQL 5.7 版本,使用的是这个驱动。

  • com.mysql.cj.jdbc.Driver:

这是 新版 MySQL JDBC 驱动的类名。

该驱动是 MySQL 8.x 及之后版本的标准 JDBC 驱动程序。

com.mysql.cj.jdbc.Driver 是从 mysql-connector-java 8.x 开始引入的。

com.mysql.cj 代表 "Connector/J" 的缩写,cj 指代的是 Connector/J 新的模块命名空间。

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/flowable-test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
flowable:
  # 业务流程设计的表自动创建
  database-schema-update: true
  # 异步执行耗时操作,提升性能
  async-executor-activate: false

FlowableApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FlowableApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlowableApplication.class, args);
    }
}

运行并启动项目,会在数据库自动生成工作流所使用的表

四、画流程图

这里使用 IDEA 插件 Flowable BPMN visualizer,如下图:

装好插件之后,在 resources 目录下新建 processes 目录,这个目录下的流程文件将来会被自动部署

新建一个ask_for_leave.bpmn20.xml文件(.bpmn20.xml是固定文件后缀,不需要填写)

文件创建出来之后,右键单击,选择 View BPMN(Flowable) Diagram,就打开了此文件的可视化页面了,然后就可以来绘制自己的流程图

然后在可视化页面空白处,右键点击,就可以添加不同的符号,来绘制流程图

绘制完成

我们来看下这个流程对应的 XML 文件,一些流程细节会在 XML 文件中体现出来,如下:

<?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="ask_for_leave" name="ask_for_leave" isExecutable="true">
    <startEvent id="startLeave"/>
    <userTask id="leaveTask" name="请假" flowable:assignee="#{leaveUser}"/>
    <sequenceFlow id="flowStart" sourceRef="startLeave" targetRef="leaveTask"/>
    <userTask id="zuZhangTask" name="组长审核" flowable:assignee="#{zuZhangUser}"/>
    <sequenceFlow id="modeFlow" sourceRef="leaveTask" targetRef="zuZhangTask"/>
    <exclusiveGateway id="zuZhangJudgeGateway"/>
    <sequenceFlow id="zuZhang_go_judge" sourceRef="zuZhangTask" targetRef="zuZhangJudgeGateway"/>
    <userTask id="managerTask" name="经理审核" flowable:assignee="#{managerUser}"/>
    <sequenceFlow id="zuZhangPass" sourceRef="zuZhangJudgeGateway" targetRef="managerTask" name="组长审核通过">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(zuZhangCheckResult,"通过")}</conditionExpression>
    </sequenceFlow>
    <endEvent id="endLeave"/>
    <exclusiveGateway id="managerJudgeGateway"/>
    <sequenceFlow id="manager_go_judge" sourceRef="managerTask" targetRef="managerJudgeGateway"/>
    <sequenceFlow id="managerPass" sourceRef="managerJudgeGateway" targetRef="endLeave" name="经理审核通过">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(managerCheckResult,"通过")}</conditionExpression>
    </sequenceFlow>
    <serviceTask id="sendFailMessage" flowable:exclusive="true" name="发送失败提示" isForCompensation="true" flowable:class="com.test.service.LeaveFailService"/>
    <sequenceFlow id="zuZhangRefuse" sourceRef="zuZhangJudgeGateway" targetRef="sendFailMessage" name="组长审核拒绝">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(zuZhangCheckResult,"拒绝")}</conditionExpression>
    </sequenceFlow>
    <endEvent id="failEndLeave"/>
    <sequenceFlow id="go_fail_end" sourceRef="sendFailMessage" targetRef="failEndLeave"/>
    <sequenceFlow id="managerRefuse" sourceRef="managerJudgeGateway" targetRef="sendFailMessage" name="经理审核拒绝">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(managerCheckResult,"拒绝")}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave">
    <bpmndi:BPMNPlane bpmnElement="ask_for_leave" id="BPMNPlane_ask_for_leave">
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-92e89192-ae6e-4c06-84ea-e3deb6d92402" bpmnElement="startLeave">
        <omgdc:Bounds x="-454.35" y="-60.0" width="30.0" height="30.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-9ab478cf-067b-4984-a12e-0b14d28d6bb5" bpmnElement="leaveTask">
        <omgdc:Bounds x="-381.4296" y="-85.0" width="100.0" height="80.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-5e4ce6f7-76af-4972-91a1-ce776b2ca0c7" bpmnElement="flowStart">
        <omgdi:waypoint x="-424.35" y="-45.0"/>
        <omgdi:waypoint x="-381.4296" y="-45.0"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-46866177-80d1-4db8-abd7-f0426459804a" bpmnElement="zuZhangTask">
        <omgdc:Bounds x="-234.44722" y="-85.0" width="100.0" height="80.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-c65d7d95-b9f8-44e1-89a3-0bf2a9e95738" bpmnElement="modeFlow">
        <omgdi:waypoint x="-281.4296" y="-45.0"/>
        <omgdi:waypoint x="-234.44722" y="-45.0"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-53989386-6942-4e52-8f37-9076c52057a6" bpmnElement="zuZhangJudgeGateway">
        <omgdc:Bounds x="-78.09332" y="-65.0" width="40.0" height="40.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-4b34e417-f0c0-40ef-8bb6-7690e136d67f" bpmnElement="zuZhang_go_judge">
        <omgdi:waypoint x="-134.44722" y="-45.0"/>
        <omgdi:waypoint x="-78.09332" y="-45.0"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-86d89ed3-dcb1-451c-b96c-ba3ef43baeba" bpmnElement="managerTask">
        <omgdc:Bounds x="26.607117" y="-85.0" width="100.0" height="80.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-c2ec3e89-62ab-4e90-af28-0cf551530ca0" bpmnElement="zuZhangPass">
        <omgdi:waypoint x="-38.093323" y="-45.0"/>
        <omgdi:waypoint x="26.607117" y="-45.0"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-dd6c6e8f-e05d-4e50-9852-672656a76494" bpmnElement="endLeave">
        <omgdc:Bounds x="284.48773" y="-59.999996" width="30.0" height="30.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-fa4fc0dc-9773-4944-9fde-d2741dc9386b" bpmnElement="managerJudgeGateway">
        <omgdc:Bounds x="164.3713" y="-65.0" width="40.0" height="40.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-4d924eb1-02a1-43d3-b884-d7a3e5918359" bpmnElement="manager_go_judge">
        <omgdi:waypoint x="126.60712" y="-45.0"/>
        <omgdi:waypoint x="164.3713" y="-45.0"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-9918da8c-b7e5-4ec1-866b-dbfae2c6ea05" bpmnElement="managerPass">
        <omgdi:waypoint x="204.3713" y="-45.0"/>
        <omgdi:waypoint x="284.48773" y="-44.999996"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-ccc89da3-1cf3-414b-8c39-3a90c8a42326" bpmnElement="sendFailMessage">
        <omgdc:Bounds x="-108.09332" y="41.54" width="100.0" height="80.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-98a4db5f-6ff9-48d0-a358-9470a03c8f1c" bpmnElement="zuZhangRefuse">
        <omgdi:waypoint x="-58.093323" y="-25.0"/>
        <omgdi:waypoint x="-58.093323" y="41.54"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-48b0f47e-8768-4275-b013-575039f2cd20" bpmnElement="failEndLeave">
        <omgdc:Bounds x="-199.44724" y="66.54" width="30.0" height="30.0"/>
      </bpmdi:BPMNShape>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-08d8631a-3c62-486f-9a65-6f1d1a63c317" bpmnElement="go_fail_end">
        <omgdi:waypoint x="-108.09332" y="81.54"/>
        <omgdi:waypoint x="-169.44724" y="81.54"/>
      </bpmdi:BPMNEdge>
      <bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-136a3062-a9f0-4c34-a275-0d6b51fc55d3" bpmnElement="managerRefuse">
        <omgdi:waypoint x="184.3713" y="-25.0"/>
        <omgdi:waypoint x="184.3713" y="81.54"/>
        <omgdi:waypoint x="-8.093322" y="81.53999"/>
      </bpmdi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
五、流程接口代码

AskForLeaveController.java

package com.test.controller;

import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class AskForLeaveController {
    @Autowired
    RuntimeService runtimeService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    TaskService taskService;

    @Autowired
    ProcessEngine processEngine;

    /**
     * 查看流程进度图
     * @param resp
     * @param processId 流程id
     * @throws Exception
     */
    @GetMapping("/viewFlowChart")
    public void viewFlowChart(HttpServletResponse resp, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (pi == null) {
            return;
        }
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(processId)
                .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 in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = resp.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 开启一个请假流程
     */
    @GetMapping("/askForLeave")
    public String askForLeave() {
        Map<String, Object> map = new HashMap<>();
        map.put("leaveUser", "张三");
        map.put("name", "ego");
        map.put("reason", "出去玩");
        map.put("days", 10);
        ProcessInstance instance = runtimeService.startProcessInstanceByKey("ask_for_leave", map);
        return "开启请假流程 processId:" + instance.getId();
    }

    /**
     * 员工请假提交给组长审核
     */
    @GetMapping("/submitToZuZhang")
    public String submitToZuZhang() {
        // 员工查找到自己的任务,然后提交给组长审批
        List<Task> list = taskService.createTaskQuery().taskAssignee("张三").orderByTaskId().desc().list();
        String info = null;
        for (Task task: list) {
            info = "任务 ID:" + task.getId() + ",任务申请人:" + task.getAssignee() + ",任务是否挂起:" + task.isSuspended();
            //提交给组长的时候,需要指定组长的id
            Map<String, Object> map = new HashMap<>();
            map.put("zuZhangUser", "李四");
            taskService.complete(task.getId(), map);
        }
        return info;
    }

    /**
     * 组长审核请假
     */
    @GetMapping("/zuZhangApprove")
    public String zuZhangApprove() {
        List<Task> list = taskService.createTaskQuery().taskAssignee("李四").orderByTaskId().desc().list();
        String info = null;
        for (Task task: list) {
            if ("组长审核".equals(task.getName())) {
                info = "任务 ID:" + task.getId() + ",组长审核人:" + task.getAssignee() + ",任务是否挂起:" + task.isSuspended();
                // 提交给经理的时候,需要指定经理的id
                Map<String, Object> map = new HashMap<>();
                map.put("managerUser", "王五");
                map.put("zuZhangCheckResult", "通过");
                map.put("zuZhangUser", "李四");
                try {
                    taskService.complete(task.getId(), map);
                } catch (Exception e) {
                    e.printStackTrace();
                    info = "组长审核失败:" + task.getId() + " " + task.getAssignee();
                }
            }
        }
        return info;
    }

    /**
     * 经理审核通过
     */
    @GetMapping("/managerApprove")
    public String managerApprove() {
        List<Task> list = taskService.createTaskQuery().taskAssignee("王五").orderByTaskId().desc().list();
        String info = null;
        for (Task task: list) {
            info = "经理:" + task.getAssignee() + ",审核通过:" + task.getId() + " 任务";
            Map<String, Object> map = new HashMap<>();
            map.put("managerCheckResult", "通过");
            taskService.complete(task.getId(), map);
        }
        return info;
    }

    /**
     * 经理审核拒绝
     */
    @GetMapping("/managerRefuse")
    public String managerRefuse() {
        List<Task> list = taskService.createTaskQuery().taskAssignee("王五").orderByTaskId().desc().list();
        String info = null;
        for (Task task: list) {
            info = "经理:" + task.getAssignee() + ",审核拒绝:" + task.getId() + " 任务";
            Map<String, Object> map = new HashMap<>();
            map.put("managerCheckResult", "拒绝");
            taskService.complete(task.getId(), map);
        }
        return info;
    }
}

LeaveFailService.java

对应流程图中的<serviceTask id="sendFailMessage" flowable:exclusive="true" name="发送失败提示" isForCompensation="true" flowable:class="com.test.service.LeaveFailService"/>

package com.test.service;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;

@Component
public class LeaveFailService implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("审批不通过 " + delegateExecution.getCurrentActivityId());
    }
}

FlowableConfig.java,防止流程图出现中文乱码的配置类

package com.test.config;

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


@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {

    @Override
    public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
        springProcessEngineConfiguration.setActivityFontName("宋体");
        springProcessEngineConfiguration.setLabelFontName("宋体");
        springProcessEngineConfiguration.setAnnotationFontName("宋体");
    }
}
五、测试一下流程接口

1、开启一个请假流程,http://localhost:8080/askForLeave

查看流程进度图,参数是开启的流程的processId

http://localhost:8080/viewFlowChart?processId=ef67cd51-af01-11ef-8fdb-005056c00001,

可以看到,请假这个userTask用红色的框框起来了,说明当前流程走到了这一步

2、员工请假提交给组长审核,http://localhost:8080/submitToZuZhang

查看流程进度图

3、组长审核请假,http://localhost:8080/zuZhangApprove

查看流程进度

​​​​​​​​​​​​​​​​​​​​​

4、经理审核拒绝,http://localhost:8080/managerRefuse

调用LeaveFailService,控制台打印

流程结束

5、重新开启一个流程,重复1-4,试一试"经理审核通过"

​​​​​​​

相关推荐
华如锦2 个月前
低代码工作流平台概述-自研
java·spring boot·spring·spring cloud·ai·flowable·工作流
爱吃土豆的程序员4 个月前
flowable源码解读——内存缓存设计
缓存·flowable·源码解读
宁波阿成5 个月前
橙单后端项目下载编译遇到的问题与解决
java·前端·数据库·vue3·flowable
宁波阿成5 个月前
基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-抄送服务处理
java·钉钉·vue3·flowable·jeecg-boot
宁波阿成5 个月前
基于Jeecgboot3.6.3的vue3版本的流程ProcessViewer的修改
前端·vue3·flowable·jeecgboot
宁波阿成5 个月前
基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-不同panel兄弟之间的数据传递
前端·钉钉·vue3·flowable·jeecgboot
宁波阿成5 个月前
基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-支持VForm3表单的选择与支持
前端·vue3·flowable·仿钉钉·jeecgboot
宁波阿成5 个月前
基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-排它网关里数字比较大小的处理
java·钉钉·vue3·flowable·jeecgboot
宁波阿成5 个月前
基于jeecgboot-vue3的Flowable流程支持bpmn流程设计器与仿钉钉流程设计器-编辑多版本处理
java·钉钉·vue3·flowable·jeecg-boot