springboot整合activiti测试项目
最近学习了一下springboot整合activiti工作流框架,遇到了一些坑,写一篇文章记录下,写了个入门小程序。
首先用我自己的理解,白话的解释一下activiti框架,在使用之前,我们要搞清楚两个概念,一个是流程,一个是任务。接下来按照我的操作流程会说到这两个概念。
step1
新建一个springboot项目,导入相关依赖,我的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.0.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.yhz</groupId>
<artifactId>activiti-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activiti-springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>5.22.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
activiti是有自己的数据库的,所以我们需要初始化数据库,关于数据库各个表的用途联系不做叙述,百度一大堆,直接贴代码,我这里是写在一个测试类的里的,如果以后涉及到项目开发,可以把这个数据库初始化操作放到项目启动时去判断执行一下(这个以后准备试试),代码如下:
package org.yhz.activitispringboot;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.junit.Test;
/**
* @author yaohongzhen
* @date 2020/6/18 11:21
* @description
*/
public class ActivitiEnviroment {
@Test
public void test_createDatabase() {
// 创建流程引擎配置信息对象
ProcessEngineConfiguration pec = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration();
// 设置数据库的类型
pec.setDatabaseType("mysql");
// 设置创建数据库的方式
// ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE(true);//如果没有数据库表就会创建数据库表,有的话就修改表结构.
// ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE(false): 不会创建数据库表
// ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP(create-drop): 先创建、再删除.
pec.setDatabaseSchemaUpdate("true");
// 设置数据库驱动
pec.setJdbcDriver("com.mysql.jdbc.Driver");
// 设置jdbcURL
pec.setJdbcUrl("jdbc:mysql://localhost:3306/test??useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
// 设置用户名
pec.setJdbcUsername("root");
// 设置密码
pec.setJdbcPassword("root");
// pec.setJdbcPassword("XXXX");
// 构建流程引擎对象
ProcessEngine pe = pec.buildProcessEngine(); // 调用访方法才会创建数据表
// 调用close方法时,才会删除
pe.close();
}
}
初始化完数据之后,我们需要接触到一第一个概念,流程。
关于流程,我用的是idea,首先我们需要插件库去下载actiBPM插件,我的idea版本是2019.3,idea升级之后不知道为什么取消了这个插件,所以如果你搜不到的话要去idea官网插件库搜错下载这个jar包,然后通过本地导入的方式引入,这里不做赘述,当我们装好这个插件就可以创建bpmn文件,这个类似于流程图,我这里画的是最简单的流程图,模拟了一个审批流程的过程,我画的bpmn文件如下:
创建了bpmn文件之后,我们需要部署这个流程,部署流程的方法如下:
@Value("${activiti.resource.path}")
private String resourcePath;
/**
* 部署一个流程
*
* @param resourceName
*/
public void deploy(String resourceName) {
try {
Deployment deploy = repositoryService.createDeployment().addClasspathResource(resourcePath + resourceName).deploy();
log.info("流程部署完毕,流程id:{}", deploy.getId());
} catch (Exception e) {
log.error("部署失败:{}", e.getMessage());
}
}
当我们部署了这个流程之后,我们在数据库可以看到这个流程的部署信息,表名在红色框里。
流程部署完毕后,我们可以理解为我们有了个流程模板,我们可以用这个模板去跑一个流程实例,首先写了个查询所有定义流程(也就是流程模板)的方法:
/**
* 获取所有部署流程信息
*
* @return
*/
public List<ProcessVo> processList() {
List<ProcessVo> processVoList = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionId().desc().list().stream().map(processDefinition -> {
ProcessVo processVo = new ProcessVo();
processVo.setId(processDefinition.getId());
processVo.setName(processDefinition.getName());
processVo.setKey(processDefinition.getKey());
processVo.setVersion(processDefinition.getVersion());
processVo.setResourceName(processDefinition.getResourceName());
processVo.setDiagramResourceName(processDefinition.getDiagramResourceName());
processVo.setDeploymentId(processDefinition.getDeploymentId());
return processVo;
}).collect(Collectors.toList());
return processVoList;
}
我们可以获取所有部署过的流程模板,我们可以用他的key去开启一个流程,开启流程的方法如下:
/**
* 启动一个流程
*
* @param key
*/
public void start(String key) {
try {
ProcessInstance processInstance = this.runtimeService.startProcessInstanceById(key);
log.info("流程发起成功,流程id:{}", processInstance.getId());
} catch (Exception e) {
log.error("启动失败:{}", e.getMessage());
}
}
当我们开启了一个流程,流程就会进入我们画的流程图的第一个节点,这个流程相当于这个节点来说就是一个任务,我们可以流程定义id获得这个流程下所有在执行的任务:
/**
* 获取流程下的任务
*
* @param key
* @return
*/
public List<TaskVO> taskList(String key) {
List<Task> list = taskService//与正在执行的任务管理相关的Service
.createTaskQuery()//创建任务查询对象
.processDefinitionId(key)//使用流程定义ID查询
.orderByTaskCreateTime().desc()//使用创建时间的升序排列
.list();//返回列表
List<TaskVO> taskVOList = null;
if (list != null && list.size() > 0) {
taskVOList = list.stream().map(task -> {
TaskVO taskVO = new TaskVO();
taskVO.setId(task.getId());
taskVO.setName(task.getName());
taskVO.setAssignee(task.getAssignee());
taskVO.setCreateTime(task.getCreateTime());
taskVO.setExecutionId(task.getExecutionId());
taskVO.setProcessDefinitionId(task.getProcessDefinitionId());
taskVO.setProcessInstanceId(task.getProcessInstanceId());
Map<String, Object> variables = taskService.getVariables(task.getId());
taskVO.setResult((String) variables.get("审批结果"));
taskVO.setMsg((String) variables.get("审批备注"));
return taskVO;
}).collect(Collectors.toList());
}
return taskVOList;
}
当我们想把这个节点的任务完成之后,可以通过任务id去完成这个任务:
/**
* 完成一个流程
*
* @param taskId
* @throws Exception
*/
public void complete(String taskId, String result, String suggestion) {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("money", result);
try {
taskService.complete(taskId, variables);
} catch (Exception e) {
log.error("完成流程失败:" + e.getMessage());
}
}
当我们需要知道这个某个启动的流程结束没有或者走到哪个节点,可以用这个方法:
/**
* 判断流程是否结束
*
* @param processInstanceId
*/
public boolean isProcessEnd(String processInstanceId) {
ProcessInstance pi = runtimeService//表示正在执行的流程实例和执行对象
.createProcessInstanceQuery()//创建流程实例查询
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
if (pi == null) {
log.debug("流程已经结束");
return true;
} else {
log.debug("流程没有结束");
return false;
}
}
/**
* 获取流程当前所在节点
*
* @param instanceId
* @return
*/
public String getCurrectActivity(String instanceId) {
Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
log.info("name==>" + task.getName());
return task.getName();
}
到此,一些基本的activiti操作就结束了,下一篇我会写关于activiti网关的使用。
附上完整的代码:
package org.yhz.activitispringboot.service;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.yhz.activitispringboot.vo.ProcessVo;
import org.yhz.activitispringboot.vo.TaskVO;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author yaohongzhen
* @date 2020/6/18 13:20
* @description
*/
@Service
@Slf4j
public class ActivitiService {
@Resource
private RepositoryService repositoryService;
@Resource
private RuntimeService runtimeService;
@Resource
private TaskService taskService;
@Value("${activiti.resource.path}")
private String resourcePath;
/**
* 部署一个流程
*
* @param resourceName
*/
public void deploy(String resourceName) {
try {
Deployment deploy = repositoryService.createDeployment().addClasspathResource(resourcePath + resourceName).deploy();
log.info("流程部署完毕,流程id:{}", deploy.getId());
} catch (Exception e) {
log.error("部署失败:{}", e.getMessage());
}
}
/**
* 获取所有部署流程信息
*
* @return
*/
public List<ProcessVo> processList() {
List<ProcessVo> processVoList = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionId().desc().list().stream().map(processDefinition -> {
ProcessVo processVo = new ProcessVo();
processVo.setId(processDefinition.getId());
processVo.setName(processDefinition.getName());
processVo.setKey(processDefinition.getKey());
processVo.setVersion(processDefinition.getVersion());
processVo.setResourceName(processDefinition.getResourceName());
processVo.setDiagramResourceName(processDefinition.getDiagramResourceName());
processVo.setDeploymentId(processDefinition.getDeploymentId());
return processVo;
}).collect(Collectors.toList());
return processVoList;
}
/**
* 启动一个流程
*
* @param key
*/
public void start(String key) {
try {
ProcessInstance processInstance = this.runtimeService.startProcessInstanceById(key);
log.info("流程发起成功,流程id:{}", processInstance.getId());
} catch (Exception e) {
log.error("启动失败:{}", e.getMessage());
}
}
/**
* 获取流程下的任务
*
* @param key
* @return
*/
public List<TaskVO> taskList(String key) {
List<Task> list = taskService//与正在执行的任务管理相关的Service
.createTaskQuery()//创建任务查询对象
.processDefinitionId(key)//使用流程定义ID查询
.orderByTaskCreateTime().desc()//使用创建时间的升序排列
.list();//返回列表
List<TaskVO> taskVOList = null;
if (list != null && list.size() > 0) {
taskVOList = list.stream().map(task -> {
TaskVO taskVO = new TaskVO();
taskVO.setId(task.getId());
taskVO.setName(task.getName());
taskVO.setAssignee(task.getAssignee());
taskVO.setCreateTime(task.getCreateTime());
taskVO.setExecutionId(task.getExecutionId());
taskVO.setProcessDefinitionId(task.getProcessDefinitionId());
taskVO.setProcessInstanceId(task.getProcessInstanceId());
Map<String, Object> variables = taskService.getVariables(task.getId());
taskVO.setResult((String) variables.get("审批结果"));
taskVO.setMsg((String) variables.get("审批备注"));
return taskVO;
}).collect(Collectors.toList());
}
return taskVOList;
}
/**
* 完成一个流程
*
* @param taskId
* @throws Exception
*/
public void complete(String taskId, String result, String suggestion) {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("money", result);
try {
taskService.complete(taskId, variables);
} catch (Exception e) {
log.error("完成流程失败:" + e.getMessage());
}
}
/**
* 判断流程是否结束
*
* @param processInstanceId
*/
public boolean isProcessEnd(String processInstanceId) {
ProcessInstance pi = runtimeService//表示正在执行的流程实例和执行对象
.createProcessInstanceQuery()//创建流程实例查询
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
if (pi == null) {
log.debug("流程已经结束");
return true;
} else {
log.debug("流程没有结束");
return false;
}
}
/**
* 获取流程当前所在节点
*
* @param instanceId
* @return
*/
public String getCurrectActivity(String instanceId) {
Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
log.info("name==>" + task.getName());
return task.getName();
}
}
package org.yhz.activitispringboot.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.yhz.activitispringboot.service.ActivitiService;
import org.yhz.activitispringboot.vo.ResultBean;
/**
* @author yaohongzhen
* @date 2020/6/18 13:25
* @description
*/
@RestController
@RequestMapping("activiti")
public class ActivitiController {
@Autowired
private ActivitiService activitiService;
@GetMapping("deploy")
public ResultBean deploy(String resouceName) {
this.activitiService.deploy(resouceName);
return ResultBean.ok("部署成功");
}
@GetMapping("start")
public ResultBean start(String key) {
this.activitiService.start(key);
return ResultBean.ok("发起成功");
}
@GetMapping("list")
public ResultBean list() {
return ResultBean.ok(this.activitiService.processList());
}
@GetMapping("task-list")
public ResultBean taskList(String key) {
return ResultBean.ok(this.activitiService.taskList(key));
}
@GetMapping("task-complete")
public ResultBean taskComplete(String taskId, String result, String suggestion) {
this.activitiService.complete(taskId, result, suggestion);
return ResultBean.ok();
}
@GetMapping("isProcessEnd")
public ResultBean isProcessEnd(String pid) {
boolean processEnd = this.activitiService.isProcessEnd(pid);
return ResultBean.ok(processEnd);
}
@GetMapping("getCurrectActivity")
public ResultBean getCurrectActivity(String pid) {
String name = this.activitiService.getCurrectActivity(pid);
return ResultBean.ok(name);
}
}