工作流概述
在一個公司中,每一項業務的開始和結束,都可以理解為一個工作流,例如,公司的費用報銷的基本流程如下:
如圖所示的工作流:員工先提出費用報銷申請,提交該申請給部門領導,部門領導審批后,再提交給財務部門審批,審批完成后,通知提出申請的員工可以報銷,即報銷流程結束。整個步驟按照正常工作方式一步步完成,這就是一個簡單而又完整的工作流工作流可以理解為從開始節點發起流程,然后經過其中多個節點,完成動作,最后到結束節點的整個過程
工作流系統
一個軟件系統中如果具有工作流系統,我們就把它稱為工作流系統。一個系統中的工作流的功能是對系統業務流程進行自動化管理。一個軟件系統的核心根本上還是業務流程,工作流只是協助業務流程的管理,即使沒有工作流業務一樣能照常展開,只不過使用工作流可以更好地管理業務流程,提高系統的擴展性
工作流的具體應用有:
- 關鍵業務類:訂單、報價處理、合同審核、供應鏈管理等等
- 行政管理類:出差申請、請假申請、日報周報等等
- 人事管理類:員工培訓安排、變動處理等等
- 財務相關類:收付款處理、報銷處理、預算申請等等
- 客戶服務類:客戶信息管理、客戶投訴、請求處理、售后服務等等
工作流引擎
在沒有工作流引擎之前,為了實現流程控制,通常的做法是采用狀態字段的值來跟蹤流程的變化。例如設立一個字段,初始值為 0,經過某些流程后變成 1,變成 2,最后根據這個值來判斷狀態,給出相應的處理
很明顯,這樣一來工作的流程會和業務高度耦合,當我們的流程發生變更時,所編寫的代碼也必須做出調整。如果有一樣工具能幫助我們管理工作流,並做到當業務流程變化之后,程序不需要跟着發生變化,那么我們的業務系統的適應能力將會有大幅提升
Activit7 是一個工作流引擎,可以將業務系統中復雜的業務流程抽取出來,使用專門的建模語言 BPMN2.0 進行定義,業務流程將按照預定義的流程執行。系統的流程由 activit 管理,從而減少業務系統由於流程變化而導致的工作量,提高系統健壯性
官方網址:https://www.activiti.org/
BPM
BPM(Business Process Management)即業務流程管理,是一種規范化的構造端到端的業務流程,以持續提高組織業務效率
BPM 軟件就是根據企業中業務環境的變化,推進人與人之間、人與系統之間以及系統與系統之間的整理及調整的經營方法與解決方案的 IT 工具。使用 BPM 軟件對企業內部及外部的業務流程的整個生命周期進行建模、自動化、管理監控和優化,可以降低企業成本,提高利潤
BPMN(Business Process Model AndNotation)即業務流程模型和符號,是一套標准的業務流程建模符號,使用 BPMN 提供的符號可以創建業務流程。Activit 就是使用 BPMN 進行流程建模、流程執行管理的
BPMN2.0 是業務流程建模符號 2.0 的縮寫,它由 Business Process Management Initiative 這個非營利協會創建並不斷發展。BPMN2.0 是使用一些符號來明確業務流程設計流程圖的一套符號規范,能增進業務建模時的溝通效率。目前 BPMN2.0 是最新的版本,它用於在 BPM 上下文中進行布局和可視化的溝通
BPMN2.0 的基本符號主要包含:
-
事件 Event
-
活動 Activity
活動是工作或任務的一個通用術語。一個活動可以是一個任務,還可以是一個當前流程的子處理流程;其次,你還可以為活動指定不同的類型。常見活動如下:
-
網關 GateWay
網關用來處理決策,有幾種常用網關需要了解:
-
排它網關
只有一條路徑會被選擇。流程執行到該網關時,按照輸出流的順序逐個計算,當條件的計算結果為 true 時,繼續執行當前網關的輸出流;如果多條線路計算結果都是 true,則會執行第一個值為 true 的線路。如果所有網關計算結果沒有 true,則引擎會拋出異常。排他網關需要和條件順序流結合使用,default 屬性指定默認順序流,當所有的條件不滿足時會執行默認順序流
-
並行網關
所有路徑會被同時選擇
-
拆分:並行執行所有輸出順序流,為每一條順序流創建一個並行執行線路
-
合並:所有從並行網關拆分並執行完成的線路均在此等候,直到所有的線路都執行完成才繼續向下執行
-
-
包容網關
可以同時執行多條線路,也可以在網關上設置條件
- 拆分:計算每條線路上的表達式,當表達式計算結果為 true 時,創建一個並行線路並繼續執行
- 合並:所有從並行網關拆分並執行完成的線路均在此等候,直到所有的線路都執行完成才繼續向下執行
-
事件網關
專門為中間捕獲事件設置的,允許設置多個輸出流指向多個不同的中間捕獲事件。當流程執行到事件網關后,流程處於等待狀態,需要等待拋出事件才能將等待狀態轉換為活動狀態
-
-
流向 Flow
流是連接兩個流程節點的連線,常見的流向包含以下幾種:
Activit 部署流程
Activiti 是一個工作流引擎,業務系統通過訪問 Activiti 所提供的接口,就可以很方便的操作流程的相關數據,把工作流環境與業務系統環境集成在一起
首先使用 Activiti 流程建模工具(Activity-Designer)來通過 BPMN2.0 符號來定義業務流程,生成一個 .bpmn 文件。.bpmn 文件就是業務流程定義文件,通過 xml 定義業務流程
得到 .bpmn 文件后,使用 Activiti 提供的 Api 把流程定義內容存儲起來。接着啟動一個流程實例(ProcessInstance),表示一次業務流程開始運行
既然系統的業務流程已經交給 Activiti 管理,那么通過 Activiti 就可以查看當前流程執行到哪一步,當前用戶需要辦理什么任務。這些操作由 activiti 幫我們管理,而不需要開發人員自己編寫 sql 語句查詢
用戶查詢到待辦任務后,就可以開始辦理某個任務了。如果這個任務辦理完成后,還需要其它用戶繼續辦理,比如采購單創建后要交由部門經理審核,那么這個過程也是由 Activiti 幫我們完成了,總之流程可以一直走下去,直到沒有下一個任務結點,那么這個流程實例也就完成了
Activit 使用
1. 安裝數據庫
需要注意的是,Activiti 運行必須要有數據庫的支持,支持的數據庫有:h2、mysql、oracle、postgres、mssql、db2
2. Activit 流程定義工具插件安裝
安裝插件想必不用我多說,搜索 actiBPM 插件,它就是 Activit Designer 的 IDEA 版本,點擊 Install 安裝即可。但由於筆者使用的是 IDEA 2020.3 版本沒有找到這個插件,猜測是不再支持了,只好退而求其次,選擇了一款名為 Activiti BPMN Visualizer 的插件
3. 創建一個 maven 工程並添加相關依賴
首先需要在 Java 工程中ProcessEngine 所需要的依賴,包括:
- activiti 的相關包
- 其他工具包,如 mybatis、alf4j、log4j 等
- activiti 依賴的 spring 包
- mysql 數據庫驅動
- 第三方數據連接池 dbcp
- 單元測試 Junit
<properties>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<activiti.version>7.0.0.Beta1</activiti.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 模型處理 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 轉換 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn json數據轉換 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 布局 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- activiti 雲支持 -->
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- mysql驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!-- mybatis -->
<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>
<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>
</dependencies>
4. log4j 日志配置
既然使用了 log4j,那就把日志也做個配置,在 resource 目錄下創建一個 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=f:\act\activiti.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
5. 編寫 activit 配置文件
我們使用 activit 提供的默認方式來創建 mysql 表,默認方式要求在 resources 目錄下創建 activit.cfg.xml 文件,路徑和文件名都不能修改。默認方式中 activiti.cfg.xml 里面 bean 的名字要叫 processEngineConfiguration,不可以修改
<?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">
<!-- 默認 id 對應的值為 processEngineConfiguration -->
<!-- processEngine Activiti 的流程引擎 -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 配置數據庫相關信息 -->
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activit?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&useAffectedRows=true"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="123"/>
<!-- activiti 數據庫表處理策略,true 為如果數據庫中已存在相應的表,則直接使用,否則創建 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
也可以使用連接池來提高性能
<?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">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/activit?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&useAffectedRows=true"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
</bean>
<!-- 默認 id 對應的值為 processEngineConfiguration -->
<!-- processEngine Activiti 的流程引擎 -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 引入上面配置好的鏈接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- activiti 數據庫表處理策略,true 為如果數據庫中已存在相應的表,則直接使用,否則創建 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
6. 編寫 Java 類生成表
創建一個測試類,調用 activiti 的工具類,生成 acitivti 需要的數據庫表。直接使用 activiti 提供的工具類 ProcessEngines,會默認讀取 classpath 下的 activiti.cfg.xml 文件,讀取其中的數據庫配置,創建 ProcessEngine,在創建 ProcessEngine 時會自動創建表。
public class testCreate {
@Test
public void testCreateTable() {
// 讀取 activiti.cfg.xml 配置文件,創建 ProcessEngine 的同時會創建表
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
}
這種方式默認讀取 resource 目錄下的 activiti.cfg.xml 配置文件,我們也可以自定義配置文件,並指定路徑進行讀取
// 先構建ProcessEngineConfiguration
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 通過 ProcessEngineConfiguration 創建 ProcessEngine,此時會創建數據庫
ProcessEngine processEngine = configuration.buildProcessEngine();
在測試程序執行過程中,idea 的控制台會輸出日志,說明程序正在創建數據表。執行完成后我們查看數據庫, 創建了 25 張表,結果如下:
至此·,我們就完成了 activiti 運行需要的數據庫和表的創建
Activiti 表結構
Activiti 的運行支持必須要有這 25 張表的支持,主要是在業務流程運行過程中,記錄參與流程的用戶主體,用戶組信息,以及流程的定義,流程執行時的信息,和流程的歷史信息等等
1. 表的命名規則和作用
觀察創建的表,我們發現 Activiti 的表都以 act_ 開頭,緊接着是表示表的用途的兩個字母標識,也和 Activiti 所提供的服務的 API 對應:
- ACT_RE:RE 表示 repository,這個前綴的表包含了流程定義和流程靜態資源 (圖片、規則、等等)
- ACT_RU:RU 表示 runtime,這些表運行時,會包含流程實例、任務、變量、異步任務等流程業務進行中的數據。Activiti 只在流程實例執行過程中保存這些數據,在流程結束時就會刪除這些記錄。這樣表就可以一直保持很小的體積,並且速度很快
- ACT_HI:HI 表示 history,這些表包含一些歷史數據,比如歷史流程實例、變量、任務等等
- ACT_GE:GE 表示 general,通用數據
2. Activiti 數據表介紹
表分類 | 表名 | 解釋 |
---|---|---|
一般數據 | ||
[ACT_GE_BYTEARRAY] | 通用的流程定義和流程資源 | |
[ACT_GE_PROPERTY] | 系統相關屬性 | |
流程歷史記錄 | ||
[ACT_HI_ACTINST] | 歷史的流程實例 | |
[ACT_HI_ATTACHMENT] | 歷史的流程附件 | |
[ACT_HI_COMMENT] | 歷史的說明性信息 | |
[ACT_HI_DETAIL] | 歷史的流程運行中的細節信息 | |
[ACT_HI_IDENTITYLINK] | 歷史的流程運行過程中用戶關系 | |
[ACT_HI_PROCINST] | 歷史的流程實例 | |
[ACT_HI_TASKINST] | 歷史的任務實例 | |
[ACT_HI_VARINST] | 歷史的流程運行中的變量信息 | |
流程定義表 | ||
[ACT_RE_DEPLOYMENT] | 部署單元信息 | |
[ACT_RE_MODEL] | 模型信息 | |
[ACT_RE_PROCDEF] | 已部署的流程定義 | |
運行實例表 | ||
[ACT_RU_EVENT_SUBSCR] | 運行時事件 | |
[ACT_RU_EXECUTION] | 運行時流程執行實例 | |
[ACT_RU_IDENTITYLINK] | 運行時用戶關系信息,存儲任務節點與參與者的相關信息 | |
[ACT_RU_JOB] | 運行時作業 | |
[ACT_RU_TASK] | 運行時任務 | |
[ACT_RU_VARIABLE] | 運行時變量 |
Activiti 體系架構
完成了 Activiti 數據庫表的生成,我們就可以在 Java 代碼中調用 Activiti 的工具類,下面來了解 Activiti 的類關系
1. 類關系圖
在新版本中,IdentityService、FormService 這兩個 Serivce 都已經刪除了,所以后面我們對於這兩個 Service 也不講解了,但老版本中還是有這兩個 Service,有需要可以自行了解一下
2. Service 服務接口
Service 是工作流引擎提供用於進行工作流部署、執行、管理的服務接口,我們使用這些接口就可以操作服務對應的表
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
簡單介紹一下各個 Service 的實現類:
-
RepositoryService
Activiti 的資源管理類,該服務負責部署流程定義,管理流程資源。在使用 Activiti 時,一開始需要先完成流程部署,即將使用建模工具設計的業務流程圖通過 RepositoryService 進行部署
-
RuntimeService
Activiti 的流程運行管理類,用於開始一個新的流程實例,獲取關於流程執行的相關信息。流程定義用於確定一個流程中的結構和各個節點間行為,而流程實例則是對應的流程定義的一個執行,可以理解為 Java 中類和對象的關系
-
TaskService
Activiti 的任務管理類,用於處理業務運行中的各種任務,例如查詢分給用戶或組的任務、創建新的任務、分配任務、確定和完成一個任務
-
HistoryService
Activiti 的歷史管理類,可以查詢歷史信息。執行流程時,引擎會保存很多數據,比如流程實例啟動時間、任務的參與者、完成任務的時間、每個流程實例的執行路徑等等。這個服務主要通過查詢功能來獲得這些數據
-
ManagementService
Activiti 的引擎管理類,提供了對 Activiti 流程引擎的管理和維護功能,這些功能不在工作流驅動的應用程序中使用,主要用於 Activiti 系統的日常維護