工作流概述

在一個公司中,每一項業務的開始和結束,都可以理解為一個工作流,例如,公司的費用報銷的基本流程如下:

如圖所示的工作流:員工先提出費用報銷申請,提交該申請給部門領導,部門領導審批後,再提交給財務部門審批,審批完成後,通知提出申請的員工可以報銷,即報銷流程結束。整個步驟按照正常工作方式一步步完成,這就是一個簡單而又完整的工作流工作流可以理解為從開始節點發起流程,然後經過其中多個節點,完成動作,最後到結束節點的整個過程

工作流系統

一個軟體系統中如果具有工作流系統,我們就把它稱為工作流系統。一個系統中的工作流的功能是對系統業務流程進行自動化管理。一個軟體系統的核心根本上還是業務流程,工作流只是協助業務流程的管理,即使沒有工作流業務一樣能照常展開,只不過使用工作流可以更好地管理業務流程,提高系統的擴充套件性

工作流的具體應用有:

  1. 關鍵業務類:訂單、報價處理、合同稽核、供應鏈管理等等
  2. 行政管理類:出差申請、請假申請、日報週報等等
  3. 人事管理類:員工培訓安排、變動處理等等
  4. 財務相關類:收付款處理、報銷處理、預算申請等等
  5. 客戶服務類:客戶資訊管理、客戶投訴、請求處理、售後服務等等

工作流引擎

在沒有工作流引擎之前,為了實現流程控制,通常的做法是採用狀態欄位的值來跟蹤流程的變化。例如設立一個欄位,初始值為 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 所需要的依賴,包括:

  1. activiti 的相關包
  2. 其他工具包,如 mybatis、alf4j、log4j 等
  3. activiti 依賴的 spring 包
  4. mysql 資料庫驅動
  5. 第三方資料連線池 dbcp
  6. 單元測試 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&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true&amp;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&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true&amp;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 系統的日常維護