holiday-request.bpmn20.xml

Eave 2026.09.08

一、完整流程

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:flowable="http://flowable.org/bpmn"
             typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">
    <!--
    ============================================================
    每个节点「用什么事件 / 挂什么监听器」对照说明
    ============================================================
    ExecutionListener (start/end/take) → 流程引擎级别,任何节点都能挂
    TaskListener       (create/assignment/complete) → 仅 userTask

    执行顺序(以审批节点为例):
        start → assignment → create → [用户操作] → complete → end
    ============================================================
    -->
    <process id="holidayRequest" name="请假申请流程" isExecutable="true">
        <!--
        ============================================================
        【流程级】end 事件:整个流程无论走哪个 endEvent 结束都会触发
        ============================================================
        -->
        <extensionElements>
            <flowable:executionListener event="start" class="com.wizardaws.tools.flowable.listener.BmpExecutionListener"/>
            <flowable:executionListener event="end" class="com.wizardaws.tools.flowable.listener.BmpExecutionListener"/>
        </extensionElements>

        <!-- 1. 开始
        ============================================================
           - executionListener start : 初始化流程变量(approved=false 等)
           - executionListener end   : 流程正式启动后的记录
        ============================================================
        -->
        <startEvent id="startEvent" name="开始">
            <extensionElements>
                <flowable:executionListener event="start" delegateExpression="${initProcessListener}"/>
                <flowable:executionListener event="end" delegateExpression="${nodeNotifyListener}"/>
            </extensionElements>
        </startEvent>
        <sequenceFlow sourceRef="startEvent" targetRef="leaderApprove"/>

        <!-- 2. 直属领导审批
        ============================================================
           - executionListener start : 进入节点时动态算审批人 leaderId
           - taskListener assignment : 任务分配给审批人时通知
           - taskListener create    : 任务创建完成 → 通知审批人有待办
           - taskListener complete  : 审批人提交结果 → 记录/校验变量
           - executionListener end   : 节点离开前处理
        ============================================================
        -->
        <userTask id="leaderApprove" name="直属领导审批" flowable:assignee="${leaderId}">
            <extensionElements>
                <flowable:executionListener event="start" delegateExpression="${setApproverListener}"/>
                <flowable:taskListener event="assignment" class="com.wizardaws.tools.flowable.listener.BmpTaskListener"/>
                <flowable:taskListener event="create" class="com.wizardaws.tools.flowable.listener.BmpTaskListener"/>
                <flowable:taskListener event="complete" class="com.wizardaws.tools.flowable.listener.BmpTaskListener"/>
                <flowable:executionListener event="end" delegateExpression="${nodeNotifyListener}"/>
            </extensionElements>
        </userTask>
        <sequenceFlow sourceRef="leaderApprove" targetRef="leaderDecision"/>

        <!-- 2.1 直属领导审批结果判断 -->
        <exclusiveGateway id="leaderDecision" name="直属领导审批结果">
            <extensionElements>
                <flowable:executionListener event="start" delegateExpression="${nodeNotifyListener}"/>
            </extensionElements>
        </exclusiveGateway>
        <sequenceFlow sourceRef="leaderDecision" targetRef="rejectEnd">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${approved == false}]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow sourceRef="leaderDecision" targetRef="dayDecision">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${approved == true}]]>
            </conditionExpression>
        </sequenceFlow>

        <!-- 3. 请假天数判断网关 -->
        <exclusiveGateway id="dayDecision" name="请假天数判断"/>
        <sequenceFlow sourceRef="dayDecision" targetRef="taskEnd">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${days <= 3}]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow sourceRef="dayDecision" targetRef="directorApprove">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${days > 3}]]>
            </conditionExpression>
        </sequenceFlow>

        <!-- 4. 总监审批(>3天) -->
        <userTask id="directorApprove" name="总监审批" flowable:assignee="${directorId}"/>
        <sequenceFlow sourceRef="directorApprove" targetRef="directorDecision"/>

        <!-- 4.1 总监审批结果判断 -->
        <exclusiveGateway id="directorDecision" name="总监审批结果"/>
        <sequenceFlow sourceRef="directorDecision" targetRef="rejectEnd">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${approved == false}]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow sourceRef="directorDecision" targetRef="taskEnd">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${approved == true}]]>
            </conditionExpression>
        </sequenceFlow>

        <!-- 5. 统一通知
        ============================================================
           - executionListener start : 进入时记录/日志
           - executionListener end   : 邮件发送完成后处理
        ============================================================
        -->
        <serviceTask id="taskEnd" name="发送邮件通知" flowable:class="com.wizardaws.tools.flowable.delegate.TaskEndDelegate">
            <extensionElements>
                <flowable:executionListener event="start" delegateExpression="${nodeNotifyListener}"/>
                <flowable:executionListener event="end" delegateExpression="${nodeNotifyListener}"/>
            </extensionElements>
        </serviceTask>
        <sequenceFlow sourceRef="taskEnd" targetRef="approveEnd"/>

        <!-- 6. 结束 -->
        <endEvent id="approveEnd" name="审批通过结束"/>
        <endEvent id="rejectEnd" name="审批驳回结束"/>
    </process>
</definitions>

二、并行网关

并行网关(Parallel Gateway):所有分支都完成(到达汇聚网关)后,流程才继续往下走。

即:并行网关 = 分叉时同时走多条分支,汇聚时“等所有人都到齐”才放行。

<!-- 分叉 -->
<parallelGateway id="fork"/>

<sequenceFlow sourceRef="fork" targetRef="leaderApprove"/>
<sequenceFlow sourceRef="fork" targetRef="deptApprove"/>

<userTask id="leaderApprove" name="直属领导审批" flowable:assignee="${leaderId}"/>
<userTask id="deptApprove" name="部门负责人审批" flowable:assignee="${deptLeaderId}"/>

<sequenceFlow sourceRef="leaderApprove" targetRef="join"/>
<sequenceFlow sourceRef="deptApprove" targetRef="join"/>

<!-- 汇聚 -->
<parallelGateway id="join"/>
<sequenceFlow sourceRef="join" targetRef="dayDecision"/>

三、排他网关

排他网关(Exclusive Gateway)​:从多条出线里,只走第一条条件为true的分支;如果都false,走default分支;如果没配default且都false,直接抛异常。

即:排他网关 = if-else if-else,按XML顺序找第一个为true的出口,都不为true则走default,没default就报错。

<exclusiveGateway id="dayDecision" name="请假天数判断"/>
<sequenceFlow sourceRef="dayDecision" targetRef="approveEnd">
    <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${days <= 3}]]>
    </conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="dayDecision" targetRef="directorApprove">
    <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${days > 3}]]>
    </conditionExpression>
</sequenceFlow>

四、Multi-Instance会签

<userTask id="review" flowable:assignee="${assignee}" flowable:collection="${approvers}">
    <multiInstanceLoopCharacteristics isSequential="false" <!-- false=并行, true=串行 -->
                                      flowable:collection="approvers" <!-- 审批人集合变量 -->
                                      flowable:elementVariable="assignee"> <!-- 当前实例的审批人 -->
        <completionCondition>${rejected == true}</completionCondition>  <!-- 任一驳回即结束 -->
    </multiInstanceLoopCharacteristics>
</userTask>