springboot集成flowable简单实例入门

news2024/11/18 15:27:52

此案例是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

第四步.开启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 "任务不存在";
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/29779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ImageMol

ai圈也是卷的狠啊~~ 又发现一个全球首个分子图像自监督学习框架来也 分子是维持物质化学稳定性的最小单位。对分子的研究&#xff0c;是药学、材料学、生物学、化学等众多科学领域的基础性问题。 分子的表征学习&#xff08;Molecular Representation Learning&#xff09;是…

实践 | 大型基金管理公司数据脱敏体系建设

金融行业数据脱敏安全管理要求高、数据类型复杂、脱敏数据规模庞大。作为业内领先&#xff0c;且支持信创环境的数据安全产品&#xff0c;美创数据脱敏系统在金融行业应用广泛&#xff0c;可满足各类复杂环境下的数据脱敏需求&#xff1a; 台州银行—分布式大数据平台下的及时脱…

SQL Server数据库理论篇(进行时......)

SQL Server数据库理论篇 一. 数据库的关系分类 1.1.0 关系型数据库和非关系数据库区别&#xff1f; 结论&#xff1a;两种数据库的本质在于存储数据的形式不同 1.关系型数据库概念 关系型数据库最大的特征就是表&#xff0c;我们将对象的属性映射为表的一个个列名&#xff…

(九)centos7案例实战——redis一主二从三哨兵高可用服务搭建

前言 本节内容是使用centos服务器搭建一套高可用的redis服务&#xff0c;采用的是一主二从三哨兵的模式。 需要注意的是搭建集群的过程中&#xff0c;我们要保证集群服务器之间可以相互访问&#xff0c;并且redis所需要访问的端口是开放的。我们从redis的下载&#xff0c;源码…

ffmpeg 安装教程

官网&#xff1a;Download FFmpeg window 转&#xff1a;ffmpeg安装教程_moon son的博客-CSDN博客_ffmpeg安装 然后解压&#xff0c;配置全局变量环境。点击“系统属性->高级系统设置->环境变量->系统变量”&#xff0c;选择“Path”条目&#xff0c;点击“编辑->…

将项目部署到Linux系统上

目的是让我们的项目在linux上也能运行起来 有两种部署方式&#xff0c;手工部署或者是通过shell脚本自动部署 手工部署 准备工作&#xff1a;使用ifconfig指令查出服务器的ip地址&#xff1a;192.168.58.130 1.在本地Idea中开发一个springboot项目&#xff0c;并且打包成ja…

以太网模块的传输距离怎么看

光模块的关键标准组织主要有两个&#xff1a;IEEE和MSA。其中GBASE开头的标准主要是IEEE802.3提出与定义的。要弄清光模块的传输距离&#xff0c;首先让我们要弄清楚它的命名是怎样的。 100G光模块的命名规则&#xff1a; 400G光模块的命名规则&#xff1a; 其中100G和400G光模…

SpringBoot整合Mybatis方式1:使用XML方式整合Mybatis

SpringBoot整合Mybatis简介SpringBoot整合Mybatis方式1&#xff1a;使用XML方式整合Mybatis1.用idea创建一个添加mybatis需要的相关依赖的工程。2.准备数据库和表3.创建表映射类4.创建mapper文件4.1 创建UsersMapper.xml文件&#xff0c;并添加sql语句4.2 创建mapper对应的代理…

Docker的Cgroup资源限制

Docker通过Cgroup来控制容器使用的资源配额&#xff0c;包括 CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配颡和使用量控制。 Cgoup 是CotrolGroups 的缩写&#xff0c;是Linux 内核提供的一种可以限制、记录、隔高进程组所使用的物理资源&#xff08;如CPU、内存…

基于jsp的学生培训管理系统

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【教学类-18-01】20221123《蒙德里安红黄蓝格子画》(大班)

效果展示&#xff1a; 单页效果 多页效果 预设效果 背景需求&#xff1a; 2022年11月23日&#xff0c;作为艺术特色幼儿园&#xff0c;蒙德里安风格装饰在我们幼儿园的环境中。 蒙德里安是几何抽象画派的先驱&#xff0c;以几何图形为绘画的基本元素&#xff0c;与德士堡等创…

MyBatis-Plus 和swagger

MyBatis-Plus 1.1MyBatis Plus 简介 mybatisplus 官网&#xff1a; https://baomidou.com/ MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c; 为简化开发、提高效率而生。 1.2主要特性&a…

【项目管理】Java使用pdfbox调用打印机打印PDF文件

【项目管理】Java使用pdfbox调用打印机打印PDF文件1.项目前言2.项目实现3.关键代码4.效果演示5.问题处理5.1 安装对应字体5.2 修改对应代码系统&#xff1a;Win10 Java&#xff1a;1.8.0_333 IDEA&#xff1a;2020.3.4 Gitee&#xff1a;https://gitee.com/lijinjiang01/Printe…

如何使用轮播图实现水平内容自动切换

通过轮播图实现一个水平内容自动切换 效果展示 前置准备 三张背景图片 具体步骤 添加一个轮播图组件 添加一个图片 添加一个标题 添加数据表 添加事件 添加触发器 绑定数据 步骤拆解 添加一个轮播图组件 1.1 拖拽 轮播图 到 编辑区 1.2 调整 轮播图 样式 添加一个图片 …

有关QT的问题大全

文章目录现在2022年&#xff0c;Qt发展如何&#xff1f;是就业的好选择吗&#xff1f;如何学习Qt,c到什么程度可以去学qt&#xff1f;现在 Qt 好找工作吗&#xff1f;为什么工业软件开发一般用的都是QT&#xff1f;初学QT怎么学?请问目前做windows桌面应用程序&#xff0c;MFC…

工作中Java Stream的简单应用

标题开发使用filter() and map()max()根据集合对象中的某个属性取最大值先解释一下各变量含义: temps集合是userids的子集(大前提) cache是temps集合在userids集合中的补集 简单来说就是,userids集合-temps集合cache补集,如下图所示目标:此次为工作中实际遇到的问题,需要得到c…

C++编译链接

文章目录C编译链接C编译模式分离式编译是个啥&#xff1f;怎么实现上述过程&#xff1f;定义与声明引出新的问题头文件有关头文件用来干啥&#xff1f;include头文件中应该写什么&#xff1f;#ifndef通过gcc/g命令来看完整的编译链接过程预处理(Preprocessing)编译(Compilation…

院内导航系统哪家口碑好?医疗院内导航地图公司排名

“医生&#xff0c;请问验血怎么走&#xff1f;”   “护士&#xff0c;请问药房在哪儿&#xff1f;”   “您好&#xff0c;做CT在什么地方&#xff1f;”   这些问题是医生和护士经常遇到的问题。的确&#xff0c;患者就诊时&#xff0c;由于对医院环境不熟悉&#xff…

云计算(虚拟化)面试宝典

一:服务器虚拟化 1.名词解释 (1)资源动态调整(对象是虚拟机) 管理员操作,对CPU、内存、网卡、硬盘、GPU进行调整。 FusionCompute支持虚拟机资源动态调整,用户可以根据业务负载动态调整资源的使用情况。 虚拟机资源调整包括: 离线/在线调整vCPU数目 无论虚拟机处于离线…

如何使用C++图形界面开发框架Qt创建一个应用程序?(Part 3)

Qt是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 点击获取Qt组件下载 …