視頻教程在Java幫幫-免費資源分享網
www.javahelp.com.cn
下載2014年Java全套視頻即有activiti等視頻
一:Activiti第一天1:工作流的概念
說明:
1) 假設:這兩張圖就是XX兄弟的請假流程圖
2) 圖的組成部分:
A. 人物:範XX 馮X剛 王X軍
B. 事件(動作):請假、批准、不批准
工作流(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核心配置文件,配置流程引擎創建工具的基本參數和資料庫連接池參數。
定義資料庫配置參數:
l jdbcUrl: 資料庫的JDBC URL。
l jdbcDriver: 對應不同資料庫類型的驅動。
l jdbcUsername: 連接資料庫的用戶名。
l jdbcPassword: 連接資料庫的密碼。
基於JDBC參數配置的資料庫連接 會使用默認的MyBatis連接池。 下面的參數可以用來配置連接池(來自MyBatis參數):
l jdbcMaxActiveConnections: 連接池中處於被使用狀態的連接的最大值。默認為10。
l jdbcMaxIdleConnections: 連接池中處於空閒狀態的連接的最大值。
l jdbcMaxCheckoutTime: 連接被取出使用的最長時間,超過時間會被強制回收。 默認為20000(20秒)。
l 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.3.2:安裝方式二在沒有網絡的情況下,安裝流程設計器步驟如下:
1) 解壓老師發給大家的
2) 把壓縮包中的內容放入eclipse根目錄的dropins文件夾下
3) 重啟eclipse,點擊新建工程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:核心API5.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:ExecutionActiviti用這個對象去描述流程執行的每一個節點。在沒有並發的情況下,Execution就是同ProcessInstance。流程按照流程定義的規則執行一次的過程,就可以表示執行對象Execution。
如圖為ProcessInstance的原始碼:
從原始碼中可以看出ProcessInstance就是Execution。但在現實意義上有所區別:
在單線流程中,如上圖的貸款流程,ProcessInstance與Execution是一致的。
這個例子有一個特點:wire money(匯錢)和archive(存檔)是並發執行的。 這個時候,總線路代表ProcessInstance,而分線路中每個活動代表Execution。
總結:
* 一個流程中,執行對象可以存在多個,但是流程實例只能有一個。
* 當流程按照規則只執行一次的時候,那麼流程實例就是執行對象。
6:HelloWorld程序(模擬流程的執行)6.1:流程圖:
6.2:部署流程定義
這裡使用RepositoryService部署流程定義
addClasspathResource表示從類路徑下加載資源文件,一次只能加載一個文件
6.3:啟動流程實例這裡使用RuntimeService啟動流程實例
6.4:查看我的個人任務這裡使用TaskService完成任務的查詢
6.5:完成我的個人任務這裡使用TaskService完成任務的辦理
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來管理。
這樣做的好處在於,加快流程執行的速度,因為正在執行的流程的表中數據不會很大。
二:Activiti第二天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 TaskListenerImpl implements 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 TaskListenerImpl implements 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");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
/**在部署流程定義和啟動流程實例的中間,設置組任務的辦理人,向Activity表中存放組和用戶的信息*/
IdentityService identityService = processEngine.getIdentityService();//認證:保存組和用戶信息
identityService.saveGroup(new GroupEntity("部門經理"));//建立組
identityService.saveGroup(new GroupEntity("總經理"));//建立組
identityService.saveUser(new UserEntity("小張"));
identityService.saveUser(new UserEntity("小李"));
identityService.saveUser(new UserEntity("小王"));
identityService.createMembership("小張", "部門經理");//建立組和用戶關係
identityService.createMembership("小李", "部門經理");//建立組和用戶關係
identityService.createMembership("小王", "總經理");//建立組和用戶關係
// 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 = "4408";
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 completeTask(){
String taskId = "5108";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任務");
}
}
16.3.3:分配任務負責的組(IdentityService)/**在部署流程定義和啟動流程實例的中間,設置組任務的辦理人,向Activiti表中存放組和用戶的信息*/
IdentityService identityService = processEngine.getIdentityService();//認證:保存組和用戶信息
identityService.saveGroup(new GroupEntity("部門經理"));//建立組
identityService.saveGroup(new GroupEntity("總經理"));//建立組
identityService.saveUser(new UserEntity(「小張」));//建立用戶
identityService.saveUser(new UserEntity("小李")); //建立用戶
identityService.saveUser(new UserEntity("小王")); //建立用戶
identityService.createMembership("小張", "部門經理");//建立組和用戶關係
identityService.createMembership("小李", "部門經理");//建立組和用戶關係
identityService.createMembership(「小王」, 「總經理」);//建立組和用戶關係
表結構介紹
act_id_group:角色組表
act_id_user:用戶表:
act_id_membership:用戶角色表
指定組任務的辦理人,查詢組任務
String userId = 「小張」;//小張,小李可以查詢結果,小王不可以,因為他不是部門經理角色
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定組任務查詢
.list();
Activiti總結: