手写一个简易的排他网关

361 阅读2分钟

排他网关定义

1. 排他网关exclusive gateway 经常使用流程变量决定流程下一步要选择的路径
2. 排他网关(也叫异或网关 XOR gateway,或者更专业的,基于数据的排他网关 exclusive data¬based gateway),用于为流程中的决策建模。
3. 当执行到达这个网关时,所有出口顺序流会按照它们定义的顺序进行计算。条件计算为true的顺序流(当没有设置条件时,认为顺序流定义为true)会被选择用于继续流程。
4. 用排他网关时,只会选择一条顺序流。当多条顺序 流的条件都计算为true时,其中在XML中定义的第一条(也只有这条)会被选择,用于继续流程。如果没有可选的顺序流,会抛出异常。
5. 排他网关,用内部带有’X’图标的标准网关(菱形)表示,'X’图标代表异或(XOR)的含义。请注意内部没有图标的网关默认为排他网关。BPMN 2.0规范不允许在同一个流程中,混合使用带有及没有X的菱形标志
6. 一个排他网关对应一个以上的顺序流

案例描述

WX20230520-201353@2x.png 首先我们模拟一下业务场景,就以请假流程为例。 如图所示根据流程变量days决定路径选择。其中HR是一条默认路径。接着我们看一下生成的BPMN文件的定义。

 <bpmn:process id="Process_1ojbyyd" isExecutable="true">
        <bpmn:startEvent id="StartEvent_1">
          <bpmn:outgoing>Flow_00pc376</bpmn:outgoing>
        </bpmn:startEvent>
        <bpmn:exclusiveGateway id="Gateway_1rc1dcw">
          <bpmn:incoming>Flow_00pc376</bpmn:incoming>
          <bpmn:outgoing>Flow_02rt1d1</bpmn:outgoing>
          <bpmn:outgoing>Flow_104rp3g</bpmn:outgoing>
          <bpmn:outgoing>Flow_1ugfvox</bpmn:outgoing>
        </bpmn:exclusiveGateway>
        <bpmn:sequenceFlow id="Flow_00pc376" sourceRef="StartEvent_1" targetRef="Gateway_1rc1dcw" />

       <!--days小于2天的路径-->
        <bpmn:sequenceFlow id="Flow_02rt1d1" name="days&#60;=2" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0sopid2">
          <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days &lt; 2</bpmn:conditionExpression>
        </bpmn:sequenceFlow>
        
          <!--默认路径-->
        <bpmn:sequenceFlow id="Flow_104rp3g" name="default" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0dw2l26" />

        <bpmn:userTask id="Activity_0sopid2" name="直属领导">
          <bpmn:incoming>Flow_02rt1d1</bpmn:incoming>
          <bpmn:outgoing>Flow_1u8dcgu</bpmn:outgoing>
        </bpmn:userTask>
        <bpmn:userTask id="Activity_0dw2l26" name="HR">
          <bpmn:incoming>Flow_104rp3g</bpmn:incoming>
          <bpmn:outgoing>Flow_1n7t0sw</bpmn:outgoing>
        </bpmn:userTask>
        <bpmn:endEvent id="Event_1aaq42i">
          <bpmn:incoming>Flow_1u8dcgu</bpmn:incoming>
          <bpmn:incoming>Flow_1n7t0sw</bpmn:incoming>
          <bpmn:incoming>Flow_0mbnt14</bpmn:incoming>
        </bpmn:endEvent>
        
        <bpmn:sequenceFlow id="Flow_1u8dcgu" sourceRef="Activity_0sopid2" targetRef="Event_1aaq42i" />
        <bpmn:sequenceFlow id="Flow_1n7t0sw" sourceRef="Activity_0dw2l26" targetRef="Event_1aaq42i" />
          <!--days大于2天且小于5天的路径-->
        <bpmn:sequenceFlow id="Flow_1ugfvox" name="2&#60;days&#60;=5" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0ylxig7">
          <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days &gt;2 and days &lt;=5</bpmn:conditionExpression>
        </bpmn:sequenceFlow>
        
        <bpmn:userTask id="Activity_0ylxig7" name="部门经理">
          <bpmn:incoming>Flow_1ugfvox</bpmn:incoming>
          <bpmn:outgoing>Flow_0mbnt14</bpmn:outgoing>
        </bpmn:userTask>
        <bpmn:sequenceFlow id="Flow_0mbnt14" sourceRef="Activity_0ylxig7" targetRef="Event_1aaq42i" />
      </bpmn:process>

上面是案例完整的BPMN文件定义。 具体到排他网关。 我们需要将顺序流相关定义信息单独抽出进行分析。经过整理。 如下就是排他网关定义相关的信息。

 <!--days小于2天的路径-->
<bpmn:sequenceFlow id="Flow_02rt1d1" name="days&#60;=2" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0sopid2">
  <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days &lt; 2</bpmn:conditionExpression>
</bpmn:sequenceFlow>
            

<!--days大于2天且小于5天的路径-->
<bpmn:sequenceFlow id="Flow_1ugfvox" name="2&#60;days&#60;=5" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0ylxig7">
  <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days &gt;2 and days &lt;=5</bpmn:conditionExpression>
</bpmn:sequenceFlow>

<!--默认路径-->
<bpmn:sequenceFlow id="Flow_104rp3g" name="default" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0dw2l26" />

模拟排他网关的执行代码

根据上述定义, 我们首先要思考如何将xml文件转换成Java对象。首先就是这个顺序流的java映射。

<bpmn:sequenceFlow id="Flow_1ugfvox" name="2&#60;days&#60;=5" sourceRef="Gateway_1rc1dcw" targetRef="Activity_0ylxig7">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=days &gt;2 and days &lt;=5</bpmn:conditionExpression>
</bpmn:sequenceFlow>

根据这个定义文件。我们可以映射的定义一个Transition迁移类。

public class Transition{
  //顺序流的标识
  public String id;
  //顺序流的名称
  public String name;
  //是否默认
  public Boolean isDefault;
  //开始节点
  public String startRef;
  //结束节点
  public String targetRef;
  //条件
  private Condtion condition;
}

然后模拟一个排他网关的类。ExclusiveGateway.并完成如下伪代码

    public class ExclusiveGateway{
       //默认路径
       private List<Transition> defaultList;
       //非默认路径
       private List<Transition> unDefaultList;
       
      //路径计算方法
      public State doLeave(){
         //目标节点
        State To = null;
        //优先遍历非默认路径
        for(Transition transtion : unDefaultList){
          //处理迁移逻辑
        }
          //未找到默认路径。遍历默认路径集合
        if(ObjectUtils.isNull(To)){
          for(Transition transtion : defaultList){
                 //处理迁移逻辑
           }
        }
      
      }
      
    }