# 排他网关
排他网关(Exclusive Gateway)是工作流中用于决策的节点,它根据预设条件从多个流出路径中仅选择唯一的一条路径继续执行流程

# 工作原理
当流程实例执行到排他网关时,工作流引擎会启动一个条件评估过程: 1. 顺序评估:引擎按照排他网关流出顺序流(Sequence Flow)在BPMN模型中定义的先后顺序,依次评估每个顺序流上定义的条件表达式 2. 选择第一条符合条件的路径:当找到第一个条件表达式计算结果为true的顺序流时,引擎立即选择该路径继续执行流程,停止后续条件的评估 3. 单路径执行:排他网关只会选择一条顺序流执行,即使后续顺序流的条件也为true,引擎也会忽略它们
# 条件表达式
排他网关的分支条件通过条件表达式定义,这些表达式直接设置在从网关流出的顺序流上,而不是在网关上本身表达式通常使用统一表达式语言(UEL),基本格式为${expression},其中expression是返回布尔值的逻辑语句。
常见表达式
${approvalResult == 'approved'}- 字符串比较
${holiday.num > 3}- 数值比较
${order.amount > 1000 && order.priority == 'high'}- 复合条件
${userService.isManager(employeeId)}- 方法调用
# 异常处理机制
当所有外出顺序流的条件都不满足时,排他网关的行为如下 检查默认序列流:引擎会检查是否存在标记为"默认流"的顺序流(在BPMN中通常通过箭头加"/"标识) 执行默认流:如果存在默认流,引擎会选择此路径继续执行流程,忽略条件表达式的值 抛出异常:如果既没有条件为true的顺序流,也没有定义默认序列流,工作流引擎会抛出异常
# 最佳实践
1.条件顺序安排:将最可能被满足或最重要的条件放在前面,因为引擎按顺序评估且选择第一个满足的条件。例如,在紧急程度判断中,将"紧急"条件置于"普通"条件之前。 2.避免条件重叠:确保条件之间互斥或明确优先级,尽管排他网关会选择第一个满足的条件,但清晰的条件设计能提高流程可读性 。例如
<!-- 不推荐:条件有重叠 -->
<conditionExpression>${days > 3}</conditionExpression>
<conditionExpression>${days > 5}</conditionExpression>
<!-- 推荐:条件互斥 -->
<conditionExpression>${days <= 3}</conditionExpression>
<conditionExpression>${days > 3 && days <= 10}</conditionExpression>
<conditionExpression>${days > 10}</conditionExpression>
3.始终定义默认流:为了防止因条件不满足导致的流程异常,建议总是为排他网关定义一个默认序列流。默认流可以用于处理异常情况、提供降级方案或记录未预期路径
# 实战案例
# 需求描述
现假设实现一个请假流程, 请假1天走直属领导审批,请假2天走HR审批, 其他情况走老板审批。
# 建模过程
将网关修改成排他网关用小扳手将网关的类型修改成排他网关

设置请假申请节点的属性请假申请是一个用户任务节点,我们需要设置两部分信息,一个指定人assignee,一个用户提交的数据信息(使用内置表单)

设置排他网关的连线的条件 设置从排他网关流出的连线的条件信息, 如果是默认线路则不需要设置。

设置审批节点的属性信息在这里我们使用了服务任务来完成这个测试动作, 实际业务一般使用用户任务节点。

# 演示效果
提交请假申请表单 用户登陆若依工作流演示站 (opens new window),找到菜单提交请假信息,此时会将登陆用户名赋予上面的${startUser}

最终效果 这个时候我们可以看到流程的高亮显示和路线选择是符合业务需求。

在线体验,请访问ruoyiflow (opens new window)
# 自由递进
# 需求描述
现在流程设置了财务部和人事部审批两个节点, 这两个部门是否需要审批,完全由部门主管来决定。 并且要求如果两个部门都需要审批,那么财务部必须先进行审批。
这种模式在中国式审批场景常见, 可以抽象成自由递进。和用户任务串行多实例,逐级审批不一样。 通过排他网关可以更加显示的表达这种规则的定义。
# 流程建模

流程运行到部门主管这个节点, 给他的交互效果如下:
评论: 多行文本输入框
财务部: 下拉框
人事部: 下拉框
部门主管可以自由选择财务部或人事部相关人员。 也可以不选择。 这样就实现了财务部和人事部自由选择!
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="Process_5224" name="报销流程" isExecutable="true">
<bpmn:startEvent id="Event_1jh0145">
<bpmn:outgoing>Flow_0skyz9l</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0skyz9l" sourceRef="Event_1jh0145" targetRef="Activity_089894z" />
<bpmn:userTask id="Activity_089894z" name="报销申请" camunda:assignee="a">
<bpmn:incoming>Flow_0skyz9l</bpmn:incoming>
<bpmn:outgoing>Flow_1m6omny</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1m6omny" sourceRef="Activity_089894z" targetRef="Activity_04i7kzq" />
<bpmn:userTask id="Activity_04i7kzq" name="部门主管" camunda:assignee="b">
<bpmn:incoming>Flow_1m6omny</bpmn:incoming>
<bpmn:outgoing>Flow_1tz04av</bpmn:outgoing>
</bpmn:userTask>
<bpmn:exclusiveGateway id="Gateway_1ixezgl">
<bpmn:incoming>Flow_1tz04av</bpmn:incoming>
<bpmn:outgoing>Flow_09r02r6</bpmn:outgoing>
<bpmn:outgoing>Flow_1499rp2</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1tz04av" sourceRef="Activity_04i7kzq" targetRef="Gateway_1ixezgl" />
<bpmn:sequenceFlow id="Flow_09r02r6" sourceRef="Gateway_1ixezgl" targetRef="Activity_07f4vsy">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${needFinance == true}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:userTask id="Activity_07f4vsy" name="财务部">
<bpmn:incoming>Flow_09r02r6</bpmn:incoming>
<bpmn:outgoing>Flow_1xpk6ao</bpmn:outgoing>
</bpmn:userTask>
<bpmn:exclusiveGateway id="Gateway_1386e9z">
<bpmn:incoming>Flow_1xpk6ao</bpmn:incoming>
<bpmn:incoming>Flow_1499rp2</bpmn:incoming>
<bpmn:outgoing>Flow_1ycovik</bpmn:outgoing>
<bpmn:outgoing>Flow_0luhhfm</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1xpk6ao" sourceRef="Activity_07f4vsy" targetRef="Gateway_1386e9z" />
<bpmn:sequenceFlow id="Flow_1499rp2" sourceRef="Gateway_1ixezgl" targetRef="Gateway_1386e9z" />
<bpmn:sequenceFlow id="Flow_1ycovik" sourceRef="Gateway_1386e9z" targetRef="Activity_10fq7l8">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${needHuman == true}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:userTask id="Activity_10fq7l8" name="人事部">
<bpmn:incoming>Flow_1ycovik</bpmn:incoming>
<bpmn:outgoing>Flow_1susnnc</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1susnnc" sourceRef="Activity_10fq7l8" targetRef="Activity_0c9j3yh" />
<bpmn:userTask id="Activity_0c9j3yh" name="老板" camunda:assignee="boss">
<bpmn:incoming>Flow_1susnnc</bpmn:incoming>
<bpmn:incoming>Flow_0luhhfm</bpmn:incoming>
<bpmn:outgoing>Flow_15rb2lx</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="Event_0qo39ca">
<bpmn:incoming>Flow_15rb2lx</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_15rb2lx" sourceRef="Activity_0c9j3yh" targetRef="Event_0qo39ca" />
<bpmn:sequenceFlow id="Flow_0luhhfm" sourceRef="Gateway_1386e9z" targetRef="Activity_0c9j3yh" />
<bpmn:textAnnotation id="TextAnnotation_1ewvdm1">
<bpmn:text>部门可以在待办任务,决定财务部和人事部是否需要审批</bpmn:text>
</bpmn:textAnnotation>
<bpmn:association id="Association_0soai2r" associationDirection="None" sourceRef="Activity_04i7kzq" targetRef="TextAnnotation_1ewvdm1" />
</bpmn:process>
一个实战的踩坑点!

在上面的建模中,排他网关到财务部是默认条件(没有设置), 排他网关->人事部设置了网关条件(need=true) ,在实际运行中,如果期望走人事部, 会出现先走到财务部。
原因分析: 排他网关会从为true的分支选第一条进行执行。 所有会自动走第一条。 解决办法,就是每条路径都加条件。
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="Process_3582" name="测试流程" isExecutable="true">
<bpmn:startEvent id="Event_1qz8fti">
<bpmn:outgoing>Flow_0znme7e</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0znme7e" sourceRef="Event_1qz8fti" targetRef="Activity_12ffett" />
<bpmn:userTask id="Activity_12ffett" name="部门主管" camunda:assignee="a">
<bpmn:incoming>Flow_0znme7e</bpmn:incoming>
<bpmn:outgoing>Flow_1nx1og2</bpmn:outgoing>
</bpmn:userTask>
<bpmn:exclusiveGateway id="Gateway_1gw99w2">
<bpmn:incoming>Flow_1nx1og2</bpmn:incoming>
<bpmn:outgoing>Flow_06enq8d</bpmn:outgoing>
<bpmn:outgoing>Flow_1ri4jc5</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1nx1og2" sourceRef="Activity_12ffett" targetRef="Gateway_1gw99w2" />
<bpmn:sequenceFlow id="Flow_06enq8d" sourceRef="Gateway_1gw99w2" targetRef="Activity_0ttkdt8" />
<bpmn:sequenceFlow id="Flow_1ri4jc5" sourceRef="Gateway_1gw99w2" targetRef="Activity_0d86poz">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${need == true}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="Flow_1huvtoa" sourceRef="Activity_0d86poz" targetRef="Activity_0ttkdt8" />
<bpmn:userTask id="Activity_0d86poz" name="人事部" camunda:assignee="b">
<bpmn:incoming>Flow_1ri4jc5</bpmn:incoming>
<bpmn:outgoing>Flow_1huvtoa</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="Event_1vcg2jy">
<bpmn:incoming>Flow_0af8uo6</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0af8uo6" sourceRef="Activity_0ttkdt8" targetRef="Event_1vcg2jy" />
<bpmn:userTask id="Activity_0ttkdt8" name="财务部" camunda:assignee="n">
<bpmn:incoming>Flow_06enq8d</bpmn:incoming>
<bpmn:incoming>Flow_1huvtoa</bpmn:incoming>
<bpmn:outgoing>Flow_0af8uo6</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
</bpmn:definitions>