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