【精选】Camunda 开源流程引擎学习笔记(三) |
您所在的位置:网站首页 › f4b2f0ea-3621-11ec-82a8-dac502259ad0jpg › 【精选】Camunda 开源流程引擎学习笔记(三) |
语雀原文链接: Camunda 开源流程引擎学习笔记(三)——实战 实战思路本文通过一个模拟的复杂流程, 全面演示 审批驳回重新打开流程自动化异常处理事件消息网关信号子流程DMN条件脚本(外部任务模式不可执行脚本)流程修改中间事件边界事件可选操作(待定) 拟订流程 提货单分解、审批(二级)、驳回、重新编辑提货单审批完成后,流程接受其它系统消息, 执行进场流程。(流程自动化)提供两种进场流程(子流程)使用DMN决定提货单执行哪个流程某一指令可以将流程修改为任一指定的节点.(流程修改)子流程, 任务节点, 均可消息事件 信号事件 BPMN规范与Camunda实现 实施步骤 画bpmn流程图部署流程图对于人工节点, 手动提交审核对于事件捕获节点, 通过编程的方式, 编写代码, 驱动流程前进 写模拟接口, 通过postman调用接口定时器事件, 定义具体日期,循环时间, 时间周期的格式, 参考: https://docs.camunda.org/manual/latest/reference/bpmn20/events/timer-events/ 程序结构 Camunda run 作为工作流引擎的服务器.app-demo作为应用服务, 它包含两个部分 控制台程序, 用于用户手动完成用户任务, 以及查询流程/任务状态. 此程序也可以用Camunda web app 替代.web 服务, 用于模拟外部事件, 触发应用提交工作流事件使用rest-client连接camunda服务器, 参考: https://github.com/camunda-community-hub/camunda-engine-rest-client-java/ org.camunda.community camunda-engine-rest-client-openapi-springboot 7.16.0-alpha1 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" }部署前: 返回结果: { "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 }该方法可以返回 @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 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 tasks = taskApi.queryTasks(0, 1, new TaskQueryDto().processInstanceId(instanceId).taskDefinitionKey(taskDefinitionKey)); TaskDto taskDto = null; if (tasks.size() == 1){ taskDto = tasks.get(0); } Map taskVariables = taskVariableApi.getTaskVariables(taskDto.getId(), true); prettyPrint(taskVariables, taskDto.getName() + " variables"); Map submit = taskApi.complete(taskDto.getId(), new CompleteTaskDto()); prettyPrint(submit, taskDto.getName() + "submit response"); }提交之后, 用user1登录camunda web, 显示当前任务已完成, 下一个任务分配给user2了. 显示, 指定的查询条件, 已经查询不到任务了, 说明这个方法查询的是等待中的任务. 第二个任务是分配给了user2, 我们用user1提交任务:
重新创建一个实例, 提交所有用户任务.此时查看流程状态: 查询结果: { "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 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: 发送后, 查询流程状态, 发现开始排队的外部任务居然处于激活状态: 完成外部任务后: 并行网关分开和汇聚都要有网关, 分开是发token, 汇聚时收token. 插入元素是一个比较常见的场景, 如下, 可以使用建模工具提供扩展空间工具在开始排队节点之前插入空间: 正确使用并行网关, 则必须三个事件都捕获之后才会执行到开始排队 执行完成后: 依次执行消息/外部服务, 直到事件网关处:
异常事件参考: error-events 异常事件通常指的是bpmn 异常是业务异常, 可以预定义的, 而非技术性异常. 当然, 在嵌入式模式下, 可以用java 异常代替bpmn异常. 提交异常前: Camunda 中的子流程允许基于可重用性和分组进行建模。以下是 Camunda 支持的不同类型的子流程: 嵌入式子流程调用子流程: 调用另一个独立的流程事件子流程事务子流程 嵌入式子流程https://docs.camunda.org/manual/latest/reference/bpmn20/subprocesses/embedded-subprocess/ 子流程有两个主要用例: 子流程允许分层建模。许多建模工具允许折叠子流程,隐藏子流程的所有细节并显示业务流程的高级、端到端概览。子流程为事件创建了一个新范围。在子流程执行期间抛出的事件可以被子流程边界上的边界事件捕获,从而为该事件创建一个范围,仅限于子流程。使用子流程确实会施加一些限制: 一个子流程只能有一个无启动事件,不允许有其他启动事件类型。一个子流程必须至少有一个结束事件。请注意,BPMN 2.0 规范允许在子流程中省略开始和结束事件,但当前的引擎实现不支持这一点。序列流不能跨越子流程边界。 正常流程嵌入式子流程和主流程有相同的变量作用域. 异常流程略 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |