SpringBoot搭Flowable搞工作流的實現(xiàn)示例
一、先把環(huán)境整明白:準備工作不踩坑
1. 新建 Spring Boot 項目(手殘黨也能秒懂)
打開你的 IDEA,新建一個 Spring Boot 項目。記得選 Web 模塊,畢竟咱后續(xù)可能要搞點接口測試啥的。如果用命令行的話,一行命令搞定:
spring init --name=flowable-demo --groupId=com.example --artifactId=flowable-demo --version=2.7.12 --packaging=jar --dependencies=web,mysql,flowable-spring-boot-starter-process flowable-demo
這里重點說下依賴 flowable-spring-boot-starter-process,這可是 Flowable 和 Spring Boot 聯(lián)姻的關鍵信物,自帶自動配置功能,能讓我們少寫一大堆繁瑣配置。
2. 數(shù)據(jù)庫配置:和 MySQL 做好朋友
咱先給項目找個「倉庫」存流程數(shù)據(jù)。打開 application.properties,加上數(shù)據(jù)庫配置:
spring.datasource.url=jdbc:mysql://localhost:3306/flowable_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 1.2.3.4.
注意這里用了 MySQL 8.0 的驅動,如果你的數(shù)據(jù)庫是 5.x 版本,記得把驅動換成 com.mysql.jdbc.Driver,不然可能會報「找不到驅動」的玄學錯誤哦。
3. Flowable 核心配置:讓引擎聽指揮
接著告訴 Flowable 咱們的需求:
# 關閉自動部署(開發(fā)階段建議關閉,避免每次啟動都重新部署流程) flowable.engine.auto-deploy-process-definitions=false # 開啟流程模型編輯器(后面畫流程圖全靠它) flowable.ui.modeler.enabled=true # 設置歷史記錄級別(記錄所有操作,方便排查問題) flowable.history.level=full
這里重點說下 history.level,默認是 audit,只會記錄關鍵事件,咱們設為 full 后,連任務的創(chuàng)建、分配、完成時間都會記錄下來,調(diào)試的時候簡直不要太方便。
二、流程設計初體驗:用可視化工具畫流程圖
1. 啟動項目,打開 Modeler 界面
把項目跑起來,訪問 http://localhost:8080/flowable-modeler,會看到一個登錄界面。別慌,F(xiàn)lowable 默認的賬號密碼是 admin/admin,輸進去就能看到高大上的流程設計器了。
這個界面有點像 Visio,左邊是各種流程元素,什么開始事件、用戶任務、結束事件,拖出來就能用。咱們來畫一個最簡單的請假流程:
- 拖一個「開始事件」放在畫布上,這就是流程的起點。
- 拖一個「用戶任務」,改名叫「部門經(jīng)理審批」,這一步需要人來處理。
- 再拖一個「用戶任務」,改名叫「HR 審批」。
- 最后拖一個「結束事件」。
然后用連接線把它們按順序連起來,一個簡單的串行審批流程就畫好了。
2. 給任務節(jié)點加屬性:告訴引擎找誰干活
雙擊「部門經(jīng)理審批」節(jié)點,在右邊的屬性面板里,找到「Assignee」(任務負責人),這里可以寫固定的用戶 ID,比如 manager,不過實際項目中肯定是動態(tài)指定的,后面咱會用代碼實現(xiàn)。先記住這個地方,后面會用到。
3. 導出流程定義:讓代碼認識你的流程圖
畫好流程圖后,點擊左上角的保存,會讓你輸入流程名稱、key 等信息。流程 key 很重要,后面啟動流程的時候要靠它來識別,比如咱們設為 leaveProcess。保存后,點擊右上角的「導出為 XML」,會得到一個 .bpmn20.xml 文件,這個文件就是流程定義的核心,后面要部署到引擎里。
三、代碼實現(xiàn):讓流程跑起來才是硬道理
1. 引入流程引擎對象:Flowable 的核心工具
在 Spring Boot 里,F(xiàn)lowable 會自動注入一個 ProcessEngine 對象,里面封裝了所有操作流程的方法。咱們可以在需要的地方直接注入:
@Autowired private ProcessEngine processEngine;
這個對象就像一個超級管家,能幫我們部署流程、啟動流程、查詢?nèi)蝿盏鹊?,后面咱會頻繁用到它。
2. 部署流程定義:把流程圖交給引擎管理
有兩種方式部署流程,一種是直接讀取本地的 .bpmn20.xml 文件,另一種是通過 Modeler 界面上傳部署。咱們先試試代碼部署:
@RestController @RequestMapping("/process") public class ProcessController { @Autowired private RepositoryService repositoryService; // 流程定義相關服務 @PostMapping("/deploy") public String deployProcess() { // 讀取流程文件 ClassPathResource resource = new ClassPathResource("processes/leaveProcess.bpmn20.xml"); try { InputStream inputStream = resource.getInputStream(); // 部署流程 Deployment deployment = repositoryService.createDeployment() .name("請假流程") .addInputStream("leaveProcess.bpmn20.xml", inputStream) .deploy(); return "部署成功,部署 ID:" + deployment.getId(); } catch (IOException e) { e.printStackTrace(); return "部署失敗"; } } }
這里用到了 RepositoryService,它是專門管理流程定義和部署的服務。部署成功后,數(shù)據(jù)庫里的 ACT_RE_DEPLOYMENT、ACT_RE_PROCDEF 等表就會有數(shù)據(jù)了,這就是流程定義的元數(shù)據(jù)。
3. 啟動流程實例:讓流程開始跑起來
部署完流程后,就可以啟動流程實例了。比如小明要請假,就需要啟動一個請假流程的實例:
@PostMapping("/start") public String startProcess(@RequestParam String userId) { // 使用流程 key 啟動流程,并傳遞參數(shù)(這里傳遞申請人 ID) Map<String, Object> variables = new HashMap<>(); variables.put("applicant", userId); // 申請人 ID,后面審批時可能會用到 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess", variables); return "流程啟動成功,流程實例 ID:" + processInstance.getId(); }
這里用到了 RuntimeService,它是管理運行中的流程實例和執(zhí)行對象的服務。啟動流程時可以傳遞變量,這些變量可以在流程節(jié)點中使用,比如在任務分配時,根據(jù)變量動態(tài)指定負責人。
4. 查詢用戶任務:讓用戶知道該干啥了
當流程走到用戶任務節(jié)點時,需要查詢當前用戶有哪些待辦任務。比如部門經(jīng)理登錄系統(tǒng),需要看到自己待審批的任務:
@GetMapping("/tasks") public List<Task> getTasks(@RequestParam String userId) { List<Task> tasks = taskService.createTaskQuery() .taskAssignee(userId) // 根據(jù)任務負責人查詢 .orderByTaskCreateTime().desc() // 按創(chuàng)建時間倒序排列 .list(); return tasks; }
這里用到了 TaskService,專門管理任務的創(chuàng)建、分配、完成等操作。taskAssignee 就是咱們之前在流程圖里設置的任務負責人,不過實際項目中,更多是通過候選人或者候選組來分配任務,后面咱會講到。
5. 完成任務:推動流程往下走
用戶處理完任務后,需要完成任務,讓流程走到下一個節(jié)點。比如部門經(jīng)理審批通過,任務就會到 HR 那里:
@PostMapping("/complete") public String completeTask(@RequestParam String taskId, @RequestParam Map<String, Object> variables) { taskService.complete(taskId, variables); // 完成任務時可以傳遞變量,比如審批意見 return "任務完成,流程繼續(xù)"; }
這里的 variables 可以傳遞審批結果、意見等信息,這些信息可以在后續(xù)節(jié)點中使用,比如 HR 審批時可以看到部門經(jīng)理的意見。
四、進階操作:讓流程更靈活更強大
1. 動態(tài)分配任務:別再寫死負責人了
前面咱們在流程圖里寫死了任務負責人 manager,這在實際項目中肯定不行,畢竟不同的流程可能有不同的審批人。Flowable 支持多種任務分配方式:
- 候選人(Candidate Users):任務可以分配給多個候選人,任何一個候選人都可以認領任務。
// 分配候選人 taskService.addCandidateUser(taskId, "user1"); taskService.addCandidateUser(taskId, "user2"); // 查詢候選人任務 List<Task> candidateTasks = taskService.createTaskQuery() .taskCandidateUser(userId) .list();
- 候選組(Candidate Groups):把用戶分組,任務分配給組,組內(nèi)成員都可以處理。
taskService.addCandidateGroup(taskId, "managerGroup");
- 表達式分配:在流程圖里用 EL 表達式動態(tài)指定負責人,比如根據(jù)流程變量 managerId 來分配。
assignee="${managerId}"
2. 流程變量的妙用:讓流程更智能
流程變量就像流程里的「全局變量」,可以在各個節(jié)點之間傳遞數(shù)據(jù)。比如請假流程里的請假天數(shù)、請假原因,都可以作為流程變量存儲,還可以在流程條件中使用。比如添加一個條件分支,請假天數(shù)超過 3 天需要總經(jīng)理審批:
在流程圖里加一個「排他網(wǎng)關」,連接線的條件表達式可以寫:
${days > 3}
然后在啟動流程時傳遞 days 變量,流程就會根據(jù)這個條件自動選擇走向。
3. 歷史數(shù)據(jù)查詢:出了問題別慌,有記錄可查
前面咱們設置了 history.level=full,現(xiàn)在可以用 HistoryService 來查詢歷史數(shù)據(jù)了。比如查詢某個流程實例的所有歷史任務:
@Autowired private HistoryService historyService; public void queryHistoryTasks(String processInstanceId) { List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .orderByHistoricTaskInstanceStartTime().asc() .list(); for (HistoricTaskInstance task : tasks) { System.out.println("任務名稱:" + task.getName() + ",負責人:" + task.getAssignee() + ",狀態(tài):" + task.getTaskStatus()); } }
還可以查詢歷史流程變量、歷史審批意見等,簡直是排查問題的神器。
4. 與 Spring Security 集成:安全問題不能忽視
如果你的項目用了 Spring Security 做權限管理,F(xiàn)lowable 的 Modeler 界面也需要做權限控制??梢宰远x一個認證處理器,實現(xiàn) FlowableAuthenticationProvider 接口,把 Spring Security 的用戶信息傳遞給 Flowable。具體代碼有點多,這里就不展開了,后面咱們可以專門寫一篇講權限集成的文章。
五、實戰(zhàn)案例:搞一個完整的請假流程
1. 流程圖設計(升級版)
咱們來設計一個更真實的請假流程:
- 開始事件 -> 申請人填寫請假單(用戶任務,收集請假天數(shù)、原因等信息)
- -> 排他網(wǎng)關(判斷請假天數(shù)是否超過 3 天)
是 -> 部門經(jīng)理審批 -> HR 審批 -> 總經(jīng)理審批(如果是管理層請假,跳過總經(jīng)理審批)
否 -> HR 審批
- 結束事件
這里用到了網(wǎng)關和條件分支,讓流程更靈活。畫流程圖的時候,記得給每個節(jié)點起有意義的名字,方便后續(xù)代碼處理。
2. 服務層代碼實現(xiàn)(關鍵邏輯)
@Service publicclass LeaveProcessService { @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; // 啟動請假流程 publicString startLeaveProcess(String applicant, int days, String reason) { Map<String, Object> variables = new HashMap<>(); variables.put("applicant", applicant); variables.put("days", days); variables.put("reason", reason); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess", variables); return processInstance.getId(); } // 審批任務(支持通過/拒絕) publicvoid approveTask(String taskId, String approver, boolean approved, String comment) { Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if (task == null) { thrownew RuntimeException("任務不存在"); } // 先認領任務(如果是候選人) taskService.claim(taskId, approver); Map<String, Object> variables = new HashMap<>(); variables.put("approved", approved); variables.put("comment", comment); taskService.complete(taskId, variables); } // 查詢當前用戶的待辦任務(包括候選任務) public List<Task> get待辦任務(String userId) { return taskService.createTaskQuery() .taskAssignee(userId) .or() .taskCandidateUser(userId) .endOr() .orderByTaskCreateTime().desc() .list(); } }
這里重點處理了候選人任務的認領、審批意見的記錄,以及復雜的任務查詢條件。
3. 測試流程(Postman 走起)
- 啟動流程:POST /process/start?userId=xiaoming,請求體傳 {"days": 5, "reason": "去看偶像演唱會"}
- 查詢小明的任務:此時沒有任務,因為第一個任務是申請人填寫請假單,這里咱們簡化了,假設啟動流程后直接到部門經(jīng)理審批。
- 部門經(jīng)理審批:POST /process/complete?taskId=123&variables={"approved": true, "comment": "同意"}
- 查看流程狀態(tài):通過 runtimeService.createProcessInstanceQuery().processInstanceId(xxx).singleResult() 查看是否走到下一個節(jié)點。
六、常見問題與解決方案:踩過的坑都給你填上
1. 依賴沖突:找不到 Flowable 的類
有時候引入 Flowable 依賴后,會和 Spring Boot 的某些版本沖突,比如出現(xiàn) ClassNotFoundException: org.flowable.engine.ProcessEngine。這時候可以檢查 pom.xml 里的依賴版本,確保 Flowable 的版本和 Spring Boot 兼容,或者排除沖突的依賴:
<dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter-process</artifactId> <version>6.8.0</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>
2. 流程定義版本問題:改了流程圖沒生效
每次部署流程定義時,F(xiàn)lowable 會生成新的版本。如果啟動流程時還是用舊的 key,可能會走到舊版本的流程??梢栽诓渴饡r加上 deploymenyName 和 key,或者在啟動流程時指定版本:
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess", "1.0"); // 指定版本 1.0
或者直接用最新版本:
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder() .processDefinitionKey("leaveProcess") .latestVersion() .start();
3. 任務查詢性能問題:數(shù)據(jù)量大了查不動
當任務表數(shù)據(jù)量很大時,taskService.createTaskQuery().list() 會很慢。可以給 ACT_RU_TASK 表的 ASSIGNEE、CANDIDATE_USER 等字段加索引,或者分頁查詢:
Page<Task> tasks = taskService.createTaskQuery() .taskAssignee(userId) .orderByTaskCreateTime().desc() .listPage(pageable.getPageNumber(), pageable.getPageSize());
4. 中文亂碼問題:流程節(jié)點名稱顯示亂碼
如果在流程圖里用了中文,部署后數(shù)據(jù)庫里顯示亂碼,需要檢查數(shù)據(jù)庫連接是否設置了正確的字符集,以及 Modeler 保存時是否編碼正確。確保數(shù)據(jù)庫、連接 URL、表結構都是 UTF-8 編碼。
七、Flowable 對比其他工作流引擎:為啥選它?
1. 對比 Activiti
Flowable 其實是從 Activiti 團隊分裂出來的,兼容性很好,Activiti 的流程定義可以直接拿到 Flowable 里用。但 Flowable 在性能和擴展性上做了優(yōu)化,比如支持分布式部署、更好的異步處理,而且社區(qū)活躍度更高,文檔更詳細。
2. 對比 Camunda
Camunda 功能很強大,支持云部署,但商業(yè)版收費較高,社區(qū)版功能有限。Flowable 保持了開源免費策略,同時提供了企業(yè)版支持,性價比更高,適合中小團隊使用。
3. 為啥和 Spring Boot 是絕配?
- 自動配置:不用手動寫一堆配置類,開箱即用。
- 無縫集成:和 Spring 的依賴注入、事務管理、安全框架完美結合。
- 生態(tài)豐富:可以和 Spring Cloud、MyBatis 等框架輕松搭配,拓展性強。
八、總結:爽在哪里?
用 Spring Boot 搭 Flowable 搞工作流,爽就爽在這幾個地方:
- 開發(fā)效率高:不用從頭寫工作流邏輯,可視化設計器讓流程建模像搭積木。
- 維護成本低:流程變更只需要改流程圖,部署新版本即可,代碼幾乎不用動。
- 擴展性強:支持各種復雜流程場景,無論是串行、并行、分支、循環(huán),都能輕松搞定。
- 調(diào)試方便:豐富的歷史記錄和查詢接口,讓你隨時知道流程走到哪一步,出了問題分分鐘定位。
到此這篇關于SpringBoot搭Flowable搞工作流的實現(xiàn)示例的文章就介紹到這了,更多相關SpringBoot搭Flowable工作流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解在spring中使用JdbcTemplate操作數(shù)據(jù)庫的幾種方式
這篇文章主要介紹了詳解在spring中使用JdbcTemplate操作數(shù)據(jù)庫的幾種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07springboot調(diào)用python腳本的實現(xiàn)示例
本文介紹了在SpringBoot應用中調(diào)用Python腳本,包括ProcessBuilder類和ApacheCommonsExec庫兩種方法,具有一定的參考價值,感興趣的可以了解一下2024-12-12SpringBoot配置Druid數(shù)據(jù)監(jiān)控代碼實例
這篇文章主要介紹了SpringBoot配置Druid數(shù)據(jù)監(jiān)控代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-06-06spring-core組件詳解——PropertyResolver屬性解決器
這篇文章主要介紹了spring-core組件詳解——PropertyResolver屬性解決器,需要的朋友可以參考下2016-05-05SpringBoot大學心理服務系統(tǒng)實現(xiàn)流程分步講解
本系統(tǒng)主要論述了如何使用JAVA語言開發(fā)一個大學生心理服務系統(tǒng) ,本系統(tǒng)將嚴格按照軟件開發(fā)流程進行各個階段的工作,采用B/S架構,面向對象編程思想進行項目開發(fā)2022-09-09