activiti入门
在本章内容中,我们来创建一个Activiti工作流,并启动这个流程。
创建Activiti工作流主要包含以下几步:
1、定义流程,按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来
2、部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据
3、启动流程,使用java代码来操作数据库表中的内容
1.流程符号
BPMN2.0就是使用一些符号来明确业务流程设计流程图的一整套符号规范。
基本流程符号:
事件Event

活动Activity
活动是一个工作或任务的通用术语。一个活动可以是一个任务,也可以是一个当前流程的子处理流程。
常见的活动如下:

网关Getway
网关用来处理决策,常用的网关如下:

- 排他网关
只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流。如果多条线路计算结果都是true,则会执行第一个值为true的线路。如果所有网关计算结果没有true,则引擎会抛出异常。
排他网关需要和条件顺序流结合使用,default属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
- 并行网关
所有路径都会被同时选择。
拆分:并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。
合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才可以继续向下执行。
- 包容网关
可以同时执行多条线路,也可以在网关上设置条件。
拆分:计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并执行。
合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才可以挤继续向下执行。
- 事件网关
专门为中间捕获事件设置的,运行设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
流向Flow
流是连接两个流程节点的连线。常见的流向包含:

2.流程设计器使用
在idea中已经安装好bpmn插件,安装好的插件面板(可以自行百度,我这里装的是activiti bpmn visualizer):

2.1新建流程
在resource目录下新建bpmn目录,然后点击右键菜单新建:

文件名随意,默认生成一个xml文件,右键文件:

弹出绘图层

2.2绘制流程
在面板右键,选择Activities > User Task,创建一个用户任务,并定义属性值:该节点为zhangsan创建出差申请单

再次选择Activities > User Task,创建下一个任务,并定义属性值:部门经理审批人为lisi

再创建一个任务,定义属性值:总经理审批人为wangwu

再创建一个任务。定义属性值:财务审批为zhaoliu

最后创建一个结束事件:

然后将所有的流程符号连接起来,如下:



3.流程操作
流程定义是线下按照bpmn2.0标准去描述 业务流程,通常使用idea中的插件对业务流程进行建模。
上面步骤,创建的流程文件是.xml文件,我们需要改成bpmn文件,然后还需要该流程的png文件
png文件,直接右键导出保存到resource/bpmn路径下

bnmn文件直接将上面创建的xml文件改后缀名即可

最后在resource/bpmn路径下有两个文件,evection.png,evection.bpmn
4.流程部署
将上面的流程部署到activiti数据库中,就是流程定义部署。
通过调用activiti的api将流程定义的bpmn和png两个文件添加部署到activiti数据库中,也可以将两个文件打成zip包进行部署。
4.1单文件部署方式
/*** 流程部署*/@Testpublic void testDeployment() {//创建ProcessEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//得到RepositoryService实例,该实例是activiti的资源管理类RepositoryService repositoryService = processEngine.getRepositoryService();//使用repositoryService进行部署Deployment deployment = repositoryService.createDeployment()//添加bpmn资源.addClasspathResource("bpmn/evection.bpmn")//添加png.addClasspathResource("bpmn/evection.png").name("出差申请流程").deploy();//输出部署信息System.out.println("流程id:" + deployment.getId());System.out.println("流程部署名:" + deployment.getName());}
执行上面代码后activiti会将上边代码中指定的bpm文件和图片文件保存在activiti数据库。
act_re_deployment 流程定义部署表,每部署一次增加一条记录

act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录

act_ge_bytearray 流程资源表

注意:act_re_deployment和act_re_procdef是一对多的关系,一次部署在流程部署表生成一条记录,但一次部署可部署多个流程定义,每个流程定义在流程定义表生成一条记录。每个流程定义在act_ge_bytearray表会存在两个资源记录,bpmn和png。
建议:一次部署一个流程,这样部署表和流程定义表是一对一的关系,方便读取流程部署及流程定义信息。
4.2压缩包部署方式
/*** 压缩包方式部署*/@Testpublic void testDeploymentByZip() {//定义zip流InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");ZipInputStream zipInputStream = new ZipInputStream(inputStream);//创建ProcessEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取RepositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();//使用repositoryService进行部署Deployment deployment = repositoryService.createDeployment()//添加zip.addZipInputStream(zipInputStream).name("出差申请流程").deploy();//输出部署信息System.out.println("流程id:" + deployment.getId());System.out.println("流程部署名:" + deployment.getName());}
5.启动流程
流程定义部署在activiti后就可以通过工作流管理业务流程了。
针对该流程,启动一个流程表示发起一个新的出差申请单。张三发起一个出差申请单需要启动一个流程实例,出差申请单发起一个出差单也需要启动一个流程实例。
/*** 启动流程实例*/@Testpublic void startProcess() {//创建ProcessEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取runtimeServiceRuntimeService runtimeService = processEngine.getRuntimeService();//根据流程key启动流程ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("evection");//输出内容System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());System.out.println("流程实例id:" + processInstance.getId());System.out.println("当前活动id:" + processInstance.getActivityId());}
流程定义id:evection:1:7504
流程实例id:15001
当前活动id:null
这里根据的key查询:就是act_re_procdef的key字段值,也是evection.bpmn20.xml中process标签的id值,后续的查询key同样

操作数据表
act_hi_actinst 流程实例执行历史
act_hi_identitylink 流程的参与用户历史信息
act_hi_procinst 流程实例历史信息
act_hi_taskinst 流程任务历史信息
act_ru_execution 流程执行信息
act_ru_identitylink 流程的参与用户信息
act_ru_task 任务信息
6.任务查询
流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是当前用户的待办任务。
/*** 查询当前个人待执行任务*/@Testpublic void queryTask() {//任务负责人String assignee = "zhagnsan";//创建processEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//创建taskServiceTaskService taskService = processEngine.getTaskService();//根据流程任务key和任务负责人查询任务List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("evection") //流程key.taskAssignee(assignee) //只查询该任务负责人的任务.list();taskList.forEach(task -> {System.out.println("流程实例id:" + task.getProcessInstanceId());System.out.println("任务id:" + task.getId());System.out.println("任务负责人:" + task.getAssignee());System.out.println("任务名称:" + task.getName());});}
流程实例id:15001
任务id:15005
任务负责人:zhagnsan
任务名称:创建出差申请单
7.流程任务处理
任务负责人查询待办任务,选择任务进行处理完成该任务。
/*** 完成任务*/@Testpublic void completeTask() {//获取processEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取taskServiceTaskService taskService = processEngine.getTaskService();//根据流程key和任务负责人查询任务List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("evection") //流程key.taskAssignee("zhagnsan") //要查询的负责人.list();//完成任务,参数任务idtaskList.forEach(task -> {taskService.complete(task.getId());});}
8.流程定义信息查询
查询流程相关信息,包含流程定义、流程部署、流程定义版本等
/*** 查询流程定义*/@Testpublic void queryProcessDefinition() {//获取processEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取RepositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();//获取ProcessDefinitionQueryProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();/*** 查询到当前流程的所有定义* 条件:processDefinitionKey = evection* orderByProcessDefinitionVersion desc 按照版本倒序* 返回list集合*/List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("evection").orderByProcessDefinitionVersion().desc().list();//输出流程定义信息definitionList.forEach(processDefinition -> {System.out.println("流程定义id:" + processDefinition.getId());System.out.println("流程定义name:" + processDefinition.getName());System.out.println("流程定义key:" + processDefinition.getKey());System.out.println("流程定义version:" + processDefinition.getVersion());System.out.println("流程部署id:" + processDefinition.getDeploymentId());});}
流程定义id:evection:1:7504
流程定义name:evection
流程定义key:evection
流程定义version:1
流程部署id:7501
9.流程删除
/*** 流程删除*/@Testpublic void deleteProcess() {//流程部署idString deploymentId = "20001";//获取processEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取RepositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();//删除流程定义,如果该流程定义已有流程实例启动,则删除报错repositoryService.deleteDeployment(deploymentId);//级联删除,即时流程已有流程实例启动也可以删除//repositoryService.deleteDeployment(deploymentId, true); //true为级联删除}
说明:
-
使用repositoryService删除流程定义,历史表信息不会被删除
-
如果流程定义下没有流程实例启动,使用普通删除,只传流程id即可
-
如果流程定义下有流程实例启动,使用普通删除会报错,需要使用级联删除且级联删除会将相关记录全部删除
项目开发中级联删除操作一般只开放给超级管理员使用
10.流程资源下载
上面流程部署的时候,已经将流程资源文件bpmn和png文件上传到数据库中,如果需要查看这些资源文件的话,可以从数据库将资源文件下载到本地。
解决方案有:
- 在
act_ge_bytearray表中可以看到资源文件是以BLOB的格式存储的,可以通过jdbc操作进行读取再存储到本地 - 直接使用activiti的api来实现
使用commons-io.jar 解决IO的操作
引入commons-io依赖包
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version>
</dependency>
使用api的方式实现:
/*** 下载流程资源文件 bpmn png*/@Testpublic void downloadFile() throws Exception {//得到processEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//得到repositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();//得到流程定义查询器,设置查询条件查询想要的流程定义ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("evection").singleResult();//获取到流程部署idString deploymentId = processDefinition.getDeploymentId();//通过deploymentId获取到png和bpmnInputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());//构建outputStreamFile pngFile = new File("d:/data/evection.png");File bpmnFile = new File("d:/data/evection.bpmn");FileOutputStream pngOutput = new FileOutputStream(pngFile);FileOutputStream bpmnOutput = new FileOutputStream(bpmnFile);//输入流输出流转换IOUtils.copy(pngInput, pngOutput);IOUtils.copy(bpmnInput, bpmnOutput);//关闭流pngOutput.close();bpmnOutput.close();pngInput.close();bpmnInput.close();}
说明:
- deploymentId是流程部署id
- resource_name是
act_ge_bytearray表中的NAME_列的值 - 使用repositoryService的
getDeploymentResourceNames方法可以获取指定部署下的所有文件的名称 - 使用repositoryService的
getResourceAsStream方法传入部署id和资源文件名称可以获取指定文件的输入流 - 最后将资源文件的输入流输出到本地
11.流程历史信息查询
即时流程定义已经删除,流程执行的历史信息还是可以在act_hi_*相关的表中查询到。级联删除的不可以。
/*** 历史信息查询*/@Testpublic void queryHistory() {//获取processEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取historyServiceHistoryService historyService = processEngine.getHistoryService();//获取actinst表的查询对象HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();//排序,根据开始时间排序instanceQuery.orderByHistoricActivityInstanceStartTime().asc();//查询actinst表,条件:根据instanceId查询//instanceQuery.processInstanceId("25001");//查询 actinst表,条件:根据 DefinitionId 查询instanceQuery.processDefinitionId("evection:1:22504");List<HistoricActivityInstance> instanceList = instanceQuery.list();instanceList.forEach(instance -> {System.out.println(instance.getId());System.out.println("流程活动id:" + instance.getActivityId());System.out.println("流程活动名称:" + instance.getActivityName());System.out.println("流程实例id:" + instance.getProcessInstanceId());System.out.println("流程定义id:" + instance.getProcessDefinitionId());System.out.println("===================================");});}
25003
流程活动id:sid-07bae378-ef8d-4dab-83a7-ea0943ba27f3
流程活动名称:开始出差申请单任务
流程实例id:25001
流程定义id:evection:1:22504
===================================
25004
流程活动id:sid-60d04e4f-b36f-4e69-ad4f-4a51be3d1c3c
流程活动名称:创建出差申请单
流程实例id:25001
流程定义id:evection:1:22504
===================================
27501
流程活动id:sid-cc56cae5-1d4f-48a1-9df1-c7987b1e3ce6
流程活动名称:部门经理审批
流程实例id:25001
流程定义id:evection:1:22504
===================================














