文章
本文总阅读量

flowable 分支示例

flowable 分支示例

本文将介绍带有分支节点的flawable使用示例,在我们之前的文章中,我们已经介绍了Flowable无分支流程示例,如果你还没有阅读过,可以点击这里查看。

1.分支流程的概念:

在许多实际的业务场景中,我们可能会遇到需要根据不同的条件选择不同的处理流程的情况,这就是所谓的分支流程。在Flowable中,我们可以使用网关(Gateway)来实现这种分支流程。

以请假流程为例,假设一个员工需要请假,那么他首先需要找HR审批。如果请假天数不超过10天,那么HR的审批就足够了。但是,如果请假天数超过10天,那么就需要进一步找主管审批。这就形成了一个分支流程:根据请假天数的不同,选择不同的审批流程。

在这个例子中,”请假天数是否超过10天”就是一个分支条件,”找HR审批”和”找主管审批”就是两个分支流程。在Flowable中,我们可以使用网关来实现这个分支条件,然后根据条件的结果选择不同的流程进行处理。如图所示
alt text

2.Flowable实现分支流程

在Flowable中,我们可以使用网关(Gateway)来实现分支流程。网关是一种特殊的流程节点,它可以根据一定的条件来决定流程的走向。在我们的请假流程中,我们可以使用一个网关来决定是走HR审批流程,还是走主管审批流程。

Flowable中的网关主要有以下几种类型:

排他网关(Exclusive Gateway):也称为决策网关,它会根据条件选择一条分支执行,其他分支不执行。
并行网关(Parallel Gateway):它会同时启动所有的出口序列流,等所有的出口序列流都完成后,再汇聚到一起。
包含网关(Inclusive Gateway):它会根据条件启动一个或多个出口序列流,等所有启动的出口序列流都完成后,再汇聚到一起。
在我们的请假流程中,我们可以使用排他网关来实现分支流程。我们可以定义一个条件表达式,如果请假天数超过10天,那么走主管审批流程,否则走HR审批流程。

在Flowable中,我们可以使用BPMN 2.0的XML语言来定义流程。以下是一个简单的例子:

1
2
3
4
5
6
7
<bpmn:exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" default="sequenceFlow1" />
<bpmn:sequenceFlow id="sequenceFlow1" sourceRef="exclusiveGw" targetRef="hrApproval">
  <bpmn:conditionExpression xsi:type="tFormalExpression">${days <= 10}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="sequenceFlow2" sourceRef="exclusiveGw" targetRef="managerApproval">
  <bpmn:conditionExpression xsi:type="tFormalExpression">${days > 10}</bpmn:conditionExpression>
</bpmn:sequenceFlow>

3.完整的分支流程示例

这段代码是用于创建一个Flowable流程定义的。下面是对这段代码的详细解释:

  1. 首先,创建一个新的Process对象,并设置其ID和名称。
    1
    2
    3
    
     Process process = new Process();
     process.setId(createProcessDefinitionBranchDTO.getFlowName());
     process.setName(createProcessDefinitionBranchDTO.getFlowName());
    
  2. 创建一个开始节点(StartEvent),并将其添加到流程中。
    1
    2
    3
    4
    
     StartEvent startEvent = new StartEvent();
     startEvent.setId("StartEvent"+ UUID.randomUUID().toString().substring(0, 11));
     startEvent.setName("开始");
     process.addFlowElement(startEvent);
    
  3. 创建一个服务任务(ServiceTask),并将其添加到流程中。服务任务是一种特殊类型的任务,它代表了一个自动执行的步骤,比如调用一个Java类或者一个外部系统。
    1
    2
    3
    4
    5
    6
    
     ServiceTask startServiceTask = new ServiceTask();
     startServiceTask.setId("StartServiceTask"+UUID.randomUUID().toString().substring(0,11));
     startServiceTask.setName("提交");
     startServiceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_CLASS);
     startServiceTask.setImplementation(StartServiceTask.class.getName());
     process.addFlowElement(startServiceTask);
    
  4. 创建一个序列流(SequenceFlow),并将其添加到流程中。序列流是流程中的箭头,它定义了流程的执行顺序。在这里,序列流从开始节点指向服务任务。
    1
    2
    3
    4
    5
    
     SequenceFlow startServiceSequence = new SequenceFlow();
     startServiceSequence.setId("StartServiceSequence"+UUID.randomUUID().toString().substring(0,11));
     startServiceSequence.setSourceRef(startEvent.getId());
     startServiceSequence.setTargetRef(startServiceTask.getId());
     process.addFlowElement(startServiceSequence);
    
  5. 创建网关(Gateway),并将其添加到流程中。网关用于控制流程的分支和合并。这段代码根据输入的网关类型创建了相应的网关,并将其添加到了流程中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
     Map<String, Gateway> gateways = new HashMap<>();
     for (GatewayDefinitionDTO gatewayDTO : createProcessDefinitionBranchDTO.getGatewayDefinitionDTOS()) {
         Gateway gateway;
         if ("ExclusiveGateway".equals(gatewayDTO.getType())) {
             gateway = new ExclusiveGateway();
         } else if ("ParallelGateway".equals(gatewayDTO.getType())) {
             gateway = new ParallelGateway();
         } else {
             throw new IllegalArgumentException("Unsupported gateway type: " + gatewayDTO.getType());
         }
         gateway.setId(gatewayDTO.getId()); // 使用前端提供的ID
         gateway.setName(gatewayDTO.getName());
         process.addFlowElement(gateway);
    
         gateways.put(gatewayDTO.getId(), gateway); // 将网关添加到映射中
     }
    
  6. 创建用户任务(UserTask),并将其添加到流程中。用户任务代表了需要人工完成的步骤。这段代码根据输入的节点信息创建了用户任务,并设置了多实例特性。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     for (ProcessNodeDefinitionDTO nodeDTO : createProcessDefinitionBranchDTO.getProcessNodeDefinitionDTOS()) {
         UserTask userTask = new UserTask();
         userTask.setId(nodeDTO.getNodeId()); // 使用前端提供的ID
         userTask.setName(nodeDTO.getNodeName());
         userTask.setAssignee("${" + nodeDTO.getNodeName() + "Assignee}");
    
         MultiInstanceLoopCharacteristics loopCharacteristics = new MultiInstanceLoopCharacteristics();
         loopCharacteristics.setSequential(false);
         loopCharacteristics.setInputDataItem(nodeDTO.getNodeName() + "Assignees");
         loopCharacteristics.setElementVariable( nodeDTO.getNodeName() + "Assignee");
    
         String completionCondition = nodeDTO.getNodeCategory() == 1 ?
                 "${nrOfCompletedInstances == nrOfInstances}" :
                 "${nrOfCompletedInstances >= 1}";
         loopCharacteristics.setCompletionCondition(completionCondition);
    
         userTask.setLoopCharacteristics(loopCharacteristics);
         process.addFlowElement(userTask);
     }
    
  7. 创建结束节点(EndEvent),并将其添加到流程中。结束节点是流程的终点,每个流程都应该有一个或多个结束节点。

    1
    2
    3
    4
    
     EndEvent endEvent = new EndEvent();
     endEvent.setId("endEvent"+UUID.randomUUID().toString().substring(0,11));
     endEvent.setName("结束");
     process.addFlowElement(endEvent);
    
  8. 创建序列流(SequenceFlow),并将其添加到流程中。这段代码根据输入的序列流信息创建了序列流,并将其添加到了流程中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     for (SequenceFlowDefinitionDTO sequenceFlowDTO : createProcessDefinitionBranchDTO.getSequenceFlowDefinitionDTOS()) {
     SequenceFlow sequenceFlow = new SequenceFlow();
     sequenceFlow.setId(sequenceFlowDTO.getId()); // 使用前端提供的ID
    
     // 判断并设置sourceRef
     if ("StartEvent".equals(sequenceFlowDTO.getSourceRef())){
         sequenceFlow.setSourceRef(startServiceTask.getId());
     } else {
         sequenceFlow.setSourceRef(sequenceFlowDTO.getSourceRef()); // 使用映射中的网关ID
     }
    
     // 判断并设置targetRef
     if ("EndEvent".equals(sequenceFlowDTO.getTargetRef())){
         sequenceFlow.setTargetRef(endEvent.getId());
     } else {
         sequenceFlow.setTargetRef(sequenceFlowDTO.getTargetRef()); // 使用映射中的用户任务ID
     }
    
     sequenceFlow.setConditionExpression(sequenceFlowDTO.getConditionExpression());
     process.addFlowElement(sequenceFlow);
     }
    
  9. 创建BPMN模型(BpmnModel),并将流程添加到模型中。BPMN模型是流程定义的容器,它可以包含多个流程定义。最后,这段代码将创建的流程定义部署到Flowable引擎中。
    1
    2
    3
    
     BpmnModel bpmnModel = new BpmnModel();
     bpmnModel.addProcess(process);
     deploy(bpmnModel, createProcessDefinitionBranchDTO);
    

4.无分支流程与分支流程比较

  1. 单条线流程(Sequential Flow):
    这种流程中的任务按照一定的顺序依次执行,每个任务都有一个明确的前驱任务和后继任务。这种流程设计简单明了,适用于流程中的任务有明确的依赖关系,且不需要并行处理的场景。
    A --> B --> C --> D
  2. 分支流程(Branching Flow):
    这种流程中的某些任务可以并行执行,流程在某个点会分叉,分别进入不同的分支,每个分支可以独立执行,最后再在某个点汇合。这种流程设计灵活,适用于流程中的任务可以并行处理,或者需要根据条件选择执行不同任务的场景。
    1
    2
    3
    4
    5
    
          A
         / \
        B   C
         \ /
          D
    

    完整代码,从此跳转

本文由作者按照 CC BY 4.0 进行授权