蘇阿細 - Camunda
type: channel
link: https://wangchouchou.com/tag/Camunda/
items: 动态表单
items: 任务回退
items: 多人会签
items: 事件
items: 网关
items: 租户
items: 身份服务
items: 流程变量
items: 任务分配
items: SpringBoot整合Camunda

动态表单
type: item
date: 2024-08-21T13:44:00+00:00
description: 1. 内置表单流程图测试package com.sw.camundademo; import org.camunda.bpm.engine.FormService; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.form.FormField; import org.camunda.bpm.engine.form.StartFormData; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author Suaxi * @Date 2024/8/20 21:02 * @Description */ @SpringBootTest public class FlowFormsTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Autowired private FormService formService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("动态表单-内置表单") .addClasspathResource("flow/40.动态表单-内置表单.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void getDeploymentData() { String processId = "Process_0fc8vlb:1:791e3a95-5ef4-11ef-9b8d-10ffe00abd05"; StartFormData startFormData = formService.getStartFormData(processId); System.out.println("FormKey: " + startFormData.getFormKey()); List<FormField> formFieldList = startFormData.getFormFields(); if (formFieldList != null && formFieldList.size() > 0) { for (FormField formField : formFieldList) { System.out.println(formField.getId() + " - " + formField.getLabel() + ": " + formField.getValue().getValue()); } } } @Test public void startFlow() { String processId = "Process_0fc8vlb:1:791e3a95-5ef4-11ef-9b8d-10ffe00abd05"; Map<String, Object> map = new HashMap<>(); map.put("reason", "有事要请假！"); map.put("days", 3); runtimeService.startProcessInstanceById(processId, map); } @Test public void startFormFlow() { String processId = "Process_0fc8vlb:1:791e3a95-5ef4-11ef-9b8d-10ffe00a…

任务回退
type: item
date: 2024-08-15T14:14:00+00:00
description: 1. 串行的回退流程图测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @Author Suaxi * @Date 2024/8/13 21:27 * @Description */ @SpringBootTest public class FlowRollbackTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("任务回退-串行的回退") .addClasspathResource("flow/35.任务回退-串行的回退.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startFlow() { String processId = "Process_15y1ksp:1:f147328e-5977-11ef-8eb4-10ffe00abd05"; runtimeService.startProcessInstanceById(processId); } @Test public void taskRollback() { String processInstanceId = "076d6028-597c-11ef-98e6-10ffe00abd05"; runtimeService.createProcessInstanceModification(processInstanceId) //.startBeforeActivity("Activity_0l16yzx") //跳转到指定结点之前 .startAfterActivity("Activity_0l16yzx") //跳转到指定结点之后 .cancelAllForActivity("Activity_0azdt7x") //取消某个指定节点 .execute(); } } 此处以人事审批节点回退至项目经理审批节点为例，执行回退方法后，历史任务表数据如下补充：类别方法在活动前启动回退到这个节点 startBeforeActivity(String activityId); startBeforeActivity(String activityId, String ancestorActivityInstanceId);在活动后启动通过 startAfterActivity() 方法，跳转到指定节点之后的节点 startAfterActivity(String activityId); startAfterActivity(String activityId, String ancestorActivityInstanceId);启动一个过渡通过 startTransition() 方法，在一个指定的序列流上开始执行，当有多个流出的序列流时，此时可以与 startAfterActivity() 方法一起使用 startTransition(String activityId); startTransition(String activityId, String ancestorActivityInstanceId);取消活动实例通过 cancelActivityInstance() 方法取消指定的活动实例 cancelActivityInstance(String activityInstanceId);取消过渡实例过渡实例表示即将以异步延续的形式进入/离开一个活动的执行流，一个已经创建但尚未执行的异步延续Job被表示为一个过渡实例，可以通过 cancelTransitionInstance()…

多人会签
type: item
date: 2024-08-13T14:08:00+00:00
description: 1. 概念多实例活动是为业务流程中的某个步骤定义重复的一种方式，在编程概念中，多实例与 foreach 结构相匹配：它允许对给定集合中的每个项目按顺序或并行地执行某个步骤或一个完整的子流程多实例是一个有额外属性的常规活动，它将导致该活动在运行时被多次执行，以下活动可成为多实例活动：Service Task 服务任务Send Task 发送任务User Task 用户任务Business Rule Task 业务规则任务Script Task 脚本任务Receive Task 接收任务Manual Task 手动任务（Embedded）Sub-Process （嵌入）子流程Call Activity 发起活动Transaction Subprocess 事务子流程注：网关或事件不能用于多实例中如果一个活动是多实例的，这将由活动底部的三条短线表示：三条垂直线 - 并行，三条竖线 - 顺序执行按照规范，每个实例所创建的执行的每个父执行将有以下变量：nrOfInstances：实例总数量nrOfActiveInstances：当前活动的（且未完成）的实例数量，在顺序执行中这个数量恒为1nrOfCompletedInstances：已完成的实例数量这些值可以通过 execution.getVariables() 方法获取此外，每个创建的执行将有一个执行本地变量（即对其他执行不可见，且不存储在流程实例级别）loopCounter：表示该特定实例的 foreach 循环中的索引为了使一个活动实例成为多实例，活动 xml 元素必须有一个 multiInstanceLoopCharacteristics 子元素<multiInstanceLoopCharacteristics isSequential="false|true"> ...... </multiInstanceLoopCharacteristics>2. 并行会签-案例一流程图测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author Suaxi * @Date 2024/8/8 22:25 * @Description */ @SpringBootTest public class FlowMultiInstanceTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("多人会签-并行会签-案例一") .addClasspathResource("flow/32.多人会签-并行会签-案例一.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startFlow() { String processId = "Process_0riwap9:1:302f5906-5592-11ef-a63f-10ffe00abd05"; Map<String, Object> variables = new HashMap<>(); variables.put("persons", Arrays.asList("admin", "zhangsan", "lisi")); runtimeService.startProcessInstanceById(processId, variables); } } 启动流程后，可看到有三个待审批的并…

事件
type: item
date: 2024-08-05T14:18:00+00:00
description: 事件（event）通常用于为流程生命周期中发生的事情建模，事件总是图形化为圆圈。在BPMN2.0中，主要分为：捕获（catching）与抛出（throwing）事件。捕获：当流程执行到达这个事件时，会等待直到触发器执行，触发器类型由XML中的类型声明定义抛出：当流程执行到达这个事件时，会触发对应的触发器1. 定时器事件定时触发相关的事件：定时器启动事件、定时器捕获中间事件、定时器边界事件（1）定时器启动事件定时器启动事件（timer start event）在指定的时间创建流程实例，可用于流程只需要启动一次，或流程需要在特定的时间间隔重复启动等场景。其中需要注意：子流程不能有定时器启动事件在流程部署时就开始计时，不需要单独调用 startProcessInstanceByxxx() 方法来启动，当调用该方法时，会在定时启动的基础上再额外启动一个对应的流程当部署带有定时器启动事件的流程更新版本时，上一个版本的定时器作业会被清除，即Camunda流程引擎不希望旧版本的流程仍然自动启动更新之后的新版本流程实例a. Date 固定时间点触发ISO8601相关内容：https://zh.wikipedia.org/wiki/ISO_8601当该流程部署后，在任务表中能看到对应的固定时间点启动事件任务b. Duration 间隔时间触发同理，流程部署后可在任务表看到对应的任务记录c. Cycle重复多次触发注：timeCycle 表达式包含可选属性 EndDate ，即 R3/PT1M/${EndDate} 表示执行过程中到达 EndDate 指定的时间时应用停止运行，并为该任务执行其他作业（2）定时器中间事件当组长审批完成之后，流程引擎会在任务记录表中创建对应的中间捕获事件对应的任务记录（3）定时器边界事件流程启动后等待一分钟不进行任何操作，等待边界事件触发如果在流程启动后正常进行组长审批，则边界事件不会触发，流程正常走向“项目经理1”审批2. 消息事件（1）启动事件在接收到某些消息后来启动对应的流程实例，消息类型包括不限于邮件、短信等等测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @Author Suaxi * @Date 2024/7/24 21:53 * @Description */ @SpringBootTest public class FlowEventMessageTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("事件-消息事件-启动事件") .addClasspathResource("flow/18.事件-消息事件-启动事件.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startMessageFlow() { runtimeService.startProcessInstanceByMessage("MsgStartEventMsg"); } } 注：启动流程时hi，可以通过常用的方式来启动，也可以通过对应的 startProcessInstanceByMessage() 方法启动（2）中间事件中间捕获事件即在流程运行过程中需要消息来触发的场景测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm…

网关
type: item
date: 2024-07-17T13:48:00+00:00
description: 1. 排他网关排他网关（exclusive gateway）用于对流程中的决策建模，当执行到达这个网关时，会按照所有出口顺序流定义的顺序对它们进行计算，选择第一个条件计算为 true 的顺序流（当没有设置条件时，认为当前顺序流为 true）继续流程。（1）流程图（2）测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.HashMap; import java.util.Map; /** * @Author Suaxi * @Date 2024/7/15 21:26 * @Description */ @SpringBootTest public class ExclusiveGatewayTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("网关-排他网关") .addClasspathResource("flow/10.网关-排他网关.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startProcess() { String processId = "Process_18phe80:1:5fc9d4f7-42ae-11ef-b9f8-a8a1592cf182"; runtimeService.startProcessInstanceById(processId); } @Test public void completeTask() { String taskId = "ad9c02f2-42ae-11ef-ad23-a8a1592cf182"; Map<String, Object> variables = new HashMap<>(); variables.put("day", 4); taskService.complete(taskId, variables); } } 注：当排他网关执行过程中有多个条件都满足时，流程引擎会根据bpmn流程图中顺序流的绘制顺序从前往后（从上到下）的顺序走2. 并行网关并行网关（parallel gateway）允许将流程分为多条分支，也可以把多条分支汇聚到一起，并行网关的功能基于进入/外出顺序流：fork分支：并行后的所有外出顺序流，为每个顺序流都创建一个并发分支join汇聚：所有到达并行网关，在此等待的进入分支，直到所有进入顺序流的分支都到达以后，流程就会通过汇聚网关如果一个并行网关既有进入，也有外出顺序六流（即同时具有分支和汇聚的功能），此时网关会先汇聚所有进入的顺序流，然后再切分成多个并行分支并行网关不会解析条件，即使在顺序流中定义了，也会被忽略（1）流程图（2）测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframe…

租户
type: item
date: 2024-07-13T14:25:00+00:00
description: 多租户是指一个单一的Camunda应用需要为多个租户服务的情况，对于每个租户来说，应该有某些隔离的保证，例如：A租户的流程实例不应该干扰B租户的流程实例多租户可以通过两种不同的方式实现：一个租户使用一个流程引擎多个租户使用同一个流程引擎（将数据与租户标识符相关联）此处以第二种方式为例1. 租户管理package com.sw.camundademo; import org.camunda.bpm.engine.IdentityService; import org.camunda.bpm.engine.identity.Tenant; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @Author Suaxi * @Date 2024/7/11 22:09 * @Description */ @SpringBootTest public class FlowTenantTest { @Autowired private IdentityService identityService; /** * 创建租户 */ @Test public void createTenant() { Tenant tenant = identityService.newTenant("tenant3"); tenant.setName("租户3"); identityService.saveTenant(tenant); } /** * 更新租户 */ @Test public void updateTenant() { Tenant tenant = identityService.createTenantQuery().tenantId("tenant1").singleResult(); tenant.setName("租户1-更新测试"); identityService.saveTenant(tenant); } /** * 删除租户 */ @Test public void deleteTenant() { identityService.deleteTenant("tenant3"); } /** * 绑定租户和组的关系 */ @Test public void tenantGroupShip() { identityService.createTenantGroupMembership("bj", "userGroup"); identityService.createTenantGroupMembership("bj", "userGroup1"); identityService.createTenantGroupMembership("sh", "userGroup1"); identityService.createTenantGroupMembership("sh", "userGroup2"); identityService.createTenantGroupMembership("sz", "userGroup"); identityService.createTenantGroupMembership("sz", "userGroup2"); } } 2. 流程部署（1）流程图（2）部署流程及流程相关数据的查询/** * 部署流程 * 1.部署时关联租户 - 租户特有 * 2.部署时不关联租户 - 所有用户都有 */ @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("财务审批-租户") .addClasspathResource("flow/09.财务审批-租户.bpmn") .tenantId("bj") .deploy(); System.out.println(deploy.getId()); } @Test public void deployWithTenant() { Deployment deploy = repositoryService.createDeployment() .name("财务审批-租户（不关联租户）") .addClasspathResource("flow/09.财务审批-租户.bpmn") //.tenantId("bj") .deploy(); System.o…

身份服务
type: item
date: 2024-07-13T14:22:50+00:00
description: 1. 候选人（1）流程图候选人设置方式：直接指定通过表达式设置（当不清楚具体是谁来审批时）（2）部署和启动流程package com.sw.camundademo; import org.camunda.bpm.engine.*; import org.camunda.bpm.engine.repository.Deployment; import org.camunda.bpm.engine.task.IdentityLink; import org.camunda.bpm.engine.task.Task; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author Suaxi * @Date 2024/7/8 21:52 * @Description */ @SpringBootTest public class FlowCandidateTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("财务审批-候选人") .addClasspathResource("flow/07.财务审批-候选人.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startProcess() { String processId = "Process_1su8y7i:1:7f77bcb5-3d31-11ef-933e-a8a1592cf182"; runtimeService.startProcessInstanceById(processId); } }（3）拾取任务在候选人流程中，组要通过拾取行为把候选人转换为处理人通过GUI操作通过API操作/** * 查询当前登录用户可拾取的任务 */ @Test public void getUserCandidateUserTask() { List<Task> currentUserTaskList = taskService.createTaskQuery() .taskCandidateUser("admin") .list(); if (currentUserTaskList != null && currentUserTaskList.size() > 0) { for (Task task : currentUserTaskList) { System.out.println("task.getId() = " + task.getId()); System.out.println("task.getName() = " + task.getName()); System.out.println("===================================="); } } else { System.out.println("当前登录用户没有可候选的task"); } } @Test public void claimTask() { Task task = taskService.createTaskQuery() .taskCandidateUser("admin") .singleResult(); if (task != null) { taskService.claim(task.getId(), "admin"); } }注：已经拾取过的任务不能被其他候选人强行拾取，且拾取后其他候选人查不到该任务（4）归还任务拾取后的任务如果因为一些原因不想/不能操作，可以归还通过GUI操作通过API操作@Test public void unClaimTak() { Task task = taskService.crea…

流程变量
type: item
date: 2024-07-05T14:51:00+00:00
description: 1. 运行时变量流程图（1）全局变量测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.repository.Deployment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.HashMap; import java.util.Map; /** * @Author Suaxi * @Date 2024/7/4 21:31 * @Description */ @SpringBootTest public class GlobalProcessVariablesTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("请假流程-流程变量-全局变量") .addClasspathResource("flow/05.流程变量-全局变量.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startProcess() { String processId = "Process_17lc2nv:1:f92e9184-3a09-11ef-b11b-a8a1592cf182"; Map<String, Object> variables = new HashMap<>(); variables.put("name", "孙笑川"); variables.put("age", 33); variables.put("flag", true); runtimeService.startProcessInstanceById(processId, variables); } /** * 获取运行时流程变量 */ @Test public void getVariable() { Map<String, Object> variables = runtimeService.getVariables("649c18cb-3a0a-11ef-a405-a8a1592cf182"); for (String key : variables.keySet()) { Object obj = variables.get(key); System.out.println(key + " - " + obj); } } /** * 修改运行时流程变量 */ @Test public void updateVariable() { String executionId = "649c18cb-3a0a-11ef-a405-a8a1592cf182"; runtimeService.setVariable(executionId, "flag", false); runtimeService.setVariable(executionId, "address", "四川成都"); } @Test public void completeTask() { String taskId = "649e88d4-3a0a-11ef-a405-a8a1592cf182"; Map<String, Object> variables = taskService.getVariables(taskId); variables.put("sex", "男"); taskService.complete(taskId, variables); } } （2）局部变量概念：仅针对在一个任务或一个执行实例中，范围没有流程…

任务分配
type: item
date: 2024-07-05T14:48:33+00:00
description: 1. 固定分配在绘制流程时直接指定 Assignee测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.repository.Deployment; import org.camunda.bpm.engine.runtime.ProcessInstance; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @Author Suaxi * @Date 2024/7/3 22:11 * @Description */ @SpringBootTest public class AssigneeTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Test public void deploy() { Deployment deploy = repositoryService.createDeployment() .name("请假流程-固定分配") .addClasspathResource("flow/01.任务分配-固定分配.bpmn") .deploy(); System.out.println(deploy.getId()); } @Test public void startProcess() { ProcessInstance processInstance = runtimeService.startProcessInstanceById("Process_0sa1c18:1:db75f76f-3946-11ef-9303-a8a1592cf182"); System.out.println("processInstance.getId() = " + processInstance.getId()); } @Test public void completeTask() { taskService.complete("44488821-3947-11ef-98ee-a8a1592cf182"); } } 2. 值表达式通过 ${value} 的形式来设置 Assignee测试package com.sw.camundademo; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.repository.Deployment; import org.camunda.bpm.engine.runtime.ProcessInstance; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.HashMap; import java.util.Map; /** * @Author Suaxi * @Date 2024/7/3 22:11 * @Description */ @SpringBootTest public class AssigneeTest { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowi…

SpringBoot整合Camunda
type: item
date: 2024-07-05T14:44:00+00:00
description: 1. 根据官方文档https://docs.camunda.org/get-started/spring-boot/2. 自定义（1）新建springboot项目pom.xml配置<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> </parent> <groupId>com.sw</groupId> <artifactId>camunda-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>camunda-demo</name> <description>camunda-demo</description> <properties> <java.version>1.8</java.version> <camunda.version>7.15.0</camunda.version> <camunda.spin.dataformat.version>1.14.4</camunda.spin.dataformat.version> <mysql.version>8.0.33</mysql.version> <lombok.version>1.18.32</lombok.version> </properties> <dependencies> <dependency> <groupId>org.camunda.bpm</groupId> <artifactId>camunda-bom</artifactId> <version>${camunda.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.camunda.bpm.springboot</groupId> <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId> <version>${camunda.version}</version> </dependency> <dependency> <groupId>org.camunda.bpm.springboot</groupId> <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId> <version>${camunda.version}</version> </dependency> <dependency> <groupId>org.camunda.bpm</groupId> <artifactId>camunda-engine-plugin-spin</artifactId> <version>${camunda.version}</version> </dependency> <dependency> <groupId>org.camunda.spin</groupId> <artifactId>camunda-spin-dataformat-all</artifactId> <version>${camunda.spin.dataformat.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifact…