写在最前:flowable和activiti本是一家,所以有很多api和设计是一样的,这里用的api是flowable的,流程设计器war包也可以共用,建议搜不到activiti某些资料的搜flowable的试试。
1.导包
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>activiti_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mysql.version>5.1.29</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--两种使用坐标任选其一-->
<!--activiti坐标始-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M3.1</version>
</dependency>
<!--解决activiti坐标导致的security注入问题-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<!--activiti坐标末-->
<!--flowable坐标始-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-logic</artifactId>
<version>6.6.0</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--flowable坐标末-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.新建启动类
(1)activiti方式:启动类上的exclude = {}一定要有,排除这两个配置类,不然会与activiti的权限管理冲突报错
(2)flowable方式则不需要启动类上的exclude = {}
package com.wanglj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Author 955
* @Date 2022-06-21 13:47
* @Description
*/
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class
})
public class ActivitiApplication {
public static void main(String[] args) {
SpringApplication.run(ActivitiApplication.class, args);
}
}
3.idea下载bpmn画图插件Activiti BPMN visualizer
,安装重启idea(actiBPM插件早就不适配2021的idea了,想用可以降级idea版本或者用eclipse画,我相信没人想用这两个方法,推荐使用下文官方画图工具,Activiti BPMN visualizer
这个插件只能有一些简单的功能)
4.在resources目录下新建processes文件夹,并点击右键选择bpmn文件新建(注意:流程名字随便取,但文件必须是xx.bpmn20.xml
格式)
5.填写相应参数
6.打开新建的xml文件,点击右键打开流程图工具
7.画流程图(以user task为例,其他功能自行参考官网),点击右键新建开始–>Activities–>User task,注意每个步骤写好流程名称
注:流转下一步人名称得写上(这里提交请假申请-->worker,部门经理审批-->leader,财务审批-->finance)
拖动箭头使流程连接
最终效果
8.保存流程图到项目,右键Save to PNG,选择路径为xx.bpmn20.xml同路径
完整结构如图
9.新建配置
(1)activitiXML方式配置(注:必须叫activiti.cfg.xml
且必须放resources根目录下,否则需要在代码配置放置路径,内容如下)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://xxxxxxx:3306/xxxx" />
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver" />
<property name="jdbcUsername" value="xxxx" />
<property name="jdbcPassword" value="xxxx" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
(2)flowable配置文件方式(activiti也能使用此方式配置,只是少许方法参数不一样):
import liquibase.Liquibase;
import lombok.AllArgsConstructor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.ui.modeler.service.FlowableModelQueryService;
import org.flowable.ui.modeler.service.ModelImageService;
import org.flowable.ui.modeler.service.ModelServiceImpl;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/**
* @Author 955
* @Date 2022-06-21 15:01
* @Description
*/
@Configuration
@AllArgsConstructor
public class ActivitiConfig {
private final DataSource dataSource;
private final PlatformTransactionManager transactionManager;
@Bean
public SpringProcessEngineConfiguration getProcessEngineConfiguration() {
SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
// 流程图字体设置
config.setActivityFontName("宋体");
config.setAnnotationFontName("宋体");
config.setLabelFontName("黑体");
config.setDataSource(dataSource);
config.setTransactionManager(transactionManager);
config.setAsyncExecutorActivate(true);
//数据库类型
config.setDatabaseType("mysql");
//是否使用idm
config.setDisableIdmEngine(true);
//是否开启检查表结构
config.setDatabaseSchemaUpdate("false");
return config;
}
@Bean
public ModelImageService modelImageService() {
return new ModelImageService();
}
@Bean
public ModelService modelService() {
return new ModelServiceImpl();
}
// @Bean
// public RepositoryService repositoryService(){
// return new RepositoryServiceImpl();
// }
//
//
// @Bean
// public RuntimeService runtimeService(){
// return new RuntimeServiceImpl();
// }
//
// @Bean
// public TaskService taskService(){
// return new TaskServiceImpl();
// }
//
// @Bean
// public HistoryService historyService(){
// return new HistoryServiceImpl();
// }
// @Bean
// public ProcessEngineFactoryBean processEngineFactoryBean(){
// return new ProcessEngineFactoryBean();
// }
@Bean(destroyMethod = "clearCache")
@Qualifier("flowableModeler")
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
public FlowableModelQueryService flowableModelQueryService() {
return new FlowableModelQueryService();
}
@Bean
@Primary
public TaskExecutor primaryTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
注:若使用flowable集成时,需在yml文件配置如下内容,否则启动报错
mybatis-plus:
configuration-properties:
blobType: BLOB
prefix:
10.编写测试代码
注:其中开始流程的key值为新建流程文件时的名称,如果不清楚可打开流程图面板,左键点击空白处,可显示id,对应key值
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;
/**
* @Author 955
* @Date 2022-06-21 15:01
* @Description
*/
@Slf4j
public class test {
/**
* 初始化流程部署
*/
@Test
public void testDeployment() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/process_955.bpmn20.xml").addClasspathResource("processes/process_955.png").name("请假申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
/**
* 开始流程
*/
@Test
public void testStartProcess() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process_955");
System.out.println("流程部署id:" + processInstance.getId());
System.out.println("流程部署名称:" + processInstance.getName());
System.out.println("processInstanceId:" + processInstance.getProcessInstanceId());
}
/**
* 查询流转到该所属角色的任务
*/
@Test
public void testFindPersonalTaskList() {
//对应各流程节点流转下一步人名称,这里第一步从worker开始
//调用下方completTask方法可通过审批,再查询下一个名称leader,以此类推直到结束,因为流程图没有不通过的情况所以暂不考虑
String assignee = "worker";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().processDefinitionKey("process_955").taskAssignee(assignee).list();
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
/**
* 完成任务
*/
@Test
public void completTask() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
//根据流程key和任务的负责人 查询任务
//返回一个任务对象
//对应各流程节点流转下一步人名称,这里第一步从worker开始,分别为worker-->leader-->finance
Task task = taskService.createTaskQuery().processDefinitionKey("process_955").taskAssignee("worker").singleResult();
//完成任务,参数:任务id
taskService.complete(task.getId());
}
/**
* 查询出当前所有的流程定义
*/
@Test
public void queryProcessDefinition() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//查询出当前所有的流程定义
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("process_955").orderByProcessDefinitionVersion().desc().list();
//输出流程定义信息
for (ProcessDefinition processDefinition : list) {
System.out.println("流程定义 id=" + processDefinition.getId());
System.out.println("流程定义 name=" + processDefinition.getName());
System.out.println("流程定义 key=" + processDefinition.getKey());
System.out.println("流程部署id =" + processDefinition.getDeploymentId());
System.out.println("<=========================================>");
}
}
/**
* 删除流程
*/
@Test
public void deleteDeployment(){
String deploymentId = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//删除流程定义,如果该流程定义已有流程实例启动则删除报错
repositoryService.deleteDeployment(deploymentId);
//设置为true,则有流程实例在启动也可以强制删除
// repositoryService.deleteDeployment(deploymentId,true);
}
/**
* 输出流程文件和流程图到文件夹
* @throws Exception
*/
@Test
public void queryBpmnFile()throws Exception{
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("process_955").singleResult();
//通过流程定义信息,得到部署id
String deploymentId = processDefinition.getDeploymentId();
//得到png图片流
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
//得到bpmn文件流
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
File file_png = new File("C:\\Users\\HYGK\\Desktop\\bpmn\\process_955.png");
File file_bpmn = new File("C:\\Users\\HYGK\\Desktop\\bpmn\\process_955.bpmn");
FileOutputStream pngOut = new FileOutputStream(file_png);
FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
IOUtils.copy(pngInput,pngOut);
IOUtils.copy(bpmnInput,bpmnOut);
pngOut.close();
bpmnOut.close();
}
/**
* 根据instanceId查询整个流程
*/
@Test
public void findHistoryInfo(){
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
//获取actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
//根据instanceId查询整个流程
instanceQuery.processInstanceId("5001").orderByHistoricActivityInstanceStartTime().asc();
List<HistoricActivityInstance> list = instanceQuery.list();
for (HistoricActivityInstance historicActivityInstance : list) {
System.out.println(historicActivityInstance.getActivityId());
System.out.println(historicActivityInstance.getActivityName());
System.out.println(historicActivityInstance.getProcessDefinitionId());
System.out.println(historicActivityInstance.getCalledProcessInstanceId());
System.out.println("<=========================================>");
}
}
}
==============================================================================
进阶使用:
写在前面:如下使用flowable-ui画流程图,初始操作如下,但第一次启动后所有画好的流程文件都是一次性的,关闭服务就没有的,若想存数据库,请先参考下列第5条。
要使用监听器最好使用其他的画图工具,idea的画图工具支持不是很好,这里推荐官方的工具
1.flowable下载地址(从flowable7版本开始flowable-ui被移除,若想使用建议下载6.X的版本或者前往官网使用最新的设计器):https://github.com/flowable/flowable-engine/releases 2.下载解压后复制这两个war包到tomcat的wepapps目录下
3.启动tomcat,访问localhost:8080/flowable-ui,初始账号:admin,初始密码:test
4.画流程图
5.流程数据保存到数据库
(1)关闭tomcat服务,进入webapps,发现生成了两个文件夹(注:需启动一次后才会生成)
(2)分别点击进入两个文件夹,位置如下
C:\apache-tomcat-9.0.71\webapps\flowable-ui\WEB-INF\classes\flowable-default.properties
C:\apache-tomcat-9.0.71\webapps\flowable-rest\WEB-INF\classes\flowable-default.properties
(3)修改配置文件数据库连接(注释初始连接,选择数据源放开修改,这里选择mysql,若是oracle选择对应数据源配置即可,需注意的是,配置连接信息后会出现一系列的问题,如数据库版本问题、选择其他数据源启动报错等问题,只需更换或新增X:\apache-tomcat-9.0.71\webapps\flowable-ui\WEB-INF\lib
目录下对应的jar包即可,如oracle数据库需要引入oracle的驱动jar包才行,这里不过多赘述)
提供一个mysql的连接信息:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://xxx.xxx.xxx.x:3306/flowable_biz?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
例如这里就缺少mysql的驱动,maven仓库下一个即可
6.启动tomcat,等待数据库建立这些表,只要能启动和在建表中间有报错不用管,启动完成后按上述1、2、3、4步骤即可
注:
Execution listeners为执行监听器,可以查看执行中的一些流程信息等。
Task listeners为任务监听器,可以当任务流转到某一步骤时,如动态设置受理人等。
Assignments为指定用户/用户组,可以指定当前步骤由谁执行。
流程指向箭头:
在流程指向箭头中,可以设置el表达式来设定满足某些条件时指定流程走向,具体用法为${逻辑},此外流程箭头可以点击工具类上方拆分按钮
使连线拆分,满足强迫症患者。
执行/任务监听器设定:
点击:Execution listeners/Task listeners,添加监听器,监听器配置为项目相对路径
画好流程图后点击保存,会直接保存到act_de_model表中,前提是上面配置了数据源连接
<5>编写监听类(以经理审批任务监听类为例,执行监听器和任务监听器的代码区别就是分别实现ExecutionListener
和TaskListener
,重写notify方法的参数DelegateTask
和DelegateExecution
不同):
注:想了解执行/任务监听器的设计区别,请参考这一文章:
package com.wanglj.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
/**
* @Author 955
* @Date 2022-10-14 14:34
* @Description 经理审批任务监听器
*/
public class ManagerTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
//动态配置用户组
delegateTask.addCandidateGroup("jl1,jl2,jl3");
//动态配置执行人
delegateTask.setAssignee("s");
//这里也可配置满足某些条件自动跳过下一步,简单来说就是走一下执行流程方法
//需要注意的是监听器没有被spring管理(我自己的理解),所以不能注入service进来用,得用上下文去获取bean来用
}
}
/**
* @Author 955
* @Date 2022-10-14 10:10
* @Description 经理审批执行监听器
*/
public class ManagerExecutionListener implements ExecutionListener {
@Override
public void notify(DelegateExecution delegateExecution) {
System.out.println("经理审批监听");
}
}
<6>提供一些service方法:
注:如果是新建项目未建表的,在springboot-test类执行此方法,配置好流程文件路径即可
@Test
public void testDeployment1() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/leave.bpmn20.xml").addClasspathResource("processes/leave.png").name("借款申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
(1)act业务父类
FinshBpVo
package com.wanglj.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* @Author 955
* @Date 2022-10-14 11:15
* @Description
*/
@Data
public class FinshBpVo implements Serializable {
@ApiModelProperty(value = "任务id")
private String taskId;
@ApiModelProperty(value = "下一步需要的参数,可为空")
private Map<String, Object> variables;
@ApiModelProperty(value = "用户")
private String userId;
@ApiModelProperty(value = "完成时的批注内容")
private String comment;
}
package com.wanglj.service.impl;
import com.wanglj.util.R;
import com.wanglj.vo.EndProcessVo;
import com.wanglj.vo.StartProcessInstanceVo;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* act业务父类
* @Author 955
* @Date 2022-07-04 10:21
* @Description
*/
@Service
public class BaseActService {
@Autowired
protected ManagementService managementService;
@Autowired
protected TaskService taskService;
@Autowired
protected RuntimeService runtimeService;
@Autowired
protected RepositoryService repositoryService;
@Autowired
protected HistoryService historyService;
/**
* 初始化流程部署
*/
public void testDeployment() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/leave.bpmn20.xml").addClasspathResource("processes/leave.png").name("借款申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
public R<String> startProcessInstance(StartProcessInstanceVo vo) {
testDeployment();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(vo.getProcessDefinitionKey())
.latestVersion().singleResult();
if (processDefinition != null && processDefinition.isSuspended()) {
return R.fail("该流程已存在");
}
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
.processDefinitionKey(vo.getProcessDefinitionKey().trim())
.businessKey(vo.getBusinessKey().trim())
.variables(vo.getVariables())
.start();
String processInstanceId = processInstance.getProcessInstanceId();
return R.ok(processInstanceId);
}
public R<String> stopProcessInstanceById(EndProcessVo endVo) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(endVo.getProcessInstanceId()).singleResult();
if (processInstance != null) {
//1、添加审批记录
// flowableCommentService.addComment(null,endVo.getUserId(),endVo.getUserName(),endVo.getDeptId(), endVo.getProcessInstanceId(), TaskStatusEnum.QZOVER.getStatus(),
// endVo.getMessage());
// List<EndEvent> endNodes = flowableBpmnModelService.findEndFlowElement(processInstance.getProcessDefinitionId());
// String endId = endNodes.get(0).getId();
String processInstanceId = endVo.getProcessInstanceId();
//2、执行终止
List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
// this.moveExecutionsToSingleActivityId(executionIds, endId);
return R.ok("终止成功!");
} else {
return R.fail("不存在运行的流程实例,请确认!");
}
}
public List<FlowNode> findFlowNodes(String processDefId) {
List<FlowNode> flowNodes = new ArrayList<>();
BpmnModel bpmnModel = this.getBpmnModelByProcessDefId(processDefId);
org.activiti.bpmn.model.Process process = bpmnModel.getMainProcess();
Collection<FlowElement> list = process.getFlowElements();
list.forEach(flowElement -> {
if (flowElement instanceof FlowNode) {
flowNodes.add((FlowNode) flowElement);
}
});
return flowNodes;
}
public BpmnModel getBpmnModelByProcessDefId(String processDefId) {
return repositoryService.getBpmnModel(processDefId);
}
public void testFindPersonalTaskList() {
String assignee = "xmfzr1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
// List<Task> list = taskService.createTaskQuery().processDefinitionKey("order_ex_flow").taskAssignee(assignee).list();
List<Task> list = taskService.createTaskQuery().processDefinitionKey("order_ex_flow").taskCandidateUser(assignee).list();
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("ProcessDefinitionId:" + task.getProcessDefinitionId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
System.out.println("任务名称:" + task.getCreateTime());
}
}
}
(2)具体业务实现类(只是一些很杂乱的方法,最下方有可直接用于生产的接口设计-原生):
package com.wanglj.service.impl;
import com.wanglj.service.ActivitiService;
import com.wanglj.util.Kit;
import com.wanglj.util.R;
import com.wanglj.vo.FinshBpVo;
import com.wanglj.vo.StartProcessInstanceVo;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ActivitiTaskAlreadyClaimedException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Author 955
* @Date 2022-10-09 15:38
* @Description
*/
@Service
@Slf4j
public class ActivitiServiceImpl extends BaseActService implements ActivitiService {
@Override
public R<String> startApplyProcess(StartProcessInstanceVo vo) {
return startProcessInstance(vo);
}
/**
* 获取最新版本的所有的流程定义列表
*
* @return
*/
@Override
public List<ProcessDefinition> getLastVersionProcessDefinitions() {
//我们需要的是最新的版本
return repositoryService.createProcessDefinitionQuery().latestVersion().list();
}
/**
* 根据流程定义id 获取到对应的模板节点信息 节点名称,节点id,监听事件也可以获取
*
* @param processDefinitionId
* @return
*/
@Override
public List<FlowElement> getFlowElementByProcessDefId(String processDefinitionId) {
log.info("根据流程定义获取节点信息入参:{}", processDefinitionId);
//流程定义中的模板信息
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//模板中的所有流程
List<Process> processes = bpmnModel.getProcesses();
log.info("流程定义id:{}的流程条数大小:{}", processDefinitionId, processes.size());
//取得流程的节点包括 连线实体
Process process = processes.get(0);
//把所有节点 用map装好。
Map<String, FlowElement> flowElements = process.getFlowElementMap();
List<FlowElement> flowElementList = new ArrayList<>();
if (CollectionUtils.isEmpty(flowElements)) {
return flowElementList;
}
//转换一下 成为list
for (Map.Entry<String, FlowElement> flowElement : flowElements.entrySet()) {
flowElementList.add(flowElement.getValue());
}
return flowElementList;
}
/**
* 根据用户id 或者该用户的所有 有权限审核的流程task
*
* @param userId
* @return
*/
@Override
public List<Task> getAuthorityTaskByUserId(String userId) {
return taskService
.createTaskQuery()
.taskCandidateUser(userId)
.taskUnassigned()
.list();
}
/**
* 用户认领task,如果该用户有权限并且该task未被认领
*
* @param taskId
* @param userId
*/
@Override
public R<Boolean> claimTaskByUserId(String taskId, String userId) {
try {
taskService.claim(taskId, userId);
} catch (ActivitiTaskAlreadyClaimedException e) {
log.info("该任务已经被认领taskId:{},userId:{}", taskId, userId);
R.fail("该任务已经被其他的人认领");
} catch (Exception e) {
log.info("无权限认领该任务:{},userId:{}", taskId, userId);
}
return R.ok(true);
}
/**
* 根据用户id 获取需要立即审核的流程task
*
* @param userId
* @return
*/
@Override
public List<Task> getAssignedTaskByUserId(String userId) {
return taskService.createTaskQuery().taskAssignee(userId).list();
}
/**
* 完成当前节点 进入到下一步。
*
* @param taskId finshBpVo
* @throws Exception
*/
@Override
public R<Boolean> completeTask(FinshBpVo finshBpVo) {
if (Kit.isNotEmpty(finshBpVo)) {
String taskId = finshBpVo.getTaskId();
String comment = finshBpVo.getComment();
String userId = finshBpVo.getUserId();
Map<String, Object> variables = finshBpVo.getVariables();
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (Objects.isNull(task)) {
log.info("该任务taskId:{},已被完成或者不存在", taskId);
return R.fail("任务已被完成或者不存在");
}
if (Objects.nonNull(task) && !userId.equals(task.getAssignee())) {
log.info("该用户没有权限完成处理该任务taskId:{},userId:{},yUserId:{}", taskId, userId, task.getAssignee());
return R.fail("该用户没有权限完成处理该任务");
}
//如果批注信息不为空,新增批注信息内容
if (StringUtils.isNotBlank(comment)) {
taskService.addComment(taskId, task.getProcessInstanceId(), comment);
}
taskService.complete(taskId, variables);
return R.ok(true);
}
return R.ok(false);
}
/**
* 根据实例id 获取已经完成的节点
*
* @param processInstanceId
* @return
*/
@Override
public List<HistoricTaskInstance> getHistoryTaskByProcessInstanceId(String processInstanceId) {
return historyService
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.finished().orderByTaskCreateTime().desc().list();
}
/**
* 根据用户id获取到 用户处理过的所有任务信息
*
* @param userId
* @return
*/
@Override
public List<HistoricTaskInstance> getHistoryTaskByUserId(String userId) {
return historyService
.createHistoricTaskInstanceQuery()
.taskAssignee(userId).finished()
.orderByTaskCreateTime().desc().list();
}
/**
* 根据流程实例id 结束任务 任何时候都可以结束
*
* @param processId
* @return
*/
@Override
public boolean finishProcess(String processId) {
runtimeService.deleteProcessInstance(processId, "结束");
Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
if (null == task) {
return true;
}
return false;
}
/**
* 根据taskId 获取批注消息
*
* @param taskId 任务id
* @return
*/
@Override
public Comment getComment(@RequestParam("taskId") String taskId) {
return taskService.getComment(taskId);
}
/**
* 根据instanceId查询整个流程
*/
@Override
public List<HistoricActivityInstance> findHistoryInfo(String processInstanceId) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
//获取actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
//根据instanceId查询整个流程
instanceQuery.processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc();
List<HistoricActivityInstance> list = instanceQuery.list();
return list;
}
/**
* 撤回或者驳回任务, 撤回是当前处理人 处理任务后后悔了,在下一个处理人还未处理的情况可以撤回
* 驳回:当前处理人 查看任务后发现上一步处理人未处理到位,可以驳回到上一个人。
*
* @param processInstanceId 流程实例id
* @param userId 当前用户id
* @param setUserId 撤回或者驳回后的 设置用户处理人id
* @param recallOrRebutFlag 是撤回还是驳回标识 1代表撤回 2代表驳回
* @param comment 撤回或者驳回时的批注内容
* @throws Exception
*/
@Override
public R<Boolean> cancel(String processInstanceId, String userId, String setUserId, Integer recallOrRebutFlag, String comment) {
log.info("撤回或者驳回的任务的参数processInstanceId:{}, userId:{},serUserId:{}, recallOrRebutFlag:{}",
processInstanceId, userId, setUserId, recallOrRebutFlag);
//参数验证
if (StringUtils.isBlank(processInstanceId)) {
return R.fail("流程实例id不能为空");
}
if (StringUtils.isBlank(userId)) {
return R.fail("当前用户id不能为空");
}
if (StringUtils.isBlank(setUserId)) {
return R.fail("撤回或者驳回后的处理人id不能为空");
}
if (Objects.isNull(recallOrRebutFlag)) {
return R.fail("撤回或这驳回类型不能为空");
}
//获取当前的task
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
if (task == null) {
log.info("任务节点已完成或者未启动,无法撤回processInstanceId:{}", processInstanceId);
return R.fail("任务节点已完成或者未启动");
}
//根据时间的正序 输出,最后的一个就是正在进行中的节点任务。
List<HistoricTaskInstance> historicTaskInstances = historyService
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByTaskCreateTime().asc().list();
//撤回的应该是 正在进行中的上一个节点
HistoricTaskInstance myTask = null;
for (int i = 0; i < historicTaskInstances.size(); i++) {
if (historicTaskInstances.get(i).getId().equals(task.getId()) && i > 0) {
myTask = historicTaskInstances.get(i - 1);
break;
}
}
if (myTask == null) {
log.info("流程实例id,{},上一步任务节点为空");
return R.fail("上一步任务节点为空,无法撤回或驳回");
}
log.info("流程实例id,{}的上一个节点的id:{},节点处理人:{}", myTask.getId(), myTask.getAssignee());
//权限校验
if (recallOrRebutFlag.equals(1)) {
if (!userId.equals(myTask.getAssignee())) {
log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理撤回", processInstanceId, userId, myTask.getAssignee());
return R.fail("无权限撤回");
}
} else if (recallOrRebutFlag.equals(2)) {
if (!userId.equals(task.getAssignee())) {
log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理驳回", processInstanceId, userId, myTask.getAssignee());
return R.fail("无权限驳回");
}
} else {
log.info("流程实例id:{},类型标识:{},不是撤回也不是驳回类型", processInstanceId, recallOrRebutFlag);
return R.fail("类型标识无法识别");
}
//获取到当前节点的上一个节点的id
String myTaskId = myTask.getId();
String processDefinitionId = myTask.getProcessDefinitionId();
//获取上一个节点流程定义
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService
.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//获取到 历史的activity 节点
List<HistoricActivityInstance> haiList = historyService
.createHistoricActivityInstanceQuery()
.executionId(myTask.getExecutionId())
.finished().list();
String myActivityId = null;
for (HistoricActivityInstance hai : haiList) {
if (myTaskId.equals(hai.getTaskId())) {
myActivityId = hai.getActivityId();
break;
}
}
FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
//获取当前节点的 活动节点
String activityId = execution.getActivityId();
log.info("流程实例id:{},需要撤回的活动id:{},当前活动id:{}", processInstanceId, myActivityId, activityId);
//获取当前的 node
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
//记录当前task的节点活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<>();
oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
//清理当前的活动方向
flowNode.getOutgoingFlows().clear();
//建立新的方向
List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(flowNode);
newSequenceFlow.setTargetFlowElement(myFlowNode);
newSequenceFlowList.add(newSequenceFlow);
flowNode.setOutgoingFlows(newSequenceFlowList);
taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
taskService.complete(task.getId());
//设置回原来的 方向
flowNode.setOutgoingFlows(oriSequenceFlows);
//再次获取到当前的 活动节点就已经是 撤销后的节点了
Task cuTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
//设置受理人
taskService.setAssignee(cuTask.getId(), setUserId);
return R.ok(true);
}
}
<7>swagger测试:
1.启动流程:
注:其他几个参数都不重要,processDefinitionKey
参数一定得填对,值为流程图创建时输入的model_key
,执行成功会返回processInstanceId
2.根据processInstanceId
查询任务进度
3.完成当前节点,进入下一步
注:comment
参数为批注,可不填,taskId
为第2步任务进度最后一步的taskid,userId
为流程图设置的userid,variables
参数为流程指向线的条件等
4.其他业务方法自行研究即可项目整体结构
一些工具类和返回实体:
返回实体:
import java.io.Serializable;
import java.util.List;
import com.xxxx.common.core.constant.CommonConstants;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* 响应信息体
* @author:955
* @date:2022年5月9日 下午9:53:56
*/
@Builder
@ToString
@Accessors(chain = true)
@ApiModel(description = "响应信息主体")
@AllArgsConstructor
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
//@Builder.Default
@Getter
@Setter
@ApiModelProperty(value = "返回标记:成功标记=0,失败标记=1")
private int code = CommonConstants.SUCCESS;
//@Builder.Default
@Getter
@Setter
@ApiModelProperty(value = "返回信息")
private String msg = "success";
@Getter
@Setter
@ApiModelProperty(value = "返回错误集合信息")
private List<ErrorMessage> errorMessage;
@Getter
@Setter
@ApiModelProperty(value = "数据")
private T data;
@Getter
@Setter
@ApiModelProperty(value = "是否成功")
private boolean success;
public R()
{
super();
}
public R(T data)
{
super();
this.data = data;
}
public R(T data, String msg)
{
super();
this.data = data;
this.msg = msg;
}
public R(Throwable e)
{
super();
this.msg = e.getMessage();
this.code = CommonConstants.FAIL;
}
public static <T> R<T> ok()
{
return R.restResult(null, CommonConstants.SUCCESS, null);
}
public static <T> R<T> ok(T data)
{
return R.restResult(data, CommonConstants.SUCCESS, null);
}
public static <T> R<T> ok(T data, String msg)
{
return R.restResult(data, CommonConstants.SUCCESS, msg);
}
public static <T> R<T> fail()
{
return R.restResult(null, CommonConstants.FAIL, null);
}
public static <T> R<T> fail(int code)
{
return R.restResult(null, code, null);
}
public static <T> R<T> fail(String msg)
{
return R.restResult(null, CommonConstants.FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return R.restResult(null, code, msg);
}
public static <T> R<T> fail(List<ErrorMessage> errorMessages)
{
return R.restErrorMessageResult(null, CommonConstants.FAIL, errorMessages);
}
public static <T> R<T> fail(T data)
{
return R.restResult(data, CommonConstants.FAIL, null);
}
public static <T> R<T> fail(T data, String msg)
{
return R.restResult(data, CommonConstants.FAIL, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
if(code==CommonConstants.SUCCESS) {
apiResult.setSuccess(true);
}else if(code==CommonConstants.FAIL) {
apiResult.setSuccess(false);
}
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
private static <T> R<T> restErrorMessageResult(T data, int code, List<ErrorMessage> errorMessages)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
if(code==CommonConstants.SUCCESS) {
apiResult.setSuccess(true);
}else if(code==CommonConstants.FAIL) {
apiResult.setSuccess(false);
}
apiResult.setData(data);
apiResult.setErrorMessage(errorMessages);
return apiResult;
}
/**
* @param localizedMessage
* @return
*/
public static R<?> failed(String localizedMessage) {
return R.restResult(null, CommonConstants.FAIL, localizedMessage);
}
}
错误实体:
@Builder
@Data
@ApiModel(description = "响应错误列表")
@AllArgsConstructor
public class ErrorMessage implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String field;
private String message;
}
公共属性抽象类:
public interface CommonConstants
{
/**
* 编码
*/
String UTF8 = "UTF-8";
/**
* 成功标记
*/
Integer SUCCESS = 0;
/**
* 失败标记
*/
Integer FAIL = 1;
String AC_TASK_MSG ="msg";
String AC_TASK_USER= "taskUser";
String AC_TASK_USERNAME= "taskUserName";
String AC_TASK_STATUS= "taskStatus";
String ACT_TASK_FLAG = "flag";
String AC_TASK_DEPT = "taskDept";
String AC_AGENT = "agent";
String AC_TASK_ID = "taskUnId";
}
可用于生产的接口设计(基于flowable,activiti类似。每个人设计思路不一, 不喜勿喷,原生写法永不过时)
设计思路:这里分为两步管理,使用模型管理和流程管理两个模块,模型管理model的增删改查部署,流程管理控制该流程的生效失效导出流程图等。顺序为先部署流程模型,再控制流程状态。
1.模型管理
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.repository.ModelSort;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
@AllArgsConstructor
public class MyModelServiceImpl{
private static final String BPMN20_XML = ".bpmn20.xml";
private final RepositoryService repositoryService;
private final ModelService modelService;
private final ModelRepository modelRepository;
private final ObjectMapper objectMapper;
/**
* 创建流程-在生成流程文件之后会自动有数据,一般不用此方法新建
* @param name 模型名称-随便填
* @param key 模型key-对应创建模型时填的key
* @param desc 备注
* @param modelType 模型分类字典,默认为0-bpmn
* @return
*/
@Override
public org.flowable.ui.modeler.domain.Model create(String name, String key, String desc, Integer modelType) {
try {
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode properties = objectMapper.createObjectNode();
properties.put("process_author", "模型归属-随便填");
properties.put("process_id", key);
properties.put("name", name);
editorNode.set("properties", properties);
ObjectNode stencilset = objectMapper.createObjectNode();
stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
editorNode.set("stencilset", stencilset);
org.flowable.ui.modeler.domain.Model model = new org.flowable.ui.modeler.domain.Model();
model.setKey(key);
model.setName(name);
model.setCreatedBy("anonymousUser");
model.setModelType(modelType);
model.setVersion(Integer.parseInt(
String.valueOf(modelRepository.findByKeyAndType(model.getKey(), modelType).size() + 1)));
//model.setTenantId(String.valueOf(TenantContextHolder.getTenantId()));
modelRepository.save(model);
return model;
} catch (Exception e) {
MyModelServiceImpl.log.error("UnsupportedEncodingException", e);
}
return null;
}
/**
* 分页获取流程
*
* @param params
* @return
*/
@Override
public IPage<org.flowable.ui.modeler.domain.Model> getModelPage(Integer pageNumber,Integer pageSize) {
List<org.flowable.ui.modeler.domain.Model> models = modelRepository.findByModelType(0, "nameAsc");
IPage<org.flowable.ui.modeler.domain.Model> result = new Page<org.flowable.ui.modeler.domain.Model>(pageNumber, pageSize);
result.setTotal(models.size());
result.setRecords(models);
return result;
}
/**
* 删除流程
*
* @param id
* @return
*/
@Override
public Boolean removeModelById(String id) {
modelService.deleteModel(id);
return Boolean.TRUE;
}
/**
* 部署流程
*
* @param id
* @return
*/
@Override
public Boolean deploy(String id) {
try {
// 获取模型
org.flowable.ui.modeler.domain.Model modelData = modelService.getModel(id);
BpmnModel model = modelService.getBpmnModel(modelData);
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName() + BPMN20_XML;
Deployment deploy = repositoryService.createDeployment().category(modelData.getDescription()).name(modelData.getName()).addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
// // 设置流程分类
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploy.getId()).list();
list.stream().forEach(processDefinition -> repositoryService
.setProcessDefinitionCategory(processDefinition.getId(), modelData.getName()));
} catch (Exception e) {
MyModelServiceImpl.log.error("部署失败,异常", e);
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}
页面格式:
2.流程管理
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@AllArgsCons
tructor
public class ProcessServiceImpl implements ProcessService {
private final RepositoryService repositoryService;
private final RuntimeService runtimeService;
/**
* 分页流程列表
*
* @param params
* @return
*/
@Override
public IPage<ProcessDefDTO> getProcessByPage(Integer pageNumber,Integer pageSize) {
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().latestVersion();
IPage<ProcessDefDTO> result = new Page<ProcessDefDTO>(pageNumber, pageSize);
result.setTotal(query.count());
List<ProcessDefDTO> deploymentList = query.listPage((page - 1) * limit, limit).stream().map(processDefinition -> {
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(processDefinition.getDeploymentId()).singleResult();
return ProcessDefDTO.toProcessDefDTO(processDefinition, deployment);
}).collect(Collectors.toList());
result.setRecords(deploymentList);
return result;
}
/**
* 读取xml/image资源-返回前端流程图文件
*
* @param procDefId
* @param proInsId
* @param resType
* @return
*/
@Override
public InputStream readResource(String procDefId, String proInsId, String resType) {
if (StrUtil.isBlank(procDefId)) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(proInsId).singleResult();
procDefId = processInstance.getProcessDefinitionId();
}
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId).singleResult();
String resourceName = "";
if ("image".equals(resType)) {
resourceName = processDefinition.getDiagramResourceName();
} else if ("xml".equals(resType)) {
resourceName = processDefinition.getResourceName();
}
InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
return resourceAsStream;
}
/**
* 更新状态-控制流程生效或失效(默认status为active)
*
* @param status
* @param procDefId
* @return
*/
@Override
public Boolean updateStatus(String status, String procDefId) {
//图片资源
if ("active".equals(status)) {
repositoryService.activateProcessDefinitionById(procDefId, true, null);
} else if ("suspend".equals(status)) {
//xml资源
repositoryService.suspendProcessDefinitionById(procDefId, true, null);
}
return Boolean.TRUE;
}
/**
* 删除部署的流程,级联删除流程实例
*
* @param deploymentId 流程id
* @return
*/
@Override
public Boolean removeProcIns(String deploymentId) {
repositoryService.deleteDeployment(deploymentId, true);
return Boolean.TRUE;
}
}
页面格式:
3.一些重要接口方法(如审核、发起、结办流程等)可以自行修改上面的方法,正式接口方法以后空了会放上来