1. 程式人生 > >Activiti 工作流入門

Activiti 工作流入門

基礎知識

工作流:
可以認為是一系列的規範流程,讓業務按照擬定的規則處理和運轉,本質上是一系列邏輯相關的活動的集合。例如某校學生申請獎學金這一業務,必須按照“學生申請→輔導員審批→學生處審批→教務處審批”這一流程運轉,也就是“學生申請”、“輔導員審批”、“學生處審批”以及“教務處審批”這些活動的集合。最原始的時候,在系統中完成這樣的功能,需要通過一系列的獨立活動頁面以及修改資料庫中的一些標記狀態來實現任務的流轉的,這樣的做法在開發和維護上都是十分困難的,新增任務結點,閘道器判斷,新增處理人員都十分繁瑣,之後就有了工作流引擎。
工作流引擎:
實現流程,讓任務能夠按擬定的流程自動

的驅動下去,不再需要我們實現流程的驅動,記錄流程的流轉情況等等,提供流程生命週期的管理功能,Activiti就是一種工作流引擎。
工作流平臺:
如果每一個系統(專案)都有工作流,就會產生很多相同的程式碼,為了更好的管理工作流,為了讓工作流與專案解耦,有的時候把工作流剝離出來做成微服務的形式,搭建一套完整的工作流平臺,對外提供介面,讓各個業務系統更專注於自己的核心業務。

Activiti 6.0 架包

檢視Activiti引擎的架包如圖所示,可以發現其主要包含兩個部分:db以及engine。
這裡寫圖片描述
Activiti對流程的定義,建立,維護以及查詢的實現都是是通過對錶的操作完成的,Activiti有一套自己的表,都是以“ACT”打頭命名的,記錄了流程定義等相關資訊(”ACT_RE*”)、流程驅動過程中的資訊(“ACT_RU*”)、流程歷史相關資訊(“ACT_HI*”)以及一些通用資料(“ACT_GE*”)等。如下所示:
這裡寫圖片描述


Activiti的ORM是通過Mybatis實現的,所以回到之前提到的db部分,這部分的內容主要包含對以上提到的28張表的建立,及刪除語句。表不需要我們自己建立,初始化的時候可以配置自動建立,支援db2,h2,hsql,mysql,oracle,postgres等資料庫。舉個Spring boot中配置的例子:

@Configuration
@EnableConfigurationProperties(ActivitiProperties.class)
public class ProcessEngineAutoConfiguration extends DataSourceProcessEngineAutoConfiguration
{
@Configuration @EnableConfigurationProperties(ActivitiProperties.class) public static class ProcessEngineConfiguration extends DataSourceProcessEngineConfiguration { } }

其中ActivitiProperties.class中預設的策略如下:

//表不存在就自動建立表
private String databaseSchemaUpdate = "true";

還可以配置為“false”(不自動建立表),“drop-create”(先刪除再建立表)等,此處不詳細介紹Spring boot專案中的配置與整合,主要是介紹Activiti自身的入門內容。
db中的mapping路徑下是表操作相關的sql語句xml檔案(用於介面對映,實現對錶的操作,不理解的話需要去看看ORM以及Mybatis相關的內容),剩下的就是相關配置檔案以及升級等內容。如下圖所示:
這裡寫圖片描述
相對的,engine中主要是相關的介面及其實現。Activiti引擎有七大Service介面:

RepositoryService:流程倉庫Service,用於管理流程倉庫,例如部署,刪除,讀取流程資源;
IdentifyService:身份Service,可以管理和查詢使用者、組之間的關係;
RuntimeService:執行時Service,可以處理所有正在執行狀態的流程例項、任務等;
TaskService:任務Service,用於管理、查詢任務,例如簽收,辦理,指派等;
FormService:表單Service,用於讀取和流程、任務相關的資料表單;
HistoryService:歷史Service,可以查詢所有歷史資料,例如流程例項、任務、活動、變數、附件等;
ManagementService:引擎管理Service,和具體業務無關,主要是可以查詢引擎配置、資料、作業等。
以上的Service介面都是Activiti封裝好的,底層實現都是通過Mybatis轉化為對28張表的操作,應用程式中經常會用到以上的Service,功能十分強大。

Activiti 工作流生命週期

在Activiti中,工作流的生命週期主要包含以下幾個方面:

  1. 設計流程模型,生成流程資源
  2. 根據流程模型生成流程定義
  3. 配置工作流人員
  4. 根據流程定義啟動流程生成流程例項
  5. 流程例項由一系列任務例項構成,任務自動流轉
  6. 修改流程模型,釋出新的流程版本優化流程

流程模型

Activiti為我們提供了設計流程的圖形使用者介面,該介面的渲染內容是根據stencilset.json檔案生成的,該檔案的資源網上很容易獲取到,拓展也很方便,開發人員可以根據自己的需要定製化的修改其中的內容。下面舉個簡單的流程模型建立的例子:
這裡寫圖片描述
上圖就是一個根據stencilset.json檔案渲染的流程設計介面,可以在系統需要的地方接入這個頁面,可以看到左側是元素選單,功能是非常豐富的,可以設計出非常複雜的流程,通過拖拽等操作可以在右側的畫板中設計我們的業務流程。
當我們設計完流程之後,生成的主要內容包括模型ID,模型名稱,模型的key,以及我們設計的流程內容的json資料,上圖中的json資料部分如下圖所示,此處不過多敘述,都是activiti自動生成的,瞭解便可。

這裡寫圖片描述
其主要內容包括resourceId以及其中的箭頭,任務結點等資訊。
儲存設計好的流程模型,核心實現主要通過呼叫之前提到RepositoryService來實現,如下所示:

//引入架包後Service可直接自動注入
@Autowired
private RepositoryService repositoryService;
//未編輯模型,只儲存模型名字和key
Model newModel = repositoryService.newModel();  
newModel.setMetaInfo(modelObjectNode.toString());  
newModel.setName(name);  
newModel.setKey(key);  
repositoryService.saveModel(newModel);
//也可以通過ProcessEngine獲取Service
@Autowired
private ProcessEngine processEngine;
//獲取之前儲存的模型
Model model = processEngine.getRepositoryService().getModel(modelId);
//新增模型與之前提到的json的關聯關係
processEngine.getRepositoryService().addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
//儲存一張根據json資料生成的流程圖片
processEngine.getRepositoryService().addModelEditorSourceExtra(model.getId(), result);

提及的只是部分核心實現,並非完整程式碼,主要體現如何使用Activiti實現工作流的操作,以上方法涉及的主要是ACT_RE_MODEL表以及ACT_GE_BYTEARRAY表,封裝的Service方法底層也是對這兩張表進行操作。查詢當前所有的模型也是通過RepositoryService實現,舉個例子:

ModelQuery modelQuery = repositoryService.createModelQuery();
if(StringUtil.isNotEmpty(modelKey))
{
    modelQuery.modelKey(modelKey);
}
if(StringUtil.isNotEmpty(modelName))
{
    modelQuery.modelNameLike(modelName);
}
if(StringUtil.isNotEmpty(modelId))
{
    modelQuery.modelId(modelId);
}
Page<Model> p = new Page<Model>(page, rows);
int[] pageParams = p.getPageParams(modelQuery.list().size());
List<Model> list = modelQuery.listPage(pageParams[0], pageParams[1]);
return new Datagrid<Model>(p.getTotal(), list);

在Activiti中,查詢可以使用標準查詢和Native查詢,標準查詢即我們上面程式碼中的形式:需要建立一個指定型別的Query物件,然後使用鏈式程式設計的方式設定查詢引數即可,就是一個條件查詢。這樣的查詢不能支援複雜的多表連線查詢,所以有時候就NativeQuery介面。這兩個介面的api就不詳細介紹,命名都很清晰,看看就明白。
這樣,我們設計並建立了流程模型,資訊儲存在ACT_RE_MODEL、ACT_GE_BYTEARRAY兩張表中,主要包括流程的基礎資訊,以及設計頁面的json資料和一張用於展示模型的png圖片。

流程定義

有了流程模型,就可以通過部署生成流程定義,還是通過RepositoryService實現:

Model modelData = repositoryService.getModel(modelId);
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;

BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
bpmnBytes = new BpmnXMLConverter().convertToXML(model);

String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes)).deploy();

這一個過程主要就是生成流程的XML檔案,建立流程模型的時候我們有了json資料,部署就可以將json資料轉換成工作流中認識的xml資料。上文中提到的流程模型部署生成流程定義的xml資料如下所示:
這裡寫圖片描述
部署的流程定義的資料儲存在ACT_RE_PROCDEF、ACT_RE_DEPLOYMENT以及ACT_GE_BYTEARRAY表中,同理的,查詢流程定義也很簡單:

ProcessDefinition pd =repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();

查詢流程定義的XML:

InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),resourceName);

實際執行的其實是以下sql:
select * from ACT_GE_BYTEARRAY where DEPLOYMENT_ID_ = ? AND NAME_ = ?
可以看到,底層還是對一些表的操作。
流程定義是有啟用和掛起狀態的,只有啟用狀態的流程定義才能用來啟動流程例項。啟用和掛起狀態的轉換還是通過呼叫API實現。

流程例項

有了流程定義,就可以根據流程定義的key啟動流程,每一次啟動流程就會生成一個流程例項,比如說設計好了請假的流程模型,通過流程模型生成了流程定義,並激活該定義之後,每一次學生提交請假申請就生成一個請假的流程例項,流程例項由一個個任務例項構成,如審批任務。
啟動流程使用的是RuntimeService:

ProcessInstance pi = processEngine.getRuntimeService.startProcessInstanceByKey(procDefKey);

可以在啟動流程的時候傳入一些業務引數:

ProcessInstance pi = processEngine.getRuntimeService.startProcessInstanceByKey(procDefKey, variables);

下圖是啟動流程過程中的專案日誌,通過日誌我們不難發現,啟動流程同理也是對Activiti的相關表進行操作,包括歷史表,任務例項表,使用者表,執行例項表等等:
這裡寫圖片描述

任務例項

啟動流程例項的時候,會自動驅動到第一個任務結點,也就是說會生成任務例項。檢視當前的活動任務(即流程例項運轉到的流程結點):

TaskQuery taskQuery = this.taskService.createTaskQuery();

if (!isEmpty(procDefName)) {
    taskQuery = taskQuery.processDefinitionName(procDefName);
}
if (!isEmpty(procDefKey)) {
    taskQuery = taskQuery.processDefinitionKey(procDefKey);
}
if (!isEmpty(taskName)) {
    taskQuery = taskQuery.taskName(taskName);
}
if (!isEmpty(owner)) {
    taskQuery = taskQuery.taskAssignee(owner);
}
totalSum = taskQuery.list().size();

以上便是介面提供的帶條件的查詢。當需要推動此流程時,只需要呼叫以下方法完成該任務,activiti就會自動的驅動流程的執行,到達下一結點,直至結束。

taskService.complete(taskId, variables)

指派人員

流程的設計,包括人員的指派,也就是說當流程流轉到某一任務結點時,哪些人可以看到該任務,拾取該任務,或者推動該任務的流轉。
在Activiti中,如果給任務指派了特定的人員,那麼只有特定的人員能看到該任務並推動該任務流轉;如果給任務指派了候選人,那麼所有候選人都能看到該任務,當其中某一個候選人拾取了該任務,其餘候選人無法看到並拾取該任務,只有拾取了任務的候選人能推動該任務;
Activiti實現這一功能實際上就是在ACT_RU_TASK表的assignee欄位中儲存了一個字串,該字串標識唯一的處理人員。所以必須要有一套與業務相關的使用者表,解決方案有兩種:

  1. Activiti有一套自己的簡單的使用者和組的表,我們可以把需要對接工作流的業務系統的使用者資料匯入Activiti自己的表中,自己維護一套。這種方案比較麻煩。
  2. 可以讓對接的業務系統提供使用者相關的介面,工作流平臺的使用者資訊通過調對接的業務系統的使用者介面獲取和設定。

總結

Activiti工作流引擎能夠很好的管理工作流,實現流程的自動驅動。它提供了7大Service介面已滿足開發需求,底層是通過Mybatis對28張表操作而實現。
Activiti還有很多高階特性,還有很多細節的東西在本文沒有涉及。有機會的話在之後補充博文記錄學習。