一 Activity基礎
1.1 工作流簡介
工作流(Workflow),就是通過計算機對業務流程自動化執行管理。它主要解決的是“使在多個參與者之間按照某種預定義的規則自動進行傳遞文檔、信息或任務的過程,從而實現某個預期的業務目標,或者促使此目標的實現”。
1.2 工作流系統
一個軟件系統中具有工作流的功能,我們把它稱爲工作流系統,一個系統中工作流的功能是什麼?就是對系統的業務流程進行自動化管理,所以工作流是建立在業務流程的基礎上,所以一個軟件的系統核心根本上還是系統的業務流程,工作流只是協助進行業務流程管理。即使沒有工作流業務系統也可以開發運行,只不過有了工作流可以更好的管理業務流程,提高系統的可擴展性。
1.2.1 適用行業
消費品行業,製造業,電信服務業,銀證險等金融服務業,物流服務業,物業服務業,物業管理,大中型進出口貿易公司,政府事業機構,研究院所及教育服務業等,特別是大的跨國企業和集團公司。
1.2.2 具體應用
- 關鍵業務流程:訂單、報價處理、合同審覈、客戶電話處理、供應鏈管理等
- 行政管理類:出差申請、加班申請、請假申請、用車申請、各種辦公用品申請、購買申請、日報
週報等凡是原來手工流轉處理的行政表單。 - 人事管理類:員工培訓安排、績效考評、職位變動處理、員工檔案信息管理等。
- 財務相關類:付款請求、應收款處理、日常報銷處理、出差報銷、預算和計劃申請等。
- 客戶服務類:客戶信息管理、客戶投訴、請求處理、售後服務管理等。
- 特殊服務類: ISO 系列對應流程、質量管理對應流程、產品數據信息管理、貿易公司報關處理、物流公司貨物跟蹤處理等各種通過表單逐步手工流轉完成的任務均可應用工作流軟件自動規範地實施。
1.3 工作流實現方式
在沒有專門的工作流引擎之前,我們之前爲了實現流程控制,通常的做法就是採用狀態字段的值來
跟蹤流程的變化情況。這樣不用角色的用戶,通過狀態字段的取值來決定記錄是否顯示。
針對有權限可以查看的記錄,當前用戶根據自己的角色來決定審批是否合格的操作。如果合格將狀
態字段設置一個值,來代表合格;當然如果不合格也需要設置一個值來代表不合格的情況。
這是一種最爲原始的方式。通過狀態字段雖然做到了流程控制,但是當我們的流程發生變更的時候,
這種方式所編寫的代碼也要進行調整。
那麼有沒有專業的方式來實現工作流的管理呢?並且可以做到業務流程變化之後,我們的程序可以不用改變,如果可以實現這樣的效果,那麼我們的業務系統的適應能力就得到了極大提升。
1.4 工作流實現原理分析
如何可以做到我們在業務流程發生變更後,我們的業務系統代碼可以不發生改變?此時我們就來分析一下原理。具體分析過程如下圖所示:
二 什麼是Activiti7
2.1 Activiti 介紹(博主這裏介紹的版本是Activiti7)
Alfresco 軟件在 2010 年 5 月 17 日宣佈 Activiti 業務流程管理(BPM)開源項目的正式啓動, 其首席架構師由業務流程管理 BPM 的專家 Tom Baeyens 擔任, Tom Baeyens 就是原來 jbpm 的架構師,而 jbpm 是一個非常有名的工作流引擎,當然 activiti 也是一個工作流引擎。
Activiti 是一個工作流引擎, activiti 可以將業務系統中複雜的業務流程抽取出來,使用專門的建模語言(BPMN2.0)進行定義,業務系統按照預先定義的流程進行執行,實現了業務系統的業務流程由 activiti 進行管理,減少業務系統由於流程變更進行系統升級改造的工作量,從而提高系統的健壯性,同時也減少了系統開發維護成本。
官方網站: https://www.activiti.org/
2.1.1 BPM
BPM(Business Process Management),即業務流程管理,是一種以規範化的構造端到端的卓越
業務流程爲中心,以持續的提高組織業務績效爲目的系統化方法,常見商業管理教育如 EMBA、MBA
等均將 BPM 包含在內。
企業流程管理主要是對企業內部改革,改變企業職能管理機構重疊、中間層次多、流程不閉環
等,做到機構不重疊、業務不重複,達到縮短流程週期、節約運作資本、提高企業效益的作用。
比較下邊的兩個人事加薪流程哪個效率更高?
流程一:
流程二:
上邊兩個流程的區別在於第二個流程在執行時,如果本次加薪金額在一萬元以內不再由總經理審批將比第一個流程縮短流程週期,從而提交效率。再比較下邊的例子,哪個效率更高?
2.1.2 BPM 軟件
BPM 軟件就是根據企業中業務環境的變化,推進人與人之間、人與系統之間以及系統與系統之間的整合及調整的經營方法與解決方案的 IT 工具。 通常以 Internet 方式實現信息傳遞、數據同步、業務監控和企業業務流程的持續升級優化,從而實現跨應用、跨部門、跨合作伙伴與客戶的企業運作。 通過 BPM 軟件對企業內部及外部的業務流程的整個生命週期進行建模、自動化、管理監控和優化,使企業成本降低,利潤得以大幅提升。
BPM 軟件在企業中應用領域廣泛, 凡是有業務流程的地方都可以 BPM 軟件進行管理,比如企業人事辦公管理、採購流程管理、公文審批流程管理、財務管理等。
2.1.3 BPMN
BPMN(Business Process Model And Notation) - 業務流程模型和符號, 是由 BPMI(BusinessProcess Management Initiative)開發的一套標準的業務流程建模符號,使用 BPMN 提供的符號可以創建業務流程。 2004 年 5 月發佈了 BPMN1.0 規範.BPMI 於 2005 年 9 月併入 OMG(The ObjectManagement Group 對象管理組織)組織。 OMG 於 2011 年 1 月發佈 BPMN2.0 的最終版本。具體發展歷史如下:
BPMN 是目前被各 BPM 廠商廣泛接受的 BPM 標準。 Activiti 就是使用 BPMN 2.0 進行流程建模、流程執行管理,它包括很多的建模符號,比如:
Event 用一個圓圈表示,它是流程中運行過程中發生的事情。
活動用圓角矩形表示,一個流程由一個活動或多個活動組成
一個 bpmn 圖形的例子:
首先當事人發起一個請假單;
其次他所在部門的經理對請假單進行審覈;
然後人事經理進行復核並進行備案;
最後請假流程結束。
Bpmn 圖形其實是通過 xml 表示業務流程,上邊的.bpmn 文件使用文本編輯器打開:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="myProcess" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="創建請假單"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1"
targetRef="usertask1"></sequenceFlow>
<userTask id="usertask2" name="部門經理審覈"></userTask>
<sequenceFlow id="flow2" sourceRef="usertask1"
targetRef="usertask2"></sequenceFlow>
<userTask id="usertask3" name="人事複覈"></userTask>
<sequenceFlow id="flow3" sourceRef="usertask2"
targetRef="usertask3"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow4" sourceRef="usertask3"
targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="130.0"
y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="210.0"
y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="360.0"
y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="510.0"
y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="660.0"
y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="165.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="210.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="315.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="360.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="465.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="510.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="615.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="660.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
2.2 Activit 如何使用
2.2.1 部署 activity
Activiti 是一個工作流引擎(其實就是一堆 jar 包 API),業務系統使用 activiti 來對系統的業務流程進行自動化管理,爲了方便業務系統訪問(操作)activiti 的接口或功能,通常將 activiti 環境與業務系統的環境集成在一起。
2.2.2 流程定義
使用 activiti 流程建模工具(activity-designer)定義業務流程(.bpmn 文件) 。bpmn 文件就是業務流程定義文件,通過 xml 定義業務流程。
如果使用其它公司開發的工作引擎一般都提供了可視化的建模工具(Process Designer)用於生
成流程定義文件,建模工具操作直觀,一般都支持圖形化拖拽方式、 多窗口的用戶界面、 豐富的過
程圖形元素、過程元素拷貝、粘貼、刪除等功能。
2.2.3 流程定義部署
向 activiti 部署業務流程定義(.bpmn 文件)。使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件(一般情況還需要一塊兒部署業務流程的圖片.png)
2.2.4 啓動一個流程實例(ProcessInstance)
啓動一個流程實例表示開始一次業務流程的運行,比如員工請假流程部署完成,如果張三要請假就可以啓動一個流程實例,如果李四要請假也啓動一個流程實例,兩個流程的執行互相不影響,就好比定義一個 java 類,實例化兩個對象一樣,部署的流程就好比 java 類,啓動一個流程實例就好比 new 一個 java 對象。
2.2.5 用戶查詢待辦任務(Task)
因爲現在系統的業務流程已經交給 activiti 管理,通過 activiti 就可以查詢當前流程執行到哪了,當前用戶需要辦理什麼任務了,這些 activiti幫我們管理了,而不像上邊需要我們在 sql語句中的where條件中指定當前查詢的狀態值是多少。
2.2.6 用戶辦理任務
用戶查詢待辦任務後,就可以辦理某個任務,如果這個任務辦理完成還需要其它用戶辦理,比如採購單創建後由部門經理審覈,這個過程也是由 activiti 幫我們完成了,不需要我們在代碼中硬編碼指
定下一個任務辦理人了。
2.2.7 流程結束
當任務辦理完成沒有下一個任務結點了,這個流程實例就完成了。
三 環境準備
3.1 三個環境
第一個環境:沒有加入工作流 SaaS-IHRM 系統
作用:主要是爲 activiti 工作流引擎的引入提供場景
第二個環境: activiti 測試環境
作用:用於測試 activiti 的 api,提供各種 service 接口。
需要創建一個數據庫:
僅僅有 activiti 的數據表
第三個環境: activiti 應用環境,加入工作流的 SaaS-IHRM 系統
需要創建一個數據庫:
包括 activiti 的數據表和業務表(SaaS-IHRM 系統的表)
3.2 開發環境
3.2.1 Java 環境
Jdk1.8 或以上版本。
3.2.2 數據庫
Mysql 5 及以上的版本
博主採用 5.5 版本
3.2.3 Web 容器
博主採用的是 Tomcat8.5
3.2.4 開發工具
Mysql 客戶端連接工具:Sqlyog 或其它;
文本編輯器:EditPlus 或其它;
Java 開發工具: IDEA 或 Eclipse 工具;
注意: activiti 的流程定義工具插件可以安裝在 IDEA 下,也可以安裝在 Eclipse 工具下。
3.3 Activiti 環境
Activiti7.0.0.Beta1
默認支持 spring5
3.3.1 下載 activiti7
Activiti 下載地址: http://activiti.org/download.html
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.0.0.Beta1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
- Database:
activiti 運行需要有數據庫的支持, 支持的數據庫有: h2, mysql, oracle, postgres, mssql, db2
等,該目錄存放 activiti 的建表腳本。 - Docs
Activiti 的幫助文檔。 - Wars
官方自帶的示例工程。
3.3.2 Activiti Designer 流程設計器(Eclipse 工具)
博主使用 Activiti –Designer-eclipse-plugin(activiti 流程設計器插件)完成流程的製作。
下面介紹了 activiti designer 設計器插件的兩種安裝方式,博主使用的是方式一;
3.3.2.1 Eclispe 工具下插件安裝方式 1
參數文檔開發工具目錄下的“activiti 開發環境配置.docx”中“eclipse 插件安裝”,其中包括了 Activiti
插件。
3.3.2.2 Eclispe 工具下插件安裝方式 2
網絡在線安裝:
- 打開 Help -> Install New Software. 在如下面板中:
- 在如下 Install 界面板中,點擊 Add 按鈕:
配置新裝插件的地址和名稱 - 然後填入下列字段
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
4) 回到 Install 界面,在面板正中列表中把所有展示出來的項目都勾上,在 Detail 部分記得選中 “Contact all updates sites…” , 因爲它會檢查所有當前安裝所需要的插件並可以被 Eclipse 下載
6) 安裝完以後,點擊新建工程 new->Other…打開面板,如果看到下圖內容就說明安裝成功了:
補充說明
打開菜單 Windows->Preferences->Activiti->Save 下流程圖片的生成方式:
雖然流程引擎在單獨部署 bpmn 文件時會自動生成圖片,但在實際開發過程中,自動生成的圖片會導致和 BPMN 中的座標有出入,在實際項目中展示流程當前位置圖會有問題。在完成以上配置後,會由我們自己來管理流程圖片。所以在發佈流程時把流程規則文件和流程圖片一起上傳就行了。
3.3.3 Activiti Designer 流程設計器(IDEA 工具)
在 IDEA 的 File 菜單中找到子菜單”Settings”,後面我們再選擇左側的“plugins”菜單,如下圖所示:
此時我們就可以搜索到 actiBPM 插件,它就是 Activiti Designer 的 IDEA 版本。
安裝好後,頁面如下:
3.3.4 Activiti 支持的數據庫
Activiti 的運行需要數據庫支撐,需要安裝 activiti 數據庫,支持如下版本:
Activiti 數據庫類型 | 測試版本 | JDBC URL 實例 | 備註 |
---|---|---|---|
h2 | 1.3.168 | jdbc:h2:tcp://localhost/activiti | 默認配置的數據庫 |
mysql | 5.1.21 | jdbc:mysql://localhost:3306/activiti?autoReconnect=true | 使用 mysql-connector-java驅動測試 |
oracle | 11.2.0.1.0 | jdbc:oracle:thin:@localhost:1521:xe | |
postgres | 8.1 | jdbc:postgresql://localhost:5432/activiti | |
db2 | DB2 10.1 usingdb2jcc4 | jdbc:db2://localhost:50000/activiti | |
mssql | 2008 usingsqljdbc4 | jdbc:sqlserver://localhost:1433/activiti |
3.3.5 創建 mysql 數據庫
博主使用的是 mysql 數據庫。
創建 mysql 數據庫 activiti(名字任意):
CREATE DATABASE activiti DEFAULT CHARACTER SET utf8;
3.3.6 創建表方式
通過運行 java 程序創建表。
3.3.6.1 創建 java 工程
使用 eclipse 或 idea 創建 maven 的 java 工程。
3.3.6.2 加入 maven 依賴的座標(jar 包)
首先需要在 java 工程中加入 ProcessEngine 所需要的 jar 包,包括:
- activiti-engine-7.0.0.beta1.jar
- activiti 依賴的 jar 包: mybatis、 alf4j、 log4j 等
- activiti 依賴的 spring 包
- 數據庫驅動
- 第三方數據連接池 dbcp
- 單元測試 Junit-4.12.jar
我們使用 maven 來實現項目的構建,所以應當導入這些 jar 所對應的座標到 pom.xml 文件中。
<properties>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
3.3.6.3 log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r
[%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r
[%15.15t] %-5p %30.30c %x - %m\n
3.3.6.4 activiti.cfg.xml
在 classpath 下創建 activiti.cgf.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>
在 activiti.cfg.xml 中配置數據源和 processEngineConfiguration
- 數據源
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/
itcast0711activiti" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name=" maxActive" value="3" />
<property name="maxIdle" value="1" />
</bean>
- processEngineConfiguration
processEngineConfiguration 用來創建 ProcessEngine,在創建 ProcessEngine 時會執行數據庫的操作。
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
<!-- activiti數據庫表處理策略 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
關於 processEngineConfiguration
中的 databaseSchemaUpdate
參數, 通過此參數設計 activiti
數據表的處理策略,參數如下:
false(默認):檢查數據庫表的版本和依賴庫的版本, 如果版本不匹配就拋出異常。
true: 構建流程引擎時,執行檢查,如果需要就執行更新。 如果表不存在,就創建。
create-drop: 構建流程引擎時創建數據庫表, 關閉流程引擎時刪除這些表。
drop-create:先刪除表再創建表。
create: 構建流程引擎時創建數據庫表, 關閉流程引擎時不刪除這些表。
注意:在 activiti.cfg.xml 配置文件中的 dataSource 和 processEngineConfiguration 也可以使用一次
性配置出來。
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/itcast0711activiti"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="root"/>
<property name="databaseSchemaUpdate" value="true"/>
</bean>
3.3.6.5 編寫程序
創建 ProcessEngineConfiguration,通過 ProcessEngineConfiguration 創建 ProcessEngine,在創建ProcessEngine 時會自動創建數據庫。
//創建ProcessEngineConfiguration
ProcessEngineConfiguration configuration =ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("activiti.cfg.xml")
//通過ProcessEngineConfiguration創建ProcessEngine,此時會創建數據庫
ProcessEngine processEngine =configuration.buildProcessEngine();
System.out.println(processEngine);
說明:
1、運行以上程序段即可完成 activiti 數據庫創建,通過改變 activiti.cfg.xml 中databaseSchemaUpdate 參數的值執行不同的數據表處理策略。
2 、 上 邊 的 方法 createProcessEngineConfigurationFromResource 在執行時在activiti.cfg.xml 中找固定的名稱 processEngineConfiguration也可以使用重載方法調用,這時可以不用限定 processEngineConfiguration 名稱
此時我們查看數據庫, 創建了 25 張表,結果如下:
3.3.7 數據庫表的命名規則
Activiti 的表都以 ACT_開頭。 第二部分是表示表的用途的兩個字母標識。 用途也和服務的 API 對應。
- ACT_RE_*:
RE
表示 repository。 這個前綴的表包含了流程定義和流程靜態資源 (圖片,規則,等等)。 - ACT_RU_*:
RU
表示 runtime。 這些運行時的表,包含流程實例,任務,變量,異步任務,等運行中的數據。 Activiti 只在流程實例執行過程中保存這些數據, 在流程結束時就會刪除這些記錄。 這樣運行時表可以一直很小速度很快。 - ACT_HI_*:
HI
表示 history。 這些表包含歷史數據,比如歷史流程實例, 變量,任務等等。 - ACT_GE_*:
GE
表示 general。 通用數據, 用於不同場景下。
四 Activiti 服務架構圖
在新版本中,我們通過實驗可以發現 IdentityService, FormService 兩個 Serivce 都已經刪除了。所以後面我們對於這兩個 Service 也不講解了,但老版本中還是有這兩個 Service,大家需要了解一下。
4.1 activiti.cfg.xml
activiti 的引擎配置文件,包括: ProcessEngineConfiguration 的定義、數據源定義、事務管理器等,此文件其實就是一個 spring 配置文件,下面是一個基本的配置,只配置了 ProcessEngineConfiguration和數據源:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!--數據庫連接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti" />
<property name="username" value="root" />
<property name="password" value="mysql" />
</bean>
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
<!-- 數據庫策略 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
4.2 ProcessEngineConfiguration
流程引擎的配置類,通過 ProcessEngineConfiguration 可以創建工作流引擎 ProceccEngine,常用的兩種方法如下:
4.2.1 StandaloneProcessEngineConfiguration
通過 org.activiti.engine.impl.cfg.StandaloneProcessEngineConfigurationActiviti 可以單獨運行,使用它創建的 ProcessEngine, Activiti 會自己處理事務。
配置文件方式:
通常在 activiti.cfg.xml 配置文件中定義一個 id 爲 processEngineConfiguration 的 bean,這裏會使用 spring 的依賴注入來構建引擎。
方法如下:
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
<!-- 數據庫策略 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
4.2.2 SpringProcessEngineConfiguration
通過 org.activiti.spring.SpringProcessEngineConfiguration 與 Spring 整合。
創建 spring 與 activiti 的整合配置文件:
activity-spring.cfg.xml(名稱不固定)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ">
<!-- 工作流引擎配置bean -->
<bean id="processEngineConfiguration"
class="org.activiti.spring.SpringProcessEngineConfiguration">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
<!-- 使用spring事務管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 數據庫策略 -->
<property name="databaseSchemaUpdate" value="drop-create" />
<!-- activiti的定時任務關閉 -->
<property name="jobExecutorActivate" value="false" />
</bean>
<!-- 流程引擎 -->
<bean id="processEngine"
class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<!-- 資源服務service -->
<bean id="repositoryService" factory-bean="processEngine"
factory-method="getRepositoryService" />
<!-- 流程運行service -->
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<!-- 任務管理service -->
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<!-- 歷史管理service -->
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<!-- 用戶管理service -->
<bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" />
<!-- 引擎管理service -->
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<!-- 數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti" />
<property name="username" value="root" />
<property name="password" value="mysql" />
<property name="maxActive" value="3" />
<property name="maxIdle" value="1" />
</bean>
<!-- 事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 傳播行爲 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 切面,根據具體項目修改切點配置 -->
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.itheima.ihrm.service.impl.*.*(..))" />
</aop:config>
</beans>
4.2.3 創建 processEngineConfiguration
ProcessEngineConfiguration configuration =ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml")
上邊的代碼要求 activiti.cfg.xml 中必須有一個 processEngineConfiguration 的 bean也可以使用下邊的方法,更改 bean 的名字:
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
4.3 ProcessEngine
工作流引擎,相當於一個門面接口,通過 ProcessEngineConfiguration 創建 processEngine,通過
ProcessEngine 創建各個 service 接口。
4.3.1 一般創建方式
//通過ProcessEngineConfiguration創建ProcessEngine
ProcessEngine processEngine =processEngineConfiguration.buildProcessEngine();
4.3.2簡單創建方式
將 activiti.cfg.xml 文件名及路徑固定,且 activiti.cfg.xml 文件中有 processEngineConfiguration 的配置,可以使用如下代碼創建 processEngine:
//使用classpath下的activiti.cfg.xml中的配置創建processEngine
ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
4.4 Service
4.4.1 Service 創建方式
通過 ProcessEngine 創建 Service, Service 是工作流引擎提供用於進行工作流部署、執行、管理的服務接口。
方式如下:
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
……
4.4.2 Service 總覽
RepositoryService | activiti 的資源管理類 |
RuntimeService | activiti 的流程運行管理類 |
TaskService | activiti 的任務管理類 |
HistoryService | activiti 的歷史管理類 |
ManagerService | activiti 的引擎管理類 |
注: 紅色標註爲常用 service。
4.4.3 RepositoryService
是 activiti 的資源管理類, 提供了管理和控制流程發佈包和流程定義的操作。使用工作流建模工具設計的業務流程圖需要使用此 service 將流程定義文件的內容部署到計算機。
除了部署流程定義以外還可以:
查詢引擎中的發佈包和流程定義。
暫停或激活發佈包,對應全部和特定流程定義。 暫停意味着它們不能再執行任何操作了,激活是對應的反向操作。
獲得多種資源,像是包含在發佈包裏的文件, 或引擎自動生成的流程圖。
獲得流程定義的 pojo 版本, 可以用來通過 java 解析流程,而不必通過 xml。
4.4.4 RuntimeService
它是 activiti 的流程運行管理類。可以從這個服務類中獲取很多關於流程執行相關的信息
4.4.5 TaskService
是 activiti 的任務管理類。可以從這個類中獲取任務的信息。
4.4.6 HistoryService
是 activiti 的歷史管理類,可以查詢歷史信息, 執行流程時,引擎會保存很多數據(根據配置),比
如流程實例啓動時間,任務的參與者, 完成任務的時間,每個流程實例的執行路徑,等等。 這個
服務主要通過查詢功能來獲得這些數據。
4.4.7 ManagementService
是 activiti 的引擎管理類, 提供了對 Activiti 流程引擎的管理和維護功能,這些功能不在工作流驅動
的應用程序中使用,主要用於 Activiti 系統的日常維護。
五 Activiti 入門體驗
5.1 流程定義
5.1.1 Activiti-Designer 使用
5.1.1.1 Palette(畫板)
在 eclipse 或 idea 中安裝 activiti-designer 插件即可使用,畫板中包括以下結點:
Connection—連接
Event—事件
Task—任務
Gateway—網關
Container—容器
Boundary event—邊界事件
Intermediate event- -中間事件
流程圖設計完畢保存生成.bpmn 文件。
5.1.1.2 新建流程(IDEA 工具)
首先選中存放圖形的目錄(本次我們選擇 resources 下的 bpmn 目錄),點擊菜單: New-BpmnFile,如
下圖所示:
彈出如下圖所示框:
起完名字 holiday 後(默認擴展名爲 bpmn),就可以看到進入了流程設計頁面,如圖所示:
5.1.1.3 新建流程(Eclipse 工具)
首先選中存放圖形的目錄(本次我們選擇 resources 下的 bpmn 目錄), File-New-Other 菜單,打開如
下窗口。
創建成功:
左側區域是繪圖區,右側區域是 palette 畫板區域
鼠標先點擊畫板的元素即可在左側繪圖。
5.1.2 繪製流程
圖形繪製好後會生成兩個文件:
自動生成圖形,需要設置 eclipse:
5.1.3 指定流程定義 key
流程定義 key 即流程定義的標識,在 eclipse 中通過 properties 視圖查看流程的 key;
建議:相同的業務流程,流程定義的 key 名字定義一樣,比如,如果需要創建新的業務流程,請假
流程,則使用新的 key。
5.1.4 指定任務負責人
在 properties 視圖指定每個任務結點的負責人,比如下邊是填寫請假單的負責人爲 zhangsan
5.2 部署流程定義
部署流程定義,就是要將上邊繪製的圖形,即流程定義(.bpmn),部署在工作流程引擎 activiti 中,方法如下:
使用 ProcessEngine 創建 RepositoryService,代碼如下:
// 獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//部署對象
Deployment deployment = repositoryService.createDeployment().addClasspathResource("diagram/myholiday.bpmn")// bpmn文件
.addClasspathResource("diagram/myholiday.png")// 圖 片 文件
.name("請假申請流程").deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名稱:" + deployment.getName());
執行此操作後 activiti 會將上邊代碼中指定的 bpm 文件和圖片文件保存在 activiti 數據庫。
5.3 啓動一個流程實例
流程定義部署在 activiti 後就可以通過工作流管理業務流程了,也就是說上邊部署的請假申請流程可以使用了。
針對該流程,啓動一個流程表示發起一個新的請假申請單,這就相當於 java 類與 java 對象的關係,類定義好後需要 new 創建一個對象使用,當然可以 new 多個對象。對於請假申請流程,張三發起一個請假申請單需要啓動一個流程實例, 李四發起一個請假單也需要啓動一個流程實例。
代碼如下:
// 啓動一個流程實例
@Test
public void startProcessInstance() {
// 獲取RunTimeService
RuntimeService runtimeService =processEngine.getRuntimeService();
// 根據流程定義key啓動流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday01");
System.out.println(" 流 程 定 義 id : " +processInstance.getProcessDefinitionId());
System.out.println("流程實例id: " + processInstance.getId());
System.out.println(" 當 前 活 動 Id : " +processInstance.getActivityId());
}
5.4 任務查詢
流程啓動後,各個任務的負責人就可以查詢自己當前需要處理的任務,查詢出來的任務都是該用戶的待辦任務。
// 查詢當前個人待執行的任務
@Test
public void findPersonalTaskList() {
// 任務負責人
String assignee = "zhangsan";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey("myholiday01")//
.taskAssignee(assignee)//只查詢該任務負責人的任務
.list();
for (Task task : list) {
System.out.println(" 流 程 實 例 id : " +
task.getProcessInstanceId());
System.out.println("任務id: " + task.getId());
System.out.println("任務負責人: " + task.getAssignee());
System.out.println("任務名稱: " + task.getName());
}
}
5.5 任務處理
任務負責人查詢待辦任務,選擇任務進行處理,完成任務。
// 完成任務
@Test
public void completTask() {
//任務id
String taskId = "8305";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
//完成任務
taskService.complete(taskId);
System.out.println("完成任務id="+taskId);
}
六 流程定義
6.1流程定義
6.1.1 什麼是流程定義
流程定義是線下按照 bpmn2.0 標準去描述 業務流程,通常使用 activiti-explorer(web 控制檯)或 activiti-eclipse-designer 插件對業務流程進行建模,這兩種方式都遵循 bpmn2.0 標準。這裏博主使用
activiti-eclipse-designer 插件完成流程建模。使用 designer 設計器繪製流程,會生成兩個文件: .bpmn和.png
6.1.2 .bpmn 文件
使用 activiti-desinger 設計業務流程,會生成.bpmn 文件,首先將 holiday.bpmn 文件改名爲 holiday.xml,如下圖:
.bpmn 內容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:tns="http://www.activiti.org/test"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
expressionLanguage="http://www.w3.org/1999/XPath" id="m1539820628606" name=""
targetNamespace="http://www.activiti.org/test"
typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myProcess_1" isClosed="false" isExecutable="true" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:exclusive="true" id="_3" name="UserTask"/>
<sequenceFlow id="_4" sourceRef="_2" targetRef="_3"/>
<userTask activiti:exclusive="true" id="_5" name="UserTask"/>
<sequenceFlow id="_6" sourceRef="_3" targetRef="_5"/>
</process>
<bpmndi:BPMNDiagram
documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=842
.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageab
leY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myProcess_1">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="200.0" y="70.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="165.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
<omgdc:Bounds height="55.0" width="85.0" x="160.0" y="295.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_4" id="BPMNEdge__4" sourceElement="_2"
targetElement="_3">
<omgdi:waypoint x="216.0" y="102.0"/>
<omgdi:waypoint x="216.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_3"
targetElement="_5">
<omgdi:waypoint x="205.0" y="230.0"/>
<omgdi:waypoint x="205.0" y="295.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
BPMN 2.0 根節點是 definitions 節點。 這個元素中,可以定義多個流程定義(不過我們建議每個文件只包含一個流程定義, 可以簡化開發過程中的維護難度)。 注意, definitions 元素 最少也要包含 xmlns 和 targetNamespace 的聲明。 targetNamespace 可以是任意值,它用來對流程實例進行分類。
流程定義部分:定義了流程每個結點的描述及結點之間的流程流轉。
流程佈局定義:定義流程每個結點在流程圖上的位置座標等信息。
6.1.3 .png 圖片文件
Eclipse 工具中的操作
流程圖片生成的兩種方式:
-
使用 activiti-designer 設計流程圖時自動生成需在 eclipse 中進行配置:
使用 designer 設計流程圖的同時自動生成與 bpmn 文件同名的圖片文件(.png) -
由 activiti 自動生成圖形
流程圖設計完畢向 activiti 中部署流程定義文件 bpmn,部署時由 activiti 自動生成流程圖片。 (流程部署在下面的章節講解)
注意:此方法生成時如果圖形中有中文,則生成的圖片上顯示亂碼,且.bpmn 中的座標和圖片顯示錯位。
IDEA 工具中的操作方式
第一步:將 holiday.bpmn 文件改爲擴展名 xml 的文件名稱: holiday.xml
修改前的 bpmn 文件,效果如下:
第二步: 在 holiday.xml 文件上面,點右鍵並選擇 Diagrams 菜單,再選擇 Show BPMN2.0 Designer…
第三步: 打開後的效果圖如下:
第四步:點擊 Export To File 的小圖標,如下:
打開如下窗口,注意填寫文件名及擴展名,選擇好保存圖片的位置:
第五步:中文亂碼的解決
1.打開 IDEA 安裝路徑,找到如下的安裝目錄
根據自己所安裝的版本來決定,我使用的是 64 位的 idea,所以在 idea64.exe.vmoptions 文件的最後
一行追加一條命令: -Dfile.encoding=UTF-8
如下所示:
一定注意,不要有空格,否則重啓 IDEA 時會打不開,然後 重啓 IDEA,把原來的 png 圖片刪掉,再重新生成,即可解決亂碼問題
6.2 流程定義部署
6.2.1 什麼是流程定義部署
將線下定義的流程部署到 activiti 數據庫中,這就是流程定義部署,通過調用 activiti 的 api 將流程定義的 bpmn 和 png 兩個文件一個一個添加部署到 activiti 中,也可以將兩個文件打成 zip 包進行部署。
6.2.2 單個文件部署方式
分別將 bpmn 文件和 png 圖片文件部署。
@Test
public void deployProcess() {
// 獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// bpmn輸入流
InputStream inputStream_bpmn = this.getClass().getClassLoader().getResourceAsStream("diagram/holiday.bpmn");
// 圖片輸入流
InputStream inputStream_png = this.getClass().getClassLoader().getResourceAsStream(" diagram/holiday.png");
// 流程部署對象
Deployment deployment = repositoryService.createDeployment().addInputStream("holiday.bpmn", inputStream_bpmn).addInputStream("holiday.png", inputStream_png).deploy();
System.out.println("流程部署id: " + deployment.getId());
System.out.println("流程部署名稱: " + deployment.getName());
}
執行此操作後 activiti 會將上邊代碼中指定的 bpm 文件和圖片文件保存在 activiti 數據庫。
6.2.3 壓縮包部署方式
將 holiday.bpmn 和 holiday.png 壓縮成 zip 包。
@Test
public void deployProcessByZip() {
// 定義zip輸入流
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(" diagram/holiday.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment()//.addZipInputStream(zipInputStream).deploy();
System.out.println("流程部署id: " + deployment.getId());
System.out.println("流程部署名稱: " + deployment.getName());
}
執行此操作後 activiti 會將上邊代碼中指定的 bpm 文件和圖片文件保存在 activiti 數據庫。
6.2.4 操作數據表
流程定義部署後操作 activiti 數據表如下:
#流程定義部署表,記錄流程部署信息
SELECT * FROM act_re_deployment
#流程定義表,記錄流程定義信息
SELECT * FROM act_re_procdef
#資源表
SELECT * FROM act_ge_bytearray
說明:
act_re_deployment 和 act_re_procdef 一對多關係,一次部署在流程部署表生成一條記錄,但一次部署可以部署多個流程定義,每個流程定義在流程定義表生成一條記錄。每一個流程定義在
act_ge_bytearray 會存在兩個資源記錄, bpmn 和 png。
建議:一次部署一個流程,這樣部署表和流程定義表是一對一的關係,方便讀取流程部署及流程定義信息。
6.3 流程定義查詢
查詢部署的流程定義。
@Test
public void queryProceccDefinition() {
// 流程定義key
String processDefinitionKey = "holiday";
// 獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 查詢流程定義
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//遍歷查詢結果
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
for (ProcessDefinition processDefinition : list) {
System.out.println("------------------------");
System.out.println(" 流 程 部 署 id : " +processDefinition.getDeploymentId());
System.out.println("流程定義id: " + processDefinition.getId());
System.out.println("流程定義名稱: " + processDefinition.getName());
System.out.println("流程定義key: " + processDefinition.getKey());
System.out.println("流程定義版本: " + processDefinition.getVersion());
}
}
6.4 流程定義刪除
刪除已經部署成功的流程定義。
public void deleteDeployment() {
// 流程部署id
String deploymentId = "8801";
// 通過流程引擎獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//刪除流程定義, 如果該流程定義已有流程實例啓動則刪除時出錯
repositoryService.deleteDeployment(deploymentId);
//設置true 級聯刪除流程定義,即使該流程有流程實例啓動也可以刪除,設
置爲false非級別刪除方式,如果流程
//repositoryService.deleteDeployment(deploymentId, true);
}
說明:
- 使用 repositoryService 刪除流程定義
- 如果該流程定義下沒有正在運行的流程,則可以用普通刪除。
- 如果該流程定義下存在已經運行的流程,使用普通刪除報錯,可用級聯刪除方法將流程及相關
記錄全部刪除。 項目開發中使用級聯刪除的情況比較多, 刪除操作一般只開放給超級管理員使
用。
6.5 流程定義資源查詢
6.5.1 方式 1
通過流程定義對象獲取流程定義資源,獲取 bpmn 和 png。
@Test
public void getProcessResources() throws IOException {
// 流程定義id
String processDefinitionId = "";
// 獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程定義對象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
//獲取bpmn
String resource_bpmn = processDefinition.getResourceName();
//獲取png
String resource_png =processDefinition.getDiagramResourceName();
// 資源信息
System.out.println("bpmn: " + resource_bpmn);
System.out.println("png: " + resource_png);
File file_png = new File("d:/purchasingflow01.png");
File file_bpmn = new File("d:/purchasingflow01.bpmn");
// 輸出bpmn
InputStream resourceAsStream = null;
resourceAsStream = repositoryService.getResourceAsStream(
processDefinition.getDeploymentId(), resource_bpmn);
FileOutputStream fileOutputStream = new FileOutputStream(file_bpmn);
byte[] b = new byte[1024];
int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
fileOutputStream.write(b, 0, len);
}
// 輸出圖片
resourceAsStream = repositoryService.getResourceAsStream(
processDefinition.getDeploymentId(), resource_png);
fileOutputStream = new FileOutputStream(file_png);
// byte[] b = new byte[1024];
// int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
fileOutputStream.write(b, 0, len);
}
}
6.5.2 方式 2
通過查詢流程部署信息獲取流程定義資源。
// 獲取流程定義圖片資源
@Test
public void getProcessResources() throws IOException {
//流程部署id
String deploymentId = "9001";
// 通過流程引擎獲取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//讀取資源名稱
List<String> resources =repositoryService.getDeploymentResourceNames(deploymentId);
String resource_image = null;
//獲取圖片
for(String resource_name :resources){
if(resource_name.indexOf(".png")>=0){
resource_image = resource_name;
}
}
//圖片輸入流
InputStream inputStream =
repositoryService.getResourceAsStream(deploymentId, resource_image);
File exportFile = new File("d:/holiday.png");
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
byte[] buffer = new byte[1024];
int len = -1;
//輸出圖片
while((len = inputStream.read(buffer))!=-1){
fileOutputStream.write(buffer, 0, len);
}
inputStream.close();
fileOutputStream.close();
}
說明:
- deploymentId 爲流程部署 ID
- resource_name 爲 act_ge_bytearray 表中 NAME_列的值
- 使用 repositoryService 的 getDeploymentResourceNames方法可以獲取指定部署下得所有文件的名稱
- 使用 repositoryService 的 getResourceAsStream 方法傳入部署 ID和資源圖片名稱可以獲取部署下指定名稱文件的輸入流
- 最後的將輸入流中的圖片資源進行輸出。
6.6 流程歷史信息的查看
即使流程定義已經刪除了,流程執行的歷史信息通過前面的分析,依然保存在 activiti 的 act_hi_*
相
關的表中。所以我們還是可以查詢流程執行的歷史信息,可以通過 HistoryService 來查看相關的歷史
記錄。
public void testHistoric01(){
HistoryService historyService = pe.getHistoryService();
HistoricActivityInstanceQuery query =
historyService.createHistoricActivityInstanceQuery();
query.processInstanceId("1501");
List<HistoricActivityInstance> list = query.list();
for(HistoricActivityInstance ai :list){
System.out.println(ai.getActivityId());
System.out.println(ai.getActivityName());
System.out.println(ai.getProcessDefinitionId());
System.out.println(ai.getProcessInstanceId());
System.out.println("==============================");
}
}
七 流程實例
7.1 什麼是流程實例
參與者(可以是用戶也可以是程序)按照流程定義內容發起一個流程,這就是一個流程實例。是動
態的。流程定義和流程實例的圖解:
7.2啓動流程實例
流程定義部署在 activiti 後,就可以在系統中通過 activiti 去管理該流程的執行,執行流程表示流程的一次執行。比如部署系統請假流程後,如果某用戶要申請請假,這時就需要執行這個流程,如果另外一個用戶也要申請請假,則也需要執行該流程,每個執行互不影響,每個執行是單獨的流程實例。
執行流程首先需要啓動流程實例。
@Test
public void startProcessInstance() {
// 流程定義key
String processDefinitionKey = "";
// 獲取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 根據流程定義key啓動流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);
System.out.println(" 流 程 定 義 id : " +
processInstance.getProcessDefinitionId());
System.out.println("流程實例id: " + processInstance.getId());
System.out.println("當前活動Id: " + processInstance.getActivityId());
}
7.3Businesskey(業務標識)
啓動流程實例時,指定的businesskey,就會在act_ru_execution流程實例的執行表中存儲businesskey。
Businesskey:業務標識,通常爲業務表的主鍵,業務標識和流程實例一一對應。業務標識來源於業務系統。存儲業務標識就是根據業務標識來關聯查詢業務系統的數據。
比如: 請假流程啓動一個流程實例,就可以將請假單的 id 作爲業務標識存儲到 activiti 中,將來查詢activiti 的流程實例信息就可以獲取請假單的 id 從而關聯查詢業務系統數據庫得到請假單信息。
代碼:
// 根據流程定義的key啓動一個流程實例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,
businessKey);
Activiti 中存儲業務標識:
7.4 操作數據庫表
啓動流程實例,操作如下數據庫表:
#流程實例執行表,記錄當前流程實例的執行情況
SELECT * FROM act_ru_execution
說明:
流程實例執行,如果當前只有一個分支時,一個流程實例只有一條記錄且執行表的主鍵 id 和流程實
例 id 相同,如果當前有多個分支正在運行,則該執行表中有多條記錄,存在執行表的主鍵和流程實例
id 不相同的記錄。 不論當前有幾個分支,總會有一條記錄的執行表的主鍵和流程實例 id 相同。
一個流程實例運行完成,此表中與流程實例相關的記錄刪除。
#任務執行表,記錄當前執行的任務
SELECT * FROM act_ru_task
說明:啓動流程實例,流程當前執行到第一個任務結點,此表會插入一條記錄表示當前任務的執行
情況,如果任務完成則記錄刪除。
#任務參與者,記錄當前參與任務的用戶或組
SELECT * FROM act_ru_identitylink
#流程實例歷史表
SELECT * FROM act_hi_procinst
流程實例啓動,會在此表插入一條記錄,流程實例運行完成記錄也不會刪除。
#任務歷史表,記錄所有任務
SELECT * FROM act_hi_taskinst
開始一個任務,不僅在 act_ru_task 表插入記錄,也會在歷史任務表插入一條記錄,任務歷史表的主
鍵就是任務 id,任務完成此表記錄不刪除。
#活動歷史表,記錄所有活動
SELECT * FROM act_hi_actinst
活動包括任務,所以此表中不僅記錄了任務,還記錄了流程執行過程的其它活動,比如開始事件、結束事件。
7.5 查詢流程實例
流程在運行過程中可以查詢流程實例的狀態,當前運行結點等信息。
@Test
public void queryProcessInstance() {
// 流程定義key
String processDefinitionKey = "holiday";
// 獲取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefinitionKey).list();
for (ProcessInstance processInstance : list) {
System.out.println("----------------------------");
System.out.println("流程實例id: "+ processInstance.getProcessInstanceId());
System.out.println("所屬流程定義id: "+ processInstance.getProcessDefinitionId());
System.out.println("是否執行完成: " + processInstance.isEnded());
System.out.println("是否暫停: " + processInstance.isSuspended());
System.out.println(" 當 前 活 動 標 識 : " + processInstance.getActivityId());
}
}
7.5.1 關聯 businessKey
需求:
在 activiti 實際應用時,查詢流程實例列表時可能要顯示出業務系統的一些相關信息,比如:查詢當
前運行的請假流程列表需要將請假單名稱、 請假天數等信息顯示出來, 請假天數等信息在業務系統
中存在,而並沒有在 activiti 數據庫中存在,所以是無法通過 activiti 的 api 查詢到請假天數等信息。
實現:
在查詢流程實例時,通過 businessKey(業務標識 )關聯查詢業務系統的請假單表,查詢出請假天
數等信息。
通過下面的代碼就可以獲取 activiti 中所對應實例保存的業務 Key。而這個業務 Key 一般都會保存相
關聯的業務操作表的主鍵,再通過主鍵 ID 去查詢業務信息,比如通過請假單的 ID,去查詢更多的
請假信息(請假人,請假時間,請假天數,請假事由等)
String businessKey = processInstance.getBusinessKey();
在 activiti 的 act_ru_execution 表,字段 BUSINESS_KEY 就是存放業務 KEY 的。
7.6 掛起、激活流程實例
某些情況可能由於流程變更需要將當前運行的流程暫停而不是直接刪除,流程暫停後將不會繼續執行。
7.6.1 全部流程實例掛起
操作流程定義爲掛起狀態,該流程定義下邊所有的流程實例全部暫停:
流程定義爲掛起狀態,該流程定義將不允許啓動新的流程實例,同時該流程定義下所有的流程實例將
全部掛起暫停執行。
// 掛起激活流程定義
@Test
public void suspendOrActivateProcessDefinition() {
// 流程定義id
String processDefinitionId = "";
RepositoryService repositoryService = processEngine.getRepositoryService();
// 獲得流程定義
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
//是否暫停
boolean suspend = processDefinition.isSuspended();
if(suspend){
//如果暫停則激活,這裏將流程定義下的所有流程實例全部激活
repositoryService.activateProcessDefinitionById(processDefinitionId,true, null);
System.out.println("流程定義: "+processDefinitionId+"激活");
}else{
//如果激活則掛起,這裏將流程定義下的所有流程實例全部掛起
repositoryService.suspendProcessDefinitionById(processDefinitionId,true, null);
System.out.println("流程定義: "+processDefinitionId+"掛起");
}
}
7.6.2 單個流程實例掛起
操作流程實例對象,針對單個流程執行掛起操作,某個流程實例掛起則此流程不再繼續執行, 完成該流程實例的當前任務將報異常。
@Test
public void suspendOrActiveProcessInstance() {
// 流程實例id
String processInstanceId = "";
// 獲取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//根據流程實例id查詢流程實例
ProcessInstance processInstance =runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
boolean suspend = processInstance.isSuspended();
if(suspend){
//如果暫停則激活
runtimeService.activateProcessInstanceById(processInstanceId);
System.out.println("流程實例: "+processInstanceId+"激活");
}else{
//如果激活則掛起
runtimeService.suspendProcessInstanceById(processInstanceId);
System.out.println("流程實例: "+processInstanceId+"掛起");
}
}
八 個人任務
8.1 分配任務負責人
8.1.1 固定分配
在進行業務流程建模時指定固定的任務負責人
在 properties 視圖中,填寫 Assignee 項爲任務負責人。
8.1.1.1 注意事項
由於固定分配方式,任務只管一步一步執行任務,執行到每一個任務將按照 bpmn 的配置去分配任務負責人。
2.1.2 表達式分配
2.1.2.1 UEL 表達式
Activiti 使用 UEL 表達式, UEL 是 java EE6 規範的一部分, UEL(Unified Expression Language)即統一表達式語言, activiti 支持兩個 UEL 表達式: UEL-value
和 UEL-method
。
-
UEL-value 定義如下:
assignee 這個變量是 activiti 的一個流程變量。
或:
user 也是 activiti 的一個流程變量, user.assignee 表示通過調用 user 的 getter 方法獲取值。 -
UEL-method 方式如下:
userBean 是 spring 容器中的一個 bean,表示調用該 bean 的 getUserId()方法。 -
UEL-method 與 UEL-value 結合
再比如:
${ldapService.findManagerForEmployee(emp)}
ldapService 是 spring 容器的一個 bean,findManagerForEmployee 是該 bean 的一個方法,emp 是 activiti流程變量, emp 作爲參數傳到 ldapService.findManagerForEmployee 方法中。 -
其它
表達式支持解析基礎類型、 bean、 list、 array 和 map,也可作爲條件判斷。
如下:
${order.price > 100 && order.price < 250}
8.1.2.2 使用流程變量分配任務
- 定義任務分配流程變量
- 設置流程變量
在啓動流程實例時設置流程變量,如下:
//啓動流程實例時設計流程變量
//定義流程變量
Map<String, Object> variables = new HashMap<String, Object>();
//設置流程變量assignee
variables.put("assignee", "張三");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
8.1.2.3 注意事項
由於使用了表達式分配,必須保證在任務執行過程表達式執行成功,比如:
某個任務使用了表達式${order.price > 100 && order.price < 250},當執行該任務時必須保證 order 在
流程變量中存在,否則 activiti 異常。
8.1.3 監聽器分配
任務監聽器是發生對應的任務相關事件時執行自定義 java 邏輯 或表達式。
任務相當事件包括:
Create:任務創建後觸發
Assignment:任務分配後觸發
Delete:任務完成後觸發
All:所有事件發生都觸發
java 邏輯 或表達式:
表達式參考上邊的介紹的 UEL 表達式,這裏主要介紹監聽類使用。
定義任務監聽類,且類必須實現 org.activiti.engine.delegate.TaskListener 接口
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
//這裏指定任務負責人
delegateTask.setAssignee("張三");
}
}
8.1.3.1 注意事項
使用監聽器分配方式,按照監聽事件去執行監聽類的 notify 方法,方法如果不能正常執行也會影響任務的執行。
8.2查詢任務
查詢任務負責人的待辦任務:
// 查詢當前個人待執行的任務
@Test
public void findPersonalTaskList() {
// 流程定義key
String processDefinitionKey = "holiday";
// 任務負責人
String assignee = "張三丰";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey(processDefinitionKey)
.includeProcessVariables().taskAssignee(assignee).list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程實例id: " + task.getProcessInstanceId());
System.out.println("任務id: " + task.getId());
System.out.println("任務負責人: " + task.getAssignee());
System.out.println("任務名稱: " + task.getName());
}
}
8.2.1 關聯 businessKey
需求:
在 activiti 實際應用時,查詢待辦任務可能要顯示出業務系統的一些相關信息,比如:查詢待審批請
假單任務列表需要將請假單的日期、 請假天數等信息顯示出來, 請假天數等信息在業務系統中存在,
而並沒有在 activiti 數據庫中存在,所以是無法通過 activiti 的 api 查詢到請假天數等信息。
實現:
在查詢待辦任務時,通過 businessKey(業務標識 )關聯查詢業務系統的請假單表,查詢出請假天
數等信息。
8.3辦理任務
指定任務 id,調用 TaskService 完成任務:
// 完成任務
@Test
public void completTask() {
//任務id
String taskId = "10305";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
taskService.complete(taskId);
System.out.println("完成任務");
}
注意:在實際應用中,完成任務前需要校驗任務的負責人是否具有該任務的辦理權限。
九 流程變量
9.1 什麼是流程變量
流程變量在 activiti 中是一個非常重要的角色,流程運轉有時需要靠流程變量,業務系統和 activiti結合時少不了流程變量,流程變量就是 activiti 在管理工作流時根據管理需要而設置的變量。比如在請假流程流轉時如果請假天數大於 3 天則由總經理審覈,否則由人事直接審覈, 請假天數就可以設置爲流程變量,在流程流轉時使用。
注意:雖然流程變量中可以存儲業務數據可以通過 activiti 的 api 查詢流程變量從而實現查詢業務數據,但是不建議這樣使用,因爲業務數據查詢由業務系統負責, activiti 設置流程變量是爲了流程執行需要而創建。
9.2流程變量類型
注意:如果將 pojo 存儲到流程變量中,必須實現序列化接口 serializable,爲了防止由於新增字段無
法反序列化,需要生成 serialVersionUID。
9.3 流程變量作用域
流程變量的作用域默認是一個流程實例(processInstance),也可以是一個任務(task)或一個執行實例
(execution),這三個作用域流程實例的範圍最大,可以稱爲 global 變量
,任務和執行實例僅僅是針對
一個任務和一個執行實例範圍,範圍沒有流程實例大, 稱爲 local 變量
。
如下:
一個流程實例
Global 變量: userId(變量名): zhangsan(變量值)
變量名不允許重複
global 變量中變量名不允許重複,設置相同名稱的變量,後設置的值會覆蓋前設置的變量值。
Local 變量由於在不同的任務或不同的執行實例中,作用域互不影響,變量名可以相同沒有影響。
Local 變量名也可以和 global 變量名相同,沒有影響。
9.4 流程變量的使用方法
第一步:設置流程變量
第二步:通過 UEL 表達式使用流程變量
- 可以在 assignee 處設置 UEL 表達式,表達式的值爲任務的負責人
比如: ${assignee}, assignee 就是一個流程變量名稱
Activiti 獲取 UEL 表達式的值 ,即流程變量 assignee 的值 ,將 assignee 的值作爲任務的負責人進行任務分配 - 可以在連線上設置 UEL 表達式,決定流程走向
比如: {price<10000}: price 就是一個流程變量名稱, uel 表達式結果類型爲布爾類型
如果 UEL 表達式是 true,要決定 流程執行走向。
9.5 使用 Global 變量控制流程
9.5.1 需求:
員工創建請假申請單,由部門經理審覈,部門經理審覈通過後請假 3 天及以下由人事經理直接審覈, 3 天以上先由總經理審覈,總經理審覈通過再由人事經理存檔。
9.5.2流程程定義
請假天數大於等於 3 連線條件:
請假天數小於 3 連線條件:
9.5.3 設置 global 流程變量
在部門經理審覈前設置流程變量,變量值爲請假單信息(包括請假天數),部門經理審覈後可以根據
流程變量的值決定流程走向。
9.5.3.1 啓動流程時設置
在啓動流程時設置流程變量,變量的作用域是整個流程實例。
通過 map<key,value>設置流程變量, map 中可以設置多個變量,這個 key 就是流程變量的名字。
// 啓動流程時設置流程變量
@Test
public void startProcessInstance() {
// 流程定義key
String processDefinitionKey = "";
Holiday holiday = new Holiday();
holiday.setNum(3);
// 定義流程變量
Map<String, Object> variables = new HashMap<String, Object>();
//變量名是num,變量值是holiday.getNum(),變量名也可以是一個對象
variables.put("num", holiday.getNum());
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
System.out.println(" 流 程 實 例 id:" + processInstance.getProcessInstanceId());
}
說明:
startProcessInstanceByKey(processDefinitionKey, variables)流程變量作用域是一個流
程實例,流程變量使用 Map 存儲,同一個流程實例設置變量 map 中 key 相同,後者覆蓋前者。
9.5.3.2 任務辦理時設置
在完成任務時設置流程變量,該流程變量只有在該任務完成後其它結點纔可使用該變量,它的作用域是整個流程實例,如果設置的流程變量的 key 在流程實例中已存在相同的名字則後設置的變量替換前邊設置的變量。
這裏需要在創建請假單任務完成時設置流程變量
// 辦理任務時設置流程變量
@Test
public void completTask() {
//任務id
String taskId = "";
TaskService taskService = processEngine.getTaskService();
Holiday holiday = new Holiday();
holiday.setNum(4);
// 定義流程變量
Map<String, Object> variables = new HashMap<String, Object>();
//變量名是holiday,變量值是holiday對象
variables.put("holiday", holiday);
taskService.complete(taskId, variables);
}
說明:
通過當前任務設置流程變量,需要指定當前任務 id,如果當前執行的任務 id 不存在則拋出異常。
任務辦理時也是通過 map<key,value>設置流程變量,一次可以設置多個變量。
9.5.3.3 通過當前流程實例設置
通過流程實例 id 設置全局變量,該流程實例必須未執行完成。
public void setGlobalVariableByExecutionId(){
//當前流程實例執行 id,通常設置爲當前執行的流程實例
String executionId="2601";
RuntimeService runtimeService = processEngine.getRuntimeService();
Holiday holiday = new Holiday();
holiday.setNum(3);
//通過流程實例 id設置流程變量
runtimeService.setVariable(executionId, "holiday", holiday);
//一次設置多個值
//runtimeService.setVariables(executionId, variables)
}
注意:
executionId 必須當前未結束 流程實例的執行 id,通常此 id 設置流程實例 的 id。
也可以通過 runtimeService.getVariable()獲取流程變量
9.5.3.4 通過當前任務設置
@Test
public void setGlobalVariableByTaskId(){
//當前待辦任務id
String taskId="1404";
TaskService taskService = processEngine.getTaskService();
Holiday holiday = new Holiday();
holiday.setNum(3);
//通過任務設置流程變量
taskService.setVariable(taskId, "holiday", holiday);
//一次設置多個值
//taskService.setVariables(taskId, variables)
}
注意:
任務id必須是當前待辦任務id, act_ru_task中存在。 如果該任務已結束,報錯:
也可以通過 taskService.getVariable()獲取流程變量。
9.5.4測試
-
正常測試:
設置流程變量的值大於等於 3 天
設計流程變量的值小於 3 天 -
異常測試:
流程變量不存在
流程變量的值爲空 NULL, price 屬性爲空
UEL 表達式都不符合條件
不設置連線的條件
9.5.5注意事項
- 如果 UEL 表達式中流程變量名不存在則報錯。
- 如果 UEL 表達式中流程變量值爲空 NULL,流程不按 UEL 表達式去執行,而流程結束 。
- 如果 UEL 表達式都不符合條件,流程結束
- 如果連線不設置條件,會走 flow 序號小的那條線
操作數據庫表
設置流程變量會在當前執行流程變量表插入記錄,同時也會在歷史流程變量表也插入記錄。
SELECT * FROM act_ru_variable #當前流程變量表
記錄當前運行流程實例可使用的流程變量,包括 global 和 local 變量
Id_:主鍵
Type_:變量類型
Name_:變量名稱
Execution_id_:所屬流程實例執行 id, global 和 local 變量都存儲
Proc_inst_id_:所屬流程實例 id, global 和 local 變量都存儲
Task_id_:所屬任務 id, local 變量存儲
Bytearray_: serializable 類型變量存儲對應 act_ge_bytearray 表的 id
Double_: double 類型變量值
Long_: long 類型變量值
Text_: text 類型變量值
SELECT * FROM act_hi_varinst #歷史流程變量表
記錄所有已創建的流程變量,包括 global 和 local 變量
字段意義參考當前流程變量表。
9.6設置 local 流程變量
9.6.1 任務辦理時設置
任務辦理時設置 local 流程變量,當前運行的流程實例只能在該任務結束前使用,任務結束該變量無法在當前流程實例使用,可以通過查詢歷史任務查詢。
// 辦理任務時設置local流程變量
@Test
public void completTask() {
//任務id
String taskId = "";
TaskService taskService = processEngine.getTaskService();
// 定義流程變量
Map<String, Object> variables = new HashMap<String, Object>();
Holiday holiday = new Holiday ();
holiday.setNum(3);
// 定義流程變量
Map<String, Object> variables = new HashMap<String, Object>();
//變量名是holiday,變量值是holiday對象
variables.put("holiday", holiday);
// 設置local變量,作用域爲該任務
taskService.setVariablesLocal(tasked, variables);
taskService.complete(taskId);
}
說明:
設置作用域爲任務的 local 變量,每個任務可以設置同名的變量,互不影響。
9.6.2 通過當前任務設置
@Test
public void setLocalVariableByTaskId(){
//當前待辦任務id
String taskId="1404";
TaskService taskService = processEngine.getTaskService();
Holiday holiday = new Holiday ();
holiday.setNum(3);
//通過任務設置流程變量
taskService.setVariableLocal(taskId, "holiday", holiday);
//一次設置多個值
//taskService.setVariablesLocal(taskId, variables)
}
注意:
任務 id 必須是當前待辦任務 id, act_ru_task 中存在。
9.6.3 Local 變量測試 1
如果上邊例子中設置 global 變量改爲設置 local 變量是否可行?爲什麼?
Local 變量在任務結束後無法在當前流程實例執行中使用,如果後續的流程執行需要用到此變量則會
報錯。
9.6.4 Local 變量測試 2
在部門經理審覈、總經理審覈、人事經理審覈時設置 local 變量,可通過 historyService 查詢每個歷
史任務時將流程變量的值也查詢出來。
代碼如下:
// 創建歷史任務查詢對象
HistoricTaskInstanceQuery historicTaskInstanceQuery =historyService.createHistoricTaskInstanceQuery();
// 查詢結果包括 local變量
historicTaskInstanceQuery.includeTaskLocalVariables();
for (HistoricTaskInstance historicTaskInstance : list) {
System.out.println("==============================");
System.out.println(" 任 務 id : " + historicTaskInstance.getId());
System.out.println(" 任 務 名 稱 : " + historicTaskInstance.getName());
System.out.println(" 任 務 負 責 人 : " + historicTaskInstance.getAssignee());
System.out.println(" 任 務 local 變 量 : "+ historicTaskInstance.getTaskLocalVariables());
}
注意:查詢歷史流程變量,特別是查詢 pojo 變量需要經過反序列化,不推薦使用。
十 組任務
10.1 Candidate-users 候選人
10.1.1需求
在流程定義中在任務結點的 assignee 固定設置任務負責人,在流程定義時將參與者固定設置在.bpmn 文件中,如果臨時任務負責人變更則需要修改流程定義,系統可擴展性差。針對這種情況可以給任務設置多個候選人,可以從候選人中選擇參與者來完成任務。
設置任務候選人
在流程圖中任務節點的配置中設置 candidate-users(候選人),多個候選人之間用逗號分開。
查看 bpmn 文件:
我們可以看到部門經理的審覈人已經設置爲 zhangsan,lishi 這樣的一組候選人,可以使用activiti:candiateUsers=”用戶 1,用戶 2,用戶 3”的這種方式來實現設置一組候選人。
辦理組任務
10.1.1.1 組任務辦理流程
第一步:查詢組任務
指定候選人,查詢該候選人當前的待辦任務。
候選人不能辦理任務。
第二步:拾取(claim)任務
該組任務的所有候選人都能拾取。
將候選人的組任務,變成個人任務。原來候選人就變成了該任務的負責人。
***如果拾取後不想辦理該任務?
需要將已經拾取的個人任務歸還到組裏邊,將個人任務變成了組任務。
第三步:查詢個人任務
查詢方式同個人任務部分,根據 assignee 查詢用戶負責的個人任務。
第四步:辦理個人任務
10.1.1.2 用戶查詢組任務
根據候選人查詢組任務
@Test
public void findGroupTaskList() {
// 流程定義key
String processDefinitionKey = "holiday4";
// 任務候選人
String candidateUser = "zhangsan";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
//查詢組任務
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey(processDefinitionKey)//
.taskCandidateUser(candidateUser)//根據候選人查詢
.list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程實例id: " + task.getProcessInstanceId());
System.out.println("任務id: " + task.getId());
System.out.println("任務負責人: " + task.getAssignee());
System.out.println("任務名稱: " + task.getName());
}
}
10.1.1.3 用戶拾取組任務
候選人員拾取組任務後該任務變爲自己的個人任務。
@Test
public void claimTask(){
TaskService taskService = processEngine.getTaskService();
//要拾取的任務id
String taskId = "6302";
//任務候選人id
String userId = "lisi";
//拾取任務
//即使該用戶不是候選人也能拾取(建議拾取時校驗是否有資格)
//校驗該用戶有沒有拾取任務的資格
Task task = taskService.createTaskQuery()//
.taskId(taskId)
.taskCandidateUser(userId)//根據候選人查詢
.singleResult();
if(task!=null){
taskService.claim(taskId, userId);
System.out.println("任務拾取成功");
}
}
說明: 即使該用戶不是候選人也能拾取, 建議拾取時校驗是否有資格;組任務拾取後,該任務已有負責人,通過候選人將查詢不到該任務
10.1.1.4 用戶查詢個人待辦任務
查詢方式同個人任務查詢
@Test
public void findPersonalTaskList() {
// 流程定義key
String processDefinitionKey = "holiday4";
// 任務負責人
String assignee = "zhangsan";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey(processDefinitionKey)//
.taskAssignee(assignee).list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println(" 流 程 實 例 id : " +
task.getProcessInstanceId());
System.out.println("任務id: " + task.getId());
System.out.println("任務負責人: " + task.getAssignee());
System.out.println("任務名稱: " + task.getName());
}
}
10.1.1.5 用戶辦理個人任務
同個人任務辦理
/**完成任務*/
@Test
public void completeTask(){
//任務ID
String taskId = "12304";
processEngine.getTaskService().complete(taskId);
System.out.println("完成任務: "+taskId);
}
說明:建議完成任務前校驗該用戶是否是該任務的負責人。
10.1.1.6 歸還組任務
如果個人不想辦理該組任務,可以歸還組任務,歸還後該用戶不再是該任務的負責人
// 歸還組任務,由個人任務變爲組任務,還可以進行任務交接
@Test
public void setAssigneeToGroupTask() {
// 查詢任務使用TaskService
TaskService taskService = processEngine.getTaskService();
// 當前待辦任務
String taskId = "6004";
// 任務負責人
String userId = "zhangsan2";
// 校驗userId是否是taskId的負責人,如果是負責人才可以歸還組任務
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
// 如果設置爲null,歸還組任務,該 任務沒有負責人
taskService.setAssignee(taskId, null);
}
}
說明: 建議歸還任務前校驗該用戶是否是該任務的負責人,也可以通過 setAssignee 方法將任務委託給其它用戶負責,注意被委託的用戶可以不是候選人(建議不要這樣使用)
10.1.1.7 任務交接
任務交接,任務負責人將任務交給其它候選人辦理該任務
@Test
public void setAssigneeToCandidateUser() {
// 查詢任務使用TaskService
TaskService taskService = processEngine.getTaskService();
// 當前待辦任務
String taskId = "6004";
// 任務負責人
String userId = "zhangsan2";
// 校驗userId是否是taskId的負責人,如果是負責人才可以歸還組任務
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
// 將此任務交給其它候選人辦理該 任務
String candidateuser = "zhangsan";
// 根據候選人和組任務id查詢,如果有記錄說明該 候選人有資格拾取該 任務
Task task2 = taskService.createTaskQuery().taskId(taskId)
.taskCandidateUser(candidateuser).singleResult();
if (task2 != null) {
// 纔可以交接
taskService.setAssignee(taskId, candidateuser);
}
}
}
10.1.1.8 數據庫表操作
SELECT * FROM act_ru_task #任務執行表,記錄當前執行的任務,由於該任務當前是組任務,所有
assignee 爲空,當拾取任務後該字段就是拾取用戶的 id
SELECT * FROM act_ru_identitylink #任務參與者,記錄當前參考任務用戶或組,當前任務如果設置
了候選人,會向該表插入候選人記錄,有幾個候選就插入幾個
與 act_ru_identitylink 對應的還有一張歷史表 act_hi_identitylink,向 act_ru_identitylink 插入記錄的同
時也會向歷史表插入記錄。任務完成
十一 網關
11.1 排他網關
11.1.1 什麼是排他網關:
排他網關(也叫異或(XOR)網關,或叫基於數據的排他網關),用來在流程中實現決策。 當流程
執行到這個網關,所有分支都會判斷條件是否爲 true,如果爲 true 則執行該分支,注意,排他網關只會選擇一個爲 true 的分支執行。 (即使有兩個分支條件都爲 true, 排他網關也會只選擇一條分支去執行)
爲什麼要用排他網關?
不用排他網關也可以實現分支,如下圖:
上圖中,在連線的 condition 條件上設置分支條件。
缺點:
如果條件都不滿足,不使用排他網關,流程就結束了(是異常結束)。
如果 使用排他網關決定分支的走向,如下:
如果從網關出去的線所有條件都不滿足則系統拋出異常。
org.activiti.engine.ActivitiException: No outgoing sequence flow of the
exclusive gateway 'exclusivegateway1' could be selected for continuing
the process
at
org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehav
ior.leave(ExclusiveGatewayActivityBehavior.java:85)
說明 :經過排他網關必須要有一條且只有一條分支走。
11.1.2流程定義
圖標:
11.1.3測試
在部門經理審覈後,走排他網關,從排他網關出來的分支有兩條,一條是判斷請假天數是否大於 3天,另一條是判斷請假天數是否小於等於 3 天。
設置分支條件時,如果所有分支條件都不是 true,報錯:
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway
‘exclusivegateway1’ could be selected for continuing the process
at
org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivit
yBehavior.java:85)
11.2 並行網關
11.2.1什麼是並行網關
並行網關允許將流程分成多條分支,也可以把多條分支匯聚到一起,並行網關的功能是基於進入和外出順序流的:
- fork 分支:
並行後的所有外出順序流,爲每個順序流都創建一個併發分支。 - join 匯聚:
所有到達並行網關,在此等待的進入分支, 直到所有進入順序流的分支都到達以後, 流程就會通
過匯聚網關。
注意,如果同一個並行網關有多個進入和多個外出順序流, 它就同時具有分支和匯聚功能。 這時,網關會先匯聚所有進入的順序流,然後再切分成多個並行分支。
與其他網關的主要區別是,並行網關不會解析條件。 即使順序流中定義了條件,也會被忽略。
例子:
說明:
財務結算和入庫是兩個 execution 分支,在 act_ru_execution 表有兩條記錄分別是財務結算和入庫,
act_ru_execution 還有一條記錄表示該流程實例。
待財務結算和入庫任務全部完成,在匯聚點匯聚,通過 parallelGateway 並行網關。
並行網關在業務應用中常用於會籤任務,會籤任務即多個參與者共同辦理的任務。
11.2.2流程定義
圖標:
11.2.3測試
當執行到並行網關數據庫跟蹤如下:
當前任務表: SELECT * FROM act_ru_task #當前任務表
上圖中:有兩個(多個)任務當前執行。
通過流程實例執行表: SELECT * FROM act_ru_execution #流程實例的執行表
上圖中,說明當前流程實例有多個分支(兩個)在運行。
對並行任務的執行:
並行任務執行不分前後,由任務的負責人去執行即可。
當完成並任務中一個任務後:
已完成的任務在當前任務表 act_ru_task_已被刪除。
在流程實例執行表: SELECT * FROM act_ru_execution 有中多個分支存在且有並行網關的匯聚結點。
有並行網關的匯聚結點:說明有一個分支已經到匯聚,等待其它的分支到達。
當所有分支任務都完成,都到達匯聚結點後:
流程實例執行表: SELECT * FROM act_ru_execution,執行流程實例不存在,說明流程執行結束。
總結:所有分支到達匯聚結點,並行網關執行完成。
11.3 包含網關
11.3.1 什麼是包含網關
包含網關可以看做是排他網關和並行網關的結合體。 和排他網關一樣,你可以在外出順序流上定義條件,包含網關會解析它們。 但是主要的區別是包含網關可以選擇多於一條順序流,這和並行網關一樣。
包含網關的功能是基於進入和外出順序流的:
- 分支:
所有外出順序流的條件都會被解析,結果爲 true 的順序流會以並行方式繼續執行, 會爲每個順序流創建一個分支。 - 匯聚:
所有並行分支到達包含網關,會進入等待狀態, 直到每個包含流程 token 的進入順序流的分支都到達。 這是與並行網關的最大不同。換句話說,包含網關只會等待被選中執行了的進入順序流。 在匯聚之後,流程會穿過包含網關繼續執行。
11.3.2 流程定義:
企業體檢流程,公司全體員工進行常規項檢查、抽血化驗,公司管理層除常規檢查和抽血化驗還要進行增加項檢查。
圖標:
員工類型:
通過流程變量 userType 來表示,如果等於 1 表示普通員工,如果等於 2 表示領導
注意:通過包含網關的每個分支的連線上設置 condition 條件。
11.3.3測試
如果包含網關設置的條件中,流程變量不存在,報錯;
org.activiti.engine.ActivitiException: Unknown property used in expression: ${userType==‘1’ ||
userType==‘2’}
需要在流程啓動時設置流程變量 userType
當執行到包含網關:
流程實例執行表: SELECT * FROM act_ru_execution
第一條記錄:包含網關分支。
後兩條記錄:兩個分支:常規項體檢,抽血化驗
當前任務表: ACT_RU_TASK
上圖中,常規項體檢,抽血化驗都是當前的任務,在並行執行。
如果有一個分支執行到匯聚:
先走到匯聚結點的分支,要等待其它分支走到匯聚。
等所有分支走到匯聚,包含網關就執行完成。
包含網關執行完成,分支和匯聚就從 act_ru_execution 刪除。
小結:在分支時,需要判斷條件, 符合條件的分支,將會執行,符合條件的分支最終才進行匯聚。
十二 總結
什麼是工作流?
就是通過計算機對業務流程進行自動化管理,實現多個參與者按照預定義的流程去自動執行業務流程。
什麼 activiti?
Activiti 是一個工作流的引擎,開源的架構,基本 bpmn2.0 標準進行流程定義,它的是前身是 jbpm。Activiti 通過是要嵌入到業務系統開發使用。
如何使用 activiti 開發?
-
第一步:部署 activiti 的環境。
環境包括: jar 包和數據庫(25 張表)
業務系統通過 spring 和 activiti 整合進行開發。 -
第二步:使用 activiti 提供流程設計器(和 idea 或 eclipse 集成的 designer)工具進行流程定義
流程定義生成兩個文件: .bpmn 和.png(不是必須的)。 -
第三步;將流程定義文件部署到 activiti 的數據庫
SELECT * FROM act_re_deployment #流程定義部署表
一次部署插入一條記錄,記錄流程定義的部署信息
SELECT * FROM act_re_procdef #流程定義表
一次部署流程定義信息,如果一次部署兩個流程定義,插入兩條記錄
建議:一次部署只部署一個流程定義,這樣 act_re_deployment 和 act_re_procdef 一對一關係
常用兩個方法:單個文件部署和 zip 文件部署。
建議單個文件部署。
-
第四步: 啓動一個流程實例
業務系統就可以按照流程定義去執行業務流程,執行前需要啓動一個流程實例根據流程定義來啓動一個流程實例。
指定一個流程定義的 key 啓動一個流程實例, activiti 根據 key 找最新版本的流程定義。
指定一個流程定義的 id 啓動一個流程實例。
啓動一個實例需要指定 businesskey(業務標識), businessKey 是 activiti 和業務系統整合時橋樑。
比如: 請假流程, businessKey 就是請假單 id。
啓動一個實例還可以指定流程變量,流程變量是全局變量(生命期是整個流程實例,流程實例結束,變量就消失) -
第五步:查詢待辦任務
查詢個人任務:使用 taskService,根據 assignee 查詢該用戶當前的待辦任務。
查詢組任務:使用 taskService,根據 candidateuser 查詢候選用戶當前的待辦組任務。 -
第六步:辦理任務
辦理個人任務:調用 taskService 的 complete 方法完成任務。
如果是組任務,需要先拾取任務,調用 taskService 的 claim 方法拾取任務,拾取任務之後組任務就變成了個人任務(該任務就有負責人)。
網關:
排他網關:任務執行之後的分支,經過排他網關分支只有一條有效。
並行網關:任務執行後,可以多條分支,多條分支總會匯聚,匯聚完成,並行網關結束。
包含網關:是排他網關和並行網關結合體。