轉:
在做某個管理項目時,被要求實現一套流程管理,比如請假的申請審批流程等,在參考了很多資料,並和同事討論后,得到了一個自主實現的流程管理。
以下提供我的設計思路,知道了思路,實現起來就簡單很多了。
首先我設計了5個類來實現流程的自主設置,主要是對流程的定義和流程流轉。
注:這是設計的圖,並不是實現
Dictionary:數據字典,不多說,流程類型存在這里面
Flow:流程,即流程的定義,其中包括流程名稱,描述,類型,啟用時間,備注等;目前是通過判斷某個類別的流程啟用時間來進行判斷當前流程是否啟用的。
一個類別只啟用一個流程。所以只需要通過流程類別即可確定流程,並不要特定的狀態字段。
FlowInfoMovingNode:流程節點,在分析流程流轉的時候,我們發現,流轉一步就相當於從一個節點跳到另一個節點,所以我們設計這個流程節點類來表示每一步。
其中包括,所屬流程,節點名稱,節點描述,監聽權限。
解釋下監聽權限是什么. 由於我們做的大部分是審核的流程,所以每個節點都需要有個審核的過程才進入下一個節點,所以我們要這個handlerRole屬性來確定這個節點究竟是什么權限來審核。我們也知道,審核一般是某個人審核,這個我們后面說。這里是規定某個權限,即可以審核這個節點的權限。
FlowInfoMovingRole:流程流轉規則,為了解決從節點出來的各個分支,我們設計了這個流轉規則,本來其實可以一起放到FlowInfoMovingNode中,但這樣話無論從數據上 還是管理上來說都不如加流轉規則方便清楚。FlowInfoMovingRole主要用來確定流轉規則,比如某個節點通過了應該去哪個節點,某個節點沒通過應該去哪個節點,這樣無論是分支還是單支還是循環都可以通過相同的方式來進行設置。transition為變換規則,參照shiro驗證權限的方式,我們也使用純字符串格式來進行判斷變換規則。
FlowInfoMoving:流程流轉信息,這里是每一步流轉信息的存放,基本在進行流程流轉的過程中,都是通過此類,其中包括:所屬節點(得到所屬節點同時也就得到所屬流 程),申請源(因為我們不知道申請源是什么,我們只是管流程是怎么運轉的,其申請源跟我們沒有任何關系,我們保存申請源的唯一標識,若是想在審核的過程中進行查看申請源信息,則可以請求在Flow中監聽的Url(handlerUrl),來進行查看,我會把申請源的唯一標識當做參數傳遞到Url中)。
由於這個是操作最頻繁的,所以我來具體解釋下這個類。
以下為本人具體實現的類設計,屬性字段均有注釋進行解釋:

/** * 流程流轉信息 * @author lichao * */ @Entity @Table(name = FlowInfoMoving.TABLE_NAME) public class FlowInfoMoving extends BaseAuditEntity { private static final long serialVersionUID = 1L; public static final String TABLE_NAME = "t_flowInfoMoving"; // 流程節點,標識此流轉信息是流轉到了哪個節點 @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "node_id",nullable = true) private FlowInfoMovingNode node; // 申請源,具體申請信息的唯一標識 @Column private Long applyId; // 申請人,具體申請信息的申請人 @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "applyuser_id",nullable = false) private User applyUser; // 申請信息,具體申請信息的簡要概述,由申請提供 @Column(length = 50) @Size(max = 50) private String discription; // 申請類別,即申請的流程的類型 @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "flowtype_id",nullable = false) private Dictionary applyType; // 變換的結果,即審核通過未通過等條件,此條件同FlowInfoMovingRole種的transition, // 但有一些區別,因為有添加待審核,已結束等其他信息條件 @Column(length = 50) @Size(max = 50) private String transition; // 處理信息,即審核信息,由審核人添加 @Column(length = 100) @Size(max = 100) private String handlerInfo; // 處理人,即此流轉應該由誰來申請,由FlowInfoMovingNode中的handlerRole來進行篩選, //並由上一個節點的處理人來進行選擇具體審核人。 @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "handleruser_id",nullable = false) private User handlerUser; // 此流轉信息的狀態,用於查詢,0未審核,1已審核,2本申請已經結束 @Column() private int status; //getter...and setter.... }
這樣整個流程定義就結束了,我們可以自由設置流程的流程規則,來設置流程的流轉方式。這樣無論任何復雜的流程都可以進行自定義,並且可以隨意的修改。
這樣設計的結果首先是可以任意的自定義流程,其次是申請源不需要去管流程的流轉了,只需要提交一份申請,其他的事情均由流程進行操作實現。
那么我們來看看一個流程是如何進行運轉的。
1.首先通過一個接口來傳遞具體的一些申請信息。
2.通過接口中的getType()來確定是哪個流程
3.查詢到開始的流程節點。
4.將開始的信息保存到流轉信息中,並等待處理
5.由審核人處理,指定下一個審核人
6.通過處理結果來獲得下一個流程節點
7.查找下一個節點
8.將這次的處理信息和下一個節點的信息保存到流轉信息中,並等待處理
9.由審核人處理,這樣一直循環知道流程結束
10.通過審核信息查找的下一個節點為null,則表示此流程已經結束,
11.將結束的信息保存到流程流轉信息中
12.將整個流程流轉的狀態改為已結束。
首先是Applyable是怎么設計的:其實一個接口,用於提供一些申請的信息

public interface Applyable { // 得到申請的id,與類別進行聯合查詢,用於確定具體是哪個流程 Long getId(); // 得到申請的類別,用於確定具體是哪類流程 String getApplyType(); // 得到變換條件,用於確定申請后的第一個步驟 String getTransition(); // 得到此次申請的描述 String getDiscription(); // 得到審批人,即第一個步驟由誰審批 User getHandlerUser(); }
然后是主要的控制流程流轉和申請的接口

/** * *@Description:處理流程業務的Service *@Author:lichao *@Since:Oct 10, 201412:02:39 PM */ public interface FlowMainService { /** * *@Description: 用於申請 *@Author: lichao *@Since: Oct 10, 20143:19:07 PM *@param applyable *@return * @throws Exception */ public void applyFlow(Applyable applyable) throws Exception; /** * *@Description: 查找當前所處的流轉信息 *@Author: medees *@Since: Oct 13, 20148:56:00 AM *@param applyid 申請源 *@param applytype 申請類別 *@return */ public FlowInfoMoving findNowMoving(Long applyid,Dictionary applyType); /** * *@Description: 查找某一流程下的所有走過的流程 按創建時間升序排序 *@Author: lichao *@Since: Oct 13, 20141:46:54 PM *@param applyid 申請源 *@param applytype 申請類別 *@return */ public List<FlowInfoMoving> findAllMoving(Long applyid,Dictionary applyType); /** * *@Description: 獲取所有變換條件 *@Author: lichao *@Since: Oct 13, 20144:27:41 PM *@param nodeid 所處節點的id *@return */ public List<String> findNextTransition(Long nodeid); /** * *@Description: 得到此節點可以審核的所有用戶 *@Author: lichao *@Since: Oct 14, 201410:22:50 AM *@param nodeid 節點id *@param applyid 申請源 *@param applyUserId 申請人id *@return */ public List<User> findNextAppUser(Long nodeid,String transition,Long applyUserId); /** * *@Description: 分頁查詢自己所審批的流程 *@Author: lichao *@Since: Oct 14, 20141:39:50 PM *@return */ public Page<FlowInfoMoving> findMyMoving(Pageable pageRequest); /** * *@Description: 進行審核的方法 *@Author: lichao *@Since: Oct 15, 20142:11:18 PM *@param moving * @throws Exception 如果審核出錯則拋出異常 */ public boolean approval(FlowInfoMoving moving) throws Exception; /** * *@Description: 查找一個流程的所有流轉 *@Author: lichao *@Since: Oct 16, 201411:04:03 AM *@param applyid *@param applytype *@return */ public Page<FlowInfoMoving> findAllMoving(Long applyid,Long applytype,Pageable Pageable); /** * *@Description: 重新申請 *@Author: lichao *@Since: Oct 17, 20141:43:03 PM *@return */ public void resetApply(Applyable applyable) throws Exception; /** * *@Description: 是否需要重新申請 *@Author: lichao *@Since: Oct 17, 20141:43:03 PM *@return */ public boolean isNeedReset(Long applyid,Dictionary dic); }
申請源只要調用此接口中的applyFlow進行申請即可。
具體實現就不在提供了!