语雀原文链接: Camunda 开源流程引擎学习笔记(三)——实战

实战思路

本文通过一个模拟的复杂流程, 全面演示

  • 审批
  • 驳回
  • 重新打开
  • 流程自动化
  • 异常处理
  • 事件
  • 消息
  • 网关
  • 信号
  • 子流程
  • DMN
  • 条件
  • 脚本(外部任务模式不可执行脚本)
  • 流程修改
  • 中间事件
  • 边界事件
  • 可选操作(待定)

拟订流程

  1. 提货单分解、审批(二级)、驳回、重新编辑
  2. 提货单审批完成后,流程接受其它系统消息, 执行进场流程。(流程自动化)
  3. 提供两种进场流程(子流程)
  4. 使用DMN决定提货单执行哪个流程
  5. 某一指令可以将流程修改为任一指定的节点.(流程修改)
  6. 子流程, 任务节点, 均可

消息事件
信号事件
BPMN规范与Camunda实现

实施步骤

  1. 画bpmn流程图
  2. 部署流程图
  3. 对于人工节点, 手动提交审核
  4. 对于事件捕获节点, 通过编程的方式, 编写代码, 驱动流程前进
    1. 写模拟接口, 通过postman调用接口

定时器事件, 定义具体日期,循环时间, 时间周期的格式, 参考: https://docs.camunda.org/manual/latest/reference/bpmn20/events/timer-events/

程序结构

  1. Camunda run 作为工作流引擎的服务器.
  2. app-demo作为应用服务, 它包含两个部分
  • 控制台程序, 用于用户手动完成用户任务, 以及查询流程/任务状态. 此程序也可以用Camunda web app 替代.
  • web 服务, 用于模拟外部事件, 触发应用提交工作流事件

使用rest-client连接camunda服务器, 参考: https://github.com/camunda-community-hub/camunda-engine-rest-client-java/

    <dependency>
      <groupId>org.camunda.community</groupId>
      <artifactId>camunda-engine-rest-client-openapi-springboot</artifactId>
      <version>7.16.0-alpha1</version>
    </dependency>
    
    camunda.bpm.client.base-url: http://localhost:8080/engine-rest

测试API

测试代码结构:

package com.hchw.camundademoapp;


import com.alibaba.fastjson.JSON;
import org.camunda.community.rest.client.api.DeploymentApi;
import org.camunda.community.rest.client.api.MessageApi;
import org.camunda.community.rest.client.api.ProcessDefinitionApi;
import org.camunda.community.rest.client.api.ProcessInstanceApi;
import org.camunda.community.rest.client.dto.*;
import org.camunda.community.rest.client.invoker.ApiClient;
import org.camunda.community.rest.client.invoker.ApiException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Date;

import static com.hchw.camundademoapp.config.Constant.TRANS_TASK_ID;

public class CamundaTest {
    ProcessDefinitionApi processDefinitionApi;
    DeploymentApi deploymentApi;
    ProcessInstanceApi processInstanceApi;
    MessageApi messageApi;


    @BeforeEach
    public void before(){
        ApiClient apiClient = new ApiClient();
        processDefinitionApi = new ProcessDefinitionApi();
        deploymentApi = new DeploymentApi();
        processDefinitionApi = new ProcessDefinitionApi();
        messageApi = new MessageApi();
    }


    @Test
    public void testCreateDeployment() throws FileNotFoundException, ApiException {
        DeploymentWithDefinitionsDto deployment = deploymentApi.createDeployment(null, null, true, true, "demo-process", new Date(), new File("C:\\code\\camunda\\camunda-demo-app\\src\\main\\resources\\demo-process.bpmn"));
        prettyPrint(deployment);
    }

    @Test
    public void testDeleteDeployment() throws FileNotFoundException, ApiException {
        String deployId = "09449dfe-d440-11ec-aa04-e89eb412cf47";

        deploymentApi.deleteDeployment(deployId, true, true, true);
    }

    private void prettyPrint(Object obj){
        System.out.println(JSON.toJSONString(obj, true));
    }

}

部署流程

   @Test
    public void testCreateDeployment() throws FileNotFoundException, ApiException {
        DeploymentWithDefinitionsDto deployment = deploymentApi.createDeployment(null, null, true, true, "demo-process", new Date(), new File("C:\\code\\camunda\\camunda-demo-app\\src\\main\\resources\\demo-process.bpmn"));
        prettyPrint(deployment);
    }

首次部署方法返回:

{
	"deployedProcessDefinitions":{
		"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47":{
			"category":"http://bpmn.io/schema/bpmn",
			"deploymentId":"0ac15197-d456-11ec-aa04-e89eb412cf47",
			"id":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
			"key":"Process_1ih13rv",
			"resource":"demo-process.bpmn",
			"startableInTasklist":true,
			"suspended":false,
			"version":1
		}
	},
	"deploymentTime":1652622655433,
	"id":"0ac15197-d456-11ec-aa04-e89eb412cf47",
	"links":[
		{
			"href":"http://localhost:8080/engine-rest/deployment/0ac15197-d456-11ec-aa04-e89eb412cf47",
			"method":"GET",
			"rel":"self"
		}
	],
	"name":"demo-process"
}

部署前:
image.png
部署后:
image.png
再次部署相同流程, 因为引擎中已有流程, 则直接返回已有流程的数据:
方法返回:

{
	"deploymentTime":1652614825541,
	"id":"cfc675ce-d443-11ec-aa04-e89eb412cf47",
	"links":[
		{
			"href":"http://localhost:8080/engine-rest/deployment/cfc675ce-d443-11ec-aa04-e89eb412cf47",
			"method":"GET",
			"rel":"self"
		}
	],
	"name":"demo-process"
}

创建流程

    @Test
    public void testStartProcessInstance() throws ApiException {
        String transTaskId = "T001";
        String definitionId = "Process_1ih13rv:1:cfcfebb0-d443-11ec-aa04-e89eb412cf47";

        StartProcessInstanceDto dto = new StartProcessInstanceDto();
        dto.putVariablesItem(TRANS_TASK_ID, new VariableValueDto().value(transTaskId).type("string"));

        ProcessInstanceWithVariablesDto instance = processDefinitionApi.startProcessInstance(definitionId, dto);

        prettyPrint(instance);
    }

返回结果:

{
	"definitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
	"ended":false,
	"id":"8437fc0a-d456-11ec-aa04-e89eb412cf47",
	"links":[
		{
			"href":"http://localhost:8080/engine-rest/process-instance/8437fc0a-d456-11ec-aa04-e89eb412cf47",
			"method":"GET",
			"rel":"self"
		}
	],
	"suspended":false
}

image.pngimage.pngimage.png

查询流程中的任务

使用ProcessInstanceApi.

该方法可以返回

    @Test
    public void testGetTasks() throws ApiException {
        String instanceId = "7fd73ea8-d444-11ec-aa04-e89eb412cf47";
//        taskApi.getTasks(null, null, instanceId,)
        ActivityInstanceDto activityInstanceTree = processInstanceApi.getActivityInstanceTree(instanceId);
        prettyPrint(activityInstanceTree);
    }
{
	"activityId":"Process_1ih13rv:1:cfcfebb0-d443-11ec-aa04-e89eb412cf47",
	"activityType":"processDefinition",
	"childActivityInstances":[
		{
			"activityId":"Activity_0i00a8u",
			"activityName":"提交提货单",
			"activityType":"userTask",
			"childActivityInstances":[],
			"childTransitionInstances":[],
			"executionIds":[
				"7fd73ea8-d444-11ec-aa04-e89eb412cf47"
			],
			"id":"Activity_0i00a8u:7fd78ccc-d444-11ec-aa04-e89eb412cf47",
			"incidentIds":[],
			"incidents":[],
			"parentActivityInstanceId":"7fd73ea8-d444-11ec-aa04-e89eb412cf47",
			"processDefinitionId":"Process_1ih13rv:1:cfcfebb0-d443-11ec-aa04-e89eb412cf47",
			"processInstanceId":"7fd73ea8-d444-11ec-aa04-e89eb412cf47"
		}
	],
	"childTransitionInstances":[],
	"executionIds":[
		"7fd73ea8-d444-11ec-aa04-e89eb412cf47"
	],
	"id":"7fd73ea8-d444-11ec-aa04-e89eb412cf47",
	"incidentIds":[],
	"incidents":[],
	"processDefinitionId":"Process_1ih13rv:1:cfcfebb0-d443-11ec-aa04-e89eb412cf47",
	"processInstanceId":"7fd73ea8-d444-11ec-aa04-e89eb412cf47"
}
使用TaskApi

这个api的参数也不封装一下…

    @Test
    public void testGetTasks3() throws ApiException {
        String instanceId = "8437fc0a-d456-11ec-aa04-e89eb412cf47";
        String taskDefinitionKey = TransProcessUserTasks.USER_1_SUBMIT_FORM.getDefinitionKey();
        List<TaskDto> tasks = taskApi.queryTasks(0, 1, new TaskQueryDto().processInstanceId(instanceId).taskDefinitionKey(taskDefinitionKey));
        prettyPrint(tasks, TransProcessUserTasks.USER_1_SUBMIT_FORM.getName());
    }

响应:

--------  提交提货单  --------
[
	{
		"assignee":"user1",
		"created":1652622859277,
		"executionId":"8437fc0a-d456-11ec-aa04-e89eb412cf47",
		"id":"8440fcbf-d456-11ec-aa04-e89eb412cf47",
		"name":"提交提货单",
		"priority":50,
		"processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
		"processInstanceId":"8437fc0a-d456-11ec-aa04-e89eb412cf47",
		"suspended":false,
		"taskDefinitionKey":"u-task-submit-form"
	}
]
--------  提交提货单  --------

完成用户任务

完成第一个用户任务
    @Test
    public void testUserTasks() throws ApiException {
        String userName = "user1";
        String expectTaskName = "提交提货单";
        String instanceId = "8437fc0a-d456-11ec-aa04-e89eb412cf47";
        String taskDefinitionKey = TransProcessUserTasks.USER_1_SUBMIT_FORM.getDefinitionKey();
        List<TaskDto> tasks = taskApi.queryTasks(0, 1, new TaskQueryDto().processInstanceId(instanceId).taskDefinitionKey(taskDefinitionKey));
        TaskDto taskDto = null;
        if (tasks.size() == 1){
            taskDto = tasks.get(0);
        }

        Map<String, VariableValueDto> taskVariables = taskVariableApi.getTaskVariables(taskDto.getId(), true);
        prettyPrint(taskVariables, taskDto.getName() + " variables");

        Map<String, VariableValueDto> submit = taskApi.complete(taskDto.getId(), new CompleteTaskDto());
        prettyPrint(submit, taskDto.getName() + "submit response");
    }

提交之后, 用user1登录camunda web, 显示当前任务已完成, 下一个任务分配给user2了.
image.png
用user2登录, user2可以操作这个任务:
image.png

再次查询第一个任务

显示, 指定的查询条件, 已经查询不到任务了, 说明这个方法查询的是等待中的任务.
image.png
使用第二个用户任务的definitionKey查询, 则可以查询出结果:
image.png

提交第二个用户任务

第二个任务是分配给了user2, 我们用user1提交任务:
image.png
此处报错, 是因为第二个任务后的排他网关, 需要一个firstApproved的参数, 用于判断下一步执行. 因此, 提交任务时, 应同时提交这个变量.
增加变量后, 成功提交该任务, 说明使用api完成任务, 引擎是不检查用户的, 用户和其它变量一样, 只是一个特殊的变量.
image.png
此时, 使用user2访问camunda web, 出现错误提示. 但流程已经流转到第三节点了.

image.png

驳回第三用户任务

image.png
查看web,已经查不到流程实例了, camunda默认不展示流程历史.流程审计是商业版的功能, 想要相关功能只能定制开发了.

查看历史实例

image.png

提交第三任务

重新创建一个实例, 提交所有用户任务.此时查看流程状态:
image.png
通过代码查询流程活动实例树:

@Test
public void testGetTasks() throws ApiException {
    String instanceId = "7b2b9935-d45b-11ec-aa04-e89eb412cf47";
    ActivityInstanceDto activityInstanceTree = processInstanceApi.getActivityInstanceTree(instanceId);
    prettyPrint(activityInstanceTree);
}

查询结果:

{
  "activityId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
  "activityType":"processDefinition",
  "childActivityInstances":[
    {
      "activityId":"Event_tidan_date",
      "activityName":"到达提单日期",
      "activityType":"intermediateMessageCatch",
      "childActivityInstances":[],
      "childTransitionInstances":[],
      "executionIds":[
        "78f059db-d45c-11ec-aa04-e89eb412cf47"
      ],
      "id":"Event_tidan_date:78f0a7fd-d45c-11ec-aa04-e89eb412cf47",
      "incidentIds":[],
      "incidents":[],
      "parentActivityInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
      "processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
      "processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
    }
  ],
  "childTransitionInstances":[],
  "executionIds":[
    "7b2b9935-d45b-11ec-aa04-e89eb412cf47"
  ],
  "id":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
  "incidentIds":[],
  "incidents":[],
  "processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
  "processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
}

消息事件

发送消息事件:到达提货日期
@Test
public void testSendMsg() throws ApiException {
    String instanceId = "7b2b9935-d45b-11ec-aa04-e89eb412cf47";
    String transTaskId = "T001";
    CorrelationMessageDto dto = new CorrelationMessageDto();
    dto.processInstanceId(instanceId)
        .messageName(TransProcessMessages.TO_TRANS_DATE.getMsgName(transTaskId));
    List<MessageCorrelationResultWithVariableDto> messageCorrelationResultWithVariableDtos = messageApi.deliverMessage(dto);
    prettyPrint(messageCorrelationResultWithVariableDtos, "消息返回");
    
}

发送消息后, 流程执行到下一个事件处:
image.png
使用代码查询流程状态:

{
  "activityId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
  "activityType":"processDefinition",
  "childActivityInstances":[
    {
      "activityId":"Event_17g9vf1",
      "activityName":"车辆进入工厂",
      "activityType":"intermediateMessageCatch",
      "childActivityInstances":[],
      "childTransitionInstances":[],
      "executionIds":[
        "a6e97f1a-d45d-11ec-aa04-e89eb412cf47"
      ],
      "id":"Event_17g9vf1:a6e9cd3c-d45d-11ec-aa04-e89eb412cf47",
      "incidentIds":[],
      "incidents":[],
      "parentActivityInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
      "processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
      "processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
    }
  ],
  "childTransitionInstances":[],
  "executionIds":[
    "7b2b9935-d45b-11ec-aa04-e89eb412cf47"
  ],
  "id":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
  "incidentIds":[],
  "incidents":[],
  "processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
  "processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
}
发送消息: 车辆进入工厂
@Test
public void testSendMsg2() throws ApiException {
    String instanceId = "7b2b9935-d45b-11ec-aa04-e89eb412cf47";
    String transTaskId = "T001";
    CorrelationMessageDto dto = new CorrelationMessageDto();
    dto.processInstanceId(instanceId)
        .messageName(TransProcessMessages.TO_FACTORY.getMsgName(transTaskId));
    List<MessageCorrelationResultWithVariableDto> messageCorrelationResultWithVariableDtos = messageApi.deliverMessage(dto);
    prettyPrint(messageCorrelationResultWithVariableDtos, "消息返回");
}

发送成功后再次查询流程状态, 可以看到, 流程活动实例有3个, 都是中间捕获事件:

{
	"activityId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
	"activityType":"processDefinition",
	"childActivityInstances":[
		{
			"activityId":"Event_0n7ke7r",
			"activityName":"接收安检结果",
			"activityType":"intermediateMessageCatch",
			"childActivityInstances":[],
			"childTransitionInstances":[],
			"executionIds":[
				"53c42334-d45e-11ec-aa04-e89eb412cf47"
			],
			"id":"Event_0n7ke7r:53c42336-d45e-11ec-aa04-e89eb412cf47",
			"incidentIds":[],
			"incidents":[],
			"parentActivityInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
			"processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
			"processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
		},
		{
			"activityId":"Event_0r5kmd0",
			"activityName":"接收空车过磅数据",
			"activityType":"intermediateMessageCatch",
			"childActivityInstances":[],
			"childTransitionInstances":[],
			"executionIds":[
				"53c3fc21-d45e-11ec-aa04-e89eb412cf47"
			],
			"id":"Event_0r5kmd0:53c42333-d45e-11ec-aa04-e89eb412cf47",
			"incidentIds":[],
			"incidents":[],
			"parentActivityInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
			"processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
			"processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
		},
		{
			"activityId":"Event_1ssfe9w",
			"activityName":"司机报道",
			"activityType":"intermediateMessageCatch",
			"childActivityInstances":[],
			"childTransitionInstances":[],
			"executionIds":[
				"53c44a47-d45e-11ec-aa04-e89eb412cf47"
			],
			"id":"Event_1ssfe9w:53c47159-d45e-11ec-aa04-e89eb412cf47",
			"incidentIds":[],
			"incidents":[],
			"parentActivityInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
			"processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
			"processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
		}
	],
	"childTransitionInstances":[],
	"executionIds":[
		"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
		"53c3adfe-d45e-11ec-aa04-e89eb412cf47",
		"53c3fc1f-d45e-11ec-aa04-e89eb412cf47",
		"53c3fc20-d45e-11ec-aa04-e89eb412cf47"
	],
	"id":"7b2b9935-d45b-11ec-aa04-e89eb412cf47",
	"incidentIds":[],
	"incidents":[],
	"processDefinitionId":"Process_1ih13rv:1:0ad500a9-d456-11ec-aa04-e89eb412cf47",
	"processInstanceId":"7b2b9935-d45b-11ec-aa04-e89eb412cf47"
}

查询web:
image.png

发送消息: 接收空车过磅数据

发送后, 查询流程状态, 发现开始排队的外部任务居然处于激活状态:
image.png
查询web:
image.png
尝试完成开始排队任务:

    @Test
    public void testExtService() throws ApiException {
        String definitionKey = "Process_1ih13rv";
        String workId = "the-only-worker";
        List<LockedExternalTaskDto> lockedExternalTaskDtos = externalTaskApi.fetchAndLock(new FetchExternalTasksDto()
                .maxTasks(1)
                .workerId(workId)
                .topics(Collections.singletonList(new FetchExternalTaskTopicDto()
                        .topicName(TransProcessExtTasks.EXT_1_START_QUEUE.getTopic())
                        .processDefinitionKey(definitionKey)
                        .lockDuration(1000 * 1L)
                ))
        );

        prettyPrint(lockedExternalTaskDtos);

        for (LockedExternalTaskDto dto: lockedExternalTaskDtos){
            externalTaskApi.completeExternalTaskResource(dto.getId(), new CompleteExternalTaskDto().workerId(workId));
        }

    }

完成外部任务后:
image.png

修改并行网关

并行网关分开和汇聚都要有网关, 分开是发token, 汇聚时收token.
插入元素是一个比较常见的场景, 如下, 可以使用建模工具提供扩展空间工具在开始排队节点之前插入空间:
image.png
如下图, 增加一个汇聚网关
注意: 此处建模工具有个坑, 把网关拖动到开始排队节点之后, 一定要把网关和**开始排队**的连接线删干净, 然后重新连接, 不然可能会存在中间事件直接连接到**开始排队**节点的情况, 由于线条重合而无法注意到.
image.png

重新部署流程, 并执行到并行网关处

正确使用并行网关, 则必须三个事件都捕获之后才会执行到开始排队
image.png

外部任务

执行外部任务
    @Test
    public void extService() throws ApiException {
        List<LockedExternalTaskDto> lockedExternalTaskDtos = externalTaskApi.fetchAndLock(new FetchExternalTasksDto()
                .maxTasks(1)
                .workerId(workId)
                .topics(Collections.singletonList(new FetchExternalTaskTopicDto()
                        .topicName(TransProcessExtTasks.EXT_1_START_QUEUE.getTopic())
                        .processDefinitionKey(definitionKey)
                        .lockDuration(1000 * 1L)
                ))
        );
        prettyPrint(lockedExternalTaskDtos);
        for (LockedExternalTaskDto dto: lockedExternalTaskDtos){
            externalTaskApi.completeExternalTaskResource(dto.getId(), new CompleteExternalTaskDto().workerId(workId));
        }
    }

执行完成后:
image.png

事件网关

事件网关: 时钟事件

依次执行消息/外部服务, 直到事件网关处:

image.png
一分钟后, 定时器事件自动执行完成, 进入下一节点:
image.png

异常事件

异常事件参考: error-events
异常事件通常指的是bpmn 异常是业务异常, 可以预定义的, 而非技术性异常. 当然, 在嵌入式模式下, 可以用java 异常代替bpmn异常.
image.png
注意: 中间异常和结束异常, 都需要订阅异常编码和异常消息:
image.png

提交异常
    @Test
    public void extService6() throws ApiException {
        completeExtServiceWithError(ErrorProcessExtTasks.EXT_1_CALL_OTHER_SERVICE, ErrorProcessError.EXT_1_CALL_OTHER_SERVICE);
    }
    private void completeExtServiceWithError(ExtTask extTask, ErrorProcessError err) throws ApiException {
        List<LockedExternalTaskDto> lockedExternalTaskDtos = externalTaskApi.fetchAndLock(new FetchExternalTasksDto()
                .maxTasks(1)
                .workerId(workId)
                .topics(Collections.singletonList(new FetchExternalTaskTopicDto()
                        .topicName(extTask.getTopic())
                        .processDefinitionKey(definitionKey)
                        .lockDuration(1000 * 1L)
                ))
        );
        prettyPrint(lockedExternalTaskDtos);
        for (LockedExternalTaskDto dto: lockedExternalTaskDtos){
            externalTaskApi.handleExternalTaskBpmnError(dto.getId(),
                    new ExternalTaskBpmnError()
                    .errorCode(err.getCode())
                    .errorMessage(err.getMsg())
                    .workerId(workId)
            );
        }
    }

提交异常前:
image.png
提交异常后:
image.png

子流程

Camunda 中的子流程允许基于可重用性和分组进行建模。以下是 Camunda 支持的不同类型的子流程:

  • 嵌入式子流程
  • 调用子流程: 调用另一个独立的流程
  • 事件子流程
  • 事务子流程
嵌入式子流程

https://docs.camunda.org/manual/latest/reference/bpmn20/subprocesses/embedded-subprocess/

子流程有两个主要用例:

  • 子流程允许分层建模。许多建模工具允许折叠子流程,隐藏子流程的所有细节并显示业务流程的高级、端到端概览。
  • 子流程为事件创建了一个新范围。在子流程执行期间抛出的事件可以被子流程边界上的边界事件捕获,从而为该事件创建一个范围,仅限于子流程。

使用子流程确实会施加一些限制:

  • 一个子流程只能有一个无启动事件,不允许有其他启动事件类型。一个子流程必须至少有一个结束事件。请注意,BPMN 2.0 规范允许在子流程中省略开始和结束事件,但当前的引擎实现不支持这一点。
  • 序列流不能跨越子流程边界。
正常流程

image.png

image.png

image.png

image.png

嵌入式子流程和主流程有相同的变量作用域.

异常流程

image.png

image.png

调用子流程

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐