acitivi 的表的文檔說明鏈接:https://www.devdoc.cn/activiti-table-act_ru_identitylink.html
-
activiti的表說明
- 使用25張表
- ACT_RE 流程定義和流程資源
- ACT_RU 運行時涩笤,流程實例、任務(wù)、變量
- ACT_HI 歷史表
- ACT_GE 通用表
- 使用25張表
-
Activiti的架構(gòu)举畸、類關(guān)系圖
- 獲取流程引擎的工具類
- ProcessEngines.使用默認方式獲取配置文件电抚,構(gòu)造流程引擎郑诺。配置文件名字activiti.cfg.xml 放在class path下
- ProcessEngineConfiguration.可以自定義配置文件名
- 使用上面兩個工具類都可以獲得流程引擎
- ProcessEngine:流程引擎羡鸥,獲取各種服務(wù)的接口
- 服務(wù)接口:用于流程的部署想括、執(zhí)行曾沈、管理尘颓,使用這些接口就是在操作對應(yīng)的數(shù)據(jù)表
- RepositoryService 資源管理類
- RuntimeService 運行時管理類
- TaskService 任務(wù)管理類
- HistoryService 歷史數(shù)據(jù)管理類
- ManagementService 流程引擎
-
BPMN插件
- idea安裝actiBPM
-
流程符號、畫流程圖
- 流程符號:事件Event晦譬、活動Activity疤苹,網(wǎng)關(guān) geteway,流向
- 使用流程設(shè)計器畫出流程圖
- bpmn文件本質(zhì)上是xml文件敛腌,因為安裝actibpm插件
- 創(chuàng)建bpmn文件卧土,在流程設(shè)計器使用流程符號來表達流程,指定流程的key像樊,指定任務(wù)負責(zé)人
- 生成png文件尤莺,把bpmn文件后綴改為xml,在這個文件上右鍵選擇diagrams-》show BPMN2.0 Designer生棍,打開窗口颤霎,點擊導(dǎo)出文件
-
部署流程
使用activiti提供的API把流程圖的內(nèi)容寫入數(shù)據(jù)庫中
屬于資源類操作,使用repositoryService
單文件部署:把BPMN文件和png文件一個一個來處理
壓縮包部署:把BPMN文件和png打壓縮包來處理
//1、創(chuàng)建processEngine ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2友酱、獲取repositoryService RepositoryService service = processEngine.getRepositoryService(); //3晴音、使用service進行流程的部署,定義一個流程的名字缔杉,把bpmn和png部署到數(shù)據(jù)中 Deployment deploy = service.createDeployment() .name("出差申請流程") .addClasspathResource("bpmn/evection.bpmn") .addClasspathResource("bpmn/evection.png") .deploy(); //4锤躁、輸出部署信息 System.out.println("流程部署id=" + deploy.getId()); System.out.println("流程部署名稱=" + deploy.getName());
-
部署操作表:
- act_re_deployment 部署表
- act_re_procdef 流程定義表
- act_ge_bytearray 資源表
-
啟動流程實例
使用runtimeService 根據(jù)流程定義的key
//1、創(chuàng)建processEngine ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2或详、獲取runtimeService RuntimeService service = processEngine.getRuntimeService(); //3系羞、根據(jù)流程定義的ID啟動流程 ProcessInstance instance = service.startProcessInstanceByKey("myEvection"); //4、輸出內(nèi)容 System.out.println("流程定義ID:" + instance.getProcessDefinitionId()); System.out.println("流程實例ID:" + instance.getId()); System.out.println("當(dāng)前活動的ID:" + instance.getActivityId());
-
操作表:
- act_hi_actinst 流程實例執(zhí)行歷史信息
- act_hi_identitylink 流程參與用戶的歷史信息
- act_hi_procinst 流程實例的歷史信息
- act_hi_taskinst 流程任務(wù)的歷史信息
- act_ru_execution 流程執(zhí)行信息
- act_ru_identitylink 流程正在參與用戶信息
- act_ru_task 流程當(dāng)前任務(wù)信息
-
任務(wù)查詢
使用Task Service霸琴,根據(jù)流程定義的key椒振,任務(wù)的負責(zé)人來進行查詢
//1、獲取流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2梧乘、獲取taskService TaskService taskService = processEngine.getTaskService(); //3杠人、根據(jù)流程key和任務(wù)的負責(zé)人查詢?nèi)蝿?wù) List<Task> taskList = taskService.createTaskQuery() .processDefinitionKey("myEvection") .taskAssignee("zhangsan") .list(); for (Task task : taskList) { System.out.println("流程實例ID:" + task.getProcessDefinitionId()); System.out.println("任務(wù)ID:" + task.getId()); System.out.println("任務(wù)負責(zé)人:" + task.getAssignee()); System.out.println("任務(wù)名稱:" + task.getName()); }
-
任務(wù)完成
使用TaskService用任務(wù)的ID來完成任務(wù)
TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService(); taskService.complete("30005");
-
刪除流程
/** * 刪除流程部署信息 * act_ge_bytearray * act_re_deployment * act_re_procdef * 刪除流程不會刪除流程歷史信息 * 刪除注意事項:如果當(dāng)前的流程并沒有完成,想要刪除流程的話宋下,需要進行級聯(lián)刪除 */ @Test public void deleteDeployment() { RepositoryService service = ProcessEngines.getDefaultProcessEngine().getRepositoryService(); String deploymentID = "27501"; service.deleteDeployment(deploymentID); //刪除注意事項:如果當(dāng)前的流程并沒有完成嗡善,想要刪除流程的話,需要進行級聯(lián)刪除,true代表級聯(lián)刪除 service.deleteDeployment(deploymentID,true); }
-
添加業(yè)務(wù)key到Activiti表
/** * 添加業(yè)務(wù)key到activiti表 */ @Test public void addBusinessKey() { RuntimeService service = ProcessEngines.getDefaultProcessEngine().getRuntimeService(); ProcessInstance instance = service.startProcessInstanceByKey("myEvection", "1001"); System.out.println(instance.getBusinessKey()); }
-
全部流程實例的掛起和激活
/** * 全部流程實例的掛起和激活 */ @Test public void suspendAllPrecessInstance() { RepositoryService service = ProcessEngines.getDefaultProcessEngine().getRepositoryService(); //查詢流程定義学歧,獲取流程定義的查詢對象 ProcessDefinition evection = service.createProcessDefinitionQuery() .processDefinitionKey("myEvection") .singleResult(); //獲取當(dāng)前流程定義的實例是否都是掛起狀態(tài) boolean suspended = evection.isSuspended(); //獲取流程定義的ID String id = evection.getId(); //如果是掛起狀態(tài)罩引,改為激活狀態(tài) if (suspended) { //如果是掛起,執(zhí)行激活 //args1 流程定義id args2 是否激活 args3 激活時間 service.activateProcessDefinitionById(id, true, null); System.out.println("流程定義id:" + id + "枝笨,已激活"); } else { //如果是激活狀態(tài)袁铐,改為掛起狀態(tài) //args1 流程定義id args2 是否掛起 args3 掛起時間 service.suspendProcessDefinitionById(id, true, null); System.out.println("流程定義id:" + id + ",已掛起"); } }
-
掛起激活單個流程實例
/** * 掛起激活單個流程實例 */ @Test public void suspendSingleProcessInstance() { RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService(); ProcessInstance instance = runtimeService.createProcessInstanceQuery() .processInstanceId("") .singleResult(); boolean suspended = instance.isSuspended(); // true 已暫停 false 激活 String instanceId = instance.getId(); if (suspended) { runtimeService.activateProcessInstanceById(instanceId); System.out.println("流程實例ID:" + instanceId + "横浑,已激活"); } else { runtimeService.suspendProcessInstanceById(instanceId); System.out.println("流程實例ID:" + instanceId + "剔桨,已掛起"); } }
處理工作流回退MySQL存儲過程
CREATE DEFINER=`admin`@`` PROCEDURE `activiti_back`(IN proc_inst_id VARCHAR(64))
BEGIN
DECLARE v_id_ VARCHAR(64);
DECLARE v_proc_def_id_ VARCHAR(64);
DECLARE v_task_def_key_ VARCHAR(255);
DECLARE v_name_ VARCHAR(255);
DECLARE v_assignee_ VARCHAR(255);
DECLARE cur_act_hi_taskinst CURSOR FOR SELECT ID_,PROC_DEF_ID_,TASK_DEF_KEY_,NAME_,ASSIGNEE_
FROM act_hi_taskinst
where PROC_INST_ID_ = proc_inst_id and TASK_DEF_KEY_ is not null
order by START_TIME_ desc ;
OPEN cur_act_hi_taskinst;
set @i = 0;
label: LOOP
SET @i = @i + 1;
FETCH cur_act_hi_taskinst INTO v_id_, v_proc_def_id_, v_task_def_key_, v_name_, v_assignee_;
IF @i = 1 THEN
-- 1、處理歷史任務(wù) 將當(dāng)前任務(wù)設(shè)置為完成狀態(tài)
UPDATE ACT_HI_TASKINST SET END_TIME_ = now(),duration_ = 1 ,DELETE_REASON_ = 'completed' where id_ = v_id_;
ELSEIF @i = 2 THEN
-- 2徙融、處理歷史任務(wù) 添加當(dāng)前新任務(wù)
INSERT INTO ACT_HI_TASKINST (id_ ,proc_def_id_ ,task_def_key_,proc_inst_id_,execution_id_
,name_,assignee_,start_time_,priority_)
values (uuid(),v_proc_def_id_,v_task_def_key_,proc_inst_id,proc_inst_id
,v_name_,v_assignee_,now(),50);
LEAVE label;
ELSE
LEAVE label;
END IF;
END LOOP label;
CLOSE cur_act_hi_taskinst;
-- 3洒缀、修改當(dāng)前執(zhí)行信息
UPDATE act_ru_execution SET rev_ = rev_ + 1 , ACT_ID_ = v_task_def_key_ where PROC_INST_ID_ = proc_inst_id;
-- 4、修改當(dāng)前任務(wù)信息
UPDATE act_ru_task SET name_ = v_name_,TASK_DEF_KEY_ = v_task_def_key_,ASSIGNEE_ = v_assignee_ where PROC_INST_ID_ = proc_inst_id;
END
一欺冀、創(chuàng)建Spring boot項目
- pom文件依賴
<!--新增activiti7依賴-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M4</version>
</dependency>
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0.M4</version>
<type>pom</type>
</dependency>
<!--新增activiti7依賴-->
<!--其它依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
File | Settings | Editor | File Encodings 編碼改為 UTF-8
-
application.yml依賴
server: port: 8089 servlet: context-path: / spring: datasource: url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/activiti?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai&allowMultiQueries=true username: xxxx password: xxxxxx driver-class-name: com.mysql.cj.jdbc.Driver activiti: #activiti創(chuàng)建歷史記錄表 db-history-used: true #歷史記錄為所有記錄 history-level: full #不自動部署流程 check-process-definitions: false
resources目錄下創(chuàng)建bpmn文件夾树绩,將流程文件放入(bpmn文件和png文件)
-
activiti剔除security
-
新建SpringSecurityConfig
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {}
-
新建SelfUserDetailsServiceImpl
import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Slf4j @Component public class SelfUserDetailsServiceImpl implements UserDetailsService { private final UserService userService; public SelfUserDetailsServiceImpl(UserService userService) { this.userService = userService; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userService.findOneUserByName(username); } }
-
新建UserService
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { private final Logger logger = LoggerFactory.getLogger(UserService.class); public User findOneUserByName(String username) { logger.info("username:"); logger.info(username); List<GrantedAuthority> authorities= AuthorityUtils.commaSeparatedStringToAuthorityList("admin"); // 密碼置空 return new User(username,"",authorities); }
-
需要在啟動類上面加
@SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
-
-
activiti 7 的M4版本中有字段缺失bug需要手動更新
-- ---------------------------- -- 修復(fù)Activiti7的M4版本缺失字段Bug -- ---------------------------- alter table ACT_RE_DEPLOYMENT add column PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL; alter table ACT_RE_DEPLOYMENT add column VERSION_ varchar(255) DEFAULT NULL;
至此activiti7配置完畢
二、部署流程隐轩、流程相關(guān)
-
流程部署
@SpringBootTest class ActivityDemoApplicationTests { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; /** * 裝柜通知單流程部署 * act_re_deployment 流程部署信息表 * ID_ 流程部署ID * NAME_ 流程部署名稱 * DEPLOY_TIME_ 流程部署時間 */ @Test public void initPackDeployment() { Deployment deploy = repositoryService .createDeployment() .name("裝柜通知流程") .addClasspathResource("bpmn/Container.bpmn") .addClasspathResource("bpmn/Container.png") .deploy(); System.out.println("流程部署成功:id==》" + deploy.getId()); System.out.println("流程部署成功:name==》" + deploy.getName()); } /** * 開啟一個裝柜通知單流程實例 * act_ru_task 運行時任務(wù)節(jié)點表 * PROC_INST_ID_ 流程實例ID 開啟一個新的流程的實例ID * PROC_DEF_ID_ 流程部署時的ID * NAME_ 節(jié)點定義名稱 * ASSIGNEE_ 任務(wù)執(zhí)行人 */ @Test public void startProcessInstance() { ProcessInstance instance = runtimeService.startProcessInstanceByKey("Container", "1"); System.out.println("流程實例ID:" + instance.getProcessInstanceId()); //9ab732ff-8002-11ec-8ac6-8cc6819ca54f } /** * 單證查詢個人代辦的任務(wù) */ @Test public void getTaskByAssignee() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("rxy").list(); for (Task task : list) { System.out.println("單證任務(wù)ID:" + task.getId()); //任務(wù)TaskID 9abab573-8002-11ec-8ac6-8cc6819ca54f System.out.println("單證任務(wù)名稱:" + task.getName()); } } /** * 單證執(zhí)行任務(wù) (轉(zhuǎn)發(fā)貨代饺饭,此處需依據(jù)貨代標識對應(yīng)轉(zhuǎn)發(fā)相應(yīng)貨代) */ @Test public void completeTask() { taskService.complete("9abab573-8002-11ec-8ac6-8cc6819ca54f"); System.out.println("執(zhí)行任務(wù)成功"); } /** * 貨代查詢個人代辦的任務(wù) */ @Test public void getTaskByAssigneeForHd() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("dn").list(); for (Task task : list) { System.out.println("貨代任務(wù)ID:" + task.getId()); System.out.println("貨代任務(wù)名稱:" + task.getName()); } } /** * 貨代拾取任務(wù) */ @Test public void claimTaskByHd() { taskService.claim("af675955-7fd5-11ec-a892-8cc6819ca54f", "dn"); System.out.println("貨代拾取任務(wù)成功");//貨代拾取任務(wù)成功 } /** * 貨代執(zhí)行任務(wù)(修改) */ @Test public void hdCompleteTask() { //todo 此處根據(jù)裝柜通知單ID修改表單 //貨代執(zhí)行任務(wù)(修改) taskService.complete("af675955-7fd5-11ec-a892-8cc6819ca54f"); System.out.println("貨代填寫完信息,交給單證"); } /** * 單證再次查詢個人代辦的任務(wù) */ @Test public void getTaskByAssigneeByDz() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("rxy").list(); for (Task task : list) { System.out.println("單證任務(wù)ID:" + task.getId());//2dbfbfcd-7fd7-11ec-abf5-8cc6819ca54f System.out.println("單證任務(wù)名稱:" + task.getName());//單證審核貨代 } } /** * 單證再次拾取任務(wù) */ @Test public void claimTaskAgain() { taskService.claim("2dbfbfcd-7fd7-11ec-abf5-8cc6819ca54f", "rxy"); System.out.println("再次拾取任務(wù)成功"); } /** * 單證執(zhí)行任務(wù) (轉(zhuǎn)發(fā)自提) */ @Test public void completeTaskForZiTi() { taskService.complete("2dbfbfcd-7fd7-11ec-abf5-8cc6819ca54f"); System.out.println("執(zhí)行任務(wù)成功"); } /** * 自提查詢個人代辦的任務(wù) */ @Test public void getTaskByAssigneeByZT() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("hmf").list(); for (Task task : list) { System.out.println("自提任務(wù)ID:" + task.getId());//9b05d6f0-7fd7-11ec-b78d-8cc6819ca54f System.out.println("自提任務(wù)名稱:" + task.getName());//單證審核貨代 } } /** * 自提拾取任務(wù) */ @Test public void claimTaskAgainByZT() { taskService.claim("9b05d6f0-7fd7-11ec-b78d-8cc6819ca54f", "hmf"); System.out.println("再次拾取任務(wù)成功"); } /** * 自提執(zhí)行任務(wù) (轉(zhuǎn)發(fā)單證) */ @Test public void completeTaskForZiTiByDanZheng() { taskService.complete("9b05d6f0-7fd7-11ec-b78d-8cc6819ca54f"); System.out.println("執(zhí)行任務(wù)成功"); } /** * 單證再次查詢個人代辦的任務(wù) (審核自提) */ @Test public void getTaskByAssigneeByDzZT() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("rxy").list(); for (Task task : list) { System.out.println("單證任務(wù)ID:" + task.getId());//11749858-7fd8-11ec-aa53-8cc6819ca54f System.out.println("單證任務(wù)名稱:" + task.getName());//單證審核貨代 } } /** * 單證再次拾取任務(wù) (審核自提) */ @Test public void claimTaskAgainzz() { taskService.claim("11749858-7fd8-11ec-aa53-8cc6819ca54f", "rxy"); System.out.println("再次拾取任務(wù)成功"); } /** * 單證執(zhí)行任務(wù) ((審核自提)并且最終轉(zhuǎn)發(fā)貨代) */ @Test public void completeTaskForZiTizz() { taskService.complete("11749858-7fd8-11ec-aa53-8cc6819ca54f"); System.out.println("執(zhí)行任務(wù)成功"); } /** * 貨代查詢個人代辦的任務(wù) */ @Test public void getTaskByAssigneeForHdhh() { List<Task> list = taskService.createTaskQuery().taskCandidateUser("dn").list(); for (Task task : list) { System.out.println("貨代任務(wù)ID:" + task.getId());//67b7926d-7fd8-11ec-98b3-8cc6819ca54f System.out.println("貨代任務(wù)名稱:" + task.getName());//貨代確認 } } /** * 貨代拾取任務(wù) */ @Test public void claimTaskByHdhh() { taskService.claim("67b7926d-7fd8-11ec-98b3-8cc6819ca54f", "dn"); System.out.println("貨代拾取任務(wù)成功");//貨代拾取任務(wù)成功 } /** * 貨代執(zhí)行任務(wù)(最終) */ @Test public void hdCompleteTaskhh() { //貨代執(zhí)行任務(wù)(最終) taskService.complete("67b7926d-7fd8-11ec-98b3-8cc6819ca54f"); System.out.println("貨代填寫完信息职车,各自走自己的裝鞋柜流程"); } }
說明:
流程回退瘫俊,存儲過程中傳入的流程實例ID也就是 act_ru_task 表中的 PROC_INST_ID_ 鹊杖,其余流程進行中的拾取任務(wù)、執(zhí)行任務(wù)等取得都是 act_ru_task 表中的 ID_
重要說明
Bpmn文件 idea 2020 之后不能使用act BPMN插件 需要使用擴展程序camunda-modeler
下載地址:https://github.com/camunda/camunda-modeler
之后
之后 創(chuàng)建的BPMN文件會出現(xiàn)bug assignee為null
解決方案 :camunda-modeler畫完圖以后, 得到的xml中的標簽是camunda:assignee, 需要換成activiti:assignee
修改后標簽會報錯, 需要改命名空間
<pre mdtype="fences" cid="n805" lang="xml" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><bpmn:definitions xmlns:bpmn="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:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:activiti="http://activiti.org/bpmn" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_03io3g1"
targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.9.0"
modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0"></pre>