Files
qhmes/.trae/skills/jeecg-bpmn/references/bpmn-subprocess-gateway.md

13 KiB
Raw Permalink Blame History

子流程与网关类型详解

1. 网关类型一览

用户描述 BPMN 类型 XML 元素 说明
分支/条件判断/通过或拒绝 排他网关 exclusiveGateway 只走一条满足条件的路径
并行/同时 并行网关 parallelGateway 所有路径同时执行,全部完成后汇聚
包含/部分并行 包含网关 inclusiveGateway 满足条件的路径都走,全部完成后汇聚

注意: JeecgBoot 设计器不支持事件网关eventBasedGateway实际只用上面三种。


2. 排他网关exclusiveGateway

只走一条路径,适用于"通过/拒绝"、"金额判断"等场景。

<bpmn2:exclusiveGateway id="gateway_result" name="审批结果" />

<!-- 条件连线 -->
<bpmn2:sequenceFlow id="flow_approve" name="通过" sourceRef="gateway_result" targetRef="task_next">
  <bpmn2:conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == 1}]]></bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="flow_reject" name="拒绝" sourceRef="gateway_result" targetRef="end">
  <bpmn2:conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == 0}]]></bpmn2:conditionExpression>
</bpmn2:sequenceFlow>

BPMNShape 排他网关需要 isMarkerVisible="true"

<bpmndi:BPMNShape id="shape_gateway" bpmnElement="gateway_result" isMarkerVisible="true">
  <dc:Bounds x="715" y="205" width="50" height="50" />
</bpmndi:BPMNShape>

3. 并行网关parallelGateway

所有路径同时执行必须成对使用fork + join。连线不需要条件表达式

<!-- 并行分支fork -->
<bpmn2:parallelGateway id="pgw_fork" name="并行开始" />

<!-- 并行任务 -->
<bpmn2:userTask id="task_a" name="部门A审批" flowable:assignee="admin" />
<bpmn2:userTask id="task_b" name="部门B审批" flowable:assignee="admin" />

<!-- 并行汇聚join -->
<bpmn2:parallelGateway id="pgw_join" name="并行汇聚" />

<!-- 连线(无条件) -->
<bpmn2:sequenceFlow id="flow_fork_a" sourceRef="pgw_fork" targetRef="task_a" />
<bpmn2:sequenceFlow id="flow_fork_b" sourceRef="pgw_fork" targetRef="task_b" />
<bpmn2:sequenceFlow id="flow_join_a" sourceRef="task_a" targetRef="pgw_join" />
<bpmn2:sequenceFlow id="flow_join_b" sourceRef="task_b" targetRef="pgw_join" />

4. 包含网关inclusiveGateway

满足条件的路径都会执行,也必须成对使用(分支 + 汇聚)。连线需要条件表达式

4.1 基本用法(来自生产环境:包含网关测试)

开始 → 领取体检单 → 包含网关1分支
  ├─ 普通员工 (user_type=='1') → 常规体检 ──┐
  ├─ 全部 (user_type=='1'||'2') → 抽血化验 → 领取早餐 ──┤→ 包含网关2汇聚→ 结束
  └─ 领导 (user_type=='2') → 深度体检 ──┘
<!-- 包含网关分支 -->
<bpmn2:inclusiveGateway id="igw_fork" name="包含网关分支" />

<!-- 带条件的连线 -->
<bpmn2:sequenceFlow id="flow_1" name="普通员工" sourceRef="igw_fork" targetRef="task_normal">
  <bpmn2:conditionExpression xsi:type="tFormalExpression">${user_type=='1'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="flow_2" name="全部" sourceRef="igw_fork" targetRef="task_blood">
  <bpmn2:conditionExpression xsi:type="tFormalExpression">${user_type=='1' || user_type=='2'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="flow_3" name="领导" sourceRef="igw_fork" targetRef="task_deep">
  <bpmn2:conditionExpression xsi:type="tFormalExpression">${user_type=='2'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>

<!-- 包含网关汇聚 -->
<bpmn2:inclusiveGateway id="igw_join" name="包含网关汇聚" />

<!-- 所有分支汇聚到同一个网关 -->
<bpmn2:sequenceFlow id="flow_join_1" sourceRef="task_normal" targetRef="igw_join" />
<bpmn2:sequenceFlow id="flow_join_2" sourceRef="task_breakfast" targetRef="igw_join" />
<bpmn2:sequenceFlow id="flow_join_3" sourceRef="task_deep" targetRef="igw_join" />

4.2 包含网关 + 直通路径(来自生产环境:督办流程)

包含网关的一个分支可以直接连到汇聚网关(不经过任何任务),实现"无风险时跳过"的效果:

部门负责人审核 → 包含网关(分支)
  ├─ 有风险 (iz_danger=='1') → 风控审计负责 ──┐
  ├─ 有风险 (iz_danger=='1') → 部门分管领导 ──┤→ 包含网关(汇聚)→ 结束
  └─ 无风险 (iz_danger=='0') ──────────────────┘
<bpmn2:inclusiveGateway id="Gateway_fork" />
<bpmn2:inclusiveGateway id="Gateway_join" />

<!-- 有风险:走两条并行路径 -->
<bpmn2:sequenceFlow id="flow_risk1" name="有风险" sourceRef="Gateway_fork" targetRef="task_audit">
  <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${iz_danger== '1' }</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="flow_risk2" name="有风险" sourceRef="Gateway_fork" targetRef="task_leader">
  <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${iz_danger== '1' }</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>

<!-- 无风险:直接到汇聚网关 -->
<bpmn2:sequenceFlow id="flow_norisk" name="无风险" sourceRef="Gateway_fork" targetRef="Gateway_join">
  <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${iz_danger=='0'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>

<!-- 汇聚 -->
<bpmn2:sequenceFlow id="flow_j1" sourceRef="task_audit" targetRef="Gateway_join" />
<bpmn2:sequenceFlow id="flow_j2" sourceRef="task_leader" targetRef="Gateway_join" />

4.3 包含网关 vs 排他网关 vs 并行网关

特性 排他网关 并行网关 包含网关
执行路径 只走1条 全部走 满足条件的都走
条件表达式 必需 不需要 必需
汇聚行为 等1个 等全部 等所有已激活的
适用场景 二选一/多选一 同时并行 条件并行

5. 内嵌子流程subProcess

内嵌子流程在主流程 XML 内部定义,拥有自己的开始和结束事件。适用于将一组相关节点打包为一个整体。

5.1 基本结构

<bpmn2:subProcess id="subprocess_1" name="审批子流程">
  <bpmn2:incoming>Flow_in</bpmn2:incoming>
  <bpmn2:outgoing>Flow_out</bpmn2:outgoing>

  <!-- 子流程内部的开始事件 -->
  <bpmn2:startEvent id="sub_start" />

  <!-- 子流程内部的用户任务 -->
  <bpmn2:userTask id="sub_task1" name="经理审批" flowable:assignee="admin" />
  <bpmn2:userTask id="sub_task2" name="财务审批" flowable:candidateGroups="finance" />

  <!-- 子流程内部的结束事件 -->
  <bpmn2:endEvent id="sub_end" />

  <!-- 子流程内部的连线 -->
  <bpmn2:sequenceFlow id="sub_flow1" sourceRef="sub_start" targetRef="sub_task1" />
  <bpmn2:sequenceFlow id="sub_flow2" sourceRef="sub_task1" targetRef="sub_task2" />
  <bpmn2:sequenceFlow id="sub_flow3" sourceRef="sub_task2" targetRef="sub_end" />
</bpmn2:subProcess>

5.2 完整示例(来自生产环境:测试嵌套子流程)

开始 → 入职(admin) → [内嵌子流程: 开始 → 经理(qinfeng) → 财务(vue3角色) → 结束] → 人力(admin角色) → 结束

布局说明: 内嵌子流程在图形上展开显示(isExpanded="true"),需要为整个子流程框指定 Bounds。

<!-- 子流程 Shape展开模式 -->
<bpmndi:BPMNShape id="shape_subprocess" bpmnElement="subprocess_1" isExpanded="true">
  <dc:Bounds x="280" y="45" width="650" height="185" />
</bpmndi:BPMNShape>

6. 调用子流程callActivity— 主子流程

调用子流程引用另一个独立部署的流程定义,主流程和子流程各自独立管理。

6.1 基本结构

<bpmn2:callActivity id="call_sub" name="调用子流程" calledElement="子流程的processKey">
  <bpmn2:extensionElements>
    <!-- 传入变量:主流程 → 子流程 -->
    <flowable:in source="applyUserId" target="applyUserId" />
    <flowable:in source="JG_LOCAL_PROCESS_ID" target="JG_SUB_MAIN_PROCESS_ID" />

    <!-- 传出变量:子流程 → 主流程 -->
    <flowable:out source="applyUserId" target="applyUserId" />
  </bpmn2:extensionElements>
</bpmn2:callActivity>

6.2 必传变量

变量 方向 说明
applyUserId in + out 流程发起人,子流程需要知道谁发起的
JG_LOCAL_PROCESS_IDJG_SUB_MAIN_PROCESS_ID in 主流程ID子流程需要关联回主流程

6.3 完整示例(来自生产环境:出差申请主子流程)

开始 → 主管领导 → 部门领导 → 排他网关
  ├─ 预支借款 (travel_expenses_type=='1') → 借款申请 → callActivity(调用joa_loan子流程) → 归档 → 结束
  └─ 个人垫付 (travel_expenses_type=='2') → 归档 → 结束
<callActivity id="callSubProcess" name="" calledElement="joa_loan">
  <extensionElements>
    <flowable:in source="apply_no" target="id" />
    <flowable:in source="applyUserId" target="applyUserId" />
    <flowable:in source="JG_LOCAL_PROCESS_ID" target="JG_SUB_MAIN_PROCESS_ID" />
    <flowable:out source="applyUserId" target="applyUserId" />
  </extensionElements>
</callActivity>

7. 会签子流程callActivity + multiInstance

会签子流程 = 调用子流程 + 多实例循环。每个审批人各执行一次完整的子流程。

7.1 结构

<bpmn2:callActivity id="call_countersign" name="会签子流程" calledElement="子流程processKey">
  <bpmn2:extensionElements>
    <flowable:in source="assigneeUserId" target="assigneeUserId" />
    <flowable:in source="applyUserId" target="applyUserId" />
    <flowable:in source="JG_LOCAL_PROCESS_ID" target="JG_SUB_MAIN_PROCESS_ID" />
    <flowable:out source="applyUserId" target="applyUserId" />
  </bpmn2:extensionElements>

  <!-- 多实例循环:每个审批人执行一次子流程 -->
  <bpmn2:multiInstanceLoopCharacteristics
    isSequential="true"
    flowable:collection="${flowUtil.stringToList(assigneeUserIdList)}"
    flowable:elementVariable="assigneeUserId" />
</bpmn2:callActivity>

7.2 完整示例(来自生产环境:主流程会签主子流程)

开始 → 主流程经理审批(候选人) → callActivity(会签子流程, 顺序执行) → 主流程总监审批 → 结束
<callActivity id="callSubProcess" name="" calledElement="subflow">
  <extensionElements>
    <flowable:in source="assigneeUserId" target="assigneeUserId" />
    <flowable:in source="applyUserId" target="applyUserId" />
    <flowable:in source="JG_LOCAL_PROCESS_ID" target="JG_SUB_MAIN_PROCESS_ID" />
    <flowable:out source="applyUserId" target="applyUserId" />
  </extensionElements>
  <multiInstanceLoopCharacteristics
    isSequential="true"
    flowable:collection="${flowUtil.stringToList(assigneeUserIdList)}"
    flowable:elementVariable="assigneeUserId" />
</callActivity>

关键区别:

  • 普通子流程:没有 multiInstanceLoopCharacteristics,只执行一次
  • 会签子流程:有 multiInstanceLoopCharacteristics,按 assigneeUserIdList 中的人数循环执行

8. 分支条件表达式配置

8.1 排他网关条件

排他网关的每条出线都需要条件表达式(除了默认路径):

<bpmn2:sequenceFlow id="flow_1" name="通过" sourceRef="gateway" targetRef="task_next">
  <bpmn2:conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == 1}]]></bpmn2:conditionExpression>
</bpmn2:sequenceFlow>

8.2 包含网关条件

包含网关的每条出线也需要条件表达式,可以多条同时满足:

<bpmn2:sequenceFlow id="flow_1" name="普通员工" sourceRef="igw" targetRef="task_a">
  <bpmn2:conditionExpression xsi:type="tFormalExpression">${user_type=='1'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="flow_2" name="全部" sourceRef="igw" targetRef="task_b">
  <bpmn2:conditionExpression xsi:type="tFormalExpression">${user_type=='1' || user_type=='2'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>

8.3 常用条件表达式写法

场景 表达式
审批通过 ${result == 1}
审批拒绝 ${result == 0}
字段等于值 ${field_name == 'value'}
字段不等于 ${field_name != 'value'}
数值比较 ${amount > 10000}
多条件 OR ${type=='1' || type=='2'}
多条件 AND ${type=='1' && level=='high'}
布尔判断 ${iz_danger == '1'}

注意: conditionExpression 中使用 xsi:type="tFormalExpression"(排他网关)或 xsi:type="bpmn2:tFormalExpression"(新版设计器),两种都可用。