• springboot集成flowable简单实例入门


    此案例是demo。功能有创建流程、完成审批、生成流程图。适合有java基础的人员看。

    第一步.画流程图

    resources资源包下,新建processes包,新建一个文件,我命名他apply-rest.bpmmn20.xml。bpmn20.xml后缀文件是流程图配置文件。idea的右下角的流程图画板打开。
    在这里插入图片描述

    安装插件Flowable BPMN visualizer,菜单Settings->Plugins->Marketplace里搜索下载插件。
    简单画个流程图:
    在这里插入图片描述
    我的流程图配置文件展示下:

    <?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="apply-rest" name="apply-rest" isExecutable="true">
        <startEvent id="sid-f4b3bfd8-86e1-4ba4-8df8-9e7a15c8cf72" name="开始"/>
        <userTask id="sid-aa6be134-f174-4c65-91f0-7fb9f3a1be34" name="组长审批" flowable:assignee="${param}"/>
        <sequenceFlow id="sid-7a8acdf6-50cf-4d22-8807-4e6c055c75ed" sourceRef="sid-f4b3bfd8-86e1-4ba4-8df8-9e7a15c8cf72" targetRef="sid-aa6be134-f174-4c65-91f0-7fb9f3a1be34"/>
        <exclusiveGateway id="sid-1cff7fda-063b-4e28-93f2-03cc8e087c9b"/>
        <sequenceFlow id="sid-5114a1b5-d323-40a7-b968-3f99b8e15bed" sourceRef="sid-aa6be134-f174-4c65-91f0-7fb9f3a1be34" targetRef="sid-1cff7fda-063b-4e28-93f2-03cc8e087c9b"/>
        <userTask id="sid-ccbf012a-c1ea-4f12-9a41-b2195ccc5e21" name="审批通过"/>
        <sequenceFlow id="sid-081e7466-b2ae-4c6d-b633-635589b300e8" sourceRef="sid-1cff7fda-063b-4e28-93f2-03cc8e087c9b" targetRef="sid-ccbf012a-c1ea-4f12-9a41-b2195ccc5e21">
            <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${approved}]]> </conditionExpression>
        </sequenceFlow>
        <userTask id="sid-0a99d0cd-a6a1-42e7-83b2-b45fbb4d89f8" name="审批拒绝"/>
        <sequenceFlow id="sid-9bdfade6-4553-40d4-bcf6-34e2417ebdd4" sourceRef="sid-1cff7fda-063b-4e28-93f2-03cc8e087c9b" targetRef="sid-0a99d0cd-a6a1-42e7-83b2-b45fbb4d89f8">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
        </sequenceFlow>
        <endEvent id="sid-91c2843d-aa34-4ecc-9562-9f31c04ec8e5"/>
        <sequenceFlow id="sid-722ecc9d-8246-41a2-b1d5-9000df12110e" sourceRef="sid-0a99d0cd-a6a1-42e7-83b2-b45fbb4d89f8" targetRef="sid-91c2843d-aa34-4ecc-9562-9f31c04ec8e5"/>
        <sequenceFlow id="sid-36f34417-62a6-42f3-9163-755764bbfa33" sourceRef="sid-ccbf012a-c1ea-4f12-9a41-b2195ccc5e21" targetRef="sid-91c2843d-aa34-4ecc-9562-9f31c04ec8e5"/>
      </process>
      <bpmndi:BPMNDiagram id="BPMNDiagram_apply-rest">
        <bpmndi:BPMNPlane bpmnElement="apply-rest" id="BPMNPlane_apply-rest">
          <bpmndi:BPMNShape id="shape-332a555f-47de-48fe-bf3d-bb43936780d8" bpmnElement="sid-f4b3bfd8-86e1-4ba4-8df8-9e7a15c8cf72">
            <omgdc:Bounds x="-89.0" y="-20.5" width="30.0" height="30.0"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape id="shape-c5b62dce-a2f9-47e7-a937-8b73dfcf2234" bpmnElement="sid-aa6be134-f174-4c65-91f0-7fb9f3a1be34">
            <omgdc:Bounds x="-41.0" y="-36.75" width="71.0" height="62.5"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge id="edge-641875fc-9f5b-4e66-b854-b4ea408c5942" bpmnElement="sid-7a8acdf6-50cf-4d22-8807-4e6c055c75ed">
            <omgdi:waypoint x="-59.0" y="-5.5"/>
            <omgdi:waypoint x="-41.0" y="-5.5"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNShape id="shape-c2812f03-8b29-4543-a79b-304e8a4b7da5" bpmnElement="sid-1cff7fda-063b-4e28-93f2-03cc8e087c9b">
            <omgdc:Bounds x="58.5" y="-25.5" width="40.0" height="40.0"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge id="edge-f30eb05d-0ef7-4711-918d-efab271b272b" bpmnElement="sid-5114a1b5-d323-40a7-b968-3f99b8e15bed">
            <omgdi:waypoint x="30.0" y="-5.5"/>
            <omgdi:waypoint x="58.5" y="-5.5"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNShape id="shape-8cb5a0eb-3f9a-4ecd-8535-efbf234d7027" bpmnElement="sid-ccbf012a-c1ea-4f12-9a41-b2195ccc5e21">
            <omgdc:Bounds x="117.0" y="-62.5" width="81.0" height="60.5"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge id="edge-5ef67794-ecc5-48e1-93bf-6bc8dac79069" bpmnElement="sid-081e7466-b2ae-4c6d-b633-635589b300e8">
            <omgdi:waypoint x="98.5" y="-5.5"/>
            <omgdi:waypoint x="117.0" y="-17.125"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNShape id="shape-620ea3e1-7cf6-476b-9051-76ca41f68eb2" bpmnElement="sid-0a99d0cd-a6a1-42e7-83b2-b45fbb4d89f8">
            <omgdc:Bounds x="117.25" y="23.0" width="80.5" height="62.0"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge id="edge-5fb9619a-b2f6-418d-828e-ff58b10a16d0" bpmnElement="sid-9bdfade6-4553-40d4-bcf6-34e2417ebdd4">
            <omgdi:waypoint x="78.5" y="14.5"/>
            <omgdi:waypoint x="117.25" y="38.5"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNShape id="shape-5d6d2111-6aaa-45dc-bff3-54bc66628403" bpmnElement="sid-91c2843d-aa34-4ecc-9562-9f31c04ec8e5">
            <omgdc:Bounds x="238.5" y="-18.0" width="30.0" height="30.0"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge id="edge-cb0e5e37-2583-4195-9b4f-644a4e0ca4f6" bpmnElement="sid-722ecc9d-8246-41a2-b1d5-9000df12110e">
            <omgdi:waypoint x="197.75" y="38.5"/>
            <omgdi:waypoint x="238.5" y="4.5"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge id="edge-8b5f8cff-c938-499a-9fbe-568d3e3cf1e3" bpmnElement="sid-36f34417-62a6-42f3-9163-755764bbfa33">
            <omgdi:waypoint x="198.0" y="-17.125"/>
            <omgdi:waypoint x="238.5" y="-10.5"/>
          </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
      </bpmndi:BPMNDiagram>
    </definitions>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    第二步.添加pom依赖

    在这里插入代码片<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.8.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.cherry</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>flowable_demo</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>2.0.10</version>
            </dependency>
    
            <!-- 接口文档 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
    
            <!-- flowable工作流 -->
            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-spring-boot-starter</artifactId>
                <version>6.5.0</version>
            </dependency>
            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-json-converter</artifactId>
                <version>6.5.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- mysql数据库 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.22</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!-- 测试 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    工作流的拿出来格外展示下

    <!-- flowable工作流 -->
            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-spring-boot-starter</artifactId>
                <version>6.5.0</version>
            </dependency>
            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-json-converter</artifactId>
                <version>6.5.0</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第三步.添加配置文件application.yml

    server:
      #设置端口
      port: 9999
    
    spring:
      datasource:
        #驱动mysql8以上
        driver-class-name: com.mysql.cj.jdbc.Driver
        #连接flowable自己创建的数据库
        url: jdbc:mysql://localhost:3306/flowable?serverTimezone=Asia/Shanghai&useUnicode=true&charcterEncoding=UTF-8&useSSL=false
        #mysql数据库用户名
        username: root
        #mysql数据库密码
        password: 12345678
    
    flowable:
      #关闭定时任务JOB
      async-executor-activate: false
      #将databaseSchemaUpdate设置为true。当flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
      database-schema-update: true
      activity-font-name: '楷体'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    第四步.开启Swagger

    启动类展示下:

    package com.cherry.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    @EnableSwagger2
    @SpringBootApplication
    public class FlowableDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FlowableDemoApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    swagger的配置类展示一下:
    这里设置了一个token,controller测试时随便填写

    package com.cherry.demo.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Parameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                    .apis(RequestHandlerSelectors.basePackage("com.cherry.demo.controller")).paths(PathSelectors.any())
                    .build().globalOperationParameters(setHeaderToken());
    
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder().title("action-swagger").description("swagger实战").termsOfServiceUrl("")
                    .version("1.0").build();
        }
    
        /**
         * @Description: 设置swagger文档中全局参数
         * @param
         * @return: java.util.List
         */
    
        private List<Parameter> setHeaderToken() {
            List<Parameter> pars = new ArrayList<>();
            ParameterBuilder userId = new ParameterBuilder();
            userId.name("token").description("用户TOKEN").modelRef(new ModelRef("string")).parameterType("header")
                    .required(true).build();
            pars.add(userId.build());
            return pars;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    第五步.添加要用的实体类

    三个测试类,封装历史任务HistanceTaskEntity、封装流程图状态StateEnum、封装任务常用信息TaskInstanceEntity
    历史任务类展示下:

    package com.cherry.demo.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * 历史任务实例
     */
    @Data
    public class HistanceTaskEntity implements Serializable {
        private static final long serialVersionUID = 5805783585536248176L;
        // 流程实例id
        private String processInstanceId;
        // 任务id
        private String taskId;
        // 开始时间
        private Date startTime;
        // 结束时间
        private Date endTime;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    流程图状态展示下:

    package com.cherry.demo.entity;
    
    /**
     * 流程状态枚举类
     */
    public enum StateEnum {
    
        LEADER_NOLOOK("0","组长未审批"),
    
        LEADER_LOOK("1","组长审批");
    
        private String state;
        private String content;
    
    
    
        StateEnum(String state,String content){
            this.state = state;
            this.content = content;
        }
    
        StateEnum() {
        }
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    任务常用信息实体类:

    package com.cherry.demo.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * 封装任务常用信息
     */
    @Data
    public class TaskInstanceEntity implements Serializable {
        private static final long serialVersionUID = 4321159119155954142L;
        // 任务id
        private String taskId;
        // 流程实例id
        private String processInstanceId;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    第六步.设置流程图里文字的字体

    package com.cherry.demo.config;
    
    import org.flowable.spring.SpringProcessEngineConfiguration;
    import org.flowable.spring.boot.EngineConfigurationConfigurer;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * desc: flowable配置----为放置生成的流程图中中文乱码
     */
    @Configuration
    public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
    
    
        @Override
        public void configure(SpringProcessEngineConfiguration engineConfiguration) {
            engineConfiguration.setActivityFontName("微软雅黑");
            engineConfiguration.setLabelFontName("微软雅黑");
            engineConfiguration.setAnnotationFontName("微软雅黑");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    第七步.测试接口类

    package com.cherry.demo.controller;
    
    import com.cherry.demo.entity.HistanceTaskEntity;
    import com.google.common.collect.Maps;
    import io.swagger.annotations.Api;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.flowable.bpmn.model.BpmnModel;
    import org.flowable.engine.*;
    import org.flowable.engine.history.HistoricActivityInstance;
    import org.flowable.engine.history.HistoricProcessInstance;
    import org.flowable.engine.impl.cmd.DeleteProcessInstanceCmd;
    import org.flowable.engine.runtime.ProcessInstance;
    import org.flowable.image.ProcessDiagramGenerator;
    import org.flowable.task.api.Task;
    import org.flowable.variable.api.history.HistoricVariableInstance;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.util.Base64Utils;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /***
     * 测试演示
     * token随便填
     */
    @Slf4j
    @Api(value = "测试接口", tags = {"测试接口"})
    @RestController
    @RequestMapping("test")
    public class DemoController {
    
        @Autowired
        private RuntimeService runtimeService;
    
        @Autowired
        private TaskService taskService;
    
        @Autowired
        private RepositoryService repositoryService;
    
        @Autowired
        private HistoryService historyService;
    
        @Autowired
        private ProcessEngine processEngine;
    
        @Resource
        private ManagementService managementService;
    
        /**
         * 创建流程开启审批
         * @param processId bpmn20.xml的process的id。案例是"apply-rest"
         * @param param 流程的第一个userTask的flowable:assignee="${param}"绑定参数。
         *              此参数自定义,可以是指定处理的人id
         * @return 流程实例唯一id
         */
        @PostMapping(value = "/start")
        public String startProcess(@RequestParam(required = false,defaultValue = "apply-rest") String processId,String param) {
            HashMap<String, Object> map = new HashMap<String, Object>(1){{ put("param", param);}};
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processId, map);
            log.info("创建请假流程 processId:{}", processInstance.getId());
            return processInstance.getId();
        }
    
        /**
         * 完成审批第二步任务 组长审批
         * @param processInstanceId 流程实例唯一id,开启流程是生成
         * @param approved 是否通过,true是通过,false是拒绝,bpmn20.xml会根据值决定流哪个流程
         * @return 任务信息包括任务id和任务名称
         */
        @GetMapping("/changeStatus")
        public String changeStatus(String processInstanceId,Boolean approved){
            // 根据流程实例id获得准备进行的任务
            Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
            // 任务不存在
            if (task == null){ return "流程不存在"; }
            // 任务存入approved
            Map<String,Object> v = new HashMap<>(1);
            v.put("approved",approved);
            // 根据任务id和参数,选择分支完成任务
            taskService.complete(task.getId(),v);
            return task.toString();
        }
    
        /**
         * 完成最后一步到结束节点
         * @param processInstanceId 流程实例
         * @return 任务信息
         */
        @GetMapping("/finish")
        public String changeStatus1(String processInstanceId){
            // 根据流程id获得准备进行的任务
            Task task =   taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
            if (task == null){ return "流程不存在"; }
            // 根据任务id,参数存在HashMap里
            taskService.complete(task.getId(),new HashMap<>());
            return task.toString();
        }
    
        /**
         * 生成流程图
         * @param httpServletResponse
         * @param processInstanceId
         * @throws Exception
         */
        @GetMapping(value = "/image")
        public void genProcessDiagram(HttpServletResponse httpServletResponse, String processInstanceId) throws Exception {
            // 产生流程实例
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            // 产生历史流程实例
            HistoricProcessInstance his = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            // 实例id
            String instanceId;
            // 如果实例不是正在进行的就是历史的
            if (pi == null) { instanceId = his.getProcessDefinitionId(); } else { instanceId = pi.getProcessDefinitionId(); }
            // 完全不存在的实例无法生成流程图
            if (StringUtils.isBlank(instanceId)){ return;}
    
            //流程走完的不显示图
            /*if (pi == null) {
                return;
            }*/
    
            // 历史活动实例
            List<HistoricActivityInstance> historyProcess = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
            // 活动id
            List<String> activityIds = new ArrayList<>();
            // 流程id
            List<String> flows = new ArrayList<>();
            // 获取流程图
            BpmnModel bpmnModel = repositoryService.getBpmnModel(instanceId);
            for (HistoricActivityInstance hi : historyProcess) {
                // 获得xml中标签
                String activityType = hi.getActivityType();
                // 流程线或者互斥网关存入流程id中,用户任务、开始任务、结束事件存入活动id中
                if (StringUtils.equalsAny(activityType,"sequenceFlow", "exclusiveGateway")) {
                    flows.add(hi.getActivityId());
                } else if (StringUtils.equalsAny(activityType,"userTask", "startEvent", "endEvent")) {
                    activityIds.add(hi.getActivityId());
                }
            }
            // 获得所有任务,根据流程实例id
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            // 补全活动id,添加任务id到活动id集合中
            for (Task task : tasks) {
                activityIds.add(task.getTaskDefinitionKey());
            }
            // 设置响应的类型格式为图片格式
            httpServletResponse.setContentType("image/png");
            // 禁止图像缓存
            httpServletResponse.setHeader("Pragma", "no-cache");
            // 信息不被存储
            httpServletResponse.setHeader("Cache-Control", "no-cache");
            // 防止缓存代理服务
            httpServletResponse.setDateHeader("Expires", 0);
            // 流程引擎配置
            ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
            // 流程图生成器
            ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
            // 生成流程图输入流
            try (InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0,false)) {
                OutputStream out = null;
                // 1kb缓存大小
                byte[] buf = new byte[1024];
                int length = 0;
                try {
                    out = httpServletResponse.getOutputStream();
                    while ((length = in.read(buf)) != -1) {
                        out.write(buf, 0, length);
                    }
                } finally {
                    if (in != null) {
                        in.close();
                    }
                    if (out != null) {
                        out.close();
                    }
                }
            }
        }
    
        /**
         * 生成流程图
         *
         * @param processInstanceId 任务ID
         */
        @GetMapping(value = "/imageBase")
        public String genProcessDiagramBase(HttpServletResponse httpServletResponse, String processInstanceId) throws Exception {
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            HistoricProcessInstance his = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            String pdi;
            if (pi == null) {
                pdi = his.getProcessDefinitionId();
            } else {
                pdi = pi.getProcessDefinitionId();
            }
            if (StringUtils.isBlank(pdi)) {
                return null;
            }
            //获得活动的节点
            List<HistoricActivityInstance> historyProcess = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
            List<String> activityIds = new ArrayList<>();
            List<String> flows = new ArrayList<>();
            //获取流程图
            BpmnModel bpmnModel = repositoryService.getBpmnModel(pdi);
            for (HistoricActivityInstance hi : historyProcess) {
                String activityType = hi.getActivityType();
                if (StringUtils.equalsAny(activityType,"sequenceFlow", "exclusiveGateway")) {
                    flows.add(hi.getActivityId());
                } else if (StringUtils.equalsAny(activityType,"userTask", "startEvent", "endEvent")) {
                    activityIds.add(hi.getActivityId());
                }
            }
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            for (Task task : tasks) {
                activityIds.add(task.getTaskDefinitionKey());
            }
            ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
            //定义流程画布生成器
            ProcessDiagramGenerator processDiagramGenerator = engConf.getProcessDiagramGenerator();
            InputStream in = processDiagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engConf.getActivityFontName()
                    , engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(), 1.0, true);
    
            // 设置响应的类型格式为图片格式
            httpServletResponse.setContentType("image/png");
            // 禁止图像缓存
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setHeader("Cache-Control", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
    
            String base64Img = null;
            try {
                // in.available()返回文件的字节长度
                byte[] buf = new byte[in.available()];
                // 将文件中的内容读入到数组中
                in.read(buf);
                // 进行Base64编码处理
                base64Img = new String(Base64Utils.encode(buf), StandardCharsets.UTF_8);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (in != null) {
                    in.close();
                }
            }
            return base64Img;
        }
    
        /**
         * 查看创建流程实例时登记的变量信息
         * @param processInstanceId 工程实例id
         * @return 变量名和值的键值对
         */
        @GetMapping(value = "/queryProcessVariables")
        public Map<String,Object> queryProcessVariables(String processInstanceId){
            // 查询历史变量实例
            List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(processInstanceId).list();
            // 没有历史变量实例返回空或抛异常
            if (historicVariableInstances == null) {
                return new HashMap<>();
            }
            // 存变量和值
            Map<String, Object> ret = Maps.newHashMap();
            for (HistoricVariableInstance var : historicVariableInstances) {
                ret.put(var.getVariableName(), var.getValue());
            }
            return ret;
        }
    
        /**
         * 获取某人的历史审批数据
         * @param processInstanceId 流程实例id
         * @param assignee 代理信息
         * @return
         */
        @GetMapping(value = "/queryHistoryProcessWithAssignee")
        public List<HistanceTaskEntity> queryHistoryProcess(String processInstanceId, String assignee){
            // 查询历史活动实例,根据流程id和代理参数
            List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
                    .processDefinitionId(processInstanceId).taskAssignee(assignee).finished().orderByHistoricActivityInstanceEndTime().desc().list();
            // 搜集历史任务实例
            List<HistanceTaskEntity> result = new ArrayList<>();
            for (HistoricActivityInstance h : activities) {
                HistanceTaskEntity d = new HistanceTaskEntity();
                d.setProcessInstanceId(h.getProcessInstanceId());
                d.setTaskId(h.getTaskId());
                d.setStartTime(h.getStartTime());
                d.setEndTime(h.getEndTime());
                result.add(d);
            }
            return result;
        }
    
    
        /**
         * 查询是否存在历史数据的流程实例
         * @param processInstanceId 流程实例id
         * @return
         */
        @GetMapping(value = "/queryHistoryProcess")
        public Boolean isExistHistoricProcessInstance(String processInstanceId){
            // 获得历史流程实例
            HistoricProcessInstance historicProcessInstance =
                    historyService.createHistoricProcessInstanceQuery().
                            processInstanceId(processInstanceId).singleResult();
            // 判断是否历史流程实例
            if (historicProcessInstance == null) {
                return false;
            }
            return true;
        }
    
        /**
         * 将指定的流程挂起
         * @param processInstanceId 流程实例id
         */
        @PutMapping("/suspendProcessInstance")
        public void suspendProcessInstance(String processInstanceId){
            runtimeService.suspendProcessInstanceById(processInstanceId);
        }
    
        /**
         * 将指定的流程激活
         * @param processInstanceId 流程实例id
         */
        @PutMapping("/activateProcessInstance")
        public void activateProcessInstance(String processInstanceId){
            runtimeService.activateProcessInstanceById(processInstanceId);
        }
    
        /**
         * 删除流程实例
         * @param processInstanceId 流程实例id
         * @param deleteReason 删除原因
         */
        @PostMapping("/deleteProcessInstance")
        public void deleteProcessInstance(String processInstanceId, String deleteReason){
            // 流程的数量
            long count = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).count();
            // 判断是否需要删除流程
            if (count > 0) {
                // 需要删除时,增加删除原因
                DeleteProcessInstanceCmd cmd = new DeleteProcessInstanceCmd(processInstanceId, deleteReason);
                // 执行命令
                managementService.executeCommand(cmd);
            } else {
                // 删除历史数据流程实体
                historyService.deleteHistoricProcessInstance(processInstanceId);
            }
        }
    
        /**
         * 查询流程实例是否完成
         * @param processInstanceId 流程实例id
         * @return 判断结果
         */
        @GetMapping("/isProcessFinished")
        public Boolean isProcessFinished(String processInstanceId){
            // 判断历史中流程的个数是否>0
            return historyService.createHistoricProcessInstanceQuery().finished()
                    .processInstanceId(processInstanceId).count() > 0;
        }
    
        /**
         * 删除给定的部署并级联删除到流程实例、历史流程实例和作业
         * @param deployId 部署的id
         * @return
         */
        @GetMapping("/deleteDeploy")
        public Boolean deleteDeploy(String deployId){
            this.repositoryService.deleteDeployment(deployId,true);
            return true;
        }
    
        /**
         * 获得即将进行的任务
         * @param processInstanceId 流程实例id
         * @return 任务
         */
        @GetMapping("/activeTask")
        public String getActiveTaskByProcessInstanceId(String processInstanceId){
            if(StringUtils.isBlank(processInstanceId)){
                return null;
            }
            Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
            if (task!=null){return task.toString();}
            return "任务不存在";
        }
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
  • 相关阅读:
    题目:2665.计数器 II
    STM32三种开发方式及标准库和HAL库的编程差异
    unity学习 --- 脚本组件
    android源码-事件分发处理机制(下)-从信号源输入到处理完成的完整源码解读
    为什么从没有负值的数据中绘制的小提琴图(Violin Plot)会出现负值部分?
    认识异步复位寄存器同时学习timing_arc约束
    游戏热更新进修——Lua编程
    微信小程序 为tabBar设置角标
    [力扣] 剑指 Offer 第二天 - 从尾到头打印链表
    社科院与杜兰大学能源管理硕士项目——惊喜会随时间慢慢酝酿而出
  • 原文地址:https://blog.csdn.net/m0_49382941/article/details/128002698