Activiti工作流-进阶
Posted 宏微
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activiti工作流-进阶相关的知识,希望对你有一定的参考价值。
流程变量
流程变量非常重要,临时存储一些变量,该变量一般是与具体业务相关的,也可以是任意想保存的,比如后面用流程跳转,也无需要流程变量;
流程变量就是工作流对业务进行交流的一个途径,窗口。
主要功能(类似一个Map):
- 存放基本的业务信息,比如主键,基本字段值
- 存放临时的判断用的变量,用来判断连线走向等等
- 存放动态的办理人,运行的时候可以做到办理人动态指定
- ….
流程变量的基本使用
- 一种是在启动流程的时候保存了变量和值,比如,保存了一个业务表的主键id。
- 一种是在任务运行中保存了变量和值,比如保存一些临时业务信息以及判断连线走向的信息。
启动流程的同时保存变量
//流程变量
//在启动流程的时候同时保存一个变量
@Test
public void setVarOnStart(){
//参数2:就是流程变量,结果是map,里面是key和value
Map<String, Object> variables = new HashMap<>();
//保存公司的名字
variables.put("username", "张三");
//。。。。放入nduo的变量--全局的变量
runtimeService.startProcessInstanceByKey("LeaveProcess", variables);
}
任务执行过程中随时保存变量
//在任务流转过程中设置变量
@Test
public void setVarOnTask(){
//某个任务,从数据库查询出来一个值,根据这个值,判断 是否走下一个节点
// taskService.setVariable(taskId, variableName, value);//key:value
// taskService.setVariables(taskId, variables);//map
//某个任务节点设置一个变量:(全局的变量)
taskService.setVariable("2505", "age", 18);
taskService.setVariable("2505", "name", "Rose");
taskService.setVariable("2505", "birthday", new Date());
taskService.setVariable("2505", "简历", new User(1001,"Jack","123456"));
}
获取流程变量(使用运行时和任务api均可)
//获取流程变量
@Test
public void getVar(){
//在流程运行过程中随时可以取出,任意的变量,那条线中的变量
//参数1:执行id,参数2:变量名字
String company =(String) runtimeService.getVariable("2501", "company");
System.out.println(company);
//在任意的任务中获取:该任务可拥有的
//参数1:任务id,参数2:变量的名字
String company2 =(String)taskService.getVariable("2505", "company");
System.out.println(company2);
System.out.println("------");
System.out.println(runtimeService.getVariable("2501", "name"));
System.out.println(runtimeService.getVariable("2501", "age"));
System.out.println(runtimeService.getVariable("2501", "birthday"));
System.out.println(runtimeService.getVariable("2501", "简历"));//反序列化回来了
}
连线
连线—SequenceFlow,连线的作用:告诉工作流引擎,流程该怎么走。
编写画流程图
对于工作流系统,开始节点必须且只能有一个,结束节点必须有但可以有多个。
换句话:流程的发起,只能是从一个点。结束,可以有多个。
大部分流程都是一个开始,一个结束。一般是谁申请,谁最后确认。
设置连线的条件:
在Condition中设置一个布尔值,里面的表达式可以用{}或者#{},如:{msgtype==’不重要’}
测试流程
//个人任务办理
@Test
public void completePersonTask2(){
Map<String, Object> variables=new HashMap<String, Object>();
variables.put("msgtype", "重要");
taskService.complete("5002", variables);
}
要点:使用流程变量控制连线的走向。
缺点:不容易清晰的描绘出分支的走向,尤其是并行或排他业务,得靠大量变量来判断。
网关(并行、排他、包含)
网关—Gateway,主要是用来控制分支流程的。并行(必须同时走多根),排他的(只能走一根),包含的走法(可以走多根,也可以走一根)。
1.并行网关—Parallel Gateway
测试流程
部署流程定义
发布流程定义
查询个人任务
完成个人任务
小结
1)一个流程中流程实例只有1个(启动一次,只能生成一个实例),执行对象有多个(有分支,同时在跑)
活动:任意的一个节点都是活动。包含用户任务。
用户任务:方块(用户任务,带小人,其他的任务,工作流不认为是用户任务),
2)并行网关的功能是基于进入和外出的顺序流的:
分支(fork): 并行后的所有外出顺序流,为每个顺序流都创建一个并发分支(执行对象)。
汇聚(join): 所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。(只有全部汇聚后,流程才能真正通过并行网关)
3)并行网关的进入和外出都是使用相同节点标识(jbpm是用了两种节点图标)
4)如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
5)并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。
6)并行网关不需要是“平衡的”(比如, 对应并行网关的进入和外出节点数目不一定相等)。如图中标示是合法的:
排他网关—Exclusive Gateway
无论如何,只会走一条线。
排他网关和直接连线的最大区别:排他网关有个默认值,连线必须是每根先都得给条件。
排他网关必须给流程变量,当判断结果没有符合条件的话,就会走默认的连线。不给流程变量,会报错,因为无法判断。
连线:如果给流程变量,按照流程变量的判断走,没有默认指定的连线。可以不给流程变量,也会走个默认(先画的那根线,不容易控制)
两者根据实际情况,都可以选择。
包含网关—Inclusive Gateway
相当于并行+排他。
inclusiveGateway与 exclusiveGateway的区别
- exclusiveGateway 只会寻找唯一一条能走完的flow,也就是说当有一个flow可以走通的情况下,它不会再次去寻找第二条可以走通的flow ,如是没有符合条件的,就走defalut的flow。
- inclusiveGateway 会寻找所有符合条件的 flow,也就是说他会走完所有的符合条件的flow,如果没有符合的,那么就去走defalut的flow。
包含网关可以使用并行+排他来代替。
任务
常用任务
常用的task,有如下几个:
- User Task:用户任务,需由用户(参与者、必须有办理人)来完成任务。
- Java Service Task:服务任务,也称之为自动节点任务,当运行到该类型的任务节点的时候,会自动执行一个Java类(需实现一个接口)的一个方法。(发短信、处理数据等)
- Jave Receive Task:接收任务,也称之为等待任务,需手动来完成任务(调一个方法)。
任务中除了用户任务,有参与者(人),其他都没有参与者。
//服务任务的代理类:用来自动执行一段代码
public class MyServiceJavaDelegate implements JavaDelegate{
@Override
//命令模式
public void execute(DelegateExecution execution) throws Exception {
//do something
//当节点走到这里的的时候,会自动调用这个方法,执行里面的逻辑,无需人工参与
System.out.println("服务节点(自动节点)正在执行");
}
}
为服务任务指定服务的类
告知完成(等待)任务:
//调用执行等待任务(一般是嵌入到程序的一段代码中):告知任务
@Test
public void signalTask(){
//申明告知:告诉工作流,我程序已经处理完了,你继续走流程
runtimeService.signal("2501");
}
等待任务没有具体的参与人。
流程的某个任务,是在程序里面运行的一部分。
比如,我们需要写一个业务保存用户业务,但保存该用户之前,需要走一段流程,在流程的某个节点,我才有资格保存该用户,而且保存完了之后,还有后面流程。在保存用户的逻辑的地方调用一个方法就行。
换句话说,该任务,就等着程序去调用他(某些业务操作后),之后,才能往下走。
用户任务—UserTask
根据任务办理人的特点,分为两大类:个人任务和群组任务
用户任务必须由人(参与者)来完成。
个人任务的分配
某个人物节点是由一个具体的人来完成的.
个人任务可以通过三种方式指定办理人
- 为assignee属性指定固定办理人(不常用)
- 为assignee属性指定“变量”(动态)办理人,即通过变量,在程序运行过程中,通过设置变量来动态指定办理人。
- 通过task listeners来动态指定办理人。操作:我们需要实现一个接口,写个类,类里面有个方法,方法中设置该节点的办理人。在画节点的时候,无需指定办理人。办理人会在进入到该节点之前,被lisener监听掉,调用上面的方法,设置进去办理人。
- 在任务节点指定办理人,使用变量的方式,变了名字叫:initiator(发起人)
- 在开始节点上设置发起人的变量名字
- 为中间节点,使用task listeners来指定class
//用户任务的监听(当走到该节点的时候会自动调用该监听)
public class MyUserTaskListener implements TaskListener{
@Override
//将原来的任务包装
public void notify(DelegateTask delegateTask) {
//设置办理人:一般实际开发的后,需要从组织机构 树中获取领导来审批
delegateTask.setAssignee("张三");
}
}
2.测试流程
部署流程:(略)
启动流程:(略)
添加spring注入:
//注入用户service
private IdentityService identityService;
//用户任务--个人任务
//启动流程
@Test
public void startProceeInstanceForPerson(){
//使用任务用户id来进行给变量赋值
//调用该方法之前,需要在开始节点告诉工作流,你的认证用户所使用的变量是谁?
identityService.setAuthenticatedUserId("张三");//当前登录人
runtimeService.startProcessInstanceByKey("myProcess");
//在启动的时候加一个变量initiator发起人--map
// runtimeService.startProcessInstanceByKey(processDefinitionKey, variables)
}
完成个人任务:(略)
发起人在一个流程中一般参与的较多。
群组任务(候选人candidate)的分配
一个任务节点是由一堆人来完成的,不需要直接指定具体的某个人。但最终还是由某个具体人来完成。
群组任务可以通过两大类(各三种方式)指定:
第一类:候选人列表:
- 为Candidate Users属性指定固定的群组人员。
- 为Candidate Users属性指定 “变量”(动态)群组人员,即通过变量,在程序运行过程中,通过设置变量来动态指定群组。
- 通过task listeners在程序中动态的来指定群组人员。
第二类:候选人群组编号(人员和组、关系,都是工作流表中给你设计好了。)
- 为Candidate groups属性指定固定的群组id,该群组中包含群组人员。
- 为Candidate groups属性指定“变量”(动态)的群组id。
- 通过task listeners在程序中动态的来指定群组id。
第二类方式以前都不用。原因是:实际业务开发的时候,组织机构、人员信息,都是由业务系统维护的。
代码略
其他api
历史记录的查询—HistoryService
<!-- 历史Service -->
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>
本地SQL查询—NativeSQL
<!-- 管理Service -->
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService"/>
@Test
public void queryByNativeSQL(){
//当默认的api不能满足你的时候,你可以自定义sql语句进行查询
List<HistoricTaskInstance> list = historyService.createNativeHistoricTaskInstanceQuery()
// .sql("select * from act_hi_taskinst")//必须传入sql
.sql("select * from "+managementService.getTableName(HistoricTaskInstance.class))//不知道对象对应的表名,怎么办
.list();
for (HistoricTaskInstance historicTaskInstance : list) {
System.out.println("历史的任务名称:"+historicTaskInstance.getName());
}
}
表单操作—FormService
业务关联工作流系统:
1.使用流程变量:存放业务表的表名#id,比如t_leave#1001
2.businiessKey(就是变量),和流程变量做法一样
3.formKey
主要用于实现动态表单。(每一个节点的表单页面都不一样)
<!-- 表单Service -->
<bean id="formService" factory-bean="processEngine" factory-method="getFormService"/>
方法1:
表单由工作流创建和管理(开始节点或任务节点):
程序中可以获取表单
formService.getStartFormData(processDefinitionId);//申请表单的数据
formService.getTaskFormData(taskId);//任务表单数据
方法2:
表单由应用程序创建和管理,工作流通过表单的url(form key)来引用
在应用程序中,可以获取该key,从而知道表单的url
formService.getStartFormKey(processDefinitionId);
formService.getTaskFormKey(processDefinitionId, taskDefinitionKey);//将得到url,放到响应中
以上是关于Activiti工作流-进阶的主要内容,如果未能解决你的问题,请参考以下文章
Activiti7工作流引擎:进阶篇 SpringBoot整合Activiti6
疯狂讲义Activiti6.X工作流进阶与项目实战,Activiti整合Drools