背景
近幾年,網際網路企業從消費網際網路向產業網際網路轉型。在消費網際網路時期,企業面對的時C端消費者,而產業網際網路面對的是B端使用者。
產業網際網路涉及方方面面,企業資訊化的建設就是B端使用者的業務之一,在企業就存在上下級關係,存在審批業務,需要流程管理。
在企業資訊化建設中流程管理也是重要的一部分,如下基於flowable簡單的分析流程定義。
流程的一點基本概念
開始節點,結束節點和人工任務節點
閘道器
自動服務任務
順序流
閘道器分支
- 並行分叉 AND-split(Parallel Split)
- 兩個分支都需要執行
- 如 下單後 發快遞和開發票,兩個動作都會做。
- 排他分叉或叫異或 XOR-split(Exclusive Choice)
- 後續分支二選一,只能走一條
- 如 下單後的支付,選擇銀聯支付,微信支付還是支付寶支付,只能選擇一種支付方式。
網關合並
- 同步AND-join(Synchronization)
- 前面的所有的分支等待都需要完成
- 如上面的下單,收快遞和收發票,都收到了,流程才完成。
- 簡單合併XOR-join(Simple Merge)
- 前面的分支只要有一個完成,流程就完成
- 如上面的下單後支付,不管是銀聯,微信支付還是支付寶支付,只要有一個渠道支付成功,支付流程就完成。
Flowable相關說明
官網地址:https://flowable.com/
幫助文件:https://flowable.com/open-source/docs/bpmn/ch02-GettingStarted/
中文幫助文件:https://tkjohn.github.io/flowable-userguide/#_introduction
github程式碼:https://github.com/flowable/flowable-engine
flowable的發展
- Flowable是來自從Activiti5.22
- Activiti6.0的bug太多
- Activiti7.0主要擁抱雲
- Flowable6版本開始按照模組進行拆分,社群比較活躍
如下是JBoss,Alfresco和Flowable三個團隊的流程軟體關鍵時間點描述:
Flowable安裝和配置
Flow安裝
第一步、從flowable的github地址上下載如下幾個war,部署到apache的webapps下
flowable-admin.war
flowable-idm.war
flowable-modeler.war
flowable-rest.war
flowable-task.war
第二步、修改如上幾個包的配置檔案,配置mysql資料地址或者使用自身的h2資料庫進行實驗。
修改admin idm modeler rest和task這5個包的配置檔案WEB-INF/classes/flowable-default.properties,本實驗使用的是mysql資料庫。
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/flowable?characterEncoding=UTF-8
spring.datasource.username=flowable
spring.datasource.password=flowable
配置後,啟動tomcat伺服器並登入。
Flowable modeler登入
訪問flowable的流程設計器
地址:http://localhost:8080/flowable-modeler
賬號密碼:admin/test
登入後,可以訪問flowable的管理頁面
地址:http://localhost:8080/flowable-admin/#/engine
可以看到flowable流程由如下幾個引擎組成
可以通過編輯REST端點檢查對應的服務執行是否正常。
本例中以流程引擎為例簡單的說明。
流程定義
定義一個簡單的請假流程,包括一個開始節點,一個審批節點,經管排他閘道器後,審批通過後傳送一個http通知,審批拒絕後也傳送一個http通知。
如上流程定義使用bpmn2.0的xml表示大致如下所示
<?xml version="1.0" encoding="UTF-8"?>
<definitions>
<process id="flow_h1" name="holiday-new" isExecutable="true">
<documentation>請求流程描述</documentation>
<startEvent id="startEvent1" name="請假開始" flowable:formKey="formKey1" flowable:formFieldValidation="true">
<extensionElements>
<flowable:formProperty id="employee" name="請假人" type="string" required="true"></flowable:formProperty>
<flowable:formProperty id="noOfHolidays" name="天數" type="long" required="true"></flowable:formProperty>
</extensionElements>
</startEvent>
<userTask id="approveKey1" name="審批節點1" flowable:formFieldValidation="true"></userTask>
<exclusiveGateway id="sid-D7F080C0-BC9D-4C03-B9EB-2D81E4010CA6" name="審批是否通過"></exclusiveGateway>
<serviceTask id="AcceptKey1" name="通過" flowable:class="org.example.tut.flowable.handler.HolidayRejectionHandler"></serviceTask>
<serviceTask id="RejectKey1" name="拒絕" flowable:class="org.example.tut.flowable.handler.HolidayApprovalHandler"></serviceTask>
<endEvent id="sid-E86D23D8-DC24-44D0-B96D-BA3E8CF801C0"></endEvent>
<serviceTask id="RejectHttpKey1" name="HTTP測試節點" flowable:type="http">
<extensionElements>
<flowable:field name="requestMethod">
<flowable:string><![CDATA[POST]]></flowable:string>
</flowable:field>
<flowable:field name="requestUrl">
<flowable:string><![CDATA[http://127.0.0.1:9901/test]]></flowable:string>
</flowable:field>
<flowable:field name="requestBody">
<flowable:expression><![CDATA[Reject action, employee ${employee} apply ${noOfHolidays} days with reason ${description}]]></flowable:expression>
</flowable:field>
</extensionElements>
</serviceTask>
<sequenceFlow id="sid-84648711-833A-4ABE-AD08-3E32B026F247" sourceRef="RejectKey1" targetRef="RejectHttpKey1"></sequenceFlow>
<sequenceFlow id="sid-448B4748-A744-43D9-B3EB-FC0846035F20" sourceRef="RejectHttpKey1" targetRef="sid-E86D23D8-DC24-44D0-B96D-BA3E8CF801C0"></sequenceFlow>
<endEvent id="sid-683697FC-2E68-45C9-BE63-0F526FC2F3AB"></endEvent>
<serviceTask id="AcceptHttpKey1" name="審批通過的HTTP訊息" flowable:type="http">
<extensionElements>
<flowable:field name="requestMethod">
<flowable:string><![CDATA[POST]]></flowable:string>
</flowable:field>
<flowable:field name="requestUrl">
<flowable:string><![CDATA[http://127.0.0.1:9901/test]]></flowable:string>
</flowable:field>
<flowable:field name="requestBody">
<flowable:expression><![CDATA[Accept Holiday employee ${employee} apply ${noOfHolidays} days with reason ${description}]]></flowable:expression>
</flowable:field>
</extensionElements>
</serviceTask>
<sequenceFlow id="sid-9788654A-171C-45B9-9A41-94A35C531AA8" sourceRef="AcceptHttpKey1" targetRef="sid-683697FC-2E68-45C9-BE63-0F526FC2F3AB"></sequenceFlow>
<sequenceFlow id="sid-C4456145-0531-492F-AA80-E733CD0C35C9" sourceRef="AcceptKey1" targetRef="AcceptHttpKey1"></sequenceFlow>
<sequenceFlow id="SeqFromStartToApprove" sourceRef="startEvent1" targetRef="approveKey1"></sequenceFlow>
<sequenceFlow id="SeqFromApproveToGW" sourceRef="approveKey1" targetRef="sid-D7F080C0-BC9D-4C03-B9EB-2D81E4010CA6"></sequenceFlow>
<sequenceFlow id="SeqRejectKey1" sourceRef="sid-D7F080C0-BC9D-4C03-B9EB-2D81E4010CA6" targetRef="RejectKey1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="ReqAcceptKey1" sourceRef="sid-D7F080C0-BC9D-4C03-B9EB-2D81E4010CA6" targetRef="AcceptKey1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_flow_h1">
... 省
</bpmndi:BPMNDiagram>
</definitions>
節點說明
這個簡單的流程中使用了flowable的幾個節點
流程定義的組成
process
- startEvent 表示開始節點
- 開始節點可以是空節點
- 開始節點也可以是定時節點
- userTask 表示人工審批節點
- 可以通過 flowable:assignee="userId" 表示這個節點的審批人是誰
- 可以通過 flowable:candidateGroups="managerGroup" 表示候選的審批組是什麼,在這個組中的人都可以做為審批人。
- 人工審批節點可以增加extensionElements增加自定義擴充套件屬性,將流程定義的擴充套件資訊儲存到CDATA中
- exclusiveGateway 表示排他閘道器
- 人工審批後的審批結果做為排他閘道器的判斷依據
- serviceTask 表示自動節點
- 有自定義的節點需要自定義一個Handler做為serviceTask節點的處理。
- serviceTask也有一些系統內建實現(如HTTP服務 MAIL服務 MULE服務等)
- 自動服務節點可以增加extensionElements增加自定義擴充套件屬性,將流程定義的擴充套件資訊儲存到CDATA中
- sequenceFlow 表示分支流
- 在分支流上會存在一個條件,排他閘道器後會跟多個sequenceFlow,排他閘道器會根據各自sequenceFlow上的條件決定流程的流向。
- 條件會使用conditionExpression來表示,這個表示式一般使用JUEL表示式實現。
- endEvent 表示結束節點
bpmndi:BPMNDiagram
- bpmndi:BPMNShape
- omgdc:Bounds 表示影象的輪廓包含寬高
- width 寬
- height 高
- omgdc:Bounds 表示影象的輪廓包含寬高
- bpmndi:BPMNEdge
- pmgdi:waypoint
- x 橫軸座標
- y 縱軸座標
- pmgdi:waypoint
startEvent的定時節點如何定義?
<startEvent id="timer_start1" name="定時器節點" isInterrupting="false">
<timerEventDefinition>
<timeDate>2021-06-30T15:01:02</timeDate>
<timeCycle>R/P1DT0S</timeCycle>
<timeDuration>P10DT0S</timeDuration>
</timerEventDefinition>
</startEvent>
時間定義參考ISO8601標準,可以參考@https://www.cnblogs.com/xdao/p/iso8601.html
格式解析
R2/2015-06-04T19:25:16.828696-07:00/P1DT10S 上面的字串通過"/"分為了三部分即: 重複次數/開始時間/執行間隔 重複次數
R - 將永遠重複
R1 - 將重複一次
R231 - 將重複231次。
開始時間
任務第一次執行的時間。如果開始日期時間已經過去,Kala將返回一個錯誤。 其中"T"用來分割日期和時間,時間後面跟著的"-07:00"表示西七區,注意"-"是連字元,不是減號。 時區預設是0時區,可以用"Z"表示,也可以不寫。 對於我國,要使用"+08:00",表示東八區。
上面的字串表示 2015年6月4日,19點25分16秒828696納秒,西七區。 執行間隔
執行間隔以"P"開始,和上面一樣也是用"T"分割日期和時間,如P1Y2M10DT2H30M15S P 開始標記
1Y - 一年
2M - 兩個月
10D - 十天
T - 時間和日期分的割標記
2H - 兩個小時
30M - 三十分鐘
15S 十五秒鐘
例子,注意如果沒有年月日,"T"也不能省略 P1DT1M - 一天一分鐘執行一次
P1W - 一週執行一次
PT1H - 一小時執行一次
PT10S - 十秒執行一次
啟動流程
啟動流程的程式碼:
public ProcessInstanceResponse startProcessInstance(HolidayRequest holidayRequest) { Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employee", holidayRequest.getEmpName());
variables.put("noOfHolidays", holidayRequest.getNoOfHolidays());
variables.put("description", holidayRequest.getRequestDescription());
variables.put("initiator", "admin"); ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey(holidayRequest.getProcessKey(), variables); return new ProcessInstanceResponse(processInstance.getId(), processInstance.isEnded());
}
啟動流程後,會在資料庫中增加記錄
ACT_RU_ACTINST 增加流程例項資訊。
ACT_RU_VARIABLE 增加variables變數列表的變數,這個變數會貫穿流程例項的全程。
分配任務
分配任務的程式碼:
TaskService taskService; // taskId是任務Id,userId是使用者id,表示將任務分配給這個使用者
taskService.setAssignee(taskId, userId);
分配任務後,會在資料庫中設定審批人資訊
ACT_RU_TASK 表的欄位 ASSIGEE_表示審批人是誰
獲取待辦任務
獲取待辦任務的程式碼
// 跟進userId使用者id查詢分配給這個使用者或將這個使用者設為候選的任務列表
List<Task> taskList = taskService.createTaskQuery().taskCandidateOrAssigned(userId).list();
會查詢ACT_RU_TASK表,獲取審批人的任務。
完成任務
完成任務也叫做審批任務
taskService.complete(taskId, variables);
流程執行
流程執行時,例項和變數說明什麼
可以在管理頁面看到啟動流程例項是填寫的變數列表
啟動流程增加的變數
variables.put("employee", holidayRequest.getEmpName());
variables.put("noOfHolidays", holidayRequest.getNoOfHolidays());
variables.put("description", holidayRequest.getRequestDescription());
variables.put("initiator", "admin");
人工節點審批時增加的變數是什麼
variables.put("approved", approved);
taskService.complete(taskId, variables);
即:變數時貫穿流程例項的全宣告週期,流程的很多自定義功能可以通過變數和擴充套件屬性進行實現。
展示流程例項的執行圖
done.
祝玩得開心~