工作流設計參考(包括PHP實現)
工作流很少有讓人滿意的,即便是國內用的比較多的jbpm,用起來也會覺得很便扭。再加上PHP中沒有什麼好用的工作流,於是乾脆自己設計一個,設計的原則如下:
1 根據80/20原則,只使用wfmc模型中最符合自身應用的20%功能
2 充分吸收國內使用jbpm開發BOSS中遇到的問題,工作流引擎只負責引數的收集和流程的流轉,具體和業務的控制,交給每個流程定製的控制類去實現。
3 表單採用簡單的html+控制標籤的方法實現
4 許可權和模板引擎,以及其它輔助函式直接使用辦公系統自帶的框架
5 充分利用PHP語言的特點,流程設計是基於資料庫的,程式上使用OO設計,但採用重物件的方法
6 不把視覺化設計流程的工作交給最終客戶,而且由設計時完成,因此不考慮流程版本更新的問題
一、 工作流資料表設計
tbl_workflow_defination :工作流定義表
defination_id |
流程 id |
|
defination_name |
流程名稱 |
|
defination_handler |
流程處理輔助檔案,每個工作流一個檔案 |
自定義處理檔案,及其物件。例如 workflow-proporsal-handler.php ,其中定義物件 proposal |
tbl_workflow_node :流程結點步驟表
node_id |
結點 id |
|
defination_id |
流程 id |
|
node_index |
結點序號 |
結點的 step |
node_name |
結點名稱 |
|
node_type |
結點型別 |
1 人為決策, 2 自動處理 ( 直接執行 execute_function) , 3 等待外部響應 (例如外部 WS 觸發 ),4 分支, 5 彙總 6結束結點(此結點執行時候自動終止程序) |
init_function |
流程初始函式 |
|
run_function |
流程執行函式 |
|
save_function |
流程儲存函式 |
|
transit_function |
流程流轉函式 |
|
prev_node_index |
前結點序號 |
例如 1 。開始結點沒有 執行前,通過此來校驗一下流程 |
next_node_index |
後結點序號 |
例如 [ 同意 ]3,[ 不同意 ]4 。尾結點或要結束的結點沒有,若沒有,直接呼叫 end |
executor |
執行角色,組,人 |
role[1,2] group[1,2] user[1,2],為空由執行時決定 |
execute_type |
執行型別 |
0 需所有人執行 1 只需一人執行 |
remind |
提醒 |
0 不提醒 1 郵件 2 簡訊 3 郵件和簡訊 |
field |
可編輯的欄位 |
name,content |
max_day |
最長時間 ( 天 ) |
tbl_workflow_process :流程執行程序表
process_id |
程序 id |
|
defination_id |
流程 id |
|
process_desc |
程序描述 |
顯示在我的工作臺中 |
context |
上下文 |
存放上下文變數 , 例如業務表的 id |
current_node_index |
當前結點序號 |
|
start_time |
流程啟動時間 |
如遇分支、匯合顯示為: 1 =》 3,4 =》 3,5 =》 6 |
finish_time |
流程完成時間 |
|
state |
狀態 |
1 執行 2 結束 |
start_user |
發起人 |
發起人,用於顯示自己的流程 |
tbl_workflow_thread :流程執行執行緒表
thread_id |
執行緒 id |
|
process_id |
程序 id |
|
process_desc |
程序描述 |
|
node_id |
結點 id |
|
node_name |
結點名稱 |
|
executor |
執行人 |
|
start_time |
執行緒生成時間 |
|
receive_time |
執行緒接收時間 |
|
finish_time |
執行緒完成時間 |
|
max_time |
結點規定的最長時間 |
|
state |
狀態 |
0 未接收 1 已接收 2已處理 |
二、 常見流程
人工決策
領導傳閱
部門領導審批
填寫表單
結束
放棄
提交
同意
重填(退回)
不同意
完成
外部響應
傳送支付資訊
接收支付成功響應(外部 WS 觸發該流程)
三、PHP 設計
執行的函式由結點在設計時候決定,如果沒有設定,就使用預設的函式。利用了 PHP 語言的以下特性
<?php
class Foo
{
function Variable ()
{
$name = 'Bar' ;
$this -> $name (); // This calls the Bar() method
}
function Bar ()
{
echo "This is Bar" ;
}
}
$foo = new Foo ();
$funcname = "Variable" ;
$foo -> $funcname (); // This calls $foo->Variable()
?>
使用前可以用 method_exists 來檢查。
WorkflowService.php
WorkflowService
$defination
$process
$node
$thread
$input 使用者輸入的和流程有關的變數
list_defination()
{
}
init_process(defination_id)
{ global user;
取得 $defination ,得到業務的 handler, 例如 WorkflowProposalHandler
建立 $process 行記錄
}
start_process()
{ 呼叫 WorkflowProposalHandler->start($process)// 新建業務物件,並把業務類的引數例如 proposal_id 放到 $process[‘context’] 裡面
init_thread(1); // 預設呼叫第一個結點
}
list_ my_thread ()
{ global user;
}
init_thread(node_index)
{
取得 $node
取得 $process
修改 $process 為執行到當前結點
Switch($node[‘node_type’])
Case 1: 人工決策
建立 $thread
WorkflowProposalHandler-> init_function ($process,$node,$thread)
傳送提醒
Case 2: 自動處理
建立 $thread
WorkflowProposalHandler-> init_function ($process,$node,$thread)
呼叫 run_thread(thread_id)
Case 3: 等待外部響應
建立 $thread
WorkflowProposalHandler-> init_function ($process,$node,$thread)
Case 4: 分支
取得所有分支的子結點
init_thread( 子結點 )
Case 5: 彙總:
取得所有前結點,如果所有前結點的 Thread 都結束了,調出下一結點
呼叫 init_thread( 子結點 )
Case 6: 結束:直接結束程序 process
end_process()
}
run_thread(thread_id)
{
取得 $node
取得 $process
取得 $thread
Switch($node[‘node_type’])
Case 1: 人工決策
修改 $thread 為已接收
WorkflowProposalHandler-> run_function ($process,$node,$thread) 顯示錶單
Case 2: 自動處理
修改 $thread 為已接收
$next_node_id=WorkflowProposalHandler-> run_function ($process,$node,$thread)
呼叫 transit_thread(thread_id, $next_node_id)
Case 3: 等待外部響應
修改 $thread 為已接收
$next_node_id=WorkflowProposalHandler-> run_function ($process,$node,$thread)
transit_thread(thread_id, $next_node_id)
Case 4: 分支
Case 5: 彙總:
Case 6: 結束:
}
save_thread(thread_id)
{ // 儲存結點資料
取得 $node
取得 $process
取得 $thread
Switch($node[‘node_type’])
Case 1: 人工決策
WorkflowProposalHandler-> save_function ($process,$node,$thread) 儲存表單
WorkflowProposalHandler-> run_function ($process,$node,$thread) 顯示錶單
Case 2: 自動處理
Case 3: 等待外部響應
Case 4: 分支
Case 5: 彙總:
Case 6: 結束:
}
transit_thread(thread_id, $next_node_id)
{ 取得 $node
取得 $process
取得 $thread
Switch($node[‘node_type’])
Case 1: 人工決策
WorkflowProposalHandler->transit_function($process,$node,$thread,$next_node_id)
修改 $thread 為已完成
If($next_node_id < $ cur_node_id) { // 回退
刪除所有大於 $next_node_id 的 Thread
}
init_thread($next_node_id)
Case 2: 自動處理
修改 $thread 為已完成
If($next_node_id < $ cur_node_id) { // 回退
刪除所有大於 $next_node_id 的 Thread
}
init _thread($next_node_id)
Case 3: 等待外部響應
修改 $thread 為已完成
If($next_node_id < $ cur_node_id) { // 回退
刪除所有大於 $next_node_id 的 Thread
}
init _thread($next_node_id)
Case 4: 分支
Case 5: 彙總:
Case 6: 結束:
}
end_process()
list_my_process
view_process
workflow_proposal_handler.php
WorkflowProposalHandler
start()
prepare_input() 準備使用者輸入變數,從 $_POST 收集
init_function () 執行緒建立後呼叫的預設函式,當流程的執行者由程式生成時,在此函式內更改 $thread 的 executor ,例如直接賦值 user[2]
run_function () 執行緒執行化時候呼叫的預設函式
save_function () 儲存執行資訊
transit_function () 執行流轉
sendmail 其它結點呼叫函式
workflow.php
switch(op)
case list_defination
引數:無
WorkflowService->list_defination()
case start_process : 啟動
引數: defination_id
WorkflowService->init_process(defination_id)
WorkflowService->start_process()
case list_ my_thread : 待處理的列表
WorkflowService->list_ my_thread()
case run_thread :
引數: thread_id
WorkflowService->run_thread(thread_id)
case save_thread :
引數: thread_id
把 input 收集起來(所有的變數以 f_ 開頭),賦給 WorkflowService 的 Input ,另外還要獲得 thread_id
WorkflowService->save_thread(thread_id)
case transit_thread :
引數: thread_id
把 input 收集起來,賦給 WorkflowService 的 Input ,另外還要獲得 thread_id
$next_node_id = 得到使用者選擇的下一結點 id
WorkflowService-> transit _thread(thread_id , $next_node_id)
case list_my_process: 所有我發起的流程
case list_all_process: 所有我發起的流程
case view_process :
在其它程式中初始化流程
1 先自行建立好業務表單
2WorkflowService->init_process(defination_id)
3 把建好的業務表單的 ID 放在 process 的 context 裡面
4WorkflowService->init_thread(1)
WorkflowService->transit_thread(1 , 2) 通過手動呼叫把前面的流程過掉
外部服務繼續流轉流程(只用於自動流程)
1 把 input 收集起來,賦給 WorkflowService 的 Input ,另外還要獲得 thread_id
2 WorkflowService->run_thread(thread_id)
參考文獻: