一個真正符合中國國情的工作流設計參考(包括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語言的以下特性
|
使用前可以用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)
順便把和daniel的聊天也附在下面:
♂蒓玥♀ 12:09:25
execute_type的作用是表示這個流程由多少人完成,例如對於會籤,就需要大家一起完成。如果是客戶報修,只要有一個客服接收了這次報修,流程就繼續
是的,我寫這個文的目的,就是想說明,自己完全可以開發一個符合實際需要的流程引擎。如果總是用那些現成的引擎,常常會束手束腳
-=Daniel=- 12:11:39
是的啊, 我知道, 但是我認為execute_type本身就是為了executer存在的. executer又是來自外部的, 比如使用者管理模組啊什麼的. 外部就可以生成一個executer的id給流程使用了, 為什麼這裡還需要描述這個呢. 比如說外部生成一個id, 這個id是描述一個人, 那就是一個人的操作, 如果是一個組, 那就是一個組的操作. 在流程內部標明這個id的含義有什麼作用呢?
♂蒓玥♀ 12:11:46
remind就是一個提醒功能,設計的時候,自己定義那些結點需要提醒,把他放進去,其實是為了利用空間換時間的原理,減少定義在其他地方的時候浪費讀取的時間
-=Daniel=- 12:13:01
其實我的意思是remind可能有很多種, 不可能在流程內部給定義完. 如果留介面而不是由流程給定, 可能會更加全面點.
♂蒓玥♀ 12:14:07
我的executor是這樣定義的,可以定義user[1]表示id為1的使用者,group[1]表示組id為1,role[1]表示角色1,這幾個組合是可以並的,最終根據定義把thread分到所有的使用者,但是execute_type的含義是,只要有一個人執行了這個thread,我根據設定判斷,如果不需要所有人做,就把其他的thread結束
恩,我把所有業務的類都交給handler,例如一個提案流程
就是workflow-proposal-handler類,裡面定義所有結點需要的處理方法,當然也可以傳送簡訊
我把一次完整的流程看作process,其中的每一條分支,都是thread
所有的thread都是可以直接由外部呼叫的,這比jpbm這些要好多了
-=Daniel=- 12:17:02
我不同意, 我認為executer不管一個人完成或者多個人完成, 都是隻有有其中一個人完成就立即判定為執行了流程. 如果一個流程需要多個人有次序的完成, 那麼就說明這個流程不夠詳細, 需要再細分.
♂蒓玥♀ 12:18:08
哦,我先定義了兩種情況:0 所有人處理,就是會籤
1 只需一個人處理,就是客服模式,
這兩個是最長用的模式
-=Daniel=- 12:18:53
哦, 這可能是你定義流程的模式跟我的有一定的差別, 或者是我們的客戶不同型別吧.
-=Daniel=- 12:19:51
對了, 有一種流程你怎麼處理的, 比如一個流程中有一個步驟, 它按A這條路走是對的, 按B走也是可以的, 只要走了子流程中的任意一個流程, 都可以.
♂蒓玥♀ 12:20:48
就是分支啊,隨便使用者選哪一條路徑
-=Daniel=- 12:22:35
是啊, 我這邊在這個分支上遇到了意外, 因為現在要做事務功能, 加上了分支很麻煩.
♂蒓玥♀ 12:23:03
哦,我的這個客戶本身就是用SAP的,他們一個子公司要上提案系統,是基於Web的。
♂蒓玥♀ 12:24:26
你的事務處理具體想幹什麼?
-=Daniel=- 12:26:47
事務嘛, 最主要的就是回溯啊, 原先在紀錄的儲存上做的都是當前狀態的值, 回溯就是返回到上一個值, 要求處理分支的回溯, 那就要求將當前的操作本身也當作一個值, 進行回溯.
♂蒓玥♀ 12:27:15
我的回退作為流程分支處理
-=Daniel=- 12:27:50
如果一個事務跨越了一個分支倒可以, 問題就是怕事務從分支的一部分開始, 到另外一個分支的某一部分結束, 那就會弄的很亂.
-=Daniel=- 12:28:39
我看了, 這是一個方法, 就是所有的節點都有一個回退功能, 做起來比較煩.
♂蒓玥♀ 12:29:39
這個問題我想過,包括所有結點的回退,我的結論是:純粹圖論上的功能對實際業務沒有任何作用,只要在流程設計的時候設計好,不要有這些問題就行了
♂蒓玥♀ 12:30:18
對於銀行這種單位,流程裡面有很多自動劃帳的結點,能讓人隨便回退麼
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1824947
相關文章
- 國外APP介面設計參考APP
- 詳情屬性圖示設計參考
- 詳情頁產品引數模組設計參考
- 七牛重新整理介面php實現參考PHP
- 參考Vue-router, 實現一個簡單的前端路由Vue前端路由
- 實現一個符合 Promise/A+ 規範的 MyPromisePromise
- 如何實現一個符合promiseA+規範的promisePromise
- 實現一個完美符合Promise/A+規範的PromisePromise
- 分享一組場景應用設計參考
- 分享一組智慧手錶介面設計參考
- UI | 一組酒店類APP介面設計參考UIAPP
- 一組郵箱類APP介面設計參考APP
- 一組動物主題APP介面設計參考APP
- 一個考過CCNA的朋友經驗參考(轉)
- 通用雙向連結串列的設計(參考Linux系統中的實現)Linux
- php 知識點參考PHP
- PHP程式設計考試PHP程式設計
- Context真正的實現與Context設計模式Context設計模式
- 求好的Dao設計參考與原則
- 一組促銷優惠卷模組設計參考
- Cheeper:《CQRS By Example》一書的參考程式碼開源實現
- 賞析 | 好看的logo設計創意參考作品(一)Go
- 一步一步實現一個符合Promises/A+規範的PromisePromise
- 編寫shell指令碼實現統計一個小組的成績情況,統計資訊包括:總分,平均分,最高分...指令碼
- 幽默:真正的程式設計師閱讀API參考文件程式設計師API
- 系統設計面試參考-設計Spotify系統面試
- 一個程式設計師的愛情自白程式設計師
- 賞析 | APP閃屏頁設計靈感參考(一)APP
- 美食類網頁設計版式參考網頁
- 輕量級工作流引擎的設計與實現
- MongoDB實現問卷/考試設計MongoDB
- 一個可以參考的JVM記憶體分配JVM記憶體
- 一個dockerfile例子(參考著寫dockerfile)Docker
- php實現一個簡單的socketPHP
- php實現一個簡單的堆PHP
- 後臺介面設計之表格設計規範參考
- 一個程式設計師的愛情表白書程式設計師
- PHP-imap 使用參考手冊PHP