Activiti工作流總結
1:工作流的概念
工作流(Workflow),就是“業務過程的部分或整體在計算機應用環境下的自動化”,它主要解決的是“使在多個參與者之間按照某種預定義的規則傳遞文件、資訊或任務的過程自動進行,從而實現某個預期的業務目標,或者促使此目標的實現”。
工作流管理系統(Workflow Management System, WfMS)是一個軟體系統,它完成工作量的定義和管理,並按照在系統中預先定義好的工作流規則進行工作流例項的執行。工作流管理系統不是企業的業務系統,而是為企業的業務系統的執行提供了一個軟體的支撐環境。
工作流管理聯盟(WfMC,Workflow Management Coalition)給出的關於工作流管理系統的定義是:工作流管理系統是一個軟體系統,它通過執行經過計算的流程定義去支援一批專門設定的業務流程。工作流管理系統被用來定義、管理、和執行工作流程。
工作流管理系統的目標:管理工作的流程以確保工作在正確的時間被期望的人員所執行——在自動化進行的業務過程中插入人工的執行和干預。
2:工作流的執行過程 演示程式:Activiti控制檯
3:Activiti介紹 Activiti5是由Alfresco軟體在2010年5月17日釋出的業務流程管理(BPM)框架,它是覆蓋了業務流程管理、工作流、服務協作等領域的一個開源的、靈活的、易擴充套件的可執行流程語言框架。Activiti基於Apache許可的開源BPM平臺,創始人Tom Baeyens是JBoss jBPM的專案架構師,它特色是提供了eclipse外掛,開發人員可以通過外掛直接繪畫出業務
流程圖。
3.1:工作流引擎 ProcessEngine物件,這是Activiti工作的核心。負責生成流程執行時的各種例項及資料、監控和管理流程的執行。
3.2:BPMN 業務流程建模與標註(Business Process Model and Notation,BPMN),描述流程的基本符號,包括這些圖元如何組合成一個業務流程圖(Business Process Diagram)
3.3:資料庫(先學後看) Activiti資料庫支援:
Activiti的後臺是有資料庫的支援,所有的表都以ACT_開頭。 第二部分是表示表的用途的兩個字母標識。 用途也和服務的API對應。
ACT_RE_*: 'RE'表示repository。 這個字首的表包含了流程定義和流程靜態資源 (圖片,規則,等等)。
ACT_RU_*: 'RU'表示runtime。 這些執行時的表,包含流程例項,任務,變數,非同步任務,等執行中的資料。 Activiti只在流程例項執行過程中儲存這些資料, 在流程結束時就會刪除這些記錄。 這樣執行時表可以一直很小速度很快。
ACT_ID_*: 'ID'表示identity。 這些表包含身份資訊,比如使用者,組等等。
ACT_HI_*: 'HI'表示history。 這些表包含歷史資料,比如歷史流程例項, 變數,任務等等。
ACT_GE_*: 通用資料, 用於不同場景下,如存放資原始檔。
表結構操作:
3.3.1:資源庫流程規則表 1) act_re_deployment 部署資訊表
2) act_re_model 流程設計模型部署表
3) act_re_procdef 流程定義資料表
3.3.2:執行時資料庫表 1) act_ru_execution 執行時流程執行例項表
2) act_ru_identitylink 執行時流程人員表,主要儲存任務節點與參與者的相關資訊
3) act_ru_task 執行時任務節點表
4) act_ru_variable 執行時流程變數資料表
3.3.3:歷史資料庫表 1) act_hi_actinst 歷史節點表
2) act_hi_attachment 歷史附件表
3) act_hi_comment 歷史意見表
4) act_hi_identitylink 歷史流程人員表
5) act_hi_detail 歷史詳情表,提供歷史變數的查詢
6) act_hi_procinst 歷史流程例項表
7) act_hi_taskinst 歷史任務例項表
8) act_hi_varinst 歷史變量表
3.3.4:組織機構表 1) act_id_group 使用者組資訊表
2) act_id_info 使用者擴充套件資訊表
3) act_id_membership 使用者與使用者組對應資訊表
4) act_id_user 使用者資訊表
這四張表很常見,基本的組織機構管理,關於使用者認證方面建議還是自己開發一套,元件自帶的功能太簡單,使用中有很多需求難以滿足
3.3.5:通用資料表 1) act_ge_bytearray 二進位制資料表
2) act_ge_property 屬性資料表儲存整個流程引擎級別的資料,初始化表結構時,會預設插入三條記錄,
3.4:activiti.cfg.xml(activiti的配置檔案) Activiti核心配置檔案,配置流程引擎建立工具的基本引數和資料庫連線池引數。
定義資料庫配置引數:
jdbcUrl: 資料庫的JDBC URL。 jdbcDriver: 對應不同資料庫型別的驅動。 jdbcUsername: 連線資料庫的使用者名稱。 jdbcPassword: 連線資料庫的密碼。 基於JDBC引數配置的資料庫連線 會使用預設的MyBatis連線池。 下面的引數可以用來配置連線池(來自MyBatis引數): jdbcMaxActiveConnections: 連線池中處於被使用狀態的連線的最大值。預設為10。 jdbcMaxIdleConnections: 連線池中處於空閒狀態的連線的最大值。
jdbcMaxCheckoutTime: 連線被取出使用的最長時間,超過時間會被強制回收。 預設為20000(20秒)。
jdbcMaxWaitTime: 這是一個底層配置,讓連線池可以在長時間無法獲得連線時, 列印一條日誌,並重新嘗試獲取一個連線。(避免因為錯誤配置導致沉默的操作失敗)。 預設為20000(20秒)。 示例資料庫配置:
也可以使用javax.sql.DataSource。 (比如,Apache Commons的DBCP):
3.5:logging.properties(日誌處理) 日誌的配置檔案,Activiti操作資料庫的時候,整合的日誌檔案
4:準備環境 4.1:activiti5軟體環境 1) JDK1.6或者更高版本
2) 支援的資料庫有:h2, mysql, oracle, postgres, mssql, db2等。
3) 支援activiti5執行的jar包
4) 開發環境為Eclipse3.7或者以上版本,myeclipse為8.6版本
4.2:相關資源下載 1) JDK可以到sun的官網下載
http://www.oracle.com/technetwork/java/javase/downloads/index.html
2) 資料庫,例如:mysql可以在官網上下載。
http://www.mysql.com
3) activiti也可以到Activiti官方網站下載得到。
http://activiti.org/download.html
4) Eclipse3.7或者MyEclipse8.6也可以到相應的網站上獲得下載。
4.3:安裝流程設計器(eclipse外掛) 4.3.1:安裝方式 在有網路的情況下,安裝流程設計器步驟如下:
1) 開啟 Help -> Install New Software. 在如下面板中:
2) 在如下Install介面板中,點選Add按鈕:
配置新裝外掛的地址和名稱
3) 然後填入下列欄位
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
4) 回到Install介面,在面板正中列表中把所有展示出來的專案都勾上:
5) 點選複選框
在Detail部分記得選中 "Contact all updates sites.." , 因為它會檢查所有當前安裝所需要的外掛並可以被Eclipse下載.
6) 安裝完以後,點選新建工程new->Other…打開面板,如果看到下圖內容:
說明安裝成功了。
4.4:對流程設計器的使用說明 開啟選單Windows->Preferences->Activiti->Save下流程流程圖片的生成方式:
雖然流程引擎在單獨部署bpmn檔案時會自動生成圖片,但在實際開發過程中,自動生成的圖片會導致和BPMN中的座標有出入,在實際專案中展示流程當前位置圖會有問題。
所在完成以上配置後,會由我們自己來管理流程圖片。在釋出流程時把流程規則檔案和流程圖片一起上傳就行了。
4.5:準備Activiti5開發環境 4.5.1:新增Activiti5的jar包 在activiti-5.13->wars目錄下是一些示例專案,解壓activiti-rest專案,匯入activiti-rest目錄中WEB-INF\lib下所有包。新增到classpath中。
由於我們使用的是Mysql資料庫,Mysql資料庫的連結驅動Activiti官方包中並沒有提供,需要我們自己匯入。手動匯入mysql-connector-java.jar,新增到classpath下。
4.5.2:初始化資料庫
在Activiti中,在建立核心的流程引擎物件時會自動建表。如果程式正常執行,mysql會自動建庫,然後建立23張表。
4.5.3:新增並制定配置檔案 在Actiiti5中定製流程必定會操作到資料庫,如果都像上面那樣寫一大段程式碼會非常麻煩,所以我們可以把資料庫連線配置寫入配置檔案。
在Activiti5的官方示例中並沒有現成的配置檔案,所以先得找到activiti-rest\WEB-INF\classes下有:
4.5.3.1:activiti-context.xml: 一個類似spring結構的配置檔案,清空內容後改名為activiti.cfg.xml,用來做流程引擎的相關配置。
按照上面程式碼配置ProcessEngineConfiguration物件,主要定義資料庫的連線配置和建表策略,配置檔案程式碼如下:
Java程式碼如下:
createProcessEngineConfigurationFromResource的引數值為我們新增的配置檔案activiti.cfg.xml的名稱,執行java程式碼,流程引擎物件建立成功執行後資料庫會自動建表。
4.5.3.2:log4j.properties日誌配置檔案 把兩個檔案放入resource目錄下即可。
5:核心API 5.1:ProcessEngine 說明:
1) 在Activiti中最核心的類,其他的類都是由他而來。
2) 產生方式:
在前面看到了兩種建立ProcessEngine(流程引擎)的方式,而這裡要簡化很多,呼叫ProcessEngines的getDefaultProceeEngine方法時會自動載入classpath下名為activiti.cfg.xml檔案。
3) 可以產生RepositoryService
4) 可以產生RuntimeService
5) 可以產生TaskService
各個Service的作用:
RepositoryService
管理流程定義
RuntimeService
執行管理,包括啟動、推進、刪除流程例項等操作
TaskService
任務管理
HistoryService
歷史管理(執行完的資料的管理)
IdentityService
組織機構管理
FormService
一個可選服務,任務表單管理
ManagerService
5.2:RepositoryService 是Activiti的倉庫服務類。所謂的倉庫指流程定義文件的兩個檔案:bpmn檔案和流程圖片。
1) 產生方式
2) 可以產生DeploymentBuilder,用來定義流程部署的相關引數
3) 刪除流程定義
5.3:RuntimeService 是activiti的流程執行服務類。可以從這個服務類中獲取很多關於流程執行相關的資訊。
5.4:TaskService 是activiti的任務服務類。可以從這個類中獲取任務的資訊。
5.5:HistoryService 是activiti的查詢歷史資訊的類。在一個流程執行完成後,這個物件為我們提供查詢歷史資訊。
5.6:ProcessDefinition 流程定義類。可以從這裡獲得資原始檔等。
5.7:ProcessInstance 代表流程定義的執行例項。如范冰冰請了一天的假,她就必須發出一個流程例項的申請。一個流程例項包括了所有的執行節點。我們可以利用這個物件來了解當前流程例項的進度等資訊。流程例項就表示一個流程從開始到結束的最大的流程分支,即一個流程中流程例項只有一個。
5.8:Execution Activiti用這個物件去描述流程執行的每一個節點。在沒有併發的情況下,Execution就是同ProcessInstance。流程按照流程定義的規則執行一次的過程,就可以表示執行物件Execution。
如圖為ProcessInstance的原始碼:
從原始碼中可以看出ProcessInstance就是Execution。但在現實意義上有所區別:
在單線流程中,如上圖的貸款流程,ProcessInstance與Execution是一致的。
這個例子有一個特點:wire money(匯錢)和archive(存檔)是併發執行的。這個時候,匯流排路代表ProcessInstance,而分線路中每個活動代表Execution。
總結:
* 一個流程中,執行物件可以存在多個,但是流程例項只能有一個。
* 當流程按照規則只執行一次的時候,那麼流程例項就是執行物件。
6:HelloWorld程式(模擬流程的執行) 6.1:流程圖:
6.2:部署流程定義
這裡使用RepositoryService部署流程定義
addClasspathResource表示從類路徑下載入資原始檔,一次只能載入一個檔案
6.3:啟動流程例項
6.4:檢視我的個人任務
這裡使用TaskService完成任務的查詢
6.5:完成我的個人任務
7:管理流程定義 7.1:設計流程定義文件 7.1.1:流程圖
7.1.2:bpmn檔案 BPMN 2.0根節點是definitions節點。 這個元素中,可以定義多個流程定義(不過我們建議每個檔案只包含一個流程定義, 可以簡化開發過程中的維護難度)。 一個空的流程定義看起來像下面這樣。注意,definitions元素 最少也要包含xmlns 和 targetNamespace的宣告。 targetNamespace可以是任意值,它用來對流程例項進行分類。
說明:流程定義文件有兩部分組成:
1) bpmn檔案
流程規則檔案。在部署後,每次系統啟動時都會被解析,把內容封裝成流程定義放入專案快取中。Activiti框架結合這個xml檔案自動管理流程,流程的執行就是按照bpmn檔案定義的規則執行的,bpmn檔案是給計算機執行用的。
2) 展示流程圖的圖片
在系統裡需要展示流程的進展圖片,圖片是給使用者看的。
7.2:部署流程定義(classpath路徑載入檔案)
說明:
1) 先獲取流程引擎物件:在建立時會自動載入classpath下的activiti.cfg.xml
2) 首先獲得預設的流程引擎,通過流程引擎獲取了一個RepositoryService物件(倉庫物件)
3) 由倉庫的服務物件產生一個部署物件配置物件,用來封裝部署操作的相關配置。
4) 這是一個鏈式程式設計,在部署配置物件中設定顯示名,上傳流程定義規則檔案
5) 向資料庫表中存放流程定義的規則資訊。
6) 這一步在資料庫中將操作三張表:
a) act_re_deployment(部署物件表)
存放流程定義的顯示名和部署時間,每部署一次增加一條記錄
b) act_re_procdef(流程定義表)
存放流程定義的屬性資訊,部署每個新的流程定義都會在這張表中增加一條記錄。
注意:當流程定義的key相同的情況下,使用的是版本升級
c) act_ge_bytearray(資原始檔表)
儲存流程定義相關的部署資訊。即流程定義文件的存放地。每部署一次就會增加兩條記錄,一條是關於bpmn規則檔案的,一條是圖片的(如果部署時只指定了bpmn一個檔案,activiti會在部署時解析bpmn檔案內容自動生成流程圖)。兩個檔案不是很大,都是以二進位制形式儲存在資料庫中。
7.3:部署流程定義(zip格式檔案)
將壓縮成zip格式的檔案,使用zip的輸入流用作部署流程定義
7.4:檢視流程定義 查詢流程定義的資訊
結果:
再部署一次執行結果為:
可以看到流程定義的key值相同的情況下,版本是從1開始逐次升級的
流程定義的Id是【key:版本:生成ID】
說明:
1) 流程定義和部署物件相關的Service都是RepositoryService。
2) 建立流程定義查詢物件,可以在ProcessDefinitionQuery上設定查詢的相關引數
3) 呼叫ProcessDefinitionQuery物件的list方法,執行查詢,獲得符合條件的流程定義列表
4) 由執行結果可以看出:
Key和Name的值為:bpmn檔案process節點的id和name的屬性值
5) key屬性被用來區別不同的流程定義。
6) 帶有特定key的流程定義第一次部署時,version為1。之後每次部署都會在當前最高版本號上加1
7) Id的值的生成規則為:{processDefinitionKey}:{processDefinitionVersion}:{generated-id},這裡的generated-id是一個自動生成的唯一的數字
8) 重複部署一次,deploymentId的值以一定的形式變化
規則act_ge_property表生成
7.5:刪除流程定義 刪除部署到activiti中的流程定義。
說明:
1) 因為刪除的是流程定義,而流程定義的部署是屬於倉庫服務的,所以應該先得到RepositoryService
2) 如果該流程定義下沒有正在執行的流程,則可以用普通刪除。如果是有關聯的資訊,用級聯刪除。專案開發中使用級聯刪除的情況比較多,刪除操作一般只開放給超級管理員使用。
7.6:獲取流程定義文件的資源(檢視流程圖附件) 查詢出流程定義文件。主要查的是圖片,用於顯示流程用。
說明:
1) deploymentId為流程部署ID
2) resourceName為act_ge_bytearray表中NAME_列的值
3) 使用repositoryService的getDeploymentResourceNames方法可以獲取指定部署下得所有檔案的名稱
4) 使用repositoryService的getResourceAsStream方法傳入部署ID和資源圖片名稱可以獲取部署下指定名稱檔案的輸入流
5) 最後的有關IO流的操作,使用FileUtils工具的copyInputStreamToFile方法完成流程流程到檔案的拷貝,將資原始檔以流的形式輸出到指定資料夾下
7.7:附加功能:查詢最新版本的流程定義
7.8:附加功能:刪除流程定義(刪除key相同的所有不同版本的流程定義)
7.9:總結 Deployment 部署物件
1、一次部署的多個檔案的資訊。對於不需要的流程可以刪除和修改。
2、對應的表:
act_re_deployment:部署物件表
act_re_procdef:流程定義表
act_ge_bytearray:資原始檔表
act_ge_property:主鍵生成策略表
ProcessDefinition流程定義
1、解析.bpmn後得到的流程定義規則的資訊,工作流系統就是按照流程定義的規則執行的。
8:流程例項、任務的執行 8.1:流程圖
8.2:部署流程定義
8.3:啟動流程例項
說明:
1) 操作資料庫的act_ru_execution表,如果是使用者任務節點,同時也會在act_ru_task新增一條記錄
8.4:查詢我的個人任務
說明:
1) 因為是任務查詢,所以從processEngine中應該得到TaskService
2) 使用TaskService獲取到任務查詢物件TaskQuery
3) 為查詢物件新增查詢過濾條件,使用taskAssignee指定任務的辦理者(即查詢指定使用者的代辦任務),同時可以新增分頁排序等過濾條件
4) 呼叫list方法執行查詢,返回辦理者為指定使用者的任務列表
5) 任務ID、名稱、辦理人、建立時間可以從act_ru_task表中查到。
6) Execution與ProcessInstance見5.6和5.7章節的介紹。在這種情況下,ProcessInstance相當於Execution
7) 如果assignee屬性為部門經理,結果為空。因為現在流程只到了”填寫請假申請”階段,後面的任務還沒有執行,即在資料庫中沒有部門經理可以辦理的任務,所以查詢不到。
8) 一個Task節點和Execution節點是1對1的情況,在task物件中使用Execution_來表示他們之間的關係
9) 任務ID在資料庫表act_ru_task中對應“ID_”列
附加:
在activiti任務中,主要分為兩大類查詢任務(個人任務和組任務):
1.確切指定了辦理者的任務,這個任務將成為指定者的私有任務,即個人任務。
2.無法指定具體的某一個人來辦理的任務,可以把任務分配給幾個人或者一到 多個小組,讓這個範圍內的使用者可以選擇性(如有空餘時間時)來辦理這類任務,即組任務。
先知道個人任務的查詢和辦理,組任務的操作後面講
8.5:辦理任務
說明:
1) 是辦理任務,所以從ProcessEngine得到的是TaskService。
2) 當執行完這段程式碼,再以員工的身份去執行查詢的時候,會發現這個時候已經沒有資料了,因為正在執行的任務中沒有資料。
3) 對於執行完的任務,activiti將從act_ru_task表中刪除該任務,下一個任務會被插入進來。
4) 以”部門經理”的身份進行查詢,可以查到結果。因為流程執行到部門經理審批這個節點了。
5) 再執行辦理任務程式碼,執行完以後以”部門經理”身份進行查詢,沒有結果。
6) 重複第3和4步直到流程執行完。
8.6:查詢流程狀態(判斷流程正在執行,還是結束)
在流程執行的過程中,建立的流程例項ID在整個過程中都不會變,當流程結束後,流程例項將會在正在執行的執行物件表中(act_ru_execution)被刪除
說明:
1) 因為是查詢流程例項,所以先獲取runtimeService
2) 建立流程例項查詢物件,設定例項ID過濾引數
3) 由於一個流程例項ID只對應一個例項,使用singleResult執行查詢返回一個唯一的結果,如果結果數量大於1,則丟擲異常
4) 判斷指定ID的例項是否存在,如果結果為空,則代表流程結束,例項在正在執行的執行物件表中已被刪除,轉換成歷史資料。
8.7:附加功能:查詢歷史任務(後面講)
8.8:附加功能:查詢歷史流程例項(後面講)
8.9:總結 Execution 執行物件
按流程定義的規則執行一次的過程.
對應的表:
act_ru_execution: 正在執行的資訊
act_hi_procinst:已經執行完的歷史流程例項資訊
act_hi_actinst:存放歷史所有完成的活動
ProcessInstance 流程例項
特指流程從開始到結束的那個最大的執行分支,一個執行的流程中,流程例項只有1個。
注意
(1)如果是單例流程,執行物件ID就是流程例項ID
(2)如果一個流程有分支和聚合,那麼執行物件ID和流程例項ID就不相同
(3)一個流程中,流程例項只有1個,執行物件可以存在多個。
Task任務
執行到某任務環節時生成的任務資訊。
對應的表:
act_ru_task:正在執行的任務資訊
act_hi_taskinst:已經執行完的歷史任務資訊
9:流程變數 9.1:流程圖
流程變數在整個工作流中扮演很重要的作用。例如:請假流程中有請假天數、請假原因等一些引數都為流程變數的範圍。流程變數的作用域範圍是只對應一個流程例項。也就是說各個流程例項的流程變數是不相互影響的。流程例項結束完成以後流程變數還儲存在資料庫中。
例如:
即:
9.2:部署流程定義
說明:
• 輸入流載入資原始檔的3種方式
9.3:啟動流程例項
9.4:設定流程變數
說明:
1) 流程變數的作用域就是流程例項,所以只要設定就行了,不用管在哪個階段設定
2) 基本型別設定流程變數,在taskService中使用任務ID,定義流程變數的名稱,設定流程變數的值。
3) Javabean型別設定流程變數,需要這個javabean實現了Serializable介面
4) 設定流程變數的時候,向act_ru_variable這個表新增資料
9.5:獲取流程變數
說明:
1) 流程變數的獲取針對流程例項(即1個流程),每個流程例項獲取的流程變數時不同的
2) 使用基本型別獲取流程變數,在taskService中使用任務ID,流程變數的名稱,獲取流程變數的值。
3) Javabean型別設定獲取流程變數,除了需要這個javabean實現了Serializable介面外,還要求流程變數物件的屬性不能發生編號,否則丟擲異常。
9.6:模擬流程變數的設定和獲取的場景
說明:
1) RuntimeService物件可以設定流程變數和獲取流程變數
2) TaskService物件可以設定流程變數和獲取流程變數
3) 流程例項啟動的時候可以設定流程變數
4) 任務辦理完成的時候可以設定流程變數
5) 流程變數可以通過名稱/值的形式設定單個流程變數
6) 流程變數可以通過Map集合,同時設定多個流程變數
Map集合的key表示流程變數的名稱
Map集合的value表示流程變數的值
9.7:查詢歷史的流程變數
說明:
1)歷史的流程變數查詢,指定流程變數的名稱,查詢act_hi_varinst表(也可以針對,流程例項ID,執行物件ID,任務ID查詢)
9.8:流程變數的支援的型別 如圖是從官網列出來的流程變數
從圖中可以看出包括了大部分封裝型別和Date、String和實現了Serializable介面的類的型別。
9.9:總結 • 1:流程變數
在流程執行或者任務執行的過程中,用於設定和獲取變數,使用流程變數在流程傳遞的過程中傳遞業務引數。
對應的表:
act_ru_variable:正在執行的流程變量表
act_hi_varinst:流程變數歷史表
• 2:擴充套件知識:setVariable和setVariableLocal的區別
setVariable:設定流程變數的時候,流程變數名稱相同的時候,後一次的值替換前一次的值,而且可以看到TASK_ID的欄位不會存放任務ID的值
setVariableLocal:
1:設定流程變數的時候,針對當前活動的節點設定流程變數,如果一個流程中存在2個活動節點,對每個活動節點都設定流程變數,即使流程變數的名稱相同,後一次的版本的值也不會替換前一次版本的值,它會使用不同的任務ID作為標識,存放2個流程變數值,而且可以看到TASK_ID的欄位會存放任務ID的值
例如act_hi_varinst 表的資料:不同的任務節點,即使流程變數名稱相同,存放的值也是不同的。
如圖:
2:還有,使用setVariableLocal說明流程變數綁定了當前的任務,當流程繼續執行時,下個任務獲取不到這個流程變數(因為正在執行的流程變數中沒有這個資料),所有查詢正在執行的任務時不能查詢到我們需要的資料,此時需要查詢歷史的流程變數。
10:流程執行歷史記錄 10.1:查詢歷史流程例項 查詢按照某個流程定義的規則一共執行了多少次流程
10.2:查詢歷史活動 某一次流程的執行一共經歷了多少個活動
10.3:查詢歷史任務 某一次流程的執行一共經歷了多少個任務
10.4:查詢歷史流程變數 某一次流程的執行一共設定的流程變數
10.5:總結 由於資料庫中儲存著歷史資訊以及正在執行的流程例項資訊,在實際專案中對已完成任務的檢視頻率遠不及對代辦和可接任務的檢視,所以在activiti採用分開管理,把正在執行的交給RuntimeService、TaskService管理,而歷史資料交給HistoryService來管理。
這樣做的好處在於,加快流程執行的速度,因為正在執行的流程的表中資料不會很大。
11:連線 11.1:流程圖
注意:如果將流程圖放置在和java類相同的路徑,需要配置:
11.2:部署流程定義+啟動流程例項
11.3:查詢我的個人任務
11.4:完成任務
說明:
1)使用流程變數,設定連線需要的流程變數的名稱message,並設定流程變數的值
對應:
11.5:總結 1、一個活動中可以指定一個或多個SequenceFlow(Start中有一個,End中沒有)。
* 開始活動中有一個SequenceFlow 。
* 結束活動中沒有SequenceFlow 。
* 其他活動中有1條或多條SequenceFlow
2、如果只有一個,則可以不使用流程變數設定codition的名稱;
如果有多個,則需要使用流程變數設定codition的名稱。message表示流程變數的名稱,‘不重要’表示流程變數的值,${}(或者#{})中間的內容要使用boolean型別的表示式,用來判斷應該執行的連線。
12:排他閘道器(ExclusiveGateWay) 12.1:流程圖
12.2:部署流程定義+啟動流程例項
12.3:查詢我的個人任務
12.4:完成我的個人任務
說明:
1) 一個排他閘道器對應一個以上的順序流
2) 由排他閘道器流出的順序流都有個conditionExpression元素,在內部維護返回boolean型別的決策結果。
3) 決策閘道器只會返回一條結果。當流程執行到排他閘道器時,流程引擎會自動檢索網關出口,從上到下檢索如果發現第一條決策結果為true或者沒有設定條件的(預設為成立),則流出。
4) 如果沒有任何一個出口符合條件,則丟擲異常
5) 使用流程變數,設定連線的條件,並按照連線的條件執行工作流,如果沒有條件符合的條件,則以預設的連線離開。例如:
則執行連線:
如果使用流程變數設定
則執行連線:
13:並行閘道器(parallelGateWay) 13.1:流程圖
13.2:部署流程定義+啟動流程例項
13.3:查詢我的個人任務
13.4:完成我的個人任務
說明:
1) 一個流程中流程例項只有1個,執行物件有多個
2) 並行閘道器的功能是基於進入和外出的順序流的:
分支(fork):並行後的所有外出順序流,為每個順序流都建立一個併發分支。
匯聚(join):所有到達並行閘道器,在此等待的進入分支,直到所有進入順序流的分支都到達以後,流程就會通過匯聚閘道器。
3) 並行閘道器的進入和外出都是使用相同節點標識
4) 如果同一個並行閘道器有多個進入和多個外出順序流, 它就同時具有分支和匯聚功能。 這時,閘道器會先匯聚所有進入的順序流,然後再切分成多個並行分支。
5) 並行閘道器不會解析條件。即使順序流中定義了條件,也會被忽略。
6)並行閘道器不需要是“平衡的”(比如, 對應並行閘道器的進入和外出節點數目不一定相等)。如圖中標示是合法的:
14:開始活動節點 14.1:流程圖
14.2:部署流程定義+啟動流程例項+查詢流程例項+查詢歷史流程例項
14.3:總結 1):結束節點沒有出口
2):其他節點有一個或多個出口。
如果有一個出口,則代表是一個單線流程;
如果有多個出口,則代表是開啟併發流程。
15:接收活動(receiveTask,即等待活動) 接收任務是一個簡單任務,它會等待對應訊息的到達。 當前,官方只實現了這個任務的java語義。 當流程達到接收任務,流程狀態會儲存到資料庫中。
在任務建立後,意味著流程會進入等待狀態,直到引擎接收了一個特定的訊息,這會觸發流程穿過接收任務繼續執行。
15.1:流程圖
15.2:部署流程定義+啟動流程例項 /**
* ReceiceTask任務,機器自動完成的任務
* 只會在act_ru_execution表中產生一條資料
* @throws Exception
*/
@Test
public void testExecution()throws Exception {
// 1釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("receiveTask.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("receiveTask.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("receiveTask.bpmn", inputStreamBpmn)//
.addInputStream("receiveTask.png", inputStreamPng)//
.deploy();
// 2啟動流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("receiveTaskDemo");
System.out.println("pid:" + pi.getId());
String pid = pi.getId();
// 3查詢是否有一個執行物件在描述”彙總當日銷售額“
Execution e1 = processEngine.getRuntimeService()//
.createExecutionQuery()//
.processInstanceId(pid)//
.activityId("彙總當日銷售額")//
.singleResult();
// 4執行一堆邏輯,並設定流程變數
Map<String,Object> vars = new HashMap<String, Object>();
vars.put("當日銷售額", 10000);
// 5流程向後執行一步:往後推移e1,使用signal給流程引擎訊號,告訴他當前任務已經完成了,可以往後執行
processEngine.getRuntimeService()
.signal(e1.getId(),vars);
// 6判斷當前流程是否在”給老闆發簡訊“節點
Execution e2 = processEngine.getRuntimeService()//
.createExecutionQuery()//
.processInstanceId(pid)//
.activityId("給總經理髮簡訊")//
.singleResult();
// 7獲取流程變數
Integer money = (Integer) processEngine.getRuntimeService()//
.getVariable(e2.getId(), "當日銷售額");
System.out.println("老闆,今天賺了" +money);
// 8向後執行一步:任務完成,往後推移”給老闆發簡訊“任務
processEngine.getRuntimeService()//
.signal(e2.getId());
// 9查詢流程狀態
pi = processEngine.getRuntimeService()//
.createProcessInstanceQuery()//
.processInstanceId(pid)//
.singleResult();
if(pi==null){
System.out.println("流程正常執行!!!,已經結束了");
}
}
說明:
1) 當前任務(一般指機器自動完成,但需要耗費一定時間的工作)完成後,向後推移流程,可以呼叫runtimeService.signal(executionId),傳遞接收執行物件的id。
16:使用者任務(userTask,即使用者操作的任務) 16.1:個人任務 16.1.1:流程圖
16.1.2::分配個人任務方式一(直接指定辦理人) 1:流程圖中任務節點的配置
2:測試程式碼:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1 釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 啟動流程
//啟動流程例項的同時,設定流程變數
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//查詢我的個人任務列表
@Test
public void findMyTaskList(){
String userId = "張三丰";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定個人任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//完成任務
@Test
public void completeTask(){
String taskId = "3209";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
說明:
1) 張三丰是個人任務的辦理人
2) 但是這樣分配任務的辦理人不夠靈活,因為專案開發中任務的辦理人不要放置XML檔案中。
16.1.3::分配個人任務方式二(使用流程變數) 1:流程圖中任務節點的配置
2:測試程式碼
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1釋出流程
InputStream inputStreamBpmn =this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng =this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2啟動流程
//啟動流程例項的同時,設定流程變數
Map<String, Object> variables =new HashMap<String, Object>();
variables.put("userID","張翠三");
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess",variables);
System.out.println("pid:" + pi.getId());
}
//查詢我的個人任務列表
@Test
public void findMyTaskList(){
String userId = "張翠三";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定個人任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//完成任務
@Test
public void completeTask(){
String taskId = "3209";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
說明:
1) 張翠山是個人任務的辦理人
2) 在開發中,可以在頁面中指定下一個任務的辦理人,通過流程變數設定下一個任務的辦理人
16.1.4::分配個人任務方式三(使用類) 1:流程圖中任務節點的配置
此時流程圖的XML檔案,如圖:
2:TaskListenerImpl類,用來設定任務的辦理人
public class TaskListenerImplimplements TaskListener {
/**指定個人任務和組任務的辦理人*/
@Override
public void notify(DelegateTask delegateTask) {
String assignee = "張無忌";
//指定個人任務
delegateTask.setAssignee(assignee);
}
}
3:測試程式碼
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1 釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 啟動流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//查詢我的個人任務列表
@Test
public void findMyTaskList(){
String userId = "張無忌";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定個人任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//完成任務
@Test
public void completeTask(){
String taskId = "3408";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
//可以分配個人任務從一個人到另一個人(認領任務)
@Test
public void setAssigneeTask(){
//任務ID
String taskId = "3408";
//指定認領的辦理者
String userId = "周芷若";
processEngine.getTaskService()//
.setAssignee(taskId, userId);
}
說明:
1) 在類中使用delegateTask.setAssignee(assignee);的方式分配個人任務的辦理人,此時張無忌是下一個任務的辦理人
2) 通過processEngine.getTaskService().setAssignee(taskId, userId);將個人任務從一個人分配給另一個人,此時張無忌不再是下一個任務的辦理人,而換成了周芷若
3) 在開發中,可以將每一個任務的辦理人規定好,例如張三的領導是李四,李四的領導是王五,這樣張三提交任務,就可以查詢出張三的領導是李四,通過類的方式設定下一個任務的辦理人
16.1.5:總結 個人任務及三種分配方式:
1:在taskProcess.bpmn中直接寫 assignee=“張三丰"
2:在taskProcess.bpmn中寫 assignee=“#{userID}”,變數的值要是String的。
使用流程變數指定辦理人
3,使用TaskListener介面,要使類實現該介面,在類中定義:
delegateTask.setAssignee(assignee);// 指定個人任務的辦理人
使用任務ID和辦理人重新指定辦理人:
processEngine.getTaskService()//
.setAssignee(taskId, userId);
16.2:組任務 16.2.1:流程圖
16.2.2::分配組任務方式一(直接指定辦理人) 1:流程圖中任務節點的配置
2:測試程式碼:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1 釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2啟動流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//3查詢我的個人任務列表
@Test
public void findMyTaskList(){
String userId = "小A";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定個人任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//4查詢組任務列表
@Test
public void findGroupList(){
String userId = "小A";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定組任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime ="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//5查詢組任務成員列表
@Test
public void findGroupUser(){
String taskId = "3709";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
//List<IdentityLink> list = processEngine.getRuntimeService()//
//.getIdentityLinksForProcessInstance(instanceId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//6查詢組任務成員歷史列表
@Test
public void findGroupHisUser(){
String taskId = "3709";
List<HistoricIdentityLink> list = processEngine.getHistoryService()//
.getHistoricIdentityLinksForTask(taskId);
//List<HistoricIdentityLink> list = processEngine.getHistoryService()//
// .getHistoricIdentityLinksForProcessInstance(processInstanceId);
for(HistoricIdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任務
@Test
public void completeTask(){
String taskId = "3709";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
/**將組任務分配給個人任務,拾取任務*/
//由1個人去完成任務
@Test
public void claim(){
//任務ID
String taskId = "5908";
//分配的辦理人
String userId = "小B";
processEngine.getTaskService()//
.claim(taskId, userId);
}
/**將個人任務回退到組任務(前提:之前組任務)*/
@Test
public void assignee(){
//任務ID
String taskId = "5508";
processEngine.getTaskService()//
.setAssignee(taskId, null);
}
/**向組任務中新增成員*/
@Test
public void addCadidateUser(){
//任務ID
String taskId = "5508";
//新增的成員
String userId = "小E";
processEngine.getTaskService()//
.addCandidateUser(taskId, userId);
}
/**從組任務中刪除成員*/
@Test
public void deleteCadidateUser(){
//任務ID
String taskId = "5508";
//新增的成員
String userId = "小D";
processEngine.getTaskService()//
.deleteCandidateUser(taskId, userId);
}
說明:
1) 小A,小B,小C,小D是組任務的辦理人
2) 但是這樣分配組任務的辦理人不夠靈活,因為專案開發中任務的辦理人不要放置XML檔案中。
3) act_ru_identitylink表存放任務的辦理人,包括個人任務和組任務,表示正在執行的任務
4) act_hi_identitylink表存放任務的辦理人,包括個人任務和組任務,表示歷史任務
區別在於:如果是個人任務TYPE的型別表示participant(參與者)
如果是組任務TYPE的型別表示candidate(候選者)和participant(參與者)
16.2.3::分配個人任務方式二(使用流程變數) 1:流程圖中任務節點的配置
2:測試程式碼
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1 釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2啟動流程
//啟動流程例項,同時設定流程變數,用來指定組任務的辦理人
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("userIDs","大大,小小,中中");
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess",variables);
System.out.println("pid:" + pi.getId());
}
//查詢我的個人任務列表
@Test
public void findMyTaskList(){
String userId = "大大";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定個人任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//查詢組任務列表
@Test
public void findGroupList(){
String userId = "大大";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定組任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//查詢組任務成員列表
@Test
public void findGroupUser(){
String taskId = "3709";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//查詢組任務成員歷史列表
@Test
public void findGroupHisUser(){
String taskId = "3709";
List<HistoricIdentityLink> list = processEngine.getHistoryService()//
.getHistoricIdentityLinksForTask(taskId);
for(HistoricIdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任務
@Test
public void completeTask(){
String taskId = "3709";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
/**將組任務分配給個人任務,拾取任務*/
//由1個人去完成任務
@Test
public void claim(){
//任務ID
String taskId = "5908";
//分配的辦理人
String userId = "小B";
processEngine.getTaskService()//
.claim(taskId, userId);
}
說明:
1) 大大,中中,小小是組任務的辦理人
2) 在開發中,可以在頁面中指定下一個組任務的辦理人,通過流程變數設定下一個任務的辦理人
16.2.4::分配個人任務方式三(使用類) 1:流程圖中任務節點的配置
此時流程圖的XML檔案,如圖:
2:TaskListenerImpl類,用來設定任務的辦理人
public class TaskListenerImplimplements TaskListener {
/**指定個人任務和組任務的辦理人*/
@Override
public void notify(DelegateTask delegateTask) {
String userId1 = "孫悟空";
String userId2 = "豬八戒";
//指定組任務
delegateTask.addCandidateUser(userId1);
delegateTask.addCandidateUser(userId2);
}
}
3:測試程式碼
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1 釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 啟動流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//查詢我的個人任務列表
@Test
public void findMyTaskList(){
String userId = "孫悟空";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定個人任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//查詢組任務列表
@Test
public void findGroupList(){
String userId = "孫悟空";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定組任務查詢
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//查詢組任務成員列表
@Test
public void findGroupUser(){
String taskId = "4008";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//查詢組任務成員歷史列表
@Test
public void findGroupHisUser(){
String taskId = "4008";
List<HistoricIdentityLink> list = processEngine.getHistoryService()//
.getHistoricIdentityLinksForTask(taskId);
for(HistoricIdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任務
@Test
public void completeTask(){
String taskId = "4008";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
//將組任務分配給個人任務(認領任務)
@Test
public void claimTask(){
String taskId = "4008";
//個人任務的辦理人
String userId = "如來";
processEngine.getTaskService().claim(taskId, userId);
}
//可以分配個人任務回退到組任務,(前提之前是個組任務)
@Test
public void setAssigneeTask(){
//任務ID
String taskId = "4008";
processEngine.getTaskService()//
.setAssignee(taskId, null);
}
//向組任務中新增成員
@Test
public void addUser(){
String taskId = "4008";
String userId = "沙和尚";
processEngine.getTaskService().addCandidateUser(taskId, userId);
}
//向組任務中刪除成員
@Test
public void removeUser(){
String taskId = "4008";
String userId = "沙和尚";
processEngine.getTaskService().deleteCandidateUser(taskId, userId);
}
說明:
1) 在類中使用delegateTask.addCandidateUser (userId);的方式分配組任務的辦理人,此時孫悟空和豬八戒是下一個任務的辦理人。
2) 通過processEngine.getTaskService().claim (taskId, userId);將組任務分配給個人任務,也叫認領任務,即指定某個人去辦理這個任務,此時由如來去辦理任務。
注意:認領任務的時候,可以是組任務成員中的人,也可以不是組任務成員的人,此時通過Type的型別為participant來指定任務的辦理人
3) addCandidateUser()即向組任務新增成員,deleteCandidateUser()即刪除組任務的成員。
4) 在開發中,可以將每一個任務的辦理人規定好,例如張三的領導是李四和王五,這樣張三提交任務,由李四或者王五去查詢組任務,可以看到對應張三的申請,李四或王五再通過認領任務(claim)的方式,由某個人去完成這個任務。
16.2.5:總結 組任務及三種分配方式:
1:在taskProcess.bpmn中直接寫 candidate-users=“小A,小B,小C,小D"
2:在taskProcess.bpmn中寫 candidate-users =“#{userIDs}”,變數的值要是String的。
使用流程變數指定辦理人
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("userIDs", "大大,小小,中中");
3,使用TaskListener介面,使用類實現該介面,在類中定義:
//新增組任務的使用者
delegateTask.addCandidateUser(userId1);
delegateTask.addCandidateUser(userId2);
組任務分配給個人任務(認領任務):
processEngine.getTaskService().claim(taskId, userId);
個人任務分配給組任務:
processEngine.getTaskService(). setAssignee(taskId, null);
向組任務新增人員:
processEngine.getTaskService().addCandidateUser(taskId, userId);
向組任務刪除人員:
processEngine.getTaskService().deleteCandidateUser(taskId, userId);
個人任務和組任務存放辦理人對應的表:
act_ru_identitylink表存放任務的辦理人,包括個人任務和組任務,表示正在執行的任務
act_hi_identitylink表存放任務的辦理人,包括個人任務和組任務,表示歷史任務
區別在於:如果是個人任務TYPE的型別表示participant(參與者)
如果是組任務TYPE的型別表示candidate(候選者)和participant(參與者)
這裡注意:組任務在專案中最好的處理方式是先拾取(claim())任務,即指定某個人去辦理任務。這樣就可以在正在執行(歷史的)任務表中可以跟著當前任務的辦理人,否則該欄位(ASSIGNEE)為null,就無法跟蹤當前辦理人。
第一種方式是固定的組任務的執行人
第二種方式是在程式碼中通過流程變數的形式給組任務的執行人賦值,這樣做的缺點是在進入該節點之前,必須給組任務賦值候選人
第三種方式可以在進入該組任務的時候,執行TaskListener,從而給組任務的候選人賦值。
缺點1、如果在TaskListener中操作了資料庫,這意味著只要進入該節點就得操作資料庫一次。
缺點2、這個類不能放入到spring容器中,所以該類中的方法不能使用spring的宣告式事務處理
優點:可以在方法中引入servletConetxt或者ApplicationContext
16.3:工作流定義的角色組(瞭解) 16.3.1:流程圖
流程圖中任務節點的配置:
分配任務負責的組
使用 candidate groups 屬性指定 任務負責組
程式碼:
<userTask id=“usertask1” name=“審批” activiti:candidateGroups=“部門經理”>
</userTask>
其中部門經理表示一個使用者組的角色
16.3.2:測試程式碼 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定義,啟動流程例項
@Test
public void testTask()throws Exception {
// 1 釋出流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");