SpringBoot集成Flowable工作流

文章目录

  • 一、了解Flowable
    • 1. 什么是Flowable
    • 2. Flowable基本流程
    • 3. Flowable主要几张表介绍
  • 二、SpringBoot集成Flowable
    • 1. 在idea中安装Flowable插件
    • 2. SpringBoot集成Flowable
    • 3. SpringBoot集成Flowable前端页面
  • 三、创建流程模版(以请假为例)


提示:以下是本篇文章正文内容,下面案例可供参考

一、了解Flowable

1. 什么是Flowable

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

具体介绍请看Flowable介绍

2. Flowable基本流程

参考知乎Flowable工作流引擎

对于业务建模,我们需要一种通用的语言来描绘,这样在沟通上和实现上会降低难度,就像中文、英文一样,BPMN2.0便是一种国际通用的建模语言,他能让自然人轻松阅读,更能被计算机所解析。
协议中元素的主要分类为,事件-任务-连线-网关
一个流程必须包含一个事件(如:开始事件)和至少一个结束(事件)。其中网关的作用是流程流转逻辑的控制。任务则分很多类型,他们各司其职,所有节点均由连线联系起来。
BPMN2.0协议
SpringBoot集成Flowable工作流_第1张图片
不同的图标代表不同的含义

  1. 网关
    SpringBoot集成Flowable工作流_第2张图片

互斥网关(Exclusive Gateway) 又称排他网关,他有且仅有一个有效出口,可以理解为if…else if… else if…else,就和我们平时写代码的一样。
SpringBoot集成Flowable工作流_第3张图片

并行网关(Parallel Gateway) 他的所有出口都会被执行,可以理解为开多线程同时执行多个任务。
SpringBoot集成Flowable工作流_第4张图片

包容性网关(Inclusive Gateway)
只要满足条件的出口都会执行,可以理解为 if(…) do, if (…) do, if (…)
do,所有的条件判断都是同级别的。
SpringBoot集成Flowable工作流_第5张图片

  1. 任务
    SpringBoot集成Flowable工作流_第6张图片

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

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

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

  1. 结构
    SpringBoot集成Flowable工作流_第10张图片

调用活动(Call Activity)
调用活动可以理解为函数调用,它会引用另外一个流程使之作为子流程运行,调用活动跟函数调用的功能一样,使流程模块化,增加复用的可能性。
SpringBoot集成Flowable工作流_第11张图片

3. Flowable主要几张表介绍

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

二、SpringBoot集成Flowable

1. 在idea中安装Flowable插件

File-->settings 搜索:flowable
SpringBoot集成Flowable工作流_第12张图片
安装完成,重启idea
SpringBoot集成Flowable工作流_第13张图片

2. SpringBoot集成Flowable

  1. 添加依赖
     	/**
     	  * 引入数据库依赖,Flowable在项目启动的时候会自动创建表
    	  */
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
         /**
     	  * lombok
    	  */
         <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.16</version>
            <scope>compile</scope>
        </dependency>
         /**
     	  * 添加flowable工作流依赖
    	  */
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.7.2</version>
        </dependency>
         /**
     	  * 添加flowable工作流前端页面依赖,如果项目没有集成页面,无需添加此依赖
    	  */
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-ui-modeler-rest</artifactId>
            <version>6.7.2</version>
        </dependency>
         /**
     	  * 添加flowable-ui-modeler配置依赖项
    	  */
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-ui-modeler-conf</artifactId>
            <version>6.7.2</version>
        </dependency>
  1. 设置application.yml
server:
  port: 8080  #tomcat端口
  servlet:
    context-path: /
spring:
  #数据库链接配置
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/flowable?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root


flowable:
  #异步执行
  async-executor-activate: true
  #自动更新数据库
  database-schema-update: true
  #校验流程文件,默认校验resources下的processes文件夹里的流程文件
  process-definition-location-prefix: classpath*:/processes/
  process-definition-location-suffixes: "**.bpmn20.xml, **.bpmn"
  
  #该配置只是防止页面报错,没有实际意义
  common:
    app:
      idm-admin:
        password: test
        user: test
      #没有实际意义
      idm-url: http://localhost:8080/flowable-demo
  1. 创建 【*.bpmn20.xml】
    通过【application.yml】可知,需要在resources下的processes文件夹里的创建流程文件
    这是ask_for_leave.bpmn20.xml我自己的流程文件
    SpringBoot集成Flowable工作流_第14张图片
    ask_for_leave.bpmn20.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="askForLeave" name="ask_for_leave_process" isExecutable="true">
    <startEvent id="sid-b3fcb298-124a-4dc3-b603-1397b392e1a5"/>
    <userTask id="employee" name="员工" flowable:assignee="#{employeeNo}" flowable:formFieldValidation="true">
      <documentation>员工提交申请</documentation>
    </userTask>
    <sequenceFlow id="sid-9a4f7da0-7df8-422c-8d93-fcfe6eed6454" sourceRef="sid-b3fcb298-124a-4dc3-b603-1397b392e1a5" targetRef="employee"/>
    <userTask id="leader" name="领导" flowable:assignee="#{leaderNo}" flowable:formFieldValidation="true"/>
    <sequenceFlow id="sid-cda53600-1fdd-4556-b1f4-434cdef4b44c" sourceRef="employee" targetRef="leader"/>
    <exclusiveGateway id="sid-53b678ee-c126-46be-9bb7-70efe235451c"/>
    <sequenceFlow id="sid-c18a730f-6932-4036-b105-a840204bbd1f" sourceRef="leader" targetRef="sid-53b678ee-c126-46be-9bb7-70efe235451c"/>
    <endEvent id="sid-dc057597-34a0-4835-bbbb-1b12f0e8e407"/>
    <sequenceFlow id="leaderExamine" sourceRef="sid-53b678ee-c126-46be-9bb7-70efe235451c" targetRef="sid-dc057597-34a0-4835-bbbb-1b12f0e8e407" name="领导审核不通过">
      <conditionExpression xsi:type="tFormalExpression">${outcome=='驳回'}</conditionExpression>
    </sequenceFlow>
    <userTask id="boss" name="老板" flowable:formFieldValidation="true" flowable:assignee="#{bossNo}"/>
    <sequenceFlow id="leaderExaminePass" sourceRef="sid-53b678ee-c126-46be-9bb7-70efe235451c" targetRef="boss" name="领导审核通过">
      <conditionExpression xsi:type="tFormalExpression">${outcome=='通过'}</conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046"/>
    <sequenceFlow id="sid-e4dd8e02-acc7-4e51-95d4-5a1ade5eb2fd" sourceRef="boss" targetRef="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046"/>
    <endEvent id="sid-311b83fa-5c04-48af-8491-4e2f9417c49c"/>
    <sequenceFlow id="bossExamine" sourceRef="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046" targetRef="sid-311b83fa-5c04-48af-8491-4e2f9417c49c" name="老板审核不通过">
      <conditionExpression xsi:type="tFormalExpression">${outcome=='驳回'}</conditionExpression>
    </sequenceFlow>
    <endEvent id="sid-3f6150ad-4b8c-47e9-a726-39f5dffd5e0a"/>
    <sequenceFlow id="bossExaminePass" sourceRef="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046" targetRef="sid-3f6150ad-4b8c-47e9-a726-39f5dffd5e0a" name="老板审核通过">
      <conditionExpression xsi:type="tFormalExpression">${outcome=='通过'}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave">
    <bpmndi:BPMNPlane bpmnElement="askForLeave" id="BPMNPlane_ask_for_leave">
      <bpmndi:BPMNShape id="shape-c5da47a1-57a1-465c-a7f8-2aad63d19b47" bpmnElement="sid-b3fcb298-124a-4dc3-b603-1397b392e1a5">
        <omgdc:Bounds x="-203.0" y="-10.75" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-71f351aa-e8f4-4656-9fa0-7979ddc1d916" bpmnElement="employee">
        <omgdc:Bounds x="-140.0" y="-10.5" width="61.0" height="29.5"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-a0cd4bce-af0a-40ce-8128-97629a5f4a23" bpmnElement="sid-9a4f7da0-7df8-422c-8d93-fcfe6eed6454">
        <omgdi:waypoint x="-173.0" y="4.25"/>
        <omgdi:waypoint x="-140.0" y="4.25"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-6a814a53-e6e2-4453-bb4a-43a1167e5559" bpmnElement="leader">
        <omgdc:Bounds x="-46.5" y="-11.0" width="63.0" height="30.5"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-3a2944ef-43d6-4409-920d-9d3d1acd422f" bpmnElement="sid-cda53600-1fdd-4556-b1f4-434cdef4b44c">
        <omgdi:waypoint x="-79.0" y="4.25"/>
        <omgdi:waypoint x="-46.5" y="4.25"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-bef28649-6416-4428-a3bd-43edb5c1b9c6" bpmnElement="sid-53b678ee-c126-46be-9bb7-70efe235451c">
        <omgdc:Bounds x="42.36" y="-15.75" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-fa69c266-5b3c-4e26-a4e2-3f58c271ebb4" bpmnElement="sid-c18a730f-6932-4036-b105-a840204bbd1f">
        <omgdi:waypoint x="16.5" y="4.25"/>
        <omgdi:waypoint x="42.36" y="4.25"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-9d577a21-ca61-4e33-bf3e-c892557f1638" bpmnElement="sid-dc057597-34a0-4835-bbbb-1b12f0e8e407">
        <omgdc:Bounds x="47.36" y="54.97" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-5651dbf2-4118-4668-bbd6-0259282cc084" bpmnElement="leaderExamine">
        <omgdi:waypoint x="62.36" y="24.25"/>
        <omgdi:waypoint x="62.36" y="54.97"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-1398abfb-fefc-4a16-881c-84a6c4b7b7f7" bpmnElement="boss">
        <omgdc:Bounds x="108.86" y="-12.75" width="68.0" height="34.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-3f5df081-8086-492f-a2c7-90aba1c1e4d7" bpmnElement="leaderExaminePass">
        <omgdi:waypoint x="82.36" y="4.25"/>
        <omgdi:waypoint x="108.86" y="4.25"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-4932221b-736b-4e85-a319-0859dbeb3e6b" bpmnElement="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046">
        <omgdc:Bounds x="203.35999" y="-15.75" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-3daa1ef4-119f-4b98-a40e-0b9cb3b205ce" bpmnElement="sid-e4dd8e02-acc7-4e51-95d4-5a1ade5eb2fd">
        <omgdi:waypoint x="176.86" y="4.25"/>
        <omgdi:waypoint x="203.35999" y="4.25"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-6becf929-e7e7-4153-a1d1-6175677742ca" bpmnElement="sid-311b83fa-5c04-48af-8491-4e2f9417c49c">
        <omgdc:Bounds x="208.35999" y="54.97" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-1f1a1414-f4f9-442a-97af-676878899415" bpmnElement="bossExamine">
        <omgdi:waypoint x="223.35999" y="24.25"/>
        <omgdi:waypoint x="223.35999" y="54.97"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-919e70dd-5004-4066-8103-427901b5c1fd" bpmnElement="sid-3f6150ad-4b8c-47e9-a726-39f5dffd5e0a">
        <omgdc:Bounds x="275.86" y="-10.75" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-542631d7-42d7-4b2a-b467-da8fdba298a1" bpmnElement="bossExaminePass">
        <omgdi:waypoint x="243.35999" y="4.25"/>
        <omgdi:waypoint x="275.86" y="4.25"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

  1. 设置配置文件
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
    /**
     * 防止生成的流程图中文乱码
     * @param springProcessEngineConfiguration
     */
    @Override
    public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
        springProcessEngineConfiguration.setActivityFontName("宋体");
        springProcessEngineConfiguration.setLabelFontName("宋体");
        springProcessEngineConfiguration.setAnnotationFontName("宋体");
    }
}

如果醒目没有集成页面,【SecurityConfiguration.java】无需配置
参考简书springboot集成flowable

import org.flowable.ui.common.security.SecurityConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * 绕过flowable的登录验证
 */
@Configuration
public class SecurityConfiguration {
    @Configuration(proxyBeanMethods = false)
    //Order配置说明
    // 这个地方相同会报错
    //这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用
    //所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权
    @Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.headers().frameOptions().disable();


            http
                    //必须要将csrf设置为disable,不然后面发送POST请求时会报403错误
                    .csrf().disable()
                    //为了简单起见,简单粗暴方式直接放行modeler下面所有请求
                    .authorizeRequests().antMatchers("/modeler/**").permitAll();
        }
    }
}
  1. 书写逻辑
import lombok.extern.slf4j.Slf4j;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

@Slf4j
@RestController
@RequestMapping("askForLeave")
public class AskForLeaveFlowableController {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine processEngine;


    /**
     * 员工提交请假申请
     *
     * @param employeeNo 员工工号
     * @param name       姓名
     * @param reason     原因
     * @param days       天数
     * @return
     */
    @GetMapping("employeeSubmit")
    public String employeeSubmitAskForLeave(
            @RequestParam(value = "employeeNo") String employeeNo,
            @RequestParam(value = "name") String name,
            @RequestParam(value = "reason") String reason,
            @RequestParam(value = "days") Integer days) {
        HashMap<String, Object> map = new HashMap<>();
        /**
         * 员工编号字段来自于配置文件
         */
        map.put("employeeNo", employeeNo);
        map.put("name", name);
        map.put("reason", reason);
        map.put("days", days);
        /**
         *      key:配置文件中的下个处理流程id
         *      value:默认领导工号为002
         */
        map.put("leaderNo", "002");

        /**
         * askForLeave:为开启流程的id  与配置文件中的一致
         */
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("askForLeave", map);
        log.info("{},提交请假申请,流程id:{}", name, processInstance.getId());
        return "提交成功,流程id:"+processInstance.getId();
    }

    /**
     * 领导审核通过
     * @param employeeNo  员工工号
     * @return
     */
    @GetMapping("leaderExaminePass")
    public String leaderExamine(@RequestParam(value = "employeeNo") String employeeNo) {
        List<Task> taskList = taskService.createTaskQuery().taskAssignee(employeeNo).orderByTaskId().desc().list();
        if (null == taskList) {
            throw  new RuntimeException("当前员工没有任何申请");
        }
        for (Task task : taskList) {
            if (task == null) {
                log.info("任务不存在 ID:{};", task.getId());
                continue;
            }
            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());
            Map<String, Object> map = new HashMap<>();
            /**
             *      key:配置文件中的下个处理流程id
             *      value:默认老板工号为001
             */
            map.put("bossNo", "001");
            /**
             *      key:指定配置文件中的条件判断id
             *      value:指定配置文件中的审核条件
             */
            map.put("outcome", "通过");
            taskService.complete(task.getId(), map);
        }
        return "领导审核通过";
    }


    /**
     * 老板审核通过
     * @param leaderNo  领导工号
     * @return
     */
    @GetMapping("bossExaminePass")
    public String bossExamine(@RequestParam(value = "leaderNo") String leaderNo) {
        List<Task> taskList = taskService.createTaskQuery().taskAssignee(leaderNo).orderByTaskId().desc().list();
        if (null == taskList) {
            throw  new RuntimeException("当前员工没有任何申请");
        }
        for (Task task : taskList) {
            if (task == null) {
                log.info("任务不存在 ID:{};", task.getId());
                continue;
            }
            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());
            Map<String, Object> map = new HashMap<>();
            /**
             *     老板是最后的审批人   无需指定下个流程
             */
//            map.put("boss", "001");
            /**
             *      key:指定配置文件中的条件判断id
             *      value:指定配置文件中的审核条件
             */
            map.put("outcome", "通过");
            taskService.complete(task.getId(), map);
        }
        return "领导审核通过";
    }

    /**
     * 驳回
     *
     * @param employeeNo  员工工号
     * @return
     */
    @GetMapping("reject")
    public String reject(@RequestParam(value = "employeeNo") String employeeNo) {
        List<Task> taskList = taskService.createTaskQuery().taskAssignee(employeeNo).orderByTaskId().desc().list();
        if (null == taskList) {
            throw  new RuntimeException("当前员工没有任何申请");
        }
        for (Task task : taskList) {
            if (task == null) {
                log.info("任务不存在 ID:{};", task.getId());
                continue;
            }
            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());
            Map<String, Object> map = new HashMap<>();
            /**
             *      key:指定配置文件中的领导id
             *      value:指定配置文件中的审核条件
             */
            map.put("outcome", "驳回");
            taskService.complete(task.getId(), map);
        }
        return "申请被驳回";
    }


    /**
     * 生成流程图
     *
     * @param processId 任务ID
     */
    @GetMapping(value = "processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        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, true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}
  1. 测试
  • 员工提交请假申请,如下图:
    http://localhost:8080/askForLeave/employeeSubmit?name=Jerry&reason=去旅游&days=30&employeeNo=006
    SpringBoot集成Flowable工作流_第15张图片

  • 查看员工提交申请的流程图
    http://localhost:8080/askForLeave/processDiagram?processId=2cd67ca3-c32e-11ee-a344-d41b812ff10e
    SpringBoot集成Flowable工作流_第16张图片

  • 领导进行审批
    http://localhost:8080/askForLeave/leaderExaminePass?employeeNo=006
    SpringBoot集成Flowable工作流_第17张图片

  • 查看领导审批后的流程图
    http://localhost:8080/askForLeave/processDiagram?processId=2cd67ca3-c32e-11ee-a344-d41b812ff10e
    SpringBoot集成Flowable工作流_第18张图片

  • 老板审批
    http://localhost:8080/askForLeave/bossExaminePass?leaderNo=002
    SpringBoot集成Flowable工作流_第19张图片

  • 查看老板审批后的流程图
    http://localhost:8080/askForLeave/processDiagram?processId=2cd67ca3-c32e-11ee-a344-d41b812ff10e
    SpringBoot集成Flowable工作流_第20张图片

3. SpringBoot集成Flowable前端页面

参考简书毛于晏

  1. 下载源码包,此处使用的是6.7.2版本的
    源码下载地址: https://github.com/flowable/flowable-engine/tree/flowable-6.7.2

    链接:https://pan.baidu.com/s/1w-iuynskMuREBlYcpbDFAg
    提取码:9gs7

  2. 将以下路径的代码复制到自己工程
    /flowable-6.7.2/modules/flowable-ui/flowable-ui-modeler-frontend/src/main/resources/static/modeler
    SpringBoot集成Flowable工作流_第21张图片

  3. 跳过验证

import org.flowable.ui.common.security.SecurityConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * 绕过flowable的登录验证
 */
@Configuration
public class SecurityConfiguration {
    @Configuration(proxyBeanMethods = false)
    //Order配置说明
    // 这个地方相同会报错
    //这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用
    //所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权
    @Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.headers().frameOptions().disable();


            http
                    //必须要将csrf设置为disable,不然后面发送POST请求时会报403错误
                    .csrf().disable()
                    //为了简单起见,简单粗暴方式直接放行modeler下面所有请求
                    .authorizeRequests().antMatchers("/modeler/**").permitAll();
        }
    }
}
  1. 启动项目,访问:http://localhost:8080/modeler/#/processes
    SpringBoot集成Flowable工作流_第22张图片

三、创建流程模版(以请假为例)

  1. 确保已在idea中安装Flowable插件

  2. 在resources下创建processes文件夹,右键,创建一个【*.bpmn20.xml】文件
    SpringBoot集成Flowable工作流_第23张图片

  3. 选中创建好的【*.bpmn20.xml】文件,右键;SpringBoot集成Flowable工作流_第24张图片

  4. 开始画流程图 任意位置右键–>启动一个流程
    SpringBoot集成Flowable工作流_第25张图片
    SpringBoot集成Flowable工作流_第26张图片

  5. 设置员工提交申请
    SpringBoot集成Flowable工作流_第27张图片
    SpringBoot集成Flowable工作流_第28张图片

  6. 设置领导
    SpringBoot集成Flowable工作流_第29张图片

  7. 设置网关,用来判断领导是否同意
    SpringBoot集成Flowable工作流_第30张图片
    SpringBoot集成Flowable工作流_第31张图片

  8. 网关条件判断条件–驳回
    SpringBoot集成Flowable工作流_第32张图片

  9. 网关条件判断条件–通过
    SpringBoot集成Flowable工作流_第33张图片

  10. 设置老板
    SpringBoot集成Flowable工作流_第34张图片

  11. 设置网关,用来判断老板是否同意
    SpringBoot集成Flowable工作流_第35张图片

  12. 网关条件判断条件–驳回
    SpringBoot集成Flowable工作流_第36张图片

  13. 网关条件判断条件–通过
    SpringBoot集成Flowable工作流_第37张图片

  14. 当我们画好图后,【*.bpmn20.xml】流程文件也已经创建成功了
    SpringBoot集成Flowable工作流_第38张图片

  15. 可以书写针对流程的逻辑了

你可能感兴趣的:(spring,boot,java,Flowable)