7、Activiti-任务类型

1. 发送任务(Send Task)

        发送任务(Send Task)是Activiti BPMN中的一个重要任务类型,用于向外部系统发送消息或触发外部事件。

1.1. 特点

  • 单向通信:只发送消息,不等待响应
  • 自动执行:不需要人工干预
  • 无状态跟踪:引擎不跟踪外部系统是否接收/处理消息
  • 常用于:通知外部系统、触发外部流程、发送邮件/SMS等

1.1.1. 两个关键属性

1.1.1.1. type
  • 用法:
    • 用于指定 Activiti 内置的发送任务类型
    • type="mail":下发邮件通知
    • type="jms":JMS 消息发送
    • type="mule":Mule 集成
  • 使用场景:
    • 需要发送标准化的消息(如邮件)
    • 希望最小化编码工作
    • 使用Activiti内置支持的功能
1.1.1.2. operation
  • 用于自定义发送行为,需要开发者自己实现处理逻辑。
  • 需要注册自定义的 ActivityBehavior,继承AbstractBpmnActivityBehavior

1.2. 实现方法(以邮件通知为例)

1.2.1. 设置相关属性(见源码:MailActivityBehavior

属性

是否必填

默认值

说明

to

-

必须至少指定一个收件人,否则抛出 ActivitiException

from

×

从流程引擎配置的 mailServerDefaultFrom 或租户配置获取

如果未指定,使用全局或租户专用的默认发件人地址

cc

×

-

抄送地址(多个地址用逗号分隔)

bcc

×

-

密送地址(多个地址用逗号分隔)

subject

×

“”

邮件主题

text

textVar

html

htmlVar

√(四选一)

-

必须提供纯文本(text/textVar)或HTML内容(html/htmlVar),否则抛出异常

charset

×

默认字符集(如 UTF-8

邮件内容的字符编码

ignoreException

×

false

设为 true 时,发送失败不会中断流程,仅记录日志

exceptionVariableName

×

-

仅当 ignoreException=true 时有效,用于存储错误信息到流程变量

attachments

×

-

附件支持文件路径(String/File)或数据源(DataSource

1.2.2. BPMN配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="sendTaskProcess" name="发送任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_1c80gzw</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="Flow_1c80gzw" sourceRef="StartEvent_1" targetRef="Node1" />
    <bpmn2:sendTask id="Node1" name="发送任务节点" activiti:type="mail">
      <bpmn2:extensionElements>
        <activiti:field name="subject">
          <activiti:string>系统通知</activiti:string>
        </activiti:field>
        <activiti:field name="to">
          <activiti:expression>${recipientEmail}</activiti:expression>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>
        &lt;html&gt;
          &lt;head&gt;
            &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
          &lt;/head&gt;
          &lt;body&gt;
            ${recipientName} 您好,&lt;br/&gt;&lt;br/&gt;
            您的快递已有【丰巢】代收。如有疑问请联系配送人员【***,150********】确认,感谢您的购物,欢迎下次光临。&lt;br/&gt;
            &lt;b&gt;${now}&lt;/b&gt;&lt;br/&gt;
          &lt;/body&gt;
        &lt;/html&gt;

</activiti:expression>
        </activiti:field>
        <activiti:field name="charset">
          <activiti:string>UTF-8</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1c80gzw</bpmn2:incoming>
      <bpmn2:outgoing>Flow_09tzp42</bpmn2:outgoing>
    </bpmn2:sendTask>
    <bpmn2:endEvent id="Event_06jgzyu">
      <bpmn2:incoming>Flow_09tzp42</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_09tzp42" sourceRef="Node1" targetRef="Event_06jgzyu" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="sendTaskProcess">
      <bpmndi:BPMNEdge id="Flow_09tzp42_di" bpmnElement="Flow_09tzp42">
        <di:waypoint x="610" y="258" />
        <di:waypoint x="662" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1c80gzw_di" bpmnElement="Flow_1c80gzw">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="510" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0efcld4_di" bpmnElement="Node1">
        <dc:Bounds x="510" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_06jgzyu_di" bpmnElement="Event_06jgzyu">
        <dc:Bounds x="662" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

1.2.3. 配置项如下

spring:
  activiti:
    check-process-definitions: false
    database-schema-update: true
    history-level: full
    db-history-used: true
    mail-server-host: smtp.163.com
    mail-server-port: 25
    mail-server-use-ssl: false
    mail-server-use-tls: false
    mail-server-username: ***@163.com
    mail-server-password: DRP***GUUH
    mail-server-default-from: ***@163.com

1.2.4. 代码示例

package com.rqtanc.service.impl;

import com.rqtanc.service.SendTaskService;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * describe:activiti- send Task -mail
 *
 * @author rqtanc
 * @date 2025/5/1 0:28
 */

@Service
public class SendTaskServiceImpl implements SendTaskService {

    @Resource
    private ProcessRuntime processRuntime;

    @Override
    public void sendTaskByJavaDelegate() {

        Map<String, Object> variables = new HashMap<>();

        variables.put("recipientEmail", "***@foxmail.com");
        variables.put("recipientName", "谭先生");

        variables.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                                                     .start()
                                                     .withProcessDefinitionKey("sendTaskProcess")
                                                     .withName("邮件通知")
                                                     .withVariables(variables)
                                                     .build());
    }
}

2. 接收任务(Receive Task)

2.1. 概述:

        接收任务是一个简单的任务,用于等待特定消息的到达。当流程执行到达接收任务时,流程状态将被提交到持久化存储中。这意味着流程将一直处于等待状态,直到引擎收到特定消息,从而触发流程在接收任务之后继续执行。

2.2. 使用场景

  • 等待外部事件:流程需要等待外部系统的某个事件(如某个消息、信号等)才能继续执行。
  • 异步操作:流程会执行到某个节点,等待外部系统进行某些操作,然后再继续。
  • 长时间等待:某些场景下,流程需要等待用户输入、外部系统返回数据,或者某个操作完成后再继续执行。

2.3. 图形表示

2.4. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="ReceiveTaskProcess" name="接收任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_0j9083s</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="Flow_0j9083s" sourceRef="StartEvent_1" targetRef="ReceiveTaskNode" />
    <bpmn2:receiveTask id="ReceiveTaskNode" name="接收任务" messageRef="Message_07e4fif">
      <bpmn2:incoming>Flow_0j9083s</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0p2sgcb</bpmn2:outgoing>
    </bpmn2:receiveTask>
    <bpmn2:sequenceFlow id="Flow_0p2sgcb" sourceRef="ReceiveTaskNode" targetRef="sendTasksenNode" />
    <bpmn2:sendTask id="sendTasksenNode" name="发送任务节点" activiti:type="mail">
      <bpmn2:extensionElements>
        <activiti:field name="subject">
          <activiti:string>系统通知</activiti:string>
        </activiti:field>
        <activiti:field name="to">
          <activiti:expression>${recipientEmail}</activiti:expression>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>&lt;html&gt;
          &lt;head&gt;
            &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
          &lt;/head&gt;
          &lt;body&gt;
            ${recipientName} 您好,&lt;br/&gt;&lt;br/&gt;
            您的快递已有【丰巢】代收。如有疑问请联系配送人员【***,150********】确认,感谢您的购物,欢迎下次光临。&lt;br/&gt;
            &lt;b&gt;${now}&lt;/b&gt;&lt;br/&gt;
          &lt;/body&gt;
        &lt;/html&gt;</activiti:expression>
        </activiti:field>
        <activiti:field name="charset">
          <activiti:string>UTF-8</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_0p2sgcb</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0igp6k2</bpmn2:outgoing>
    </bpmn2:sendTask>
    <bpmn2:endEvent id="Event_1s7xfzr">
      <bpmn2:incoming>Flow_0igp6k2</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0igp6k2" sourceRef="sendTasksenNode" targetRef="Event_1s7xfzr" />
  </bpmn2:process>
  <bpmn2:message id="Message_07e4fif" name="message" />
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="ReceiveTaskProcess">
      <bpmndi:BPMNEdge id="Flow_0j9083s_di" bpmnElement="Flow_0j9083s">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0p2sgcb_di" bpmnElement="Flow_0p2sgcb">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="660" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0igp6k2_di" bpmnElement="Flow_0igp6k2">
        <di:waypoint x="760" y="258" />
        <di:waypoint x="822" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_02nmq2d_di" bpmnElement="ReceiveTaskNode">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_16kjuxt_di" bpmnElement="sendTasksenNode">
        <dc:Bounds x="660" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1s7xfzr_di" bpmnElement="Event_1s7xfzr">
        <dc:Bounds x="822" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

2.5. 逻辑处理

2.5.1. 启动流程实例

    public AjaxResult receiveTask() {
        ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("ReceiveTaskProcess")
                .withName("ReceiveTaskProcess")
                .build());

        return AjaxResult.success("启动成功,流程ID为:" + start.getId());
    }

流程实例启动成功后,在表act_ru_execution 中生成对应数据,等待信号接收使得流程往下执行。

2.5.2. 触发 Receive Task 继续执行

根据实际业务需要,调用 runtimeService.trigger(); 系列方法触发 Receive Task 继续执行。

    public String sendMessage() {
        
        String processInstanceId = "17cd3cc2-273d-11f0-b832-005056c00001"; // 已启动的流程实例 ID
        Execution execution = runtimeService.createExecutionQuery()
                .processInstanceId(processInstanceId)
                .activityId("ReceiveTaskNode") // 必须与BPMN中的接收任务ID一致
                .singleResult();
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("recipientEmail", "rq***@foxmail.com");
        variables.put("recipientName", "谭先生");
        variables.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        
        //触发 Receive Task 继续执行
        runtimeService.trigger(execution.getId(),variables);

        return "Message sent, process will continue.";
    }

3. 用户任务(User Task)

3.1. 概述

        用户任务是需要由人工参与者完成的任务。当流程执行到达用户任务时,流程会暂停并等待用户完成指定的工作。用户任务通常代表业务流程中需要人工干预或决策的环节。

3.2. 使用场景

  • 人工审批:需要人工审核并批准或拒绝的流程节点
  • 数据录入:需要用户填写表单或提供信息的环节
  • 任务分配:需要将工作分配给特定人员或组的场景
  • 多级审批:需要不同角色或人员依次审批的流程
  • 复杂决策:需要人工判断而非规则引擎自动判断的情况

3.3. 图形表示

3.4. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="userTaskProcess" name="用户任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_1otgqq9</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:userTask id="userTaskNode" name="用户任务" activiti:assignee="${handler}">
      <bpmn2:incoming>Flow_1otgqq9</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0x3iv4w</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="Flow_1otgqq9" sourceRef="StartEvent_1" targetRef="userTaskNode" />
    <bpmn2:endEvent id="Event_0qblm48">
      <bpmn2:incoming>Flow_0x3iv4w</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0x3iv4w" sourceRef="userTaskNode" targetRef="Event_0qblm48" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="userTaskProcess">
      <bpmndi:BPMNEdge id="Flow_0x3iv4w_di" bpmnElement="Flow_0x3iv4w">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="652" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1otgqq9_di" bpmnElement="Flow_1otgqq9">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_15q55ml_di" bpmnElement="userTaskNode">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_0qblm48_di" bpmnElement="Event_0qblm48">
        <dc:Bounds x="652" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

3.5. 逻辑处理

3.5.1. 启动流程实例

public String userTask() {
    Map<String, Object> variables = new HashMap<>();
    variables.put("handler", "admin");
    ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                                                 .start()
                                                 .withProcessDefinitionKey("userTaskProcess")
                                                 .withName("用户任务")
                                                 .withVariables(variables)
                                                 .build());
    return "启动成功,流程ID为:" + start.getId();
}

3.5.2. 预期结果

4. 手工任务(Manual Task)

4.1. 概述:

        手工任务是一个需要人工参与完成的任务,不涉及任何业务流程引擎的自动化处理。当流程执行到达手工任务时,流程会暂停并等待人工操作完成。手工任务通常用于那些无法或不需要自动化处理的步骤。

4.2. 使用场景

  • 需要人工干预的操作:流程中需要人工执行某些无法自动化的操作(如手工检查、物理操作等)
  • 特殊审批环节:需要人工判断和决策的关键节点
  • 数据录入:需要人工收集和录入信息,纸质文件签字的步骤

4.3. 图形表示

4.4. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="ManualTaskProcess" name="手工任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_1jno8vq</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sendTask id="sendTasksenNode" name="发送任务节点" activiti:type="mail">
      <bpmn2:extensionElements>
        <activiti:field name="subject">
          <activiti:string>系统通知</activiti:string>
        </activiti:field>
        <activiti:field name="to">
          <activiti:expression>${recipientEmail}</activiti:expression>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>&lt;html&gt;
            &lt;head&gt;
            &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
            &lt;/head&gt;
            &lt;body&gt;
            ${recipientName} 您好,&lt;br/&gt;&lt;br/&gt;
            您的快递已有【丰巢】代收。如有疑问请联系配送人员【***,150********】确认,感谢您的购物,欢迎下次光临。&lt;br/&gt;
            &lt;b&gt;${now}&lt;/b&gt;&lt;br/&gt;
            &lt;/body&gt;
            &lt;/html&gt;</activiti:expression>
        </activiti:field>
        <activiti:field name="charset">
          <activiti:string>UTF-8</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1h7qplw</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0igp6k2</bpmn2:outgoing>
    </bpmn2:sendTask>
    <bpmn2:endEvent id="Event_1s7xfzr">
      <bpmn2:incoming>Flow_0igp6k2</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0igp6k2" sourceRef="sendTasksenNode" targetRef="Event_1s7xfzr" />
    <bpmn2:sequenceFlow id="Flow_1jno8vq" sourceRef="StartEvent_1" targetRef="ManualTaskNode" />
    <bpmn2:manualTask id="ManualTaskNode" name="手工任务">
      <bpmn2:extensionElements>
        <activiti:executionListener class="com.rqtanc.listen.ManualTaskLinsten" event="end" />
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1jno8vq</bpmn2:incoming>
      <bpmn2:outgoing>Flow_1h7qplw</bpmn2:outgoing>
    </bpmn2:manualTask>
    <bpmn2:sequenceFlow id="Flow_1h7qplw" sourceRef="ManualTaskNode" targetRef="sendTasksenNode" />
  </bpmn2:process>
  <bpmn2:message id="Message_07e4fif" />
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="ManualTaskProcess">
      <bpmndi:BPMNEdge id="Flow_0igp6k2_di" bpmnElement="Flow_0igp6k2">
        <di:waypoint x="760" y="258" />
        <di:waypoint x="822" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1jno8vq_di" bpmnElement="Flow_1jno8vq">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1h7qplw_di" bpmnElement="Flow_1h7qplw">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="660" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_16kjuxt_di" bpmnElement="sendTasksenNode">
        <dc:Bounds x="660" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1s7xfzr_di" bpmnElement="Event_1s7xfzr">
        <dc:Bounds x="822" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1nybo2t_di" bpmnElement="ManualTaskNode">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

4.5. 逻辑处理

4.5.1. 配置监听器,判断手工任务执行是否完成

package com.rqtanc.listen;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;

/**
 * describe:手工任务执行监听器
 *
 * @author rqtanc
 * @date 2025/5/2 19:28
 */

public class ManualTaskLinsten implements ExecutionListener{

    @Override
    public void notify(DelegateExecution execution) {
        System.out.println("Manual Task 执行结束!");
    }
}

4.5.2. 启动流程实例

    public String manualTask() {

        Map<String, Object> variables = new HashMap<>();

        variables.put("recipientEmail", "rq***n@foxmail.com");
        variables.put("recipientName", "谭先生");

        variables.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("ManualTaskProcess")
                .withName("手工任务流程")
                .withVariables(variables)
                .build());

        return "启动成功,流程ID为:" +  start.getId();
    }

5. 业务规则任务(Business Rule Task)

5.1. 概述

业务规则任务是一种自动执行预定义业务规则的任务类型。它允许流程在执行过程中调用外部规则引擎或决策表,根据输入数据自动做出决策,而无需人工干预。

5.2. 使用场景

  • 自动化决策:根据业务规则自动做出流程路由决策
  • 复杂计算:执行复杂的费率计算、风险评估等
  • 合规检查:自动验证业务数据是否符合法规要求
  • 动态定价:根据多种因素自动计算产品价格
  • 风险评估:基于输入数据自动评估风险等级

5.3. 常见外部规则文件类型

类型

格式

适用场景

示例工具

决策表

Excel/CSV

基于条件的简单规则

Activiti决策表

DRL规则文件

.drl

复杂业务逻辑

Drools规则引擎

DMN标准

.dmn

标准化业务决策模型

Camunda DMN引擎

自定义规则脚本

JSON/XML

轻量级规则需求

自研规则引擎

5.4. 图形表示

5.5. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="BusinessRuleTaskProcess" name="业务规则任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_1jno8vq</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sendTask id="sendTasksenNode" name="发送任务节点" activiti:type="mail">
      <bpmn2:extensionElements>
        <activiti:field name="subject">
          <activiti:string>系统通知</activiti:string>
        </activiti:field>
        <activiti:field name="to">
          <activiti:expression>${recipientEmail}</activiti:expression>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>&lt;html&gt;
            &lt;head&gt;
            &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
            &lt;/head&gt;
            &lt;body&gt;
            ${recipientName} 您好,&lt;br/&gt;&lt;br/&gt;
            您的会员等级已变更为:${customerType}。本次订单金额为¥${orderAmount},${customerType}客户折扣率为:${discountRate},最终实收金额为¥${finalAmount}。感谢您的光临&lt;br/&gt;
            &lt;b&gt;${now}&lt;/b&gt;&lt;br/&gt;
            &lt;/body&gt;
            &lt;/html&gt;</activiti:expression>
        </activiti:field>
        <activiti:field name="charset">
          <activiti:string>UTF-8</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1h7qplw</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0igp6k2</bpmn2:outgoing>
    </bpmn2:sendTask>
    <bpmn2:endEvent id="Event_1s7xfzr">
      <bpmn2:incoming>Flow_0igp6k2</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0igp6k2" sourceRef="sendTasksenNode" targetRef="Event_1s7xfzr" />
    <bpmn2:sequenceFlow id="Flow_1jno8vq" sourceRef="StartEvent_1" targetRef="BusinessRuleTaskNode" />
    <bpmn2:sequenceFlow id="Flow_1h7qplw" sourceRef="BusinessRuleTaskNode" targetRef="sendTasksenNode" />
    <bpmn2:businessRuleTask id="BusinessRuleTaskNode" name="业务规则" activiti:class="com.rqtanc.delegate.DiscountRuleTask">
      <bpmn2:extensionElements>
        <activiti:field name="rulesFile">
          <activiti:string>rules/discount-rules.xlsx</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1jno8vq</bpmn2:incoming>
      <bpmn2:outgoing>Flow_1h7qplw</bpmn2:outgoing>
    </bpmn2:businessRuleTask>
  </bpmn2:process>
  <bpmn2:message id="Message_07e4fif" />
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="BusinessRuleTaskProcess">
      <bpmndi:BPMNEdge id="Flow_1h7qplw_di" bpmnElement="Flow_1h7qplw">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="660" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1jno8vq_di" bpmnElement="Flow_1jno8vq">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0igp6k2_di" bpmnElement="Flow_0igp6k2">
        <di:waypoint x="760" y="258" />
        <di:waypoint x="822" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_16kjuxt_di" bpmnElement="sendTasksenNode">
        <dc:Bounds x="660" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1s7xfzr_di" bpmnElement="Event_1s7xfzr">
        <dc:Bounds x="822" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1bldy9v_di" bpmnElement="BusinessRuleTaskNode">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

5.6. 逻辑处理

5.6.1. 基于 excel 决策表定义业务规则

根据 BMPN 流程定义内容” rules/discount-rules.xlsx“ 创建discount-rules.xlsx文件

5.6.2. 定义业务规则对象类

package com.rqtanc.domain;

/**
 * describe:业务规则
 *
 * @author rqtanc
 * @date 2025/5/2 21:46
 */

public class DiscountRule {

    private int priority;
    private String customerType;
    private double minAmount;
    private double maxAmount;
    private double discountRate;

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public String getCustomerType() {
        return customerType;
    }

    public void setCustomerType(String customerType) {
        this.customerType = customerType;
    }

    public double getMinAmount() {
        return minAmount;
    }

    public void setMinAmount(double minAmount) {
        this.minAmount = minAmount;
    }

    public double getMaxAmount() {
        return maxAmount;
    }

    public void setMaxAmount(double maxAmount) {
        this.maxAmount = maxAmount;
    }

    public double getDiscountRate() {
        return discountRate;
    }

    public void setDiscountRate(double discountRate) {
        this.discountRate = discountRate;
    }

    public boolean matches(String customerType, double amount) {
        return this.customerType.equals(customerType)
        && amount >= minAmount
        && amount <= maxAmount;
    }

    @Override
    public String toString() {
        return "DiscountRule{" +
        "priority=" + priority +
        ", customerType='" + customerType + '\'' +
        ", minAmount=" + minAmount +
        ", maxAmount=" + maxAmount +
        ", discountRate=" + discountRate +
        '}';
    }
}

5.6.3. 定义业务规则解析类

package com.rqtanc.utils;

import com.rqtanc.domain.DiscountRule;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * describe:规则解析
 *
 * @author rqtanc
 * @date 2025/5/2 21:48
 */
public class DiscountRuleParser {

    public static List<DiscountRule> parseRules(InputStream inputStream) {
        List<DiscountRule> rules = new ArrayList<>();

        try (Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);
            // 跳过表头
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;
                DiscountRule rule = new DiscountRule();
                rule.setPriority((int) row.getCell(0).getNumericCellValue());
                rule.setCustomerType(row.getCell(1).getStringCellValue());
                rule.setMinAmount(row.getCell(2).getNumericCellValue());
                rule.setMaxAmount(row.getCell(3).getNumericCellValue());
                rule.setDiscountRate(row.getCell(4).getNumericCellValue());
                rules.add(rule);
            }
        } catch (Exception e) {
            throw new RuntimeException("解析折扣规则失败", e);
        }
        return rules;
    }
}

5.6.4. 通过定义委托类实现BusinessRuleTaskDelegate 接口,实现自动解析业务规则

package com.rqtanc.delegate;

import com.rqtanc.domain.DiscountRule;
import com.rqtanc.utils.DiscountRuleParser;
import org.activiti.engine.delegate.BusinessRuleTaskDelegate;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.List;

/**
 * describe:执行业务规则任务委托类
 *
 * @author rqtanc
 * @date 2025/5/2 21:50
 */
@Component("discountRuleTask")
public class DiscountRuleTask implements BusinessRuleTaskDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        // 1、获取流程变量
        String customerType = (String) execution.getVariable("customerType");
        double orderAmount = (double) execution.getVariable("orderAmount");
        // 2、加载决策表
        try (InputStream is = new ClassPathResource("rules/discount-rules.xlsx").getInputStream()) {
            List<DiscountRule> rules = DiscountRuleParser.parseRules(is);
            //3、应用规则
            double discountRate = 0.0;
            for (DiscountRule rule : rules) {
                if (rule.matches(customerType, orderAmount)) {
                    discountRate = rule.getDiscountRate();
                    break;
                }
            }
            // 4、设置结果变量
            execution.setVariable("discountRate", discountRate);
            execution.setVariable("finalAmount", orderAmount * (1 - discountRate));
            //5、通知流程继续
            Context.getAgenda().planTakeOutgoingSequenceFlowsOperation((ExecutionEntity) execution, true);
        } catch (Exception e) {
            throw new RuntimeException("执行折扣规则失败", e);
        }
    }

    @Override
    public void addRuleVariableInputIdExpression(Expression inputId) {
    }

    @Override
    public void addRuleIdExpression(Expression inputId) {
    }

    @Override
    public void setExclude(boolean exclude) {
    }

    @Override
    public void setResultVariable(String resultVariableName) {
    }
}

5.6.5. 启动流程实例

    public String businessRuleTask() {
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("recipientEmail", "rq***@foxmail.com");
        variables.put("recipientName", "谭先生");
        variables.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        variables.put("customerType","VIP");
        variables.put("orderAmount",15000.0);
        
        ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("BusinessRuleTaskProcess")
                .withName("业务规则任务流程")
                .withVariables(variables)
                .build());

        return "启动成功,流程ID为:" +  start.getId();
    }

5.6.6. 预期结果

6. 服务任务(Service Task)

6.1. 概述

        服务任务是一种用于调用外部服务或执行自定义业务逻辑的自动化任务,更侧重于与外部系统交互或执行复杂业务操作。

6.2. 使用场景

  • 调用外部REST API
  • 执行数据库操作
  • 调用微服务
  • 执行复杂的业务计算
  • 与消息队列交互

6.3. 常见实现方式

类型

实现方式

适用场景

Java类

实现JavaDelegate接口

复杂业务逻辑

表达式

使用EL表达式

简单逻辑

外部调用

HTTP/REST/SOAP

调用外部服务

脚本

Groovy/JavaScript

动态逻辑

6.4. 图形表示

6.5. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="ServiceTaskProcess" name="服务任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_1jno8vq</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sendTask id="sendTasksenNode" name="发送任务节点" activiti:type="mail">
      <bpmn2:extensionElements>
        <activiti:field name="subject">
          <activiti:string>系统通知</activiti:string>
        </activiti:field>
        <activiti:field name="to">
          <activiti:expression>${recipientEmail}</activiti:expression>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>&lt;html&gt;
          &lt;head&gt;
            &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
          &lt;/head&gt;
          &lt;body&gt;
            ${recipientName} 您好,&lt;br/&gt;&lt;br/&gt;
            您的会员等级提升为为:&lt;b&gt;${customerType}&lt;/b&gt;等级。本次订单金额为¥${orderAmount},${customerType}客户折扣率为:${discountRate},最终实收金额为¥${finalAmount}。感谢您的光临!&lt;br/&gt;
            &lt;b&gt;${now}&lt;/b&gt;&lt;br/&gt;
          &lt;/body&gt;
        &lt;/html&gt;</activiti:expression>
        </activiti:field>
        <activiti:field name="charset">
          <activiti:string>UTF-8</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1h7qplw</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0igp6k2</bpmn2:outgoing>
    </bpmn2:sendTask>
    <bpmn2:endEvent id="Event_1s7xfzr">
      <bpmn2:incoming>Flow_0igp6k2</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0igp6k2" sourceRef="sendTasksenNode" targetRef="Event_1s7xfzr" />
    <bpmn2:sequenceFlow id="Flow_1jno8vq" sourceRef="StartEvent_1" targetRef="ServiceTaskNode" />
    <bpmn2:sequenceFlow id="Flow_1h7qplw" sourceRef="ServiceTaskNode" targetRef="sendTasksenNode" />
    <bpmn2:serviceTask id="ServiceTaskNode" name="服务任务" activiti:class="com.rqtanc.delegate.CalculateDiscountDelegate">
      <bpmn2:incoming>Flow_1jno8vq</bpmn2:incoming>
      <bpmn2:outgoing>Flow_1h7qplw</bpmn2:outgoing>
    </bpmn2:serviceTask>
  </bpmn2:process>
  <bpmn2:message id="Message_07e4fif" />
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="ServiceTaskProcess">
      <bpmndi:BPMNEdge id="Flow_1h7qplw_di" bpmnElement="Flow_1h7qplw">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="660" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1jno8vq_di" bpmnElement="Flow_1jno8vq">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0igp6k2_di" bpmnElement="Flow_0igp6k2">
        <di:waypoint x="760" y="258" />
        <di:waypoint x="822" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_16kjuxt_di" bpmnElement="sendTasksenNode">
        <dc:Bounds x="660" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1s7xfzr_di" bpmnElement="Event_1s7xfzr">
        <dc:Bounds x="822" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0qy5rti_di" bpmnElement="ServiceTaskNode">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

6.6. 逻辑处理

6.6.1. 通过定义委托类实现JavaDelegate 接口,完成自定义业务逻辑

package com.rqtanc.delegate;

import com.rqtanc.domain.DiscountRule;
import com.rqtanc.utils.DiscountRuleParser;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.List;

/**
 * describe:服务任务委托类-计算折扣
 *
 * @author rqtanc
 * @date 2025/5/3 23:08
 */

@Component
public class CalculateDiscountDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        // 1、获取流程变量
        String customerType = (String) execution.getVariable("customerType");
        double orderAmount = (double) execution.getVariable("orderAmount");
        // 2、加载决策表
        try (InputStream is = new ClassPathResource("rules/discount-rules.xlsx").getInputStream()) {
            List<DiscountRule> rules = DiscountRuleParser.parseRules(is);
            //3、计算折扣
            double discountRate = 0.0;
            for (DiscountRule rule : rules) {
                if (rule.matches(customerType, orderAmount)) {
                    discountRate = rule.getDiscountRate();
                    break;
                }
            }
            // 4、设置结果变量
            execution.setVariable("discountRate", discountRate);
            execution.setVariable("finalAmount", orderAmount * (1 - discountRate));
        } catch (Exception e) {
            throw new RuntimeException("执行折扣计算失败", e);
        }
    }
}

6.6.2. 启动流程实例

public String serviceTask() {
        Map<String, Object> variables = new HashMap<>();
        variables.put("recipientEmail", "rq***@foxmail.com");
        variables.put("recipientName", "谭先生");
        variables.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        variables.put("customerType","VIP");
        variables.put("orderAmount",15000.0);

        ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("ServiceTaskProcess")
                .withName("服务任务流程")
                .withVariables(variables)
                .build());

        return "启动成功,流程ID为:" +  start.getId();
    }

6.6.3. 预期结果

7. 脚本任务(Script Task)

7.1. 概述

        脚本任务是一种在流程执行过程中执行脚本代码的自动化任务,它允许直接在BPMN流程中嵌入脚本逻辑而无需编写外部类。

7.2. 使用场景

  • 执行简单的数据转换或计算
  • 动态设置流程变量
  • 快速原型开发
  • 需要灵活修改的业务规则
  • 执行脚本代码(如Groovy、JavaScript)实现逻辑。

7.3. 常见实现方式

类型

实现方式

适用场景

JavaScript

ECMAScript

浏览器兼容逻辑

Groovy

Groovy脚本

JVM环境复杂逻辑

Python

Jython

数据科学相关

Ruby

JRuby

Ruby生态集成

Shell

系统命令

服务器操作

7.4. 图形表示

7.5. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="ScriptTaskProcess" name="脚本任务流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_1jno8vq</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sendTask id="sendTasksenNode" name="发送任务节点" activiti:type="mail">
      <bpmn2:extensionElements>
        <activiti:field name="subject">
          <activiti:string>系统通知</activiti:string>
        </activiti:field>
        <activiti:field name="to">
          <activiti:expression>${recipientEmail}</activiti:expression>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>&lt;html&gt;
            &lt;head&gt;
            &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
            &lt;/head&gt;
            &lt;body&gt;
            ${recipientName} 您好,&lt;br/&gt;&lt;br/&gt;
            您的会员积分累计${sum},可用于兑换好礼。&lt;br/&gt;
            &lt;b&gt;${now}&lt;/b&gt;&lt;br/&gt;
            &lt;/body&gt;
            &lt;/html&gt;</activiti:expression>
        </activiti:field>
        <activiti:field name="charset">
          <activiti:string>UTF-8</activiti:string>
        </activiti:field>
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_1h7qplw</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0igp6k2</bpmn2:outgoing>
    </bpmn2:sendTask>
    <bpmn2:endEvent id="Event_1s7xfzr">
      <bpmn2:incoming>Flow_0igp6k2</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0igp6k2" sourceRef="sendTasksenNode" targetRef="Event_1s7xfzr" />
    <bpmn2:sequenceFlow id="Flow_1jno8vq" sourceRef="StartEvent_1" targetRef="ScriptTaskNode" />
    <bpmn2:sequenceFlow id="Flow_1h7qplw" sourceRef="ScriptTaskNode" targetRef="sendTasksenNode" />
    <bpmn2:scriptTask id="ScriptTaskNode" name="脚本任务" scriptFormat="javascript">
      <bpmn2:incoming>Flow_1jno8vq</bpmn2:incoming>
      <bpmn2:outgoing>Flow_1h7qplw</bpmn2:outgoing>
      <bpmn2:script>var sum = 0;
        for (var i = 0; i &lt; inputArray.length; i++) {
        sum += inputArray[i];
        }
        execution.setVariable("sum", sum);</bpmn2:script>
    </bpmn2:scriptTask>
  </bpmn2:process>
  <bpmn2:message id="Message_07e4fif" />
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="ScriptTaskProcess">
      <bpmndi:BPMNEdge id="Flow_1h7qplw_di" bpmnElement="Flow_1h7qplw">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="660" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1jno8vq_di" bpmnElement="Flow_1jno8vq">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0igp6k2_di" bpmnElement="Flow_0igp6k2">
        <di:waypoint x="760" y="258" />
        <di:waypoint x="822" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_16kjuxt_di" bpmnElement="sendTasksenNode">
        <dc:Bounds x="660" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1s7xfzr_di" bpmnElement="Event_1s7xfzr">
        <dc:Bounds x="822" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_006pvf8_di" bpmnElement="ScriptTaskNode">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

7.6. 逻辑处理

7.6.1. 启动流程实例

public String scriptTask() {
    // 1. 准备输入数组
    int[] inputArray = {100, 200, 300, 50}; // 示例积分数据
    Map<String, Object> variables = new HashMap<>();
    variables.put("inputArray", inputArray);
    variables.put("recipientEmail", "rqt***@foxmail.com");
    variables.put("recipientName", "谭先生");
    variables.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

    ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                                                 .start()
                                                 .withProcessDefinitionKey("ScriptTaskProcess")
                                                 .withName("脚本任务流程")
                                                 .withVariables(variables)
                                                 .build());

    return "启动成功,流程ID为:" +  start.getId();
}

7.6.2. 预期结果

8. 调用活动(Call Activity)

8.1. 概述

        调用活动是一种在流程中引用外部流程定义的活动类型,它允许流程设计者将复杂的流程分解为多个可重用的子流程。

8.2. 使用场景

  • 流程模块化和复用
  • 跨流程的公共逻辑封装
  • 复杂流程的层次化分解
  • 需要独立版本控制的子流程
  • 跨部门/系统的流程协作

8.3. 常见实现方式

类型

实现方式

适用场景

同步调用

嵌入式子流程

需要等待子流程完成的场景

异步调用

消息事件触发

不需要即时结果的场景

全局子流程

可被多个流程调用

公共业务逻辑

事务性子流程

带事务边界

需要原子性操作的场景

8.4. 图形表示

8.5. BPMN 配置示例

BPMN 示例见服务任务

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="CallActivityProcess" name="调用流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_0oemtd0</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:callActivity id="CallActivity" name="调用流程" calledElement="ServiceTaskProcess">
      <bpmn2:extensionElements>
        <activiti:in source="mainRecipientEmail" target="recipientEmail" />
        <activiti:in source="mainRecipientName" target="recipientName" />
        <activiti:in source="now1" target="now" />
        <activiti:in source="customerType1" target="customerType" />
        <activiti:in source="orderAmount1" target="orderAmount" />
      </bpmn2:extensionElements>
      <bpmn2:incoming>Flow_0oemtd0</bpmn2:incoming>
      <bpmn2:outgoing>Flow_1cwq2yq</bpmn2:outgoing>
    </bpmn2:callActivity>
    <bpmn2:sequenceFlow id="Flow_0oemtd0" sourceRef="StartEvent_1" targetRef="CallActivity" />
    <bpmn2:endEvent id="Event_1hshcq3">
      <bpmn2:incoming>Flow_1cwq2yq</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_1cwq2yq" sourceRef="CallActivity" targetRef="Event_1hshcq3" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="CallActivityProcess">
      <bpmndi:BPMNEdge id="Flow_1cwq2yq_di" bpmnElement="Flow_1cwq2yq">
        <di:waypoint x="600" y="258" />
        <di:waypoint x="652" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0oemtd0_di" bpmnElement="Flow_0oemtd0">
        <di:waypoint x="448" y="258" />
        <di:waypoint x="500" y="258" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="412" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1yf6s5s_di" bpmnElement="CallActivity">
        <dc:Bounds x="500" y="218" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1hshcq3_di" bpmnElement="Event_1hshcq3">
        <dc:Bounds x="652" y="240" width="36" height="36" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

8.6. 处理逻辑

8.6.1. 启动流程实例

public String callActivity() {
        Map<String, Object> variables = new HashMap<>();

        variables.put("mainRecipientEmail", "rq***@foxmail.com");
        variables.put("mainRecipientName", "谭先生");
        variables.put("customerType1", "VIP");
        variables.put("orderAmount1", 15000.0);
        variables.put("now1", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("CallActivityProcess")
                .withName("发送任务")
                .withVariables(variables)
                .build());
        return "启动成功,流程ID为:" + start.getId();
    }

9. 子流程(Sub-Processes)

9.1. 概述

        子流程是指包含其他活动、网关、事件等的活动,这些活动本身构成一个流程,并成为更大流程的一部分。子流程完全定义在父流程内部(因此通常被称为嵌入式子流程)。与调用活动(Call Activity)不同,嵌入式子流程与父流程共享相同的执行上下文和变量空间。其中折叠子流程:在图中显示为单个节点,简化流程视图。而展开子流程:显示子流程内部的具体步骤。

9.2. 主要特点

  • 共享上下文:与父流程共享相同的变量空间
  • 无独立流程定义:不像调用活动那样引用外部流程定义
  • 可包含事件:可以定义边界事件来处理异常情况
  • 事务性:可配置为事务性子流程(支持回滚)

9.3. 使用场景

  • 逻辑分组:将相关活动组织在一起提高可读性
  • 异常处理:通过边界事件统一处理一组活动的异常
  • 事务处理:需要原子性执行的一组活动
  • 循环处理:需要重复执行的一组活动
  • 条件处理:基于条件执行或跳过的一组活动

9.4. 图形表示

9.5. BPMN 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="Eval_Process" name="嵌套子流程" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1" name="start">
      <bpmn2:outgoing>Flow_032wxzj</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="Flow_032wxzj" sourceRef="StartEvent_1" targetRef="Activity_142xw7l" />
    <bpmn2:endEvent id="Event_0e2m0i8" name="end">
      <bpmn2:incoming>Flow_00o1hzq</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:subProcess id="Activity_142xw7l" name="下发至各部门">
      <bpmn2:incoming>Flow_032wxzj</bpmn2:incoming>
      <bpmn2:outgoing>Flow_00o1hzq</bpmn2:outgoing>
      <bpmn2:multiInstanceLoopCharacteristics activiti:collection="${userList}" activiti:elementVariable="user" />
      <bpmn2:startEvent id="Event_06f2phx" name="start">
        <bpmn2:outgoing>Flow_035atnw</bpmn2:outgoing>
      </bpmn2:startEvent>
      <bpmn2:userTask id="eval_01" name="部门填报" activiti:assignee="${user}">
        <bpmn2:documentation>capacity/process/department-filling</bpmn2:documentation>
        <bpmn2:incoming>Flow_035atnw</bpmn2:incoming>
        <bpmn2:outgoing>Flow_0lmnbqc</bpmn2:outgoing>
      </bpmn2:userTask>
      <bpmn2:sequenceFlow id="Flow_035atnw" sourceRef="Event_06f2phx" targetRef="eval_01" />
      <bpmn2:sequenceFlow id="Flow_0lmnbqc" sourceRef="eval_01" targetRef="Event_13jymbu" />
      <bpmn2:endEvent id="Event_13jymbu" name="end">
        <bpmn2:incoming>Flow_0lmnbqc</bpmn2:incoming>
      </bpmn2:endEvent>
    </bpmn2:subProcess>
    <bpmn2:sequenceFlow id="Flow_00o1hzq" sourceRef="Activity_142xw7l" targetRef="Event_0e2m0i8" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Eval_Process">
      <bpmndi:BPMNEdge id="Flow_00o1hzq_di" bpmnElement="Flow_00o1hzq">
        <di:waypoint x="1160" y="230" />
        <di:waypoint x="1252" y="230" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_032wxzj_di" bpmnElement="Flow_032wxzj">
        <di:waypoint x="308" y="230" />
        <di:waypoint x="490" y="230" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="272" y="212" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="279" y="255" width="23" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_0e2m0i8_di" bpmnElement="Event_0e2m0i8">
        <dc:Bounds x="1252" y="212" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1261" y="255" width="19" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1wa75n7_di" bpmnElement="Activity_142xw7l" isExpanded="true">
        <dc:Bounds x="490" y="120" width="670" height="238" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_0lmnbqc_di" bpmnElement="Flow_0lmnbqc">
        <di:waypoint x="730" y="240" />
        <di:waypoint x="972" y="240" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_035atnw_di" bpmnElement="Flow_035atnw">
        <di:waypoint x="566" y="240" />
        <di:waypoint x="630" y="240" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Event_06f2phx_di" bpmnElement="Event_06f2phx">
        <dc:Bounds x="530" y="222" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="538" y="265" width="23" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0zbak06_di" bpmnElement="eval_01">
        <dc:Bounds x="630" y="200" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_13jymbu_di" bpmnElement="Event_13jymbu">
        <dc:Bounds x="972" y="222" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="981" y="265" width="19" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

9.6. 处理逻辑

9.6.1. 启动流程实例

public String subProcess() {
    List<String> userList = new ArrayList<>();
    userList.add("admin");
    userList.add("zhugeliang");
    Map<String, Object> variables = new HashMap<>();
    variables.put("userList", userList);
    ProcessInstance start = processRuntime.start(ProcessPayloadBuilder
                                                 .start()
                                                 .withProcessDefinitionKey("Eval_Process")
                                                 .withName("子流程任务")
                                                 .withVariables(variables)
                                                 .build());
    return "启动成功,流程ID为:" + start.getId();
}

9.6.2. 预期结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值