Flowable7.x學習筆記(十八)拾取我的待辦

前言

? ? ? ? 本文從解讀源碼到實現功能,完整的學習Flowable的【TaskService】-【claim】方法實現的任務拾取功能。

一、概述

????????當調用 TaskService.claim(taskId, userId) 時,Flowable 會先加載并校驗任務實體,再判斷該任務是否已被認領;若未被認領,則將 assignee 字段設為傳入的 userId(null 則表示“解綁”),并在身份鏈接(IdentityLink)表中做相應的增刪操作;隨后觸發任務分配事件、寫入歷史記錄(如開啟歷史配置時),最后將更新結果刷新到數據庫。所有這些操作最終都委托給了 TaskServiceImpl 中的 ClaimTaskCmd 來完成。

二、方法簽名與定位

????????claim(String taskId, String userId) 方法在 org.flowable.engine.TaskService 接口中定義,由 TaskServiceImpl 實現,實現層直接調用命令執行器(commandExecutor.execute(new ClaimTaskCmd(taskId, userId))),ClaimTaskCmd 是執行認領邏輯的核心。

????????ClaimTaskCmd 繼承自 NeedsActiveTaskCmd,用于確保任務處于激活狀態后再執行認領邏輯。

public class ClaimTaskCmd extends NeedsActiveTaskCmd {
? ? protected String userId;
? ? public ClaimTaskCmd(String taskId, String userId) {
? ? ? ? super(taskId);
? ? ? ? this.userId = userId;
? ? }
}

????????構造器保存了要認領的 taskId 和 userId,并由父類處理掛起狀態檢查,當傳入的 userId 非空時,表示要將任務分配給某位用戶。

①?設置認領時間與狀態

????????使用引擎的 Clock 獲取當前時間,調用 task.setClaimTime(...) 和 task.setClaimedBy(userId) 設置認領元數據。將任務狀態更新為 Task.CLAIMED。

②?沖突檢查

????????若該任務已被分配給某人(task.getAssignee()!=null),且與當前 userId 不同,則拋出 FlowableTaskAlreadyClaimedException,避免多用戶并發認領。

????????若已分配給同一用戶,則僅記錄一次 recordTaskInfoChange(...),保持歷史一致性。

③?首次分配

????????當任務尚未有 assignee 時,調用 TaskHelper.changeTaskAssignee(task, userId) 為任務設置新認領者。

????????如果配置了 UserTaskStateInterceptor,會觸發其 handleClaim(...) 回調,用于外部擴展。

④?寫入身份鏈接歷史

????????最后,無論是首次分配還是重復分配,都通過 HistoryManager.createUserIdentityLinkComment(...) 將 ASSIGNEE 類型的身份鏈接(認領記錄)寫入歷史審計表。

三、加載與校驗任務

①?加載任務實體

????????命令中首先通過 TaskEntityManager.findById(taskId) 從 ACT_RU_TASK 表中獲取 TaskEntity。

②?任務存在性檢查

????????若返回 null,拋出 FlowableObjectNotFoundException,提示“無此任務”。

③?已認領沖突檢查

????????若 task.getAssignee() 不為 null 且與傳入的 userId 不同,則拋出沖突異常(FlowableConflictException),禁止不同用戶重復認領.

四、賦值 assignee 與解綁

①?設為認領

????????當 userId 非空時,調用 TaskEntity.setAssignee(userId) 將任務歸屬新認領者。

②?設為解綁

????????當 userId==null 時,等價于 unclaim 操作,相當于將 assignee 設回 null。

五、身份鏈接(IdentityLink)處理

①?歷史遺留機制

????????Flowable 中“候選人”與“認領人”都存儲在 ACT_RU_IDENTITYLINK 表,但 ASSIGNEE 類型在表中常伴隨特殊處理(空 TASK_ID 或 PROC_INSTANCE_ID 字段)。

② 若是認領

????????在命令里會先 刪除 原有與該任務相關的 ASSIGNEE 類型 IdentityLink,再 新增 一條新的 IdentityLinkType.ASSIGNEE,以確保數據一致。

③?若是解綁

????????則只執行刪除操作,不新增。

六、事件分發與歷史記錄

①?事件分發

????????完成認領后,Flowable 會通過 EventDispatcher 發布 ENTITY_LINK_CREATED(或刪除時 ENTITY_LINK_DELETED)以及 TASK_ASSIGNED 等事件,供監聽器或審計插件消費。

②?歷史記錄

????????若引擎配置 historyLevel ≥ AUDIT,會在 ACT_HI_TASKINST 表中記錄任務認領時間、認領者等信息。

七、持久化更新

????????最后,命令通過 TaskEntityManager.update(taskEntity) 將變更刷回 ACT_RU_TASK 表,并同步提交事務。若配置了異步歷史,歷史記錄寫入也可能延遲到異步作業中完成,進一步提升性能。

八、完成后端接口

① 定義請求參數

? ? ? ? 這里我們只需要傳入任務ID即可,用戶ID我們從框架的session中獲取,一般的腳手架都是這樣的,請結合自己的腳手架處理。

package com.ceair.entity.request;import lombok.Data;import java.io.Serial;
import java.io.Serializable;/*** @author wangbaohai* @ClassName PickupMyTaskReq* @description: 拾取我的任務請求參數* @date 2025年05月03日* @version: 1.0.0*/
@Data
public class PickupMyTaskReq implements Serializable {@Serialprivate static final long serialVersionUID = 1L;// 任務編號private String taskId;}

② 定義服務接口

/*** 領取我的任務接口* 此方法允許用戶領取屬于自己的任務,根據提供的條件和參數** @param pickupMyTaskReq 領取任務的請求對象,包含領取任務所需的信息和條件* @return 返回一個Boolean值,表示任務領取是否成功true表示成功,false表示失敗*/
Boolean pickupMyTask(PickupMyTaskReq pickupMyTaskReq);

③ 實現服務接口

/*** 拾取我的待辦任務* <p>* 此方法允許當前登錄用戶拾取一個待辦任務通過提供任務ID和當前用戶信息,* 系統將該任務分配給當前用戶** @param pickupMyTaskReq 包含任務ID的請求對象如果請求對象或任務ID為空,*                        將拋出IllegalArgumentException異常* @return 任務拾取成功返回true,否則拋出異常* @throws BusinessException        如果用戶未登錄或任務拾取過程中發生業務異常* @throws IllegalArgumentException 如果輸入參數不合法,如任務ID為空*/
@Override
public Boolean pickupMyTask(PickupMyTaskReq pickupMyTaskReq) {try {// 參數判空if (pickupMyTaskReq == null || StringUtils.isBlank(pickupMyTaskReq.getTaskId())) {log.error("任務拾取失敗:非法的任務ID");throw new IllegalArgumentException("任務拾取失敗:非法的任務ID");}// 獲取當前登錄用戶信息UserInfo userInfo = userInfoUtils.getUserInfoFromAuthentication();if (userInfo == null) {log.error("拾取我的待辦任務失敗,原因:用戶未登錄");throw new BusinessException("拾取我的待辦任務失敗,原因:用戶未登錄");}// 通過 taskService 拾取任務taskService.claim(pickupMyTaskReq.getTaskId(), String.valueOf(userInfo.getId()));return true;} catch (IllegalArgumentException e) {log.error("任務拾取失敗,原因:參數錯誤", e);throw new BusinessException("任務拾取失敗,原因:參數錯誤", e);} catch (BusinessException e) {log.error("任務拾取失敗,原因:業務異常", e);throw new BusinessException("任務拾取失敗,原因:業務異常", e);} catch (Exception e) {log.error("任務拾取失敗,原因:未知異常", e);throw new BusinessException("任務拾取失敗,原因:未知異常", e);}
}

④ 定義功能接口

/*** 任務拾取。* <p>* 權限: /api/v1/myTask/pickupMyTask* 參數: pickupMyTaskReq - 包含任務拾取相關信息的請求對象* 返回: Result<Boolean> 表示任務拾取是否成功* <p>* 異常處理:* - 業務層異常  返回任務拾取失敗信息* - 其他未知異常  系統異常提示*/
@PreAuthorize("hasAnyAuthority('/api/v1/myTask/pickupMyTask')")
@Parameter(name = "pickupMyTaskReq", description = "任務拾取請求對象", required = true)
@Operation(summary = "任務拾取")
@PostMapping("/pickupMyTask")
public Result<Boolean> pickupMyTask(@RequestBody PickupMyTaskReq pickupMyTaskReq) {try {// 調用業務層方法,將任務分配給當前登錄用戶return Result.success(mayTaskService.pickupMyTask(pickupMyTaskReq));} catch (Exception e) {log.error("任務拾取失敗,原因:{}", e.getMessage());return Result.error("任務拾取失敗,原因:" + e.getMessage());}
}

九、完善前端功能按鈕

① 定義前端參數類型

// 拾取任務請求參數

export interface PickupMyTaskReq {

? taskId: string // 任務編號,對應 Java 中的 String taskId

}

② 封裝請求接口

/**

?* 拾取任務

?*/

export function pickupMyTask(data: PickupMyTaskReq) {

? return request.post<any>({

? ? url: '/pm-process/api/v1/myTask/pickupMyTask',

? ? data,

? })

}

③ 優化界面按鈕

? ? ? ? 這里把拾取按鈕加上權限自定義指令,以及點擊事件功能

<el-button v-if="scope.row.status === 1" v-hasButton="`btn.myTask.pickupMyTask`" type="primary" @click="onPickup(scope.row)">
? 拾取
</el-button>

④ 完成按鈕功能

/*** 異步函數用于處理任務拾取操作* @param row 任務對象,包含任務ID等信息*/
async function onPickup(row: TaskVO) {try {// 獲取當前任務ID并設置參數const param: PickupMyTaskReq = {taskId: row.taskId,}// 調用后端接口進行拾取操作const result: any = await pickupMyTask(param)// 如果接口調用成功且返回的狀態碼為200,則顯示成功提示信息if (result.success && result.code === 200) {ElMessage({message: '拾取成功',type: 'success',})// 重新加載數據handerPageData()}else {ElMessage({message: `拾取失敗: ${result.message}`,type: 'error',})}}catch (error) {// 捕獲異常并提取錯誤信息let errorMessage = '未知錯誤'if (error instanceof Error) {errorMessage = error.message}// 顯示操作失敗的錯誤提示信息ElMessage({message: `拾取失敗: ${errorMessage || '未知錯誤'}`,type: 'error',})}
}

十、添加權限

創建按鈕

分配權限

十一、驗證功能

定義一個流程并且發布,第一個節點我們作為候選人,讓我們可以拾取

啟動流程

查看待辦

拾取待辦

可以看到我們拾取任務成功后,就可以辦理或者歸還任務了。

后記

????????下一篇文章來梳理歸還任務的梳理以及具體的實現方法,本文的完整代碼倉庫地址請查看專欄第一篇文章的說明。

本文的后端分支是 process-10

本文的前端分支是 process-12

?

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

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

相關文章

SQL經典實例

第1章 檢索記錄 1.1 檢索所有行和列 知識點&#xff1a;使用SELECT *快速檢索表中所有列&#xff1b;顯式列出列名&#xff08;如SELECT col1, col2&#xff09;提高可讀性和可控性&#xff0c;尤其在編程場景中更清晰。 1.2 篩選行 知識點&#xff1a;通過WHERE子句過濾符合條…

HTTPcookie與session實現

1.HTTP Cookie 定義 HTTP Cookie &#xff08;也稱為 Web Cookie 、瀏覽器 Cookie 或簡稱 Cookie &#xff09;是服務器發送到 用戶瀏覽器并保存在瀏覽器上的一小塊數據&#xff0c;它會在瀏覽器之后向同一服務器再次發 起請求時被攜帶并發送到服務器上。通常&#xff0…

【算法基礎】冒泡排序算法 - JAVA

一、算法基礎 1.1 什么是冒泡排序 冒泡排序是一種簡單直觀的比較排序算法。它重復地走訪待排序的數列&#xff0c;依次比較相鄰兩個元素&#xff0c;如果順序錯誤就交換它們&#xff0c;直到沒有元素需要交換為止。 1.2 基本思想 比較相鄰元素&#xff1a;從頭開始&#xf…

0902Redux_狀態管理-react-仿低代碼平臺項目

文章目錄 1 Redux 概述1.1 核心概念1.2 基本組成1.3 工作流程1.4 中間件&#xff08;Middleware&#xff09;1.5 適用場景1.6 優缺點1.7 Redux Toolkit&#xff08;現代推薦&#xff09;1.8 與其他工具的對比1.9 總結 2 todoList 待辦事項案例3 Redux開發者工具3.1 核心功能3.2…

《ATPL地面培訓教材13:飛行原理》——第6章:阻力

翻譯&#xff1a;Leweslyh&#xff1b;工具&#xff1a;Cursor & Claude 3.7&#xff1b;過程稿 第6章&#xff1a;阻力 目錄 引言寄生阻力誘導阻力減少誘導阻力的方法升力對寄生阻力的影響飛機總阻力飛機總重量對總阻力的影響高度對總阻力的影響構型對總阻力的影響速度穩…

C++總結01-類型相關

一、數據存儲 1.程序數據段 ? 靜態&#xff08;全局&#xff09;數據區&#xff1a;全局變量、靜態變量 ? 堆內存&#xff1a;程序員手動分配、手動釋放 ? 棧內存&#xff1a;編譯器自動分配、自動釋放 ? 常量區&#xff1a;編譯時大小、值確定不可修改 2.程序代碼段 ?…

【Hot 100】94. 二叉樹的中序遍歷

目錄 引言二叉樹的中序遍歷我的解題代碼優化更清晰的表述建議&#xff1a; &#x1f64b;?♂? 作者&#xff1a;海碼007&#x1f4dc; 專欄&#xff1a;算法專欄&#x1f4a5; 標題&#xff1a;【Hot 100】94. 二叉樹的中序遍歷?? 寄語&#xff1a;書到用時方恨少&#xff…

大語言模型(LLMs)微調技術總結

文章目錄 全面總結當前大語言模型&#xff08;LLM&#xff09;微調技術1. 引言2. 為什么需要微調&#xff1f;3. 微調技術分類概覽4. 各種微調技術詳細介紹4.1 基礎微調方法4.1.1 有監督微調&#xff08;Supervised Fine-Tuning, SFT&#xff09;4.1.2 全參數微調&#xff08;F…

解決Maven項目中報錯“java不支持版本6即更高的版本 7”

錯誤背景 當Maven項目編譯或運行時出現錯誤提示 Java不支持版本6即更高的版本7&#xff0c;通常是由于項目配置的JDK版本與當前環境或編譯器設置不一致導致的。例如&#xff1a; 項目配置的Java版本為6或7&#xff0c;但實際使用的是JDK 17。Maven或IDE的編譯器未正確指定目標…

C++筆記-多態(包含虛函數,純虛函數和虛函數表等)

1.多態的概念 多態(polymorphism)的概念:通俗來說&#xff0c;就是多種形態。多態分為編譯時多態(靜態多態)和運行時多態(動態多態)&#xff0c;這里我們重點講運行時多態&#xff0c;編譯時多態(靜態多態)和運行時多態(動態多態)。編譯時多態(靜態多態)主要就是我們前面講的函…

【Unity】MVP框架的使用例子

在提到MVP之前&#xff0c;可以先看看這篇MVC的帖子&#xff1a; 【Unity】MVC的簡單分享以及一個在UI中使用的例子 MVC的不足之處&#xff1a; 在MVC的使用中&#xff0c;會發現View層直接調用了Model層的引用&#xff0c;即這兩個層之間存在著一定的耦合性&#xff0c;而MV…

前端js學算法-實踐

1、兩數之和 const twoSum (nums, target) > {const obj {}for (let m 0; m < nums.length; m) {const cur nums[m]const diff target - curif(obj.hasOwnProperty(diff)){ // 查詢對象中是否存在目標值-當前值鍵值對console.log([obj[diff], m]) // 存在則直接獲取…

《MATLAB實戰訓練營:從入門到工業級應用》趣味入門篇-用聲音合成玩音樂:MATLAB電子琴制作(超級趣味實踐版)

《MATLAB實戰訓練營&#xff1a;從入門到工業級應用》趣味入門篇-用聲音合成玩音樂&#xff1a;MATLAB電子琴制作&#xff08;超級趣味實踐版&#xff09; 開篇&#xff1a;當MATLAB遇見音樂 - 一場數字與藝術的浪漫邂逅 想象一下&#xff0c;你正坐在一臺古老的鋼琴前&#x…

實戰探討:為什么 Redis Zset 選擇跳表?

在了解了跳表的原理和實現后&#xff0c;一個常見的問題&#xff08;尤其是在面試中&#xff09;隨之而來&#xff1a;為什么像 Redis 的有序集合 (Zset) 這樣的高性能組件會選擇使用跳表&#xff0c;而不是大家熟知的平衡樹&#xff08;如紅黑樹&#xff09;呢&#xff1f; 對…

數據結構-線性結構(鏈表、棧、隊列)實現

公共頭文件common.h #define TRUE 1 #define FALSE 0// 定義節點數據類型 #define DATA_TYPE int單鏈表C語言實現 SingleList.h #pragma once#include "common.h"typedef struct Node {DATA_TYPE data;struct Node *next; } Node;Node *initList();void headInser…

高中數學聯賽模擬試題精選學數學系列第3套幾何題

△ A B C \triangle ABC △ABC 的內切圓 ⊙ I \odot I ⊙I 分別與邊 B C BC BC, C A CA CA, A B AB AB 相切于點 D D D, E E E, F F F, D D ′ DD DD′ 為 ⊙ I \odot I ⊙I 的直徑, 過圓心 I I I 作直線 A D ′ AD AD′ 的垂線 l l l, 直線 l l l 分別與 D E DE…

使用 ossutil 上傳文件到阿里云 OSS

在處理文件存儲和傳輸時&#xff0c;阿里云的對象存儲服務&#xff08;OSS&#xff09;是一個非常方便的選擇。特別是在需要批量上傳文件或通過命令行工具進行文件管理時&#xff0c;ossutil提供了強大的功能。本文將詳細說明如何使用 ossutil 上傳文件到阿里云 OSS&#xff0c…

DeepSeek與MySQL:開啟數據智能新時代

目錄 一、引言&#xff1a;技術融合的力量二、DeepSeek 與 MySQL&#xff1a;技術基石2.1 DeepSeek 技術探秘2.2 MySQL 數據庫深度解析 三、DeepSeek 與 MySQL 集成&#xff1a;從理論到實踐3.1 集成原理剖析3.2 集成步驟詳解 四、應用案例&#xff1a;實戰中的價值體現4.1 電商…

WebAPI項目從Newtonsoft.Json遷移到System.Text.Json踩坑備忘

1.控制器層方法返回類型不能為元組 控制器層方法返回類型為元組時&#xff0c;序列化結果為空。 因為元組沒有屬性只有field&#xff0c;除非使用IncludeFields參數專門指定&#xff0c;否則使用System.Text.Json進行序列化時不會序列化field var options new JsonSerializ…

202553-sql

目錄 一、196. 刪除重復的電子郵箱 - 力扣&#xff08;LeetCode&#xff09; 二、602. 好友申請 II &#xff1a;誰有最多的好友 - 力扣&#xff08;LeetCode&#xff09; 三、176. 第二高的薪水 - 力扣&#xff08;LeetCode&#xff09; 一、196. 刪除重復的電子郵箱 - 力扣…