新聞中心
今天我們繼續(xù)來聊聊流程實(shí)例。

創(chuàng)新互聯(lián)擁有網(wǎng)站維護(hù)技術(shù)和項(xiàng)目管理團(tuán)隊(duì),建立的售前、實(shí)施和售后服務(wù)體系,為客戶提供定制化的成都網(wǎng)站建設(shè)、成都網(wǎng)站制作、網(wǎng)站維護(hù)、BGP機(jī)房服務(wù)器托管解決方案。為客戶網(wǎng)站安全和日常運(yùn)維提供整體管家式外包優(yōu)質(zhì)服務(wù)。我們的網(wǎng)站維護(hù)服務(wù)覆蓋集團(tuán)企業(yè)、上市公司、外企網(wǎng)站、成都商城網(wǎng)站開發(fā)、政府網(wǎng)站等各類型客戶群體,為全球成百上千家企業(yè)提供全方位網(wǎng)站維護(hù)、服務(wù)器維護(hù)解決方案。
部署之后的流程,這個(gè)還不能直接運(yùn)行,例如我們部署了一個(gè)請假流程,現(xiàn)在 zhangsan 想要請假,他就需要開啟一個(gè)請假流程,lisi 想請假,他也需要開啟一個(gè)請假流程,這一個(gè)一個(gè)開啟的請假流程就是流程實(shí)例,而我們一開始部署的請假流程,則類似于一個(gè)模版,基于此模版,我們可以開啟很多個(gè)具體的流程實(shí)例。從這個(gè)角度來說,上篇文章我們定義的 ProcessDefinition 就類似于一個(gè) Java 類,今天我們要介紹的 ProcessInstance 則相當(dāng)于一個(gè) Java 對象。
1. 捋清三個(gè)概念
首先我們需要先捋清三個(gè)概念:
- 流程定義 ProcessDefinition
- 流程實(shí)例 ProcessInstance
- 執(zhí)行實(shí)例 Execution
流程定義
流程定義 ProcessDefinition 這個(gè)好說,其實(shí)就是我們上篇文章中和大家介紹的內(nèi)容。將一個(gè)流程 XML 文件部署到 flowable 中,這就是一個(gè)定義好的流程了,基于這個(gè)定義好的流程,我們可以開啟很多流程實(shí)例。
流程實(shí)例
流程實(shí)例 ProcessInstance 就是通過流程定義啟動(dòng)的一個(gè)流程,他表示一個(gè)流程從開始到結(jié)束的最大的流程分支,在一個(gè)流程中,只存在一個(gè)流程實(shí)例,流程實(shí)例和流程定義的關(guān)系就類似于 Java 對象和 Java 類之間的關(guān)系。
執(zhí)行實(shí)例
執(zhí)行實(shí)例 Execution 稍微有點(diǎn)難以理解。
首先從類的關(guān)系上來看,ProcessInstance 就是 Execution 的子類。
流程實(shí)例通常是執(zhí)行實(shí)例的根結(jié)點(diǎn),即在一個(gè)流程中,出口和入口可以算是一個(gè)流程實(shí)例的節(jié)點(diǎn),而中間的過程則是執(zhí)行實(shí)例。
假如流程本身就是一條線,那么流程實(shí)例和執(zhí)行實(shí)例基本上是一樣的,但是如果流程中包含多條線,例如下圖:
這張圖中有并行網(wǎng)關(guān),并行任務(wù)執(zhí)行的時(shí)候,每一個(gè)并行任務(wù)就是一個(gè)執(zhí)行實(shí)例,這樣大家就好理解了。
結(jié)論就是,在一個(gè)流程實(shí)例中,除了開始和結(jié)束之外,其他的都是執(zhí)行實(shí)例。即使流程只有一條線,中間的也都是執(zhí)行實(shí)例,只不過此時(shí)的執(zhí)行實(shí)例等于流程實(shí)例而已。
好啦,三個(gè)基本概念先捋清楚。
2. 五種流程啟動(dòng)方式
當(dāng)我們將流程部署好之后,接下來啟動(dòng)流程,我們有五種不同的方式去啟動(dòng)一個(gè)流程。
- 通過流程定義的 id 去啟動(dòng)
首先就是通過流程定義的 id 去啟動(dòng)一個(gè)流程,對應(yīng)的方法名稱就是 RuntimeService#startProcessInstanceById,該方法有好幾個(gè)重載的方法,不同的重載方法只是傳遞的參數(shù)不同而已,其他基本上都是一樣的。
- 通過流程的 key 去啟動(dòng)
也可以通過流程定義的 key 去啟動(dòng)一個(gè)流程,根據(jù)上篇文章?的介紹,大家知道,這個(gè)流程定義的 key 其實(shí)就是流程 XML 文件中的 id,這個(gè)對應(yīng)的方法名是 RuntimeService#startProcessInstanceByKey。
- 通過流程的 key+tenantId 去啟動(dòng)
有這樣一種情況,例如我有兩個(gè)子系統(tǒng) A 和 B,A 和 B 中都有一個(gè)請假流程的定義,現(xiàn)在當(dāng)我想要啟動(dòng)一個(gè)流程的時(shí)候,怎么知道是啟動(dòng) A 的請假流程還是啟動(dòng) B 的請假流程呢?此時(shí)我們可以通過租戶 ID 即 tenantId 去區(qū)分,所以,流程啟動(dòng)就還有一個(gè)方法 RuntimeService#startProcessInstanceByKeyAndTenantId。
- 通過流程的 message 去啟動(dòng)
通過消息去啟動(dòng)一個(gè)流程,對應(yīng)的方法是 RuntimeService#startProcessInstanceByMessage。
- 通過流程的 message+tenanId 去啟動(dòng)
通過消息+租戶 ID 去啟動(dòng)一個(gè)流程,對應(yīng)的方法是 RuntimeService#startProcessInstanceByMessageAndTenantId。
3. 簡單實(shí)踐
首先我們繪制一個(gè)簡單的流程圖,然后按照上篇文章所介紹的方式進(jìn)行部署,流程圖如下:
流程 XML 文件如下:
這個(gè) XML 文件我跟大家說一句,在啟動(dòng)節(jié)點(diǎn)上我設(shè)置了 flowable:initiator="INITIATOR",相當(dāng)于定義了流程發(fā)起人的變量為 INITIATOR,這個(gè)變量名是自定義的,定義好之后,將來我就可以在其他節(jié)點(diǎn)中就可以使用這個(gè)變量了。
很簡單的流程,其中:
- 提交請假申請是由流程的發(fā)起人完成。
- 主管是 zhangsan。
- 經(jīng)理是 lisi。
好了,先按照上篇文章我們介紹的方式部署流程。
接下來我們要啟動(dòng)流程,假設(shè)我們用流程定義的 key 來啟動(dòng)一個(gè)流程實(shí)例:
@SpringBootTest
public class RuTest {
@Autowired
RuntimeService runtimeService;
private static final Logger logger = LoggerFactory.getLogger(RuTest.class);
@Test
void test01() {
Authentication.setAuthenticatedUserId("wangwu");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave");
logger.info("id:{},activityId:{}",pi.getId(),pi.getActivityId());
}
}
啟動(dòng)的代碼其實(shí)很簡單,當(dāng)流程啟動(dòng)成功之后,流程中的每一步都會(huì)記錄在 ACT_RU_EXECUTION 表中,同時(shí),如果這個(gè)節(jié)點(diǎn)是一個(gè)用戶任務(wù)節(jié)點(diǎn)(UserTask),那么同時(shí)還會(huì)在 ACT_RU_TASK 表中添加一條記錄。
Authentication.setAuthenticatedUserId("wangwu"); 表示設(shè)置流程的發(fā)起人。
另外一種設(shè)置流程發(fā)起人的方式如下:
@Autowired
IdentityService identityService;
@Test
void test01() {
identityService.setAuthenticatedUserId("wangwu");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave");
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
對于我們上面的流程來說,啟動(dòng)之后,就會(huì)進(jìn)入到提交請假申請這個(gè)節(jié)點(diǎn)中,所以一共走了兩個(gè)節(jié)點(diǎn),那么 ACT_RU_EXECUTION 表中應(yīng)該有兩條記錄了,如下圖:
再來看看 ACT_RU_TASK 表中的內(nèi)容:
可以看到,該表中有一條記錄,這條記錄其實(shí)就是提交請假申請這個(gè)節(jié)點(diǎn),現(xiàn)在流程就停在這一步了,需要用戶手動(dòng)操作,才會(huì)繼續(xù)向下走。
從這兩張表中我們也可以大致上看出來,EXECUTION 和 ProcessInstance 之間的關(guān)系,ACT_RU_EXECUTION 表中的每一條記錄就是一個(gè) EXECUTION,多個(gè) EXECUTION 對應(yīng)同一個(gè) PROC_INST_ID_,而 ACT_RU_TASK 表中的每一條 Task 記錄也都對應(yīng)了一個(gè) EXECUTION。
現(xiàn)在我們就先去查詢 wangwu 需要完成的 Task(wangwu 是流程的發(fā)起人):
@Autowired
TaskService taskService;
@Test
void test02() {
Listlist = taskService.createTaskQuery().taskAssignee("wangwu").list();
for (Task task : list) {
logger.info("id:{};name:{};taskDefinitionKey:{}",task.getId(),task.getName(),task.getTaskDefinitionKey());
}
}
根據(jù)前面的介紹,我們知道,這個(gè)查詢肯定是去 ACT_RU_TASK 表中進(jìn)行查詢的,我們來看下執(zhí)行的 SQL:
可以看到,這里就是根據(jù) ASSIGNEE_ 字段去查詢?nèi)蝿?wù)的。
查詢到任務(wù)之后,接下來去完成任務(wù):
@Test
void test03() {
Listlist = taskService.createTaskQuery().taskAssignee("wangwu").list();
for (Task task : list) {
taskService.complete(task.getId());
}
}
這個(gè)表示查詢到 wangwu 的任務(wù)然后完成,這個(gè)方法執(zhí)行完成之后,首先會(huì)在 ACT_RU_TASK 表中插入一條新的需要 zhangsan 完成的 Task,然后會(huì)更新 ACT_RU_EXECUTION 表中對應(yīng)的執(zhí)行實(shí)例信息,最后再從 ACT_RU_TASK 表中刪除需要 wangwu 完成的記錄,這些操作是在同一個(gè)事務(wù)當(dāng)中完成的。
好了,現(xiàn)在再去執(zhí)行 test02 的查詢方法,就會(huì)發(fā)現(xiàn)查不到了,因?yàn)闆]有 wangwu 需要完成的 task 了,接下來應(yīng)該去查詢 zhangsan 需要完成的 task。
當(dāng)一個(gè)流程實(shí)例完成后,ACT_RU_TASK 和 ACT_RU_EXECUTION 表中的記錄都會(huì)被刪除,所以我們可以通過查詢 ACT_RU_EXECUTION 表中是否還有記錄,去判斷一個(gè)一個(gè)流程目前是處于執(zhí)行狀態(tài)還是完成狀態(tài),代碼如下:
@Test
void test04() {
String pId = "9c8557dd-3727-11ed-9404-acde48001122";
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(pId).singleResult();
if (pi == null) {
logger.info("{} 流程執(zhí)行結(jié)束", pId);
}else{
logger.info("{} 流程正在執(zhí)行中", pId);
}
}
最后,如果你想要去 ACT_RU_EXECUTION 表中查詢執(zhí)行實(shí)例也是 OK 的,方式如下:
@Test
void test05() {
Listlist = runtimeService.createExecutionQuery().processInstanceId("6d0341c7-3729-11ed-8e4e-acde48001122").list();
for (Execution execution : list) {
logger.info("id:{};processInstanceId:{};name:{}",execution.getId(),execution.getProcessInstanceId(),execution.getName());
}
}
查看執(zhí)行的 SQL 如下:
: ==> Preparing: SELECT RES.* , P.KEY_ as ProcessDefinitionKey, P.ID_ as ProcessDefinitionId, P.NAME_ as ProcessDefinitionName, P.VERSION_ as ProcessDefinitionVersion, P.DEPLOYMENT_ID_ as DeploymentId from ACT_RU_EXECUTION RES inner join ACT_RE_PROCDEF P on RES.PROC_DEF_ID_ = P.ID_ WHERE RES.PROC_INST_ID_ = ? order by RES.ID_ asc
: ==> Parameters: 6d0341c7-3729-11ed-8e4e-acde48001122(String)
: <== Total: 2
可以看到,就是去 ACT_RU_EXECUTION 表中查詢的。
4. 刪除流程實(shí)例
如果我們想刪除一個(gè)流程實(shí)例,操作方式如下:
@Test
void test06() {
runtimeService.deleteProcessInstance("65ab0b38-38f3-11ed-b103-acde48001122", "javaboy想刪除了");
}
注意這個(gè)是刪除正在執(zhí)行的流程實(shí)例信息,并不會(huì)刪除歷史流程信息。
5. 獲取運(yùn)行的活動(dòng)節(jié)點(diǎn)
可以根據(jù)執(zhí)行實(shí)例的 ID 去查詢活動(dòng)節(jié)點(diǎn)的 ID,方式如下:
@Test
void test07() {
Listlist = runtimeService.createExecutionQuery().list();
for (Execution execution : list) {
ListactiveActivityIds = runtimeService.getActiveActivityIds(execution.getId());
for (String activeActivityId : activeActivityIds) {
System.out.println("activeActivityId = " + activeActivityId);
}
}
}
這里查詢的其實(shí)就是 ACT_RU_EXECUTION 表,查詢到的 activeActivityId 其實(shí)就是該表的 ACT_ID 字段,我們來看下查詢的 SQL:
好啦,流程實(shí)例先聊這么多,下篇文章我們繼續(xù)~
名稱欄目:我們一起玩轉(zhuǎn)Flowable流程實(shí)例
文章地址:http://m.5511xx.com/article/dhdicge.html


咨詢
建站咨詢
