Activiti7工作流学习笔记四 组任务及网关

组任务及网关

组任务

Candidate-users 候选人

在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。
针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。

设置任务候选人

在流程图中任务节点的配置中设置 candidate-users(候选人),多个候选人之间用逗号分开。
在这里插入图片描述
查看bpmn文件:
在这里插入图片描述
我们可以看到部门经理的审核人已经设置为 zhangsan,lishi这样的一组候选人,可以使用activiti:candiateUsers=”用户 1,用户 2,用户 3”的这种方式来实现设置一组候选人。

办理组任务

  • 第一步:查询组任务
    指定候选人,查询该候选人当前的待办任务。
    候选人不能办理任务。
  • 第二步:拾取(claim)任务
    该组任务的所有候选人都能拾取。将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。
    • 如果拾取后不想办理该任务
      需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
  • 第三步:查询个人任务
    查询方式同个人任务部分,根据 assignee 查询用户负责的个人任务。
  • 第四步:办理个人任务

用户查询组任务

根据候选人查询组任务

@Test
public void findGroupTaskList() {
	// 流程定义key
	String processDefinitionKey = "holiday4";
	// 任务候选人
	String candidateUser = "zhangsan";
	// 创建TaskService
	TaskService taskService = processEngine.getTaskService();
	//查询组任务
	List<Task> list = taskService.createTaskQuery()//
	.processDefinitionKey(processDefinitionKey)//
	.taskCandidateUser(candidateUser)//根据候选人查询
	.list();
	for (Task task : list) {
		System.out.println("----------------------------");
		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 claimTask(){
	TaskService taskService = processEngine.getTaskService();
	//要拾取的任务id
	String taskId = "6302";
	//任务候选人id
	String userId = "lisi";
	//拾取任务
	//即使该用户不是候选人也能拾取(建议拾取时校验是否有资格)
	//校验该用户有没有拾取任务的资格
	Task task = taskService.createTaskQuery()//
	.taskId(taskId)
	.taskCandidateUser(userId)//根据候选人查询
	.singleResult();
	if(task!=null){
		//第一个参数:拾取的任务id
		//第二个参数:任务候选人id
		taskService.claim(taskId, userId);
		System.out.println("任务拾取成功");
	} 
}

说明:即使该用户不是候选人也能拾取,建议拾取时校验是否有资格
组任务拾取后,该任务已有负责人,通过候选人将查询不到该任务

用户查询个人待办任务

查询方式同个人任务查询

@Test
public void findPersonalTaskList() {
	// 流程定义key
	String processDefinitionKey = "holiday4";
	// 任务负责人
	String assignee = "zhangsan";
	// 创建TaskService
	TaskService taskService = processEngine.getTaskService();
	List<Task> list = taskService.createTaskQuery()
	.processDefinitionKey(processDefinitionKey)
	//设置任务执行人
	.taskAssignee(assignee)
	.list();
	for (Task task : list) {
		System.out.println("----------------------------");
		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 completeTask(){
	//任务ID
	String taskId = "12304";
	processEngine.getTaskService()//
				.complete(taskId);
	System.out.println("完成任务:"+taskId);
}

说明:建议完成任务前校验该用户是否是该任务的负责人

归还组任务

如果个人不想办理该组任务,可以归还组任务,归还后该用户不再是该任务的负责人

// 归还组任务,由个人任务变为组任务,还可以进行任务交接
@Test
public void setAssigneeToGroupTask() {
	// 查询任务使用TaskService
	TaskService taskService = processEngine.getTaskService();
	// 当前待办任务
	String taskId = "6004";
	// 任务负责人
	String userId = "zhangsan2";
	// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
	Task task = taskService.createTaskQuery().taskId(taskId)
	.taskAssignee(userId).singleResult();
	if (task != null) {
		// 如果设置为null,归还组任务,该 任务没有负责人
		taskService.setAssignee(taskId, null);
	} 
}

说明:建议归还任务前校验该用户是否是该任务的负责人
也可以通过 setAssignee方法将任务委托给其它用户负责,注意被委托的用户可以不是候选人(建议不要这样使用

任务交接

任务交接,任务负责人将任务交给其它候选人办理该任务

@Test
public void setAssigneeToCandidateUser() {
	// 查询任务使用TaskService
	TaskService taskService = processEngine.getTaskService();
	// 当前待办任务
	String taskId = "6004";
	// 任务负责人
	String userId = "zhangsan";
	// 将此任务交给其它候选人办理该 任务
	String candidateuser = "lisi";
	// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
	Task task = taskService.createTaskQuery().taskId(taskId)
	.taskAssignee(userId).singleResult();
	if (task != null) {
	    //交接任务
		taskService.setAssignee(taskId, candidateuser);
	} 
}

在这里插入图片描述
在这里插入图片描述

数据库表操作

  • 任务执行表
    SELECT * FROM act_ru_task记录当前执行的任务,由于该任务当前是组任务,所有assignee为空,当拾取任务后该字段就是拾取用户的 id
    在这里插入图片描述
  • 任务参与者
    SELECT * FROM act_ru_identitylink记录当前参考任务用户或组,当前任务如果设置了候选人,会向该表插入候选人记录,有几个候选就插入几个
    在这里插入图片描述
    若不是候选人显示为,参与者
    在这里插入图片描述

act_ru_identitylink 对应的还有一张历史表 act_hi_identitylink,向 act_ru_identitylink 插入记录的同时也会向历史表插入记录。任务完成
在这里插入图片描述

网关

排他网关

排他网关(也叫异或(XOR)网关,或叫基于数据的排他网关),用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为true,如果为 true 则执行该分支。

注意,排他网关只会选择一个为 true 的分支执行。(即使有两个分支条件都为 true,排他网关也会只选择一条分支去执行)

为什么要用排他网关?
不用排他网关也可以实现分支,如下图:
在这里插入图片描述
上图中,在连线的 condition 条件上设置分支条件。

缺点:

  • 如果条件都不满足,不使用排他网关,流程就结束了(是异常结束)。
  • condition设置为总经理holiday.num > 3 ,人事存档holiday.num >=1时且流程变量设置为holiday.num = 5,所有条件有成立时,两个分支会同时执行,流程逻辑出错。
    在这里插入图片描述

如果使用排他网关决定分支的走向,如下:
在这里插入图片描述
如果从网关出去的线所有条件都不满足则系统抛出异常。

org.activiti.engine.ActivitiException: No outgoing sequence flow of the 
exclusive gateway 'exclusivegateway1' could be selected for continuing 
the process
at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehav
ior.leave(ExclusiveGatewayActivityBehavior.java:85)

说明 :经过排他网关必须要有一条且只有一条分支走。且选择bpmn文件中Id较小的路径执行
在这里插入图片描述
所有总经理执行。
在这里插入图片描述
最终看到整个执行流程如下:
在这里插入图片描述

流程定义

在这里插入图片描述
在这里插入图片描述

测试

在部门经理审核后,走排他网关,从排他网关出来的分支有两条,一条是判断请假天数是否大于 3天,另一条是判断请假天数是否小于等于 3 天。
设置分支条件时,如果所有分支条件都不是 true,报错:

org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 
'exclusivegateway1' could be selected for continuing the process
at 
org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivit
yBehavior.java:85)

并行网关

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的

  • fork 分支:
    并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
  • join 汇聚:
    所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。

注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。

与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。(设置条件失效)
在这里插入图片描述
说明:

  • 财务结算和入库是两个execution分支,在act_ru_execution 表有两条记录分别是财务会计和行政考勤,act_ru_execution还有一条记录表示该流程实例。
  • 待财务会计和行政考勤任务全部完成,在汇聚点汇聚,通过 parallelGateway并行网关。
  • 并行网关在业务应用中常用于会签任务,会签任务即多个参与者共同办理的任务。

流程定义

在这里插入图片描述

在这里插入图片描述

测试

当执行到并行网关数据库跟踪如下

  • 当前任务表:SELECT * FROM act_ru_task
    在这里插入图片描述
    上图中:有两个(多个)任务当前执行。

  • 流程实例执行表:SELECT * FROM act_ru_execution
    在这里插入图片描述
    上图中,说明当前流程实例有多个分支(两个)在运行。

  • 对并行任务的执行:
    并行任务执行不分前后,由任务的负责人去执行即可。
    当完成并任务中一个任务后:已完成的任务在当前任务表 act_ru_task已被删除。

  • 在流程实例执行表:SELECT * FROM act_ru_execution有中多个分支存在且有并行网关的汇聚结点。
    在这里插入图片描述
    有并行网关的汇聚结点:说明有一个分支已经到汇聚,等待其它的分支到达。

  • 当所有分支任务都完成,都到达汇聚结点后:
    流程实例执行表:SELECT * FROM act_ru_execution执行流程实例不存在,说明流程执行结束

整个流程完成 act_hi_actinst表中记录整个过程
在这里插入图片描述

包含网关

包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行
网关一样。
包含网关的功能是基于进入和外出顺序流的。

  • 分支:
    所有外出顺序流的条件都会被解析,结果为 true 的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。
  • 汇聚:
    所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程 token 的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。

流程定义

在这里插入图片描述
员工类型:
通过流程变量 userType 来表示,如果等于 1 表示普通员工,如果等于 2 表示领导
在这里插入图片描述
注意:通过包含网关的每个分支的连线上设置condition 条件。

测试

如果包含网关设置的条件中,流程变量不存在,报错;

org.activiti.engine.ActivitiException: Unknown property used in expression: ${userType=='1' || 
userType=='2'}

需要在流程启动时设置流程变量userType

  • 当执行到包含网关:
    流程实例执行表:SELECT * FROM act_ru_execution
    在这里插入图片描述
  1. 第一条记录:包含网关分支。
  2. 后两条记录:两个分支:常规项体检,抽血化验
  • 当前任务表:ACT_RU_TASK_
    在这里插入图片描述
    上图中,常规项体检,抽血化验都是当前的任务,在并行执行。

如果有一个分支执行到汇聚:
在这里插入图片描述
先走到汇聚结点的分支,要等待其它分支走到汇聚。
等所有分支走到汇聚,包含网关就执行完成。
包含网关执行完成,分支和汇聚就从 act_ru_execution删除。

小结:在分支时,需要判断条件,符合条件的分支,将会执行,符合条件的分支最终才进行汇聚。

总结activiti 开发流程

  • 第一步:部署 activiti的环境。
    环境包括:jar包和数据库(25 张表)
    业务系统通过 springactiviti整合进行开发。
  • 第二步:使用 activiti提供流程设计器(和 ideaeclipse 集成的 designer)工具进行流程定义
    流程定义生成两个文件:.bpmn.png(不是必须的)。
  • 第三步;将流程定义文件部署到activiti 的数据库
    1. SELECT * FROM act_re_deployment #流程定义部署表
      一次部署插入一条记录,记录流程定义的部署信息
    2. SELECT * FROM act_re_procdef #流程定义表
      一次部署流程定义信息,如果一次部署两个流程定义,插入两条记录
    3. 建议:一次部署只部署一个流程定义,这样 act_re_deploymentact_re_procdef一对一关系
    4. 常用两个方法:单个文件部署和 zip 文件部署。
      建议单个文件部署。
  • 第四步: 启动一个流程实例
    业务系统就可以按照流程定义去执行业务流程,执行前需要启动一个流程实例
    根据流程定义来启动一个流程实例。
    指定一个流程定义的key 启动一个流程实例,activiti根据 key 找最新版本的流程定义。
    指定一个流程定义的 id启动一个流程实例。
    启动一个实例需要指定 businesskey(业务标识),businessKeyactiviti和业务系统整合时桥梁。
    比如:请假流程,businessKey就是请假单 id。
    启动一个实例还可以指定流程变量,流程变量是全局变量(生命期是整个流程实例,流程实例结束,变量就消失)
  • 第五步:查询待办任务
    查询个人任务:使用 taskService,根据 assignee 查询该用户当前的待办任务。
    查询组任务:使用 taskService,根据 candidateuser 查询候选用户当前的待办组任务。
  • 第六步:办理任务
    办理个人任务:调用 taskServicecomplete 方法完成任务。
    如果是组任务,需要先拾取任务,调用 taskServiceclaim 方法拾取任务,拾取任务之后组任务就变成了个人任务(该任务就有负责人)。
  • 网关:
    1. 排他网关:任务执行之后的分支,经过排他网关分支只有一条有效。
    2. 并行网关:任务执行后,可以多条分支,多条分支总会汇聚,汇聚完成,并行网关结束。
    3. 包含网关:是排他网关和并行网关结合体。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章