【Java 開發日記】你會不會使用 SpringBoot 整合 Flowable 快速實現工作流呢?

目錄

1、流程引擎介紹

2、創建項目

3、畫流程圖

4、開發接口

4.1 Java 類梳理

ProcessDefinition

ProcessInstance

Activity

Execution

Task

4.2 查看流程圖

4.3 開啟一個流程

4.4 將請求提交給組長

4.5 組長審批

4.6 經理審批

4.7 拒絕流程


1、流程引擎介紹

Flowable 是一個使用 Java 編寫的輕量級業務流程引擎。Flowable 流程引擎可用于部署 BPMN2.0 流程定義(用于定義流程的行業 XML 標準),創建這些流程定義的流程實例,進行查詢,訪問運行中或歷史的流程實例與相關數據,等等。
Java 領域另一個流程引擎是 Activiti,不這兩個東西只要會使用其中一個,另一個就不在話下。
咱就不廢話了,上代碼吧。?

2、創建項目

首先創建一個 Spring Boot 項目,引入 Web、和 MySQL 驅動兩個依賴,如下圖:


項目創建成功之后,引入 flowable 依賴,如下:

<dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version>
</dependency>

這個會做一些自動化配置,默認情況下,所以位于 resources/processes 的流程都會被自動部署。
接下來在 application.yaml 中配置一下數據庫連接信息,當項目啟動的時候會自動初始化數據庫,將來流程引擎運行時候的數據會被自動持久化到數據庫中。

spring:datasource:username: rootpassword: 123url: jdbc:mysql:///flowable?serverTimezone=Asia/Shanghai&useSSL=false

配置完成后,就可以啟動項目了。項目啟動成功之后,flowable 數據庫中就會自動創建如下這些表,將來流程引擎相關的數據都會自動保存到這些表中。
默認的表比較多,截圖只是其中一部分。

?

3、畫流程圖

畫流程圖算是比較有挑戰的一個步驟了,也是流程引擎使用的關鍵。官方提供了一些流程引擎繪制工具,感興趣的小伙伴可以自行去體驗;IDEA 也自帶了一個流程可視化的工具,但是特別難用。
這里說一下常用的 IDEA 插件?Flowable BPMN visualizer,如下圖:

裝好插件之后,在 resources 目錄下新建 processes 目錄,這個目錄下的流程文件將來會被自動部署。
接下來在 processes 目錄下,新建一個 BPMN 文件(插件裝好了就有這個選項了),如下:

來畫個請假的流程,就叫做 ask_for_leave.bpmn20.xml,注意最后面的 .bpmn20.xml 是固定后綴。
文件創建出來之后,右鍵單擊,選擇 View BPMN(Flowable) Diagram,就打開了可視化頁面了,就可以來繪制自己的流程圖了。
請假流程畫出來是這樣:

員工發起一個請假流程,首先是組長審核,組長審核通過了,就進入到經理審核,經理審核通過了,這個流程就結束了,如果組長審核未通過或者經理審核未通過,則流程給員工發送一個請假失敗的通知,流程結束。
來看下這個流程對應的 XML 文件,一些流程細節會在 XML 文件中體現出來,如下:

<process id="ask_for_leave" name="ask_for_leave" isExecutable="true"><userTask id="leaveTask" name="請假" flowable:assignee="#{leaveTask}"/><userTask id="zuzhangTask" name="組長審核" flowable:assignee="#{zuzhangTask}"/><userTask id="managerTask" name="經理審核" flowable:assignee="#{managerTask}"/><exclusiveGateway id="managerJudgeTask"/><exclusiveGateway id="zuzhangJudeTask"/><endEvent id="endLeave" name="結束"/><startEvent id="startLeave" name="開始"/><sequenceFlow id="flowStart" sourceRef="startLeave" targetRef="leaveTask"/><sequenceFlow id="modeFlow" sourceRef="leaveTask" targetRef="zuzhangTask"/><sequenceFlow id="zuzhang_go" sourceRef="zuzhangJudeTask" targetRef="managerTask" name="通過"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='通過'}]]></conditionExpression></sequenceFlow><sequenceFlow id="zuzhang_reject" sourceRef="zuzhangJudeTask" targetRef="sendMail" name="拒絕"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='拒絕'}]]></conditionExpression></sequenceFlow><sequenceFlow id="jugdeFlow" sourceRef="managerTask" targetRef="managerJudgeTask"/><sequenceFlow id="flowEnd" name="通過" sourceRef="managerJudgeTask" targetRef="endLeave"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='通過'}]]></conditionExpression></sequenceFlow><sequenceFlow id="rejectFlow" name="拒絕" sourceRef="managerJudgeTask" targetRef="sendMail"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='拒絕'}]]></conditionExpression></sequenceFlow><serviceTask id="sendMail" flowable:exclusive="true" name="發送失敗提示" isForCompensation="true" flowable:class="org.javaboy.flowable.AskForLeaveFail"/><sequenceFlow id="endFlow" sourceRef="sendMail" targetRef="askForLeaveFail"/><endEvent id="askForLeaveFail" name="請假失敗"/><sequenceFlow id="zuzhangTask_zuzhangJudeTask" sourceRef="zuzhangTask" targetRef="zuzhangJudeTask"/>
</process>

結合 XML 文件來解釋一下這里涉及到的 Flowable 中的組件:

  • <process>?:表示一個完整的工作流程。
  • <startEvent>?:工作流中起點位置,也就是圖中的綠色按鈕。
  • <endEvent>?:工作流中結束位置,也就是圖中的紅色按鈕。
  • <userTask>?:代表一個任務審核節點(組長、經理等角色),這個節點上有一個?flowable:assignee?屬性,這表示這個節點該由誰來處理,將來在 Java 代碼中調用的時候,需要指定對應的處理人的 ID 或者其他唯一標記。
  • <serviceTask>:這是服務任務,在具體的實現中,這個任務可以做任何事情。
  • <exclusiveGateway>?:邏輯判斷節點,相當于流程圖中的菱形框。
  • <sequenceFlow>?:鏈接各個節點的線條,sourceRef 屬性表示線的起始節點,targetRef 屬性表示線指向的節點,圖中的線條都屬于這種。

流程圖這塊松哥和大家稍微說一下,咋一看這個圖挺復雜很難畫,但是實際上只要認認真真去捋一捋這里邊的各個屬性,基本上很快就明白到底是怎么一回事。?

4、開發接口

接下來寫幾個接口,來體驗一把流程引擎。
在正式體驗之前,先來熟悉幾個類,這幾個類一會寫代碼會用到。?

4.1 Java 類梳理

ProcessDefinition

這個最好理解,就是流程的定義,也就相當于規范,每個 ProcessDefinition 都會有一個 id。?

ProcessInstance

這個就是流程的一個實例。簡單來說,ProcessDefinition 相當于是類,而 ProcessInstance 則相當于是根據類 new 出來的對象。?

Activity

Activity 是流程標準規范 BPMN2.0 里面的規范,流程中的每一個步驟都是一個 Activity。?

Execution

Execution 的含義是流程的執行線路,通過 Execution 可以獲得當前 ProcessInstance 當前執行到哪個 Activity了。?

Task

Task 就是當前要做的工作。
實際上這里涉及到的東西比較多,不過這里先整一個簡單的例子,所以上面這些知識點暫時夠用了。?

4.2 查看流程圖

在正式開始之前,先準備一個接口,用來查看流程圖的實時執行情況,這樣方便查看流程到底執行到哪一步了。
具體的代碼如下:

@RestController
public class HelloController {@AutowiredRuntimeService runtimeService;@AutowiredTaskService taskService;@AutowiredRepositoryService repositoryService;@AutowiredProcessEngine processEngine;@GetMapping("/pic")public void showPic(HttpServletResponse resp, String processId) throws Exception {ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();if (pi == null) {return;}List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processId).list();List<String> activityIds = new ArrayList<>();List<String> flows = new ArrayList<>();for (Execution exe : executions) {List<String> ids = runtimeService.getActiveActivityIds(exe.getId());activityIds.addAll(ids);}/*** 生成流程圖*/BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false);OutputStream out = null;byte[] buf = new byte[1024];int legth = 0;try {out = resp.getOutputStream();while ((legth = in.read(buf)) != -1) {out.write(buf, 0, legth);}} finally {if (in != null) {in.close();}if (out != null) {out.close();}}}
}

這就一個工具,沒啥好說的,一會大家看完后面的代碼,再回過頭來看這個接口,很多地方就都懂了。?

4.3 開啟一個流程

為了方便,接下來的代碼都在單元測試中完成。
首先來開啟一個流程,代碼如下:

String staffId = "1000";
/*** 開啟一個流程*/
@Test
void askForLeave() {HashMap<String, Object> map = new HashMap<>();map.put("leaveTask", staffId);ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("ask_for_leave", map);runtimeService.setVariable(processInstance.getId(), "name", "javaboy");runtimeService.setVariable(processInstance.getId(), "reason", "休息一下");runtimeService.setVariable(processInstance.getId(), "days", 10);logger.info("創建請假流程 processId:{}", processInstance.getId());
}

首先由員工發起一個請假流程,map 中存放的 leaveTask 是在 XML 流程文件中提前定義好的,提前定義好當前這個任務創建之后,該由誰來處理,這里是假設由工號為 1000 的員工來發起這樣一個請假流程。同時,還設置了一些額外信息。ask_for_leave 是在 XML 文件中定義的一個 process 的名稱。
好啦,現在執行這個單元測試方法,執行完成后,控制臺會打印出當前這個流程的 id,拿著這個 id 去訪問 4.2 小節的接口,結果如下:

可以看到,請假用紅色的框框起來了,說明當前流程走到了這一步。?

4.4 將請求提交給組長

接下來,就需要將這個請假流程向后推進一步,將請假事務提交給組長,代碼如下:

String zuzhangId = "90";
/*** 提交給組長審批*/
@Test
void submitToZuzhang() {//員工查找到自己的任務,然后提交給組長審批List<Task> list = taskService.createTaskQuery().taskAssignee(staffId).orderByTaskId().desc().list();for (Task task : list) {logger.info("任務 ID:{};任務處理人:{};任務是否掛起:{}", task.getId(), task.getAssignee(), task.isSuspended());Map<String, Object> map = new HashMap<>();//提交給組長的時候,需要指定組長的 idmap.put("zuzhangTask", zuzhangId);taskService.complete(task.getId(), map);}
}

首先利用 staffId 查找到當前員工的 id,進而找到當前員工需要執行的任務,遍歷這個任務,調用 taskService.complete 方法將任務提交給組長,注意在 map 中指定組長的 id。
提交完成后,再去看流程圖片,如下:

可以看到,流程圖走到組長審批了。?

4.5 組長審批

組長現在有兩種選擇,同意或者拒絕,同意的代碼如下:

/*** 組長審批-批準*/
@Test
void zuZhangApprove() {List<Task> list = taskService.createTaskQuery().taskAssignee(zuzhangId).orderByTaskId().desc().list();for (Task task : list) {logger.info("組長 {} 在審批 {} 任務", task.getAssignee(), task.getId());Map<String, Object> map = new HashMap<>();//組長審批的時候,如果是同意,需要指定經理的 idmap.put("managerTask", managerId);map.put("checkResult", "通過");taskService.complete(task.getId(), map);}
}

通過組長的 id 查詢組長的任務,同意的話,需要指定經理,也就是這個流程下一步該由誰來處理。
拒絕的代碼如下:

/*** 組長審批-拒絕*/
@Test
void zuZhangReject() {List<Task> list = taskService.createTaskQuery().taskAssignee(zuzhangId).orderByTaskId().desc().list();for (Task task : list) {logger.info("組長 {} 在審批 {} 任務", task.getAssignee(), task.getId());Map<String, Object> map = new HashMap<>();//組長審批的時候,如果是拒絕,就不需要指定經理的 idmap.put("checkResult", "拒絕");taskService.complete(task.getId(), map);}
}

拒絕的話,就沒那么多事了,直接設置 checkResult 為拒絕即可。
假設這里執行了同意,那么流程圖如下:

?4.6 經理審批

經理審批和組長審批差不多,只不過經理這里是最后一步了,不需要再指定下一位處理人了,同意的代碼如下:

/*** 經理審批自己的任務-批準*/
@Test
void managerApprove() {List<Task> list = taskService.createTaskQuery().taskAssignee(managerId).orderByTaskId().desc().list();for (Task task : list) {logger.info("經理 {} 在審批 {} 任務", task.getAssignee(), task.getId());Map<String, Object> map = new HashMap<>();map.put("checkResult", "通過");taskService.complete(task.getId(), map);}
}

拒絕代碼如下:

/*** 經理審批自己的任務-拒絕*/
@Test
void managerReject() {List<Task> list = taskService.createTaskQuery().taskAssignee(managerId).orderByTaskId().desc().list();for (Task task : list) {logger.info("經理 {} 在審批 {} 任務", task.getAssignee(), task.getId());Map<String, Object> map = new HashMap<>();map.put("checkResult", "拒絕");taskService.complete(task.getId(), map);}
}

4.7 拒絕流程

如果組長拒絕了或者經理拒絕了,也有相應的處理方案,首先在 XML 流程文件定義時,如下:

<serviceTask id="sendMail" flowable:exclusive="true" name="發送失敗提示" isForCompensation="true" flowable:class="org.javaboy.flowable.AskForLeaveFail"/>

如果請假被拒絕,會進入到這個 serviceTask,serviceTask 對應的處理類是 org.javaboy.flowable.AskForLeaveFail,該類的代碼如下:

public class AskForLeaveFail implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {System.out.println("請假失敗。。。");}
}

也就是請假失敗會進入到這個方法中,現在就可以在這個方法中該干嘛干嘛了。

如果小假的內容對你有幫助,請點贊評論收藏。創作不易,大家的支持就是我堅持下去的動力!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/88671.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/88671.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/88671.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試150 分發糖果

思路 聯想貪心算法&#xff0c;遍歷兩次數組&#xff0c;一次是從左到右遍歷&#xff0c;只比較右邊孩子評分比左邊打的情況。第二次從右到左遍歷&#xff0c;只比較左邊孩子評分比右邊大的情況。最后求和即可 class Solution:def candy(self, ratings: List[int]) -> int…

csp基礎之進制轉換器

一、進制轉換要明白的基礎知識。。。 1、什么是進制&#xff1f; 進制也就是進位計數制&#xff0c;是人為定義的帶進位的計數方法。對于任何一種進制 X 進制&#xff0c;就表示每一位置上的數運算時都是逢 X 進一位。十進制是逢十進一&#xff0c;十六進制是逢十六進一&#…

Zephyr OS藍牙廣播(Advertising)功能實現

目錄 概述 1 Advertising功能介紹 1.1 實現原理 1.2 廣播類型 1.3 廣播數據格式 1.4 優化建議 1.5 常見問題和解決方法 2 Nordic 藍牙廣播&#xff08;Advertising&#xff09;功能實現 2.1 環境準備與SDK基礎 2.2 廣播功能實現 2.3 廣播優化與最佳實踐 3 實際應用案例…

服務器不支持PUT,DELETE 的解決方案

nginx 的更改&#xff1a; set $method $request_method; if ($http_X_HTTP_Method_Override ~* PUT|DELETE) { set $method $http_X_HTTP_Method_Override; } proxy_method $method; axios 的更改&#xff1a; const method config.me…

從0開始學習計算機視覺--Day04--線性分類

從宏觀來看&#xff0c;卷積網絡可以看做是由一個個不同的神經網絡組件組合而成&#xff0c;就像積木一樣通過不同類型的組件搭建形成&#xff0c;其中線性分類器是一個很重要的組件&#xff0c;在很多卷積網絡中都有用到&#xff0c;所以了解清楚它的工作原理對我們后續的學習…

基于ComfyUI與Wan2.1模型的本地化視頻生成環境搭建指南

文章目錄 前言1.軟件準備1.1 ComfyUI1.2 文本編碼器1.3 VAE1.4 視頻生成模型2.整合配置3. 本地運行測試4. 公網使用Wan2.1模型生成視頻4.1 創建遠程連接公網地址5. 固定遠程訪問公網地址總結前言 各位小伙伴們,今天我們將為您展示一套創新的人工智能應用方案!本次教程將指導…

Vue 2 項目中內嵌 md 文件

推薦方案&#xff1a;raw-loader marked 解析 Markdown 第一步&#xff1a;安裝依賴 npm install marked --save npm install raw-loader --save-dev第二步&#xff1a;配置 webpack 支持 .md 文件 打開 vue.config.js 或 webpack.config.js&#xff0c;添加以下配置&#…

Spring AI初識及簡單使用,快速上手。

Spring AI簡介 在當今這樣一個快速發展的技術時代&#xff0c;人工智能&#xff08;AI&#xff09;已經成為各行各業的一種標配。而作為一款主流的Java應用開發框架Spring&#xff0c;肯定會緊跟時代的潮流&#xff0c;所以&#xff0c;推出了Spring AI框架。 官網描述&#…

Flask中的render_template與make_response:生動解析與深度對比

文章目錄 Flask中的render_template與make_response&#xff1a;生動解析與深度對比一、&#x1f31f; 核心概念速覽二、&#xfffd; render_template - 網頁內容的主廚特點與內部機制適用場景高級用法示例 三、&#x1f381; make_response - 響應的包裝專家核心功能解析適用…

WordPress目錄說明

在WordPress建站過程中&#xff0c;理解服務器目錄結構是非常重要的。以下是一個基礎的WordPress服務器目錄指南&#xff1a; /wp-admin/ &#xff1a;這個目錄包含了WordPress網站的所有管理功能&#xff0c;包括用于處理網站后臺的所有PHP文件。 /wp-includes/ &#xff1a;…

HTTP面試題——緩存技術

目錄 HTTP緩存技術有哪些&#xff1f; 什么是強制緩存&#xff1f; 什么是協商緩存&#xff1f; HTTP緩存技術有哪些&#xff1f; 對于一些具有重復性的HTTP請求&#xff0c;比如每次請求得到的數據都是一樣的&#xff0c;我們可以把這對 請求-響應的數據都緩存在本地&#x…

virtual box 不能分配 USB設備 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虛擬電腦 win10

VirtualBox: Failed to attach the USB device to the virtual machine – Bytefreaks.net ISSUE&#xff1a; virtual box 不能分配 USB設備 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虛擬電腦 win10. USB device IFX DAS JDS TriBoard TC2X5 V2.0 with UUID {91680aeb-e1…

Deepoc大模型重構核工業智能基座:混合增強架構與安全增強決策技術?

面向復雜系統的高可靠AI賦能體系構建 Deepoc大模型通過多維度技術突破&#xff0c;顯著提升核工業知識處理與決策可靠性。經核能行業驗證&#xff0c;其生成內容可驗證性提升68%&#xff0c;關鍵參數失真率<0.3%&#xff0c;形成覆蓋核能全鏈條的定制化方案&#xff0c;使企…

第12章:冰箱里的CT掃描儀——計算機視覺如何洞穿食材的“生命密碼“

第11章:冰箱里的CT掃描儀——計算機視覺如何成為食材健康的"超級診斷官" “糟了!冰箱里草莓長出了白色絨毛,雞胸肉滲出了可疑的粉紅色液體!” 這揭示了廚房生存的更基本挑戰:如何像經驗豐富的主廚一樣,一眼洞穿食材的健康密碼? 本章將揭示計算機視覺技術如何賦…

虛幻基礎:窗口——重定向

能幫到你的話&#xff0c;就給個贊吧 &#x1f618; 文章目錄 重定向&#xff1a;給骨架添加兼容骨架。使得不同模型間復用動畫資源 重定向&#xff1a;給骨架添加兼容骨架。使得不同模型間復用動畫資源

CSS 逐幀動畫

CSS 逐幀動畫實現指南 逐幀動畫(frame-by-frame animation)是一種通過快速連續顯示一系列靜態圖像來創造運動效果的技術。以下是使用CSS實現逐幀動畫的幾種方法。 1. 使用 steps() 計時函數 這是實現逐幀動畫最常用的方法&#xff0c;通過animation-timing-function的steps(…

高版本IDEA如何開發低版本jdk項目

問題描述 我這個人比較喜歡新的東西&#xff0c;比如使用idea的時候&#xff0c;我就喜歡最新版本。 但是有個問題&#xff0c;最新版本的idea好像不支持jdk1.6&#xff0c;導致我無法去用新版本idea開發項目。 直到有一天&#xff0c;idea給了我一個提示如下&#xff0c;之…

Java設計模式->責任鏈模式的介紹

目錄 1、責任鏈模式概念 1.1、定義介紹 1.2、流程圖 1.3、優缺點 2、實現 3、應用場景 3.1、Springmvc流程 3.2、mybatis的執行流程 3.3、Spring的過濾器和攔截器 3.4、sentinel限流熔斷 3.5、aop的加載和使用 4、舉例 前言 是一種 行為型設計模式&#xff0c;它通…

【Bluedroid】藍牙啟動之 btm_acl_device_down 流程源碼解析

本文詳細分析Android藍牙協議棧在設備故障時的處理流程。當藍牙設備發生硬件故障或系統異常時,協議棧通過btm_acl_device_down觸發多層次的資源清理和狀態重置,包括ACL連接終止、L2CAP通道釋放、SCO連接清理、BLE拓撲更新、設備數據庫重置等關鍵操作,確保系統安全恢復。 一、…

隨記:WebMvcConfigurationSupport 和WebMvcConfigurer 的區別

WebMvcConfigurationSupport &#xff08;抽象類&#xff09; 他是一個完整的 MVC 配置基類&#xff0c;他會禁用所有自動配置。默認靜態資源映射也沒有了。默認消息轉換器&#xff08;json、xml&#xff09;也沒有了。錯誤處理頁默認的error也沒有了。 WebMvcConfigurer &am…