完成创建一个Activiti工作流,并启动这个流程。
创建Activiti工作流主要包含以下几步:
-
定义流程,按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来;
-
部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据;
-
启动流程,使用java代码来操作数据库表中的内容。
流程符号
BPMN2.0是业务流程建模符号2.0的缩写。
它由Business Process Management Initiative这个非营利协会创建并不断发展。作为一种标识,BPMN 2.0是使用一些符号来明确业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。
目前BPMN2.0是最新的版本,它用于在BPM上下文中进行布局和可视化的沟通。
BPMN2.0的基本符号主要包含:
1、事件Event
2、活动Activity:活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次,你还可以为活动指定不同的类型。常见活动如下:
3、网关GateWay:网关用来处理决策。
①排他网关(x):只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流;如果多条线路计算结果都是true,则会执行第一个值为true的线路。如果所有网关计算结果没有true,则引擎会抛出异常。排他网关需要和条件顺序流结合使用,default属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
②并行网关(+):所有路径会被同时选择。拆分——并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。合并——所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
③包容网关(+):可以同时执行多条线路,也可以在网关上设置条件。拆分——计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行。合并——所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
④事件网关(+):专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
4、流向Flow:流是连接两个流程节点的连线。
使用Activiti Designer
Palette(画板),在idea中安装插件即可使用,画板中包括以下结点:
-
Connection——连接
-
Event——事件
-
Task——任务
-
Gateway——网关
-
Container——容器
-
Boundary event——边界事件
-
Intermediate event——中间事件
流程图设计完毕保存生成.bpmn
文件。
新建流程
1、选中存放图形的目录(选择resources下的bpmn目录),点击菜单:New→BpmnFile。
2、弹出框中输入evection表示出差审批流程,起完名字evection后(默认扩展名为bpmn),就可以看到流程设计页面,如图所示:
中间区域是绘图区,右侧区域是palette画板区域,鼠标先点击画板的元素即可在中间绘图。
绘制流程
使用滑板来绘制流程,通过从右侧把图标拖拽到左侧的画板,最终效果如下:
流程定义key即流程定义的标识,通过properties视图查看流程的key。
指定任务负责人
在properties视图指定每个任务结点的负责人,在Assignee中填写,如:填写出差申请的负责人为zhangsan;经理审批负责人为lisi;总经理审批负责人为wangwu;财务审批负责人为zhaoliu。
生成PNG文件
bpmn文件本质上是XML文件,因为安装actiBPw插件,才能可视化的看到流程内容。
生成PNG文件:把bpmn文件后缀改为xml,在这个文件上右键选择Diagrams→show BPNN2.0 Desinger,打开窗口,点击导出文件选择导出文件类型为PNG。
把png文件拷贝到resources下的bpmn目录,并且把evection.xml改名为evection.bpmn。
流程操作
流程定义部署
将上面在设计器中定义的流程部署到Activiti数据库中,就是流程定义部署。使用Activiti提供的API把流程图的内容写入数据库中属于资源类操作,使用RepositoryService。
单文件部署:把bpmn文件和png文件一个一个处理。
/**
* 流程定义部署:定义→部署→添加资源
* act_re_procdef流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
* act_re_deployment流程定义部署表,每部署一次增加一条记录
* act_ge_bytearray资源表
*/
@Test
public void deployProcess() {
// 1、获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RepositoryService
RepositoryService 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());
}
压缩包部署:把bpmn文件和png打压缩包来处理。
/**
* 使用zip包进行批量的部署
*/
@Test
public void deployProcessByZip() {
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
// 读取资源包文件,构造成inputStream
InputStream inputStream = this.getClass()
.getClassLoader()
.getResourceAsStream("bpmn/evection.zip");
// 用inputStream 构造ZipInputStream
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 使用压缩包的流进行流程的部署
Deployment deploy = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("流程部署id=" + deploy.getId());
System.out.println("流程部署的名称=" + deploy.getName());
}
启动流程实例
/**
* 启动流程实例,操作的表:
* act_hi_actinst 流程实例执行历史信息
* act_hi_identitylink 流程参与用户的历史信息
* act_hi_procinst 流程实例的历史信息
* act_hi_taskinst 流程任务的历史信息
* act_ru_execution 流程执行信息
* act_ru_identitylink 流程的正在参与用户信息
* act_ru_task 流程当前任务信息
*/
@Test
public void startProcess() {
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RunTimeService
RuntimeService 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());
}
任务查询
流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。
/**
* 查询个人待执行的任务
*/
@Test
public void findPersonalTaskList() {
// 1、获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取taskService
TaskService 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());
}
}
流程任务处理
任务负责人查询待办任务,选择任务进行处理,完成任务。
/**
* 完成个人任务
*/
@Test
public void completTask(){
// 获取引擎
// ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取操作任务的服务 TaskService
// TaskService taskService = processEngine.getTaskService();
// 完成任务,参数:任务id(act_ru_task表)
// 完成zhangsan的任务
// taskService.complete("2505");
// 依次完成lisi的任务 、完成wangwu的任务、完成zhaoliu的任务
completPersonalTask("lisi");
completPersonalTask("wangwu");
completPersonalTask("zhaoliu");
}
private static void completPersonalTask(String assignee) {
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取操作任务的服务 TaskService
TaskService taskService = processEngine.getTaskService();
// 个人任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("myEvection")
.taskAssignee(assignee)
.singleResult();
System.out.println("流程实例id="+task.getProcessInstanceId());
System.out.println("任务Id="+task.getId());
System.out.println("任务负责人="+task.getAssignee());
System.out.println("任务名称="+task.getName());
// 完成个人任务,参数:任务id(act_ru_task表)
taskService.complete(task.getId());
}
任务历史表记录
流程定义信息查询
查询流程相关信息,包含流程定义,流程部署,流程定义版本等。
/**
* 查询流程定义
*/
@Test
public void queryProcessDefinition() {
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取Repositoryservice
RepositoryService repositoryService = processEngine.getRepositoryService();
// 获取ProcessDifinitionQuery对象
ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
// 查询当前所有的流程定义 ,返回流程定义信息的集合
// processDefinitionKey(流程定义Key)
List<ProcessDefinition> definitionList = definitionQuery.processDefinitionKey("myEvection")
.orderByProcessDefinitionVersion() // 进行排序
.desc() // 倒叙
.list(); //查询出所有的内容
// 输出信息
for (ProcessDefinition processDefinition : definitionList) {
System.out.println("流程定义ID:" + processDefinition.getId());
System.out.println("流程定义名称:" + processDefinition.getName());
System.out.println("流程定义Key:" + processDefinition.getKey());
System.out.println("流程定义版本:" + processDefinition.getVersion());
System.out.println("流程部署ID:" + processDefinition.getDeploymentId());
}
}
输出结果:
流程定义ID:myEvection:1:4
流程定义名称:出差申请
流程定义Key:myEvection
流程定义版本:1
流程部署ID:1
流程删除
/**
* 删除流程部署信息,删除的表与创建流程定义的表一样
* act_ge_bytearray
* act_re_deployment
* act_re_procdef
* 当前的流程如果并没有完成,想要删除的话需要使用特殊方式,原理就是 级联删除
*/
@Test
public void deleteDeployment(){
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 通过引擎来获取 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 通过部署id(act_re_deployment)来删除流程部署信息
String deploymentId = "1";
// 删除流程定义,如果该流程定义已有流程实例启动则删除时出错
// repositoryService.deleteDeployment(deploymentId);
// 设置true,级联删除流程定义,即使该流程有流程实例启动也可以删除;设置为false非级别删除方式
repositoryService.deleteDeployment(deploymentId,true);
}
说明:
1、使用RepositoryService删除流程定义,历史表信息不会被删除;
2、如果该流程定义下没有正在运行的流程,则可以用普通删除。
3、如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除。先删除没有完成流程节点,最后就可以完全删除流程定义信息。
4、项目开发中级联删除操作一般只开放给超级管理员使用。
流程资源下载
流程资源文件已经上传到数据库了,如果其他用户想要查看这些资源文件,可以从数据库中把资源文件下载到本地。
1、使用commons-io.jar 解决IO的操作,引入commons-io依赖包。
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2、通过流程定义对象获取流程定义资源,获取bpmn和png。
-
resource_name为act_ge_bytearray表中NAME_列的值。
-
使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下得所有文件的名称。
-
使用repositoryService的getResourceAsStream方法传入部署ID和资源图片名称可以获取部署下指定名称文件的输入流。
-
最后的将输入流中的图片资源进行输出。
/**
* 下载 资源文件
* 使用Activiti提供的api(RespositoryService)来下载资源文件,保存到文件目录
* 解决IO操作:commons-io.jar
*/
@Test
public void getDeployment() throws IOException {
// 1、得到引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取api,RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、获取查询对象 ProcessDefinitionQuery查询流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myEvection")
.singleResult();
// 4、通过流程定义信息,获取部署ID
String deploymentId = processDefinition.getDeploymentId();
// 5、通过RepositoryService,传递部署id参数,读取资源信息(png 和 bpmn)
// 5.1、获取png图片的流
// 从流程定义表中,获取png图片的目录和名字
String pngName = processDefinition.getDiagramResourceName();
// 通过部署id和文件名字来获取图片的资源
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, pngName);
// 5.2、获取bpmn的流
String bpmnName = processDefinition.getResourceName();
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, bpmnName);
// 6、构造OutputStream流
File pngFile = new File("d:/evectionflow01.png");
File bpmnFile = new File("d:/evectionflow01.bpmn");
FileOutputStream pngOutStream = new FileOutputStream(pngFile);
FileOutputStream bpmnOutStream = new FileOutputStream(bpmnFile);
// 7、输入流,输出流的转换
IOUtils.copy(pngInput, pngOutStream);
IOUtils.copy(bpmnInput, bpmnOutStream);
// 8、关闭流
pngOutStream.close();
bpmnOutStream.close();
pngInput.close();
bpmnInput.close();
}
流程历史信息的查看
即使流程定义已经删除了,流程执行的历史信息依然保存在Activiti的act_hi_*
相关的表中。所以可以查询流程执行的历史信息,可以通过HistoryService来查看相关的历史记录。
/**
* 查看历史信息
*/
@Test
public void findHistoryInfo() {
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取HistoryService
HistoryService 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("<==========================>");
}
}
评论区