本文主要是介绍Activiti7工作流入门和基本使用-Activiti入门和流程操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
五、Activiti入门
在本章内容中,我们来创建一个 Activiti 工作流,并启动这个流程。
创建 Activiti 工作流主要包含以下几步:
1 、定义流程,按照 BPMN 的规范,使用流程定义工具,用 流程符号 把整个流程描述出来
2 、部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据
3 、启动流程,使用 java 代码来操作数据库表中的内容
5.1 流程符号
BPMN 2.0 是业务流程建模符号 2.0 的缩写。
它由 Business Process Management Initiative 这个非营利协会创建并不断发展。作为一种标识, BPMN 2.0 是使用一些 符 号 来明确业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。
目前 BPMN2.0 是最新的版本,它用于在 BPM 上下文中进行布局和可视化的沟通。
接下来我们先来了解在流程设计中常见的符号。
BPMN2.0 的 基本符合 主要包含:
事件 Event
活动 Activity
活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次,你还可以为活动指定不同的类型。常见活动如下:
网关 GateWay
网关用来处理决策,有几种常用网关需要了解:
排他网关 (x)
—— 只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为 true 时,继续执行当前网关的输出流;
如果多条线路计算结果都是 true ,则会执行第一个值为 true 的线路。如果所有网关计算结果没有 true ,则引擎会抛出异常。
排他网关需要和条件顺序流结合使用, default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
并行网关 (+)
—— 所有路径会被同时选择
拆分 —— 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
包容网关 (+)
—— 可以同时执行多条线路,也可以在网关上设置条件
拆分 —— 计算每条线路上的表达式,当表达式计算结果为 true 时,创建一个并行线路并继续执行
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
事件网关 (+) —— 专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
流向 Flow
流是连接两个流程节点的连线。常见的流向包含以下几种:
5.2 流程设计器使用
Activiti-Designer 使用
Palette (画板)
在 idea 中安装插件即可使用,画板中包括以下结点:
Connection— 连接
Event--- 事件
Task--- 任务
Gateway--- 网关
Container— 容器
Boundary event— 边界事件
Intermediate event- - 中间事件
流程图设计完毕保存生成 .bpmn 文件
新建流程 (IDEA 工具 )
首先选中存放图形的目录 ( 选择 resources 下的 bpmn 目录 ) ,点击菜单: New -> BpmnFile ,如图:
弹出如下图所示框,输入 evection 表示 出差审批流程:
起完名字 evection 后(默认扩展名为 bpmn ),就可以看到流程设计页面,如图所示:
左侧区域是绘图区,右侧区域是 palette 画板区域
鼠标先点击画板的元素即可在左侧绘图
绘制流程
使用滑板来绘制流程,通过从右侧把图标拖拽到左侧的画板,最终效果如下:
指定流程定义Key(如果左侧properties 视图出不来可以更换一下主题intellij light,然后重新打开文件)
流程定义 key 即流程定义的标识,通过 properties 视图查看流程的 key
经理审批负责人为 jerry
总经理审批负责人为 jack
财务审批负责人为 rose
六、流程操作
6.1 流程定义
概述
流程定义是线下按照 bpmn2.0 标准去描述 业务流程,通常使用 idea 中的插件对业务流程进行建模。
使用 idea 下的 designer 设计器绘制流程,并会生成两个文件: .bpmn 和 .png
.bpmn文件
使用 activiti-desinger 设计业务流程,会生成 .bpmn 文件,上面我们已经创建好了 bpmn 文件
BPMN 2.0 根节点是 definitions 节点。 这个元素中,可以定义多个流程定义(不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 注意,definitions 元素 最少也要包含 xmlns 和 targetNamespace 的声明。
targetNamespace 可以是任意值,它用来对流程实例进行分类。 流程定义部分:定义了流程每个结点的描述及结点之间的流程流转。
流程布局定义:定义流程每个结点在流程图上的位置坐标等信息。
生成.png图片文件
IDEA 工具中的操作方式
1 、修改文件后缀为 xml
首先将 evection.bpmn 文件改名为 evection.xml ,如下图:
2 、使用 designer 设计器打开 .xml 文件
在 evection.xml 文件上面,点右键并选择 Diagrams 菜单,再选择 Show BPMN2.0 Designer…
3 、查看打开的文件
打开后,如图:
4 、解决中文乱码
如果出现中文乱码问题,可参考以下操作解决
工作流学习之 IDEA 使用activiti插件 出现乱码
idea解决activiti(*.bpmn)文件乱码问题
一定到重新打开文件
最后重新在 evection.xml 文件上面,点右键并选择 Diagrams 菜单,再选择 Show BPMN2.0 Designer…
5 、导出为图片文件
点击 Export To File 的小图标,打开如下窗口,注意填写文件名及扩展名,选择好保存图片的位置:
然后,我们把png文件拷贝到resources下的bpmn目录,并且把evection.xml改名为evection.bpmn。
6.2 流程定义部署
概述
将上面在设计器中定义的流程部署到 activiti 数据库中,就是流程定义部署。
通过调用 activiti 的 api 将流程定义的 bpmn 和 png 两个文件一个一个添加部署到 activiti 中,也可以将两个文件打成 zip 包进行部署。
单个文件部署方式
分别将 bpmn 文件和 png 图片文件部署。
package com.activiti.test;import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.junit.Test;public class ActivitiDemo {/*** 测试流程部署*/@Testpublic void testDeployment(){//1.创建ProcessEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//2.获取RepositorySerrviceRepositoryService repositoryService = processEngine.getRepositoryService();//3.使用service进行流程的部署,定义一个流程的名字,把bpmn和png文件部署到数据库中Deployment deploy = repositoryService.createDeployment().name("出差申请").addClasspathResource("bpmn/evection.bpmn").addClasspathResource("bpmn/evection.png").deploy();//4.输出部署信息System.out.println("流程部署id="+deploy.getId());System.out.println("流程部署名字="+deploy.getName());}
}
执行此操作后 activiti 会将上边代码中指定的 bpm 文件和图片文件保存在 activiti 数据库。
压缩包部署方式
将 evection.bpmn 和 evection.png 压缩成 zip 包。
可以同时部署多个流程
/*** 使用zip包进行批量的部署*/@Testpublic void deployProcessByZip() {//1.获取流程引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//2.获取repositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();//3.流程部署//读取资源包文件,构造成inputStreamInputStream stream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");//用inputstream 构造成zioinputstreamZipInputStream zipInputStream = new ZipInputStream(stream);//使用压缩包的流进行流程构建Deployment deploy = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();System.out.println("流程部署id=" + deploy.getId());System.out.println("流程部署名字=" + deploy.getName());}
操作数据表
流程定义部署后操作 activiti 的 3 张表如下:
act_re_deployment 流程定义部署表,每部署一次增加一条记录
act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
act_ge_bytearray 流程资源表
接下来我们来看看,写入了什么数据:
SELECT * FROM act_re_deployment; #流程定义部署表,记录流程部署信息
SELECT * FROM act_re_procdef; #流程定义表,记录流程定义信息
结果:
注意, KEY 这个字段是用来唯一识别不同流程的关键字
SELECT * FROM act_ge_bytearray; #资源表
注意:
act_re_deployment 和 act_re_procdef 一对多关系,一次部署在流程部署表生成一条记录,但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在act_ge_bytearray 会存在两个资源记录, bpmn 和png 。
建议:一次部署一个流程,这样部署表和流程定义表是一对一有关系,方便读取流程部署及流程定义信息。
流程定义部署
1、使用流程设计器,使用流程符号,画出流程图
bpmn文件、png文件
都是流程资源文件,用来描述流程,流程中需要的节点,节点的负责人
2、把流程资源文件进行部署
上传到数据库中,使用java代码来进行部署
一次部署操作:act_re_deployment 会生成一条记录
act_re_procdef 生成流程定义信息
3、deployment和procdef表 一对多关系
在procdef表中可以有多条记录,每条记录对应一个流程的定义信息
同样是出差申请 张三和李四 的流程可以不同
6.3 启动流程实例
流程定义部署在 activiti 后就可以通过工作流管理业务流程了,也就是说上边部署的出差申请流程可以使用了。
针对该流程,启动一个流程表示发起一个新的出差申请单,这就相当于 java 类与 java 对象的关系,类定义好后需要new创建一个对象使用,当然可以 new 多个对象。对于请出差申请流程,张三发起一个出差申请单需要启动一个流程实例,出差申请单发起一个出差单也需要启动一个流程实例。
代码如下:
/*** 启动流程实例*/@Testpublic void testStartProcess(){//1.创建ProcessEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//2.获取RunTimeServiceRuntimeService runtimeService = processEngine.getRuntimeService();//3.根据流程定义的id启动流程ProcessInstance instance = runtimeService.startProcessInstanceByKey("myEvection");//4.输出内容System.out.println("流程定义id="+instance.getProcessDefinitionId());System.out.println("流程实例id="+instance.getId());System.out.println("当前活动的id="+instance.getActivityId());}
输出内容如下:
操作数据表
act_hi_actinst 流程实例执行历史
act_hi_identitylink 流程的参与用户历史信息
act_hi_procinst 流程实例历史信息
act_hi_taskinst 流程任务历史信息
act_ru_execution 流程执行信息
act_ru_identitylink 流程的参与用户信息
act_ru_task 流程当前 任务信息
6.4 任务查询
流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。
@Testpublic void testFindPersonalTaskList(){//1.创建ProcessEngineProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//2.获取taskServiceTaskService taskService = processEngine.getTaskService();//3.根据流程key和任务的负责人查询任务List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("myEvection") //流程key.taskAssignee("zhangsan") //要查询的负责人.list();//4.输出for (Task task : taskList) {System.out.println("流程实例id="+task.getProcessInstanceId());System.out.println("任务id="+task.getId());System.out.println("任务负责人="+task.getAssignee());System.out.println("任务名称="+task.getName());}}
输出结果如下:
select distinct RES.*
from ACT_RU_TASK RES inner join ACT_RE_PROCDEF D on RES.PROC_DEF_ID_ = D.ID_
WHERE RES.ASSIGNEE_ = 'zhangsan' and D.KEY_ = 'myEvection'
order by RES.ID_ asc
LIMIT 2147483647 OFFSET 0
6.5 流程任务处理
任务负责人查询待办任务,选择任务进行处理,完成任务。
/*** 完成个人任务*/@Testpublic void completTask() {//1.获取流程引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//2.获取taskServiceTaskService taskService = processEngine.getTaskService();// 根据流程key 和 任务的负责人 查询任务// 返回一个任务对象Task task = taskService.createTaskQuery().processDefinitionKey("myEvection") //流程Key.taskAssignee("zhangsan") //要查询的负责人.singleResult();// 完成任务,参数:任务idtaskService.complete(task.getId());}
操作数据库表:
ACT_HI_TASKINST --insert 下一步任务
ACT_HI_ACTINST --insert 下一步任务
ACT_HI_IDENTITYLINK --insert 下一步任务负责人
ACT_RU_TASK --insert 下一步任务
ACT_RU_IDENTITYLINK --insert 下一步任务负责人
ACT_HI_ACTINST --update 完成当前操作
ACT_HI_TASKINST --update 完成当前操作
ACT_RU_EXECUTION --update 完成当前操作
ACT_RU_TASK --删除当前操作
6.6 流程定义信息查询
查询流程相关信息,包含流程定义,流程部署,流程定义版本
/*** 查询流程定义*/@Testpublic void queryProcessDefinition() {// 获取引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// repositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();// 得到ProcessDefinitionQuery 对象ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();// 查询出当前所有的流程定义// 条件:processDefinitionKey =evection// orderByProcessDefinitionVersion 按照版本排序// desc倒叙// list 返回集合List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("myEvection").orderByProcessDefinitionVersion().desc().list();// 输出流程定义信息for (ProcessDefinition processDefinition : definitionList) {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());}}
6.7 流程删除
/*** 流程删除*/@Testpublic void deleteDeployment() {// 流程部署idString deploymentId = "1";ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 通过流程引擎获取repositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();// 删除流程定义,如果该流程定义已有流程实例启动则删除时出错repositoryService.deleteDeployment(deploymentId);// 设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流 程// repositoryService.deleteDeployment(deploymentId, true);}
说明:
1) 使用 repositoryService 删除流程定义,历史表信息不会被删除
2) 如果该流程定义下没有正在运行的流程,则可以用普通删除。
如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除。
先删除没有完成流程节点,最后就可以完全删除流程定义信息
项目开发中级联删除操作一般只开放给超级管理员使用 .
6.8 流程资源下载
现在我们的流程资源文件已经上传到数据库了,如果其他用户想要查看这些资源文件,可以从数据库中把资源文件下载到本地。
解决方案有:
1 、 jdbc 对 blob 类型, clob 类型数据读取出来,保存到文件目录
2 、使用 activiti 的 api 来实现
使用 commons-io.jar 解决 IO 的操作
引入 commons-io 依赖包
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>
通过流程定义对象获取流程定义资源,获取 bpmn 和 png
@Testpublic void queryBpmnFile() throws IOException {// 1、得到引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 2、获取repositoryServiceRepositoryService repositoryService = processEngine.getRepositoryService();// 3、得到查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("myEvection").singleResult();// 4、通过流程定义信息,得到部署IDString deploymentId = processDefinition.getDeploymentId();// 5、通过repositoryService的方法,实现读取图片信息和bpmn信息// png图片的流InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());// bpmn文件的流InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());// 6、构造OutputStream流File file_png = new File("E:\\activiti\\code\\activiti01\\evectionflow01.png");File file_bpmn = new File("E:\\activiti\\code\\activiti01\\evectionflow01.bpmn");FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);FileOutputStream pngOut = new FileOutputStream(file_png);// 7、输入流,输出流的转换IOUtils.copy(pngInput, pngOut);IOUtils.copy(bpmnInput, bpmnOut);// 8、关闭流pngOut.close();bpmnOut.close();pngInput.close();bpmnInput.close();}
说明:
1) deploymentId 为流程部署 ID
2) resource_name 为 act_ge_bytearray 表中 NAME_ 列的值
3) 使用 repositoryService 的 getDeploymentResourceNames 方法可以获取指定部署下得所有文件的名称
4) 使用 repositoryService 的 getResourceAsStream 方法传入部署 ID 和资源图片名称可以获取部署下指定名称文件的输入流
最后的将输入流中的图片资源进行输出。
6.9 流程历史信息的查看
即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在 activiti 的 act_hi_* 相关的表中。所以我 们还是可以查询流程执行的历史信息,可以通过HistoryService 来查看相关的历史记录。
/*** 查看历史信息*/@Testpublic void findHistoryInfo() {// 获取引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取HistoryServiceHistoryService historyService = processEngine.getHistoryService();// 获取 actinst表的查询对象HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();// 查询 actinst表,条件:根据 InstanceId 查询// instanceQuery.processInstanceId("2501");// 查询 actinst表,条件:根据 DefinitionId 查询instanceQuery.processDefinitionId("myEvection:1:4");// 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序instanceQuery.orderByHistoricActivityInstanceStartTime().asc();// 查询所有内容List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();// 输出for (HistoricActivityInstance hi : activityInstanceList) {System.out.println(hi.getActivityId());System.out.println(hi.getActivityName());System.out.println(hi.getProcessDefinitionId());System.out.println(hi.getProcessInstanceId());System.out.println("<==========================>");}}
这篇关于Activiti7工作流入门和基本使用-Activiti入门和流程操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!