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>

第二步.添加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>

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

<!-- 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>

第三步.添加配置文件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: '楷体'

第四步.开启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);
    }

}

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<springfox.documentation.service.Parameter>
     */

    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;
    }
}

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

三个测试类,封装历史任务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;
}

流程图状态展示下:

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;
    }
}

任务常用信息实体类:

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;
}

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

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("微软雅黑");
    }
}

第七步.测试接口类

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 "任务不存在";
    }


}

猜你喜欢

转载自blog.csdn.net/m0_49382941/article/details/128002698