Camunda流程引擎多版本,如何優雅融入SpringBoot 框架,解決版本沖突問題


C amunda平台是一個輕量級、開源的業務流程管理平台。在性能、功能上優於其它開源流程框架,非常適合引入項目做二次開發。要在項目中引入Camunda流程引擎,那么我們就應該對流程引擎了解,不便能避免很多未知的問題,還能更好地發揮其作用。
 
 
一、Camunda 流程引擎版本介紹
    Camunda 是基於JAVA 語言開發,從7.0到目前最新版本 7.16 ,每一次版本的更新,流程引擎會增加一些新功能,也會優化現有性能。
 

 

 
 
 
 
 
二、SpringBoot 集成 Camunda版本對照
 
Camunda 與  Spring Boot 的集成,主要是因為每個版本的 Camunda  Boot Starter  基於特定版本的  Spring Boot 開發,並 綁定了特定版本的 Camunda BPM。
 
注意:
從7.13.0版開始,Camunda BPM及其兼容的Spring Boot Starter始終共享同一版本。 另外,Spring Boot Starter中使用的Camunda BPM版本不再需要被覆蓋。 只需選擇與您要使用的Camunda BPM版本類似的Starter版本。
 
 
Spring Boot Starter version
Camunda BPM version
Spring Boot version
1.0.0* 
7.3.0 
1.2.5.RELEASE 
1.1.0* 
7.4.0 
1.3.1.RELEASE 
1.2.0* 
7.5.0 
1.3.5.RELEASE 
1.2.1* 
7.5.0 
1.3.6.RELEASE 
1.3.0* 
7.5.0 
1.3.7.RELEASE 
2.0.0** 
7.6.0 
1.4.2.RELEASE 
2.1.x** 
7.6.0 
1.5.3.RELEASE 
2.2.x** 
7.7.0 
1.5.6.RELEASE 
2.3.x 
7.8.0 
1.5.8.RELEASE 
3.0.x 
7.9.0 
2.0.x.RELEASE 
3.1.x 
7.10.0 
2.0.x.RELEASE 
3.2.x 
7.10.0 
2.1.x.RELEASE 
3.3.1+ 
7.11.0 
2.1.x.RELEASE 
3.4.x 
7.12.0 
2.2.x.RELEASE 
7.13.x
7.13.3+*** 
7.13.x
7.13.3+ 
2.2.x.RELEASE 
 
 
 
三、 SpringBoot 集成Camunda  示例
    網上已經有很多關於   Spring Boot 集成的示例 Camunda,但很多引入太直接,並不是很理想,不但容易出現版本沖突問題,而且不便於POM文件的管理,我這里推薦大家使用官網推薦大家基於BOM的方式引入Camunda。
 
Camunda 現在最新版本是7.16.0,在我的項目中使用的是7.15.0
 
項目版本准備:
 
Spring Boot: 2.3.7.RELEASE
 
Camunda: 7.15.0
 
在我的項目是父子結構,父POM文件中定義了 Spring Boot 的版本,下面是POM文件:
 
 
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.hx</groupId>
    <artifactId>hx-fixSystem</artifactId>
    <packaging>pom</packaging>
    <version>2.0</version>
    <name>hx-fixSystem 管理系統</name>
    <modules>
        <!— Camunda 流程引擎模塊 -->
        <module>hx-bpm</module>
    </modules>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
    </parent>
 
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>11</java.version>
        <jedis.version>2.9.0</jedis.version>
        <log4jdbc.version>1.16</log4jdbc.version>
        <swagger.version>2.7.0</swagger.version>
        <fastjson.version>1.2.54</fastjson.version>
        <druid.version>1.1.22</druid.version>
        <commons-pool2.version>2.5.0</commons-pool2.version>
        <mapstruct.version>1.3.1.Final</mapstruct.version>
    </properties>
 
 
    <dependencies>
        <!--Spring boot start-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- spring cache -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--Spring boot end-->
 
 
        <!--spring2.0集成redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>${commons-pool2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
 
 
        <!--監控sql日志-->
        <dependency>
            <groupId>org.bgee.log4jdbc-log4j2</groupId>
            <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
            <version>${log4jdbc.version}</version>
        </dependency>
 
 
        <!-- RESTful APIs swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
 
 
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.2</version>
        </dependency>
 
 
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.6.2</version>
        </dependency>
 
    
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>
 
 
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>
 
        <!--Mysql依賴包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- druid數據源驅動 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
 
 
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <optional>true</optional>
        </dependency>
   
    </dependencies>
 
 
    <repositories>
        <repository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
 
 
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
 
 
</project>

 

 
下面是Camunda 流程引擎模塊的POM文件:
 
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>hx-fixSystem</artifactId>
        <groupId>com.hx</groupId>
        <version>2.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>hx-bpm</artifactId>
    <name>流程引擎</name>
    <description>流程引擎相關操作</description>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
 
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.camunda.bpm</groupId>
                <artifactId>camunda-bom</artifactId>
                <version>7.15.0</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
    <dependencies>
 
        <!--Spring boot 集成camunda-->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter</artifactId>
        </dependency>
 
 
        <!--camunda Rest 接口-->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
        </dependency>
 
 
        <!--camunda webapp 頁面操作流程-->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
        </dependency>
 
 
        <!--外部任務客戶端-->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-external-task-client</artifactId>
        </dependency>
 
 
        <!--連接器-->
        <dependency>
            <groupId>org.camunda.bpm</groupId>
            <artifactId>camunda-engine-plugin-connect</artifactId>
        </dependency>
 
 
        <!--Spin和Json  Rest Http中解析; parse http response variable with camunda-spin-->
        <dependency>
            <groupId>org.camunda.bpm</groupId>
            <artifactId>camunda-engine-plugin-spin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.camunda.spin</groupId>
            <artifactId>camunda-spin-dataformat-json-jackson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.camunda.spin</groupId>
            <artifactId>camunda-spin-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.camunda.spin</groupId>
            <artifactId>camunda-spin-dataformat-all</artifactId>
        </dependency>
 
 
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.0</version>
        </dependency>
 
 
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
 
 
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.3.0</version>
        </dependency>
 
 
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>
 
 
        <dependency>
            <groupId>org.simpleframework</groupId>
            <artifactId>simple-xml</artifactId>
            <version>2.7.1</version>
        </dependency>
 
 
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>qrcode-utils</artifactId>
            <version>1.1</version>
        </dependency>
 
 
        <dependency>
            <groupId>com.cronutils</groupId>
            <artifactId>cron-utils</artifactId>
            <version>9.0.2</version>
        </dependency>
 
 
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.13</version>
        </dependency>
 
        <!-- 服務注冊-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
 
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
            <version>2.2.5.RELEASE</version>
            <scope>compile</scope>
        </dependency>
 
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-openfeign-core</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
 
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
 
 
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>10.2.0</version>
        </dependency>
 
 
    </dependencies>
 
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
            </plugin>
        </plugins>
    </build>
</project>

 

 
在POM文件中引入 Camunda 依賴,按上面紅色字體的方式引入,不但固定了版本,而且在下面的具體功能模塊引入時,就可以不用加版本號,它們會自動按上面定好的版本引入對應依賴。
 
就這樣一個 流程引擎項目就可以正常運轉了。
 
 
四:Camunda 引擎自帶的 組件式服務
 
Camunda 流程引擎功能很強大,做了很多封裝工作,提供了很多’開箱’ 即用的組件式服務,直接調用現存的API,不用寫什么代碼就能把一般的流程跑起來。
 
下面就介紹幾個重要組件:
 
組件名稱
功能
RuntimeService
運行時服務。 提供了流程的發起、刪除、掛起、激活、獲取參數、刪除參數等方法。
 
發起流程實例有 20個方法 
 
最常用的根據流程定義ID(),流程定義KEY方法,啟動成功后返回流程示例。
ProcessInstance startProcessInstanceById(String processDefinitionId)
ProcessInstance startProcessInstanceByKey(String processDefinitionKey);
 
刪除流程實例 15個方法
其中刪除有 6個方法 (有返回);其余的為直接刪除,沒有返回
 
獲取活動實例 節點 ,活動節點實例各 1個
 
向在給定執行中等待的活動實例發送外部觸發器    3個
 
獲取參數 17個方法
 
添加參數及值  8個方法
 
刪除參數 4個方法
 
掛起流程實例 3個方法
 
激活流程實例  3個方法
 
還有其它一些方法沒使用,占時還不明白用途
TaskService
任務服務。 提供了流程中任務的產生、刪除、完成、委托、認領、設置參數、獲取參數方法
 
其中常用的方法有:
Task newTask();
void deleteTask(String taskId);
void claim(String taskId, String userId); 
void complete(String taskId);
void delegateTask(String taskId, String userId);
void setAssignee(String taskId, String userId);
void setOwner(String taskId, String userId);
void setPriority(String taskId, int priority);
void setVariable(String taskId, String variableName, Object value);
  Object getVariable(String taskId, String variableName);
void removeVariable(String taskId, String variableName);
  Comment createComment(String taskId, String processInstanceId, String message);
List<Comment> getProcessInstanceComments(String processInstanceId);
HistoryService
歷史服務。 對流程中已經完成的節點任務、參數的查詢服務,歷史流程。
 
其中常用的方法有:
HistoricProcessInstanceQuery createHistoricProcessInstanceQuery();
NativeHistoricActivityInstanceQuery createNativeHistoricActivityInstanceQuery();
IdentityService
權限服務。  想着的權限設置
 
 
對於以上的組件,在項目中直接引用即可使用,示例如下。
 
@Resource
private RuntimeService runtimeService;
 
 
@Resource
private TaskService taskService;
 
 
@Resource
private FormService formService;
 
 
@Resource
private HistoryService historyService;

 

 
如何部署一個流程代碼:
 
/**
*  file 為上傳流程圖的文件,
*
*  name 設定流程的名字
*/
 
@Override
public void bpmnModelDeployFile(MultipartFile file, String name) {
    if(file == null){
        throw new ApiException("流程文件不能為空!");
    }
 
    try {
        repositoryService.createDeployment()
                //.tenantId(String.valueOf(tenantId))
                .name(name) // 流程名字
                .source("公司的流程") // 來源
                .addInputStream(file.getOriginalFilename(), file.getInputStream())
                .deploy();
 
    } catch (Exception e) {
        throw new ApiException("流程部署失敗".concat(e.getMessage()));
    }
}

 

 
啟動流程:
/**
*  key 為定義流程的KEY
*
*  WorkflowVariableDto 為參數對象
*   返回 流程實例ID
*/
 
@Override
public String startProcess(String key, WorkflowVariableDto dto) {
 
    List<String> tenantIds = new ArrayList<>(Arrays.asList(dto.getTenantIds().split(",")));
 
    // 設置流程發起人
    if (StringUtils.isNotBlank(dto.getStartUser())) {
        identityService.setAuthentication(dto.getStartUser(), new ArrayList<>(), tenantIds);
    }
 
    // 啟動流程實例
    ProcessInstance instance = runtimeService.startProcessInstanceByKey(key, dto.getBusinessId());
 
    try {
        // 獲取當前任務,會簽可能會有多個
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(instance.getProcessInstanceId()).list();
 
        if (CollectionUtils.isNotEmpty(tasks)) {
            tasks.forEach(t -> {
                // 為當前任務分派用戶
                if (StringUtils.isNotBlank(dto.getAssignee())) {
                    taskService.setAssignee(t.getId(), dto.getAssignee());
                }
                // 為當前任務添加候選用戶
                if (StringUtils.isNotBlank(dto.getCandidateUsers())) {
                    List<String> users = WorkflowVariableDto.getIdListByStr(dto.getCandidateUsers());
                    users.forEach(u -> taskService.addCandidateUser(t.getId(), u));
                }
                // 為當前任務添加候選組
                if (StringUtils.isNotBlank(dto.getCandidateGroups())) {
                    List<String> groups = WorkflowVariableDto.getIdListByStr(dto.getCandidateGroups());
                    groups.forEach(g -> taskService.addCandidateGroup(t.getId(), g));
                }
            });
        }
 
    } catch (Exception e) {
        throw new ApiException("發起流程失敗:".concat(e.getMessage()));
    }
    return instance.getProcessInstanceId();
}
 

 

 
完成任務:
 
/**
*  taskId 為任務ID
*  WorkflowVariableDto  為參數對象
*
*/
 
@Override
public void completeTask(String taskId, WorkflowVariableDto dto) {
    try {
        // 獲取完成前任務
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(ObjectUtils.isEmpty(task)){
            log.info("沒有查找到當前任務:{}", taskId);
            return ;
        }
 
        // 完成任務填寫的備注
        if(StringUtils.isNotBlank(dto.getReason()) && StringUtils.isNotBlank(task.getProcessInstanceId())){
            taskService.createComment(task.getId(), task.getProcessInstanceId(), dto.getReason());
        }
 
        // 完成當前任務
        taskService.complete(taskId, dto.getVariables());
    } catch (Exception e) {
        throw new ApiException("完成任務失敗:".concat(e.getMessage()));
    }
}

 

駁回任務:
/**
* 駁回並返回到起點
* @param processInstanceId 流程實例ID
* @param taskId 任務ID
* @param reason 原因
*/
@Override
public boolean reject(String processInstanceId, String taskId, String reason, int step){
    Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).taskId(taskId).singleResult();
    if(task != null){
        ActivityInstance activityInstance = runtimeService.getActivityInstance(processInstanceId);
        if(activityInstance != null){
            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                                                                                        .processInstanceId(processInstanceId)
                                                                                        .executionId(task.getExecutionId())
                                                                                        .activityType("userTask")
                                                                                        .finished()
                                                                                        .orderByHistoricActivityInstanceEndTime()
                                                                                        .desc().list();
 
            historicActivityInstanceList = historicActivityInstanceList.stream().filter(it -> !it.getActivityId().equals(task.getTaskDefinitionKey())).collect(Collectors.toList());
 
            if(historicActivityInstanceList.size() > 0){
                HistoricActivityInstance first = historicActivityInstanceList.get(step);
                String toActId = first.getActivityId();
                String assignee = first.getAssignee();
 
                Map<String, Object> taskVariable = new HashMap<>();
                //設置當前處理人
                taskVariable.put("assignee", assignee)
 
                // 填寫拒絕的原因
                taskService.createComment(task.getId(), processInstanceId, reason);
 
                runtimeService.createProcessInstanceModification(processInstanceId)
                        //關閉相關任務
                        .cancelActivityInstance(getInstanceIdForActivity(activityInstance, task.getTaskDefinitionKey()))
                        .setAnnotation("進行了駁回到"+ step + "任務節點操作")
                        //啟動目標活動節點
                        .startBeforeActivity(toActId)
                        //流程的可變參數賦值
                        .setVariables(taskVariable)
                        .execute();
 
                List<Task> _taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
 
                if(ObjectUtils.isNotEmpty(_taskList)){
                    for(Task item: _taskList){
                        if(item.getTaskDefinitionKey().equals(toActId)){
                            taskService.setAssignee(item.getId(), assignee);
                            return true;
                        }
                    }
                }
            }
        }
    }
    return false;
}

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM