基础操作流程
目录
编写BPMN文件
在项目的resource文件下右键鼠标创建BPMN文件
通过拖拽activity提供的图标得到一个很基础的流程
然后点击其中事件图标,在左侧窗口中设置事件名称(name)以及责任人(assignee)
然后点击空白处,给整个流程定义文件,设置流程名称(name)以及流程id
流程id就相当于这个流程的类名,后续activity创建流程就是基于这个id确认模板的
创建工作流引擎对象
创建 processEngineConfiguration
activity会基于配置好的xml信息(activity.cfg.xml
或者是application.yml
)构建ProcessEngineConfiguration
对象
基于activity.cfg.xml
,要求 activiti.cfg.xml
中必须有一个 processEngineConfiguration
的 bean
ProcessEngineConfiguration configuration =ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml")
也可以使用下边的方法,更改 bean 的名字
ProcessEngineConfiguration configuration =ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
创建ProcessEngine
通过ProcessEngineConfiguration
创建ProcessEngine
对象
ProcessEngine
对象提供了许多函数,其中有四个核心函数,用来获取四个重要的service对象,业务逻辑基本都要靠这四个对象来实现
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
……
常用service说明
-
RepositoryService
activiti 的资源管理类
提供了管理和控制流程发布包和流程定义的操作。
使用actiBPM等工作流建模工具设计的业务流程图,需要使用此 service 将流程定义文件的内容部署到计算机。
-
RuntimeService
activiti 的流程运行管理类
可以从这个服务类中获取很多关于流程执行相关的信息
-
TaskService
activiti 的任务管理类
可以从这个类中获取任务的信息
-
HistoryService
activiti 的历史管理类
可以查询历史信息, 执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径
-
ManagerService
activiti 的引擎管理类
提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护
代码通用部分
这里先给出后续代码需要用到的包和我个人封装好的函数
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.junit.Test;
import java.util.List;
//创建操作activity的核心对象 ProcessEngine 后续所有操作都需要用它完成
public ProcessEngine createProcessEngine(){
//创建ProcessEngineConfiguration
ProcessEngineConfiguration configuration =ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
//通过ProcessEngineConfiguration创建ProcessEngine,此时根据配置文件设置决定是否创建数据库的表或者更新表
ProcessEngine processEngine =configuration.buildProcessEngine();
System.out.println("创建完成:"+processEngine);
return processEngine;
}
部署流程定义文件
说明
就是将编写好的BPMN文件所在路径,保存到activity的数据库表中
执行此操作后 activiti 会将代码中指定的 bpmn 文件和png图片文件(非必须)保存到 activiti 数据库表中
部署方式有两种,建议使用单个文件部署的方式,因为这属于一次部署一个流程,这样部署表和流程定义表是一对一的关系,方便读取流程部署及流程定义信息。
不过spring boot项目可以实现自动部署,就不用去考虑这个了
单个文件部署
单个文件部署就是将bpmn和png直接传进去
//部署一个定义好的流程
@Test
public void test(){
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//部署对象
Deployment deployment = repositoryService.createDeployment().addClasspathResource("bpmn/holiday.bpmn")// bpmn文件
.addClasspathResource("bpmn/holiday.png")//图片文件(非必须)
.name("请假申请流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
//运行后输出结果示例
流程部署id:47501
流程部署名称:请假申请流程
压缩包部署
压缩包部署就是把bpmn和png放到zip压缩包里,直接上传这个zip压缩包,可以一次性上传多个bpmn和png文件,也就是一次部署多个流程定义
@Test
public void deployProcessByZip() {
// 定义zip输入流
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(" bpmn/holiday.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment()//.addZipInputStream(zipInputStream).deploy();
System.out.println("流程部署id: " + deployment.getId());
System.out.println("流程部署名称: " + deployment.getName());
}
启动流程实例
现在activity的数据库中已经保存好了流程定义文件,等同于已经有了一个java类文件,当用户需要启动某个业务,只需要调用runtimeService.startProcessInstanceByKey("流程id")
然后activity就会根据给定的流程id,在数据库中找模板,找到后就启动一个流程实例
这个流程id就是bpmn编写时,点击bpmn文件空白处,可以看到的id
// 启动一个流程实例
@Test
public void startProcessInstance() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 获取RunTimeService
RuntimeService runtimeService =processEngine.getRuntimeService();
// 根据流程id启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday");
System.out.println(" 流 程 定 义 id : " +processInstance.getProcessDefinitionId());
System.out.println("流程实例id: " + processInstance.getId());
System.out.println(" 当 前 活 动 Id : " +processInstance.getActivityId());
}
//运行后输出结果示例
流程定义 id : myholiday:3:47504
流程实例id: 50001
当前活动Id : null
查询个人任务
每个人都可以根据流程id,查询自己的待办任务
比如根据请假申请的流程id,查询目前自己的代办任务中,有几个与请假申请相关的任务
针对“请假申请”,因为可以存在多个申请,所以可以看到查询出来多个任务,也就对应着多个流程实例
每个流程实例具有唯一且固定的ID
但流程实例每经过一个流程节点,它的任务ID是会变化的,每个人拿到上一节点传过来的流程实例时,任务ID都不一样
下面的Task
类是org.activiti.engine.task
// 查询当前个人待执行的任务
@Test
public void findPersonalTaskList() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 任务负责人 比如发起请假申请的员工
String assignee = "zhangsan";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
//下面的Task类是org.activiti.engine.task
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("myholiday")//这里的传参就是上面创建实例时用的流程id 其实就是查询这个请假流程
.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());
}
}
//运行后输出结果示例
流程实例 id : 45001
任务id: 45005
任务负责人: zhangsan
任务名称: 创建请假申请
流程实例 id : 50001
任务id: 50005
任务负责人: zhangsan
任务名称: 创建请假申请
完成任务
比如现在主管拿到了员工启动的流程实例A,审批通过后要把A推到下一环节——人事,此时就是让工作流执行“完成任务”的操作
需要先查询流程实例A在当前节点的任务ID,然后调用taskService.complete(taskId)
,把任务ID传进去,activitiy就会把流程实例A推到下一个节点
// 完成任务
@Test
public void completTask() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
//任务id 指定具体要处理的某个实例 毕竟可以有复数个请假申请
String taskId = "10005";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
//完成任务 只需要调用complete方法并传入任务id
taskService.complete(taskId);
//完成后,上面的任务负责人,比如发起请假申请的员工,就看不到这个任务了,因为已经推给下一个人了
System.out.println("完成任务id="+taskId);
}
可以看到,只需要传入任务id就可以完成任务,这是很简单且危险的,实际使用时必须先校验当前操作者的权限
具体操作就是根据任务id和任务负责人名字assignee
,尝试执行下面的代码,如果返回结果不为空,代表该用户具有权限,此时再去完成任务
Task task = taskService.createTaskQuery().taskId(id).taskAssignee(assignee).singleResult();
查询流程定义
查询已经部署好的,也就是保存到activity数据库中的流程定义文件
需要使用流程id,如果某个bpmn被反复部署到数据库中,就会查出复数个流程定义文件
//对保存到数据库里的流程文件进行增删查--现在是查询
@Test
public void queryProceccDefinition() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 流程id
String processDefinitionKey = "myholiday";
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 查询流程定义
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//遍历查询结果
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
for (ProcessDefinition processDefinition : list) {
System.out.println("------------------------");
System.out.println(" 流 程 部 署 id : " +processDefinition.getDeploymentId());
System.out.println("流程定义id: " + processDefinition.getId());
System.out.println("流程定义名称: " + processDefinition.getName());
System.out.println("流程定义key: " + processDefinition.getKey());
System.out.println("流程定义版本: " + processDefinition.getVersion());
}
}
//运行后输出结果示例
流程部署 id : 42501
流程定义id: myholiday:2:42504
流程定义名称: 请假申请表
流程定义key: myholiday
流程定义版本: 2
------------------------
流程部署 id : 7501
流程定义id: myholiday:1:7504
流程定义名称: 请假申请表
流程定义key: myholiday
流程定义版本: 1
删除流程定义
删除之后,等于数据库中之前保存的bpmn没了,此时不能基于bpmn的id去创建流程实例了
删除方式有两种,普通删除和级联删除,项目开发中级联删除的情况比较多,删除操作一般只开放给超级管理员使用
普通删除,如果流程定义已有流程实例启动,则删除时出错
级联删除,可以保留流程实例的情况下,将流程定义及相关记录全部删除
使用的流程部署id,是在流程定义文件部署到数据库时,deployment.getId()
返回的结果
//对保存到数据库里的流程文件进行增删查--现在是删除
@Test
public void deleteDeployment() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 流程部署id
String deploymentId = "1";
// 通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 使用普通删除方式删除流程定义,如果该流程定义已有流程实例启动则删除时出错
// repositoryService.deleteDeployment(deploymentId);
// 使用级联删除方式流程定义,只需要设置true,即使该流程有流程实例启动也可以删除,设置为false非级联删除方式
repositoryService.deleteDeployment(deploymentId, true);
}
查询流程定义资源文件
通过流程定义id
把部署好的流程定义的bpmn和png等文件下载下来
需要使用流程定义id,不是流程id,是创建流程实例时,使用processInstance.getProcessDefinitionId()
得到的结果,例如myholiday:3:47504
@Test
public void getProcessResources() throws IOException {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 流程定义id
String processDefinitionId = "myholiday:3:47504";
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程定义对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
//获取bpmn
String resource_bpmn = processDefinition.getResourceName();
//获取png
String resource_png =processDefinition.getDiagramResourceName();
// 资源信息
System.out.println("bpmn: " + resource_bpmn);
System.out.println("png: " + resource_png);
File file_png = new File("d:/purchasingflow01.png");
File file_bpmn = new File("d:/purchasingflow01.bpmn");
// 输出bpmn
InputStream resourceAsStream = null;
resourceAsStream = repositoryService.getResourceAsStream(
processDefinition.getDeploymentId(), resource_bpmn);
FileOutputStream fileOutputStream = new FileOutputStream(file_bpmn);
byte[] b = new byte[1024];
int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
fileOutputStream.write(b, 0, len);
}
// 输出图片
resourceAsStream = repositoryService.getResourceAsStream(
processDefinition.getDeploymentId(), resource_png);
fileOutputStream = new FileOutputStream(file_png);
// byte[] b = new byte[1024];
// int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
fileOutputStream.write(b, 0, len);
}
}
通过流程部署id
使用的流程部署id,是在流程定义文件部署到数据库时,deployment.getId()
返回的结果
// 获取流程定义图片资源
@Test
public void getProcessResources() throws IOException {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
//流程部署id
String deploymentId = "9001";
// 通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//读取资源名称
List<String> resources =repositoryService.getDeploymentResourceNames(deploymentId);
String resource_image = null;
//获取图片
for(String resource_name :resources){
if(resource_name.indexOf(".png")>=0){
resource_image = resource_name;
}
}
//图片输入流
InputStream inputStream =
repositoryService.getResourceAsStream(deploymentId, resource_image);
File exportFile = new File("d:/holiday.png");
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
byte[] buffer = new byte[1024];
int len = -1;
//输出图片
while((len = inputStream.read(buffer))!=-1){
fileOutputStream.write(buffer, 0, len);
}
inputStream.close();
fileOutputStream.close();
}
查询流程实例
查询个人任务是根据流程id和用户名进行查询,可以得到和用户相关的复数个流程实例
这里查询流程实例,只根据流程id,所以查询结果会展示所有相关流程实例
流程实例有一个businessKey
变量,其实就是activity为流程实例提供了一个变量用来保存信息 一般用来保存业务系统的某条记录的id,在查看流程实例时,可以根据拿到的这个id,去业务系统的库中查出需要的数据再显示出来
//查询流程实例
@Test
public void queryProcessInstance() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 流程id
String processDefinitionKey = "myholiday";
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefinitionKey).list();
for (ProcessInstance processInstance : list) {
System.out.println("----------------------------");
System.out.println("流程实例id: "+ processInstance.getProcessInstanceId());
System.out.println("所属流程定义id: "+ processInstance.getProcessDefinitionId());
System.out.println("是否执行完成: " + processInstance.isEnded());
System.out.println("是否暂停: " + processInstance.isSuspended());
System.out.println(" 当 前 活 动 标 识 : " + processInstance.getActivityId());
//关联 businessKey
//String businessKey = processInstance.getBusinessKey();
//System.out.println(" businessKey : " +processInstance.getBusinessKey() );
}
}
//运行后输出结果示例
----------------------------
流程实例id: 10001
所属流程定义id: myholiday:1:7504
是否执行完成: false
是否暂停: false
当 前 活 动 标 识 : null
----------------------------
流程实例id: 45001
所属流程定义id: myholiday:2:42504
是否执行完成: false
是否暂停: false
当 前 活 动 标 识 : null
查询流程历史信息
即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在 activiti 的 act_hi_*
相关的表中。
可以根据流程实例id查询历史信息
//查看流程实例历史信息
@Test
public void testHistoric01(){
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery();
//需要传入流程实例的ID
query.processInstanceId("10001");
List<HistoricActivityInstance> list = query.list();
for(HistoricActivityInstance ai :list){
System.out.println(ai.getActivityId());
System.out.println(ai.getActivityName());
System.out.println(ai.getProcessDefinitionId());
System.out.println(ai.getProcessInstanceId());
System.out.println("==============================");
}
}
//运行后输出结果示例
_2
StartEvent
myholiday:3:47504
50001
==============================
_3
创建请假申请
myholiday:3:47504
50001
==============================
挂起、激活流程实例
说明
某些情况可能由于流程变更需要将当前运行的流程暂停而不是直接删除,流程暂停后将不会继续执行。
如果流程实例被挂起,则不能继续执行,如果有人要调用complete完成任务,想推进实例到下一个环节,会报异常
某个流程定义创建出的所有流程实例
只需要给出流程id,那么基于这个id对应的流程定义文件创建出的所有流程实例,都会被挂起或激活
//挂起或激活 某个流程定义创建出的所有流程实例
@Test
public void suspendOrActivateProcessDefinition() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 流程id
String processDefinitionId = "myholiday";
RepositoryService repositoryService = processEngine.getRepositoryService();
// 获得流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
//是否暂停 暂停则为true 正常运行则为false
boolean suspend = processDefinition.isSuspended();
if(suspend){
//进入这个分支说明流程定义此时处于暂停状态
//这里将流程定义下的所有流程实例全部激活
repositoryService.activateProcessDefinitionById(processDefinitionId,true, null);
System.out.println("流程定义: "+processDefinitionId+"激活");
}else{
//进入这个分支说明流程定义此时处于正常运行状态
//这里将流程定义下的所有流程实例全部挂起
repositoryService.suspendProcessDefinitionById(processDefinitionId,true, null);
System.out.println("流程定义: "+processDefinitionId+"挂起");
}
}
指定的流程实例
只需要准备好流程实例的id
//挂起或激活 指定的流程实例
@Test
public void suspendOrActiveProcessInstance() {
//创建ProcessEngine
ProcessEngine processEngine =createProcessEngine();
// 流程实例id
String processInstanceId = "10001";
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//根据流程实例id查询流程实例
ProcessInstance processInstance =runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
boolean suspend = processInstance.isSuspended();
if(suspend){
//如果暂停则激活
runtimeService.activateProcessInstanceById(processInstanceId);
System.out.println("流程实例: "+processInstanceId+"激活");
}else{
//如果激活则挂起
runtimeService.suspendProcessInstanceById(processInstanceId);
System.out.println("流程实例: "+processInstanceId+"挂起");
}
}