Avtiviti(八)之网关

1、排他网关

1.1 概念

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

为什么要用排他网关?
不用排他网关也可以实现分支,如下图:

Avtiviti(八)之网关_第1张图片

上图中,在连线的condition条件上设置分支条件。
缺点:如果条件都不满足(比如num的值是4的时候),不使用排他网关,流程就结束了(是异常结束)。


如果 使用排他网关决定分支的走向,如下:

Avtiviti(八)之网关_第2张图片

如果从网关出去的线所有条件都不满足则系统抛出异常。

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(ExclusiveGatewayActivityBehavior.java:85)

说明 :经过排他网关必须要有一条且只有一条分支走。

1.2 定义流程定义

Avtiviti(八)之网关_第3张图片

 

1.3 编码走流程

1.3.1 部署流程定义

/**
 * 测试排他网关
 */
public class ExclusiveGetwayTest {
    //部署排他网关的流程定义
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("bpmn/holiday5.bpmn")
                .name("排他网关请假流程").deploy();
        System.out.println("流程定义的ID:"+deployment.getId());
    }
}

1.3.2 启动流程实例

//启动流程实例,设置流程变量
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //设置流程变量
        Map map = new HashMap<>();
        map.put("num",4);
        //启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday5", "businessKey:1001",map);
        System.out.println("流程定义的id:"+processInstance.getProcessDefinitionId());
    }

可以看到,我设置的num的值是4,特意为了异常而设置的。 

1.3.3 完成任务

我们先执行第一个任务,然后再次执行部门经理的任务,就会发现报错了:

//完成任务,执行流程
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
        Task task = taskQuery.processDefinitionKey("myholiday5").taskAssignee("lisi").singleResult();
        if(task!=null){
            taskService.complete(task.getId());
            System.out.println("完成任务");
        }
    }

执行发现,报错如下:

Exception in thread "main" org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway '_5' could be selected for continuing the process
	at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:105)
	at org.activiti.engine.impl.bpmn.behavior.FlowNodeActivityBehavior.execute(FlowNodeActivityBehavior.java:38)
	at org.activiti.engine.impl.agenda.ContinueProcessOperation.executeActivityBehavior(ContinueProcessOperation.java:210)
	at org.activiti.engine.impl.agenda.ContinueProcessOperation.executeSynchronous(ContinueProcessOperation.java:146)
	at org.activiti.engine.impl.agenda.ContinueProcessOperation.continueThroughFlowNode(ContinueProcessOperation.java:101)
	at org.activiti.engine.impl.agenda.ContinueProcessOperation.run(ContinueProcessOperation.java:66)
	at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:73)
	at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
	at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
	at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
	at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:63)
	at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:35)
	at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
	at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
	at org.activiti.engine.impl.TaskServiceImpl.complete(TaskServiceImpl.java:192)
	at com.zdw.activiti04.ExclusiveGetwayTest.main(ExclusiveGetwayTest.java:24)

这就跟我们前面说的,当设置的值不满足网关的任一条线路的时候,会报错的。

1.3.4 完成任务时设置流程变量

//完成任务时设置流程变量的值
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
        Task task = taskQuery.processDefinitionKey("myholiday5").taskAssignee("lisi").singleResult();
        //设置流程变量
        Map map = new HashMap<>();
        map.put("num",6);
        if(task!=null){
            taskService.setVariables(task.getId(),map);
            taskService.complete(task.getId());
            System.out.println("完成任务");
        }
    }

执行上面的程序的时候,就不会报错了,因为我们修改了流程变量num的值为6,那么排他网关就知道下一步该走哪条线了。执行完上面的代码之后,查询act_ru_task表,走的是总经理审核了。

接下来的业务操作就跟之前的是一样的了,不再赘述了。

2、并行网关

2.1 什么是并行网关

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
 fork分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
 join汇聚:所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。

与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。

2.2 流程定义

Avtiviti(八)之网关_第4张图片

2.3 业务操作

2.3.1 部署流程定义

 //部署排他网关的流程定义
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("bpmn/holiday6.bpmn")
                .name("并行网关请假流程").deploy();
        System.out.println("流程定义的ID:"+deployment.getId());
    }

2.3.2 启动流程实例,设置流程变量

//启动流程实例,设置流程变量
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //设置流程变量
        Map map = new HashMap<>();
        map.put("num",2);
        //启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday6", "businessKey:1001",map);
        System.out.println("流程定义的id:"+processInstance.getProcessDefinitionId());
    }

2.3.3 完成任务

我们这里的完成任务就不啰嗦了,之间把任务执行到并行网关那里再来看效果。因为我们设置的num的值是2,所以不会走到总经理审核,直接就是走人事经理存档的。当我们执行完人事经理zhaoliu的任务后:

//完成任务
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
        Task task = taskQuery.processDefinitionKey("myholiday6").taskAssignee("zhaoliu").singleResult();
        if(task!=null){
            taskService.complete(task.getId());
            System.out.println("完成任务");
        }
    }

查询表:act_ru_task,发现代办任务里面有两条记录:

这就是并行网关的特性,并行网关的每条线路的任务都要去执行。

当我们分别执行xiaoli和xiaoyu的任务之后,整个流程才会结束的,没有先后顺序约束,随便哪个先执行都可以的。

总结:所有分支到达汇聚结点,并行网关执行完成。

 

3、包含网关

3.1 什么是包含网关

       包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。
      包含网关的功能是基于进入和外出顺序流的:
       分支:所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。

       汇聚:所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。

3.2 流程定义

企业体检流程,公司全体员工进行常规项检查、抽血化验,公司管理层除常规检查和抽血化验还要进行增加项检查。

Avtiviti(八)之网关_第5张图片

 

只有userType参数的值等于2的员工,才参加附加项检查。

3.3 业务操作

3.3.1 部署流程定义

//部署排他网关的流程定义
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("bpmn/holiday7.bpmn")
                .name("包含网关请假流程").deploy();
        System.out.println("流程定义的ID:"+deployment.getId());
    }

3.3.2 启动流程实例

1、启动流程实例的时候,没有设置userType流程变量的值:

此时启动流程定义是可以成功的,但是当我们执行完成任务的操作的时候,会报错:

Exception in thread "main" org.activiti.engine.ActivitiException: Unknown property used in expression: ${userType==1 || userType==2}
	at org.activiti.engine.impl.el.JuelExpression.getValue(JuelExpression.java:52)
	at org.activiti.engine.impl.el.UelExpressionCondition.evaluate(UelExpressionCondition.java:36)
	at org.activiti.engine.impl.util.condition.ConditionUtil.hasTrueCondition(ConditionUtil.java:34)
	at org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.leaveFlowNode(TakeOutgoingSequenceFlowsOperation.java:141)
	at org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleFlowNode(TakeOutgoingSequenceFlowsOperation.java:87)
	at org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.run(TakeOutgoingSequenceFlowsOperation.java:75)
	at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:73)
	at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
	at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
	at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
	at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:63)
	at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:35)
	at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
	at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
	at org.activiti.engine.impl.TaskServiceImpl.complete(TaskServiceImpl.java:192)
	at com.zdw.activiti04.InclusiveGetewayTest.main(InclusiveGetewayTest.java:24)
Caused by: javax.el.PropertyNotFoundException: Cannot resolve identifier 'userType'

2、启动流程实例的时候,设置userType流程变量的值

//启动流程实例,设置流程变量
    public static void main2(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //设置流程变量
        Map map = new HashMap<>();
        map.put("userType",2);
        //启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday7", "businessKey:1001",map);
        System.out.println("流程定义的id:"+processInstance.getProcessDefinitionId());
    }

3.3.3 完成任务

1、首先完成的领取体检的任务

//完成领取体检单的任务
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
        Task task = taskQuery.processDefinitionKey("myholiday7").taskAssignee("zhangsan").singleResult();
        if(task!=null){
            taskService.complete(task.getId());
            System.out.println("完成任务");
        }
    }

执行完上面的任务之后,我们查看表act_ru_task,可以看到有三个代办任务,因为我们设置的userType的值是2,所以三条线路都会执行的。

2、完成常规检查的任务

因为我设置的完成任务的assignee都是zhangsan,所以根据zhangsan查询的时候,就会查询出三个任务,这样可以一次性就把三个任务都执行完。但是为了看到包含网关的效果:只有所有的线路都执行完成之后才会结束。所以我们选择一个一个的去完成任务,因此查询的时候就不能仅仅通过执行人去查询了,还要加上任务名称name,具体如下:

//根据任务名称和执行人查询任务,并完成
    public static void main(String[] args) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
        Task task = taskQuery.processDefinitionKey("myholiday7").taskName("常规检查").taskAssignee("zhangsan").singleResult();
        if(task!=null){
            taskService.complete(task.getId());
            System.out.println("完成任务");
        }
    }

执行之后,act_ru_task表中的常规检查的任务就会删除掉了。

我们可以只要修改任务名称,就可以按照上面的方式完成其他的任务(附加项检查,抽象化验,早餐)了。

先走到汇聚结点的分支,要等待其它分支走到汇聚。
等所有分支走到汇聚,包含网关就执行完成。
包含网关执行完成,分支和汇聚就从act_ru_execution删除。

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

你可能感兴趣的:(Activiti)