有關(guān)多表單系統(tǒng)的介紹和設(shè)計思路
在企業(yè)呆了3年,接觸最多的就是表單了,各式各樣的表單,千變?nèi)f化,好在它們之間有共同點,就是“表單內(nèi)容+動作名稱”。企業(yè)辦公最常見的業(yè)務(wù)就是和表單打交道,不管什么請假申請單,權(quán)限申請單等等,都需要經(jīng)過一系列的工作流程來完成它。由于此類業(yè)務(wù)頻繁,在信息化發(fā)展迅速的今天,開發(fā)此類系統(tǒng)是很有必要的。
1.多表單系統(tǒng)是什么?
“多表單系統(tǒng)”,顧名思義,就是多個不同形式的表單構(gòu)成的一個系統(tǒng),是一個極其抽象的概念。它在企業(yè)內(nèi)部也是一種常見的管理信息系統(tǒng),每個表單的內(nèi)容和流程不盡相同,也可以說每個表單是一個子系統(tǒng)。在企業(yè)內(nèi)部,各單位的需求不一致,以至于設(shè)計一個通用的表單系統(tǒng)非常困難。盡管不能通用,并且也避免不了新表單的增加和已有表單的修改,但是還是可以抽象出一些共同的東東來,如表單的動作名稱,因此,我們可以將不變的部分抽象出來,以方便維護和增加新需求。
2.多表單系統(tǒng)的需求
(1).需求概述
某單位主管向軟件開發(fā)部提出,我們單位有一個申請單,想把它做成Web網(wǎng)頁形式的。流程是:
本部門工程師填寫申請單;
然后本人來審核,審核不通過,則退回申請人重新申請,審核通過,則跑到處理單位主管那里審核;
處理單位主管審核不通過,則退回申請人重新申請,審核通過,則分派給處理人員處理;
處理人員處理完成,則通知處理單位主管結(jié)案、通知申請人。
這個表單流程就算跑完了。流程圖:

(2).功能需求
根據(jù)以上需求,我們可以設(shè)計如下功能:
表單申請:申請表單1、申請表單2、……、申請表單n
表單查詢:申請表單查詢、歷史表單查詢
表單審核:審核工作、代理審核
代理設(shè)置:默認(rèn)代理設(shè)置、代理區(qū)間設(shè)置
表單管理:工作分派、工作清單、處理狀況
報表管理:報表1、報表2、……、報表n
系統(tǒng)管理:表單設(shè)置、角色管理、流程管理、處理單位管理、(包括表單共同部分設(shè)置)
(3).表單動作需求
有了系統(tǒng)功能結(jié)構(gòu),但不同表單還有不同動作,概括起來,表單共有以下動作:
申請 (Apply)
修改 (Modify)
重新申請 (Return)
取消 (Cancel)
檢視 (View)
審核 (Sign)
分派 (Assign)
處理 (Process)
撤單 (Revoke)
結(jié)案 (Close)
審核可能有好幾步,如上面例子,申請人主管和處理單位主管審核,有的表單有可能在處理單位主管審核前還有好幾步審核。
(4).UI需求
首先看看UI抽象圖:
從UI層面來分析,表單系統(tǒng)也只有“表單內(nèi)容”(也就是上圖中陰影部分)在變化,各個表單的內(nèi)容不一樣,有簡單的,有復(fù)雜的。簡單的表單,也就是一個實體(可理解成一張數(shù)據(jù)庫表)就可以滿足要求。復(fù)雜的表單,那就需要n個實體的組合才能滿足需要。
關(guān)于UI邏輯,對于不同表單動作,有不同的UI界面,也就是要考慮UI的顯示邏輯,例如某審核頁面需要能修改某個欄位,如“希望完成時間”,甚至有的審核頁面可以修改所有內(nèi)容,另外表單處理的頁面和表單檢視的頁面也不一樣,處理頁要上傳處理報告,還有根據(jù)當(dāng)前登錄者的角色判斷什么欄位顯示,什么欄位不顯示,什么功能顯示,什么功能不顯示,這就是操作權(quán)限和UI的聯(lián)系。
關(guān)于UI需求應(yīng)該還有很多,沒有想出來,盼謝大家?guī)兔ρa充一下。
(5).業(yè)務(wù)需求
從業(yè)務(wù)層面來看,每個表單的動作不一樣,也就是每個動作處理的邏輯不同,簡單的動作只要將資料插入一張表或?qū)⒛硞€字段值改一下之類的簡單操作,復(fù)雜的動作就是要操作n張表,包括判斷,插入,修改,刪除等等。
業(yè)務(wù)邏輯這東西不知道怎么講好,這塊在寫起來是比較復(fù)雜的,也是系統(tǒng)的最難寫的部分,呵呵,個人體會。
(6).變化需求
像這樣的多表單系統(tǒng),最煩人的就是用戶不斷地變更需求,這在開發(fā)企業(yè)管理信息系統(tǒng)中是不可避免的。下面就簡單描述一下幾個常見的變化:
增加欄位。這點只有修改代碼,想不修改也難,除非自動生成表單什么的,但是自動生成有其局限性,不能完全滿足需求。
增加功能。這點也要修改代碼。
增加審核流程。這點需要增加頁面和動作處理邏輯了,這點只要系統(tǒng)的擴展性好(采用接口設(shè)計),加一個流程也不是很麻煩。
面對需求變化,我們應(yīng)該有應(yīng)對變化的方法,盡量減少我們修改代碼的地方。
3.多表單系統(tǒng)的設(shè)計
(1)技術(shù)框架
我們采取大家熟悉的.NET平臺來構(gòu)建B/S模式的多表單系統(tǒng),以下是主要技術(shù)框架:
.NET Framework 3.5
ASP.NET WebForm
ASP.NET Ajax
JQuery
由于企業(yè)應(yīng)用需求變更頻繁,實體修改在所難免,為了修改方便,避免更改多處代碼,所以沒有使用ORM技術(shù),而是直接SQL的方式編寫。
(2)目錄結(jié)構(gòu)
首先看看目錄結(jié)構(gòu)圖:
從圖中我們可以看出工程中有三個項目Booking、Common、Workflow。
Common類庫項目是存放系統(tǒng)公共的業(yè)務(wù)。
Workflow類庫項目是存放系統(tǒng)工作流業(yè)務(wù)。
Booking項目是WebApplication,幾個主要文件夾和頁面如圖所示。
(3)系統(tǒng)關(guān)鍵設(shè)計
系統(tǒng)的關(guān)鍵技巧就在于,每個表單子系統(tǒng)所有業(yè)務(wù)都使用上面圖中幾個頁面來完成作業(yè)的,不必為每個表單添加相應(yīng)功能的頁面。每個頁面(除了一些List頁面)有一個Panel控件,其是每個表單內(nèi)容呈現(xiàn)的容器,真正的內(nèi)容是通過動態(tài)載入Editor和Viewer來實現(xiàn)的,每個表單有兩個用戶控件(Editor和Viewer)和一個Control類,Editor是表單編輯者,Viewer是表單的監(jiān)視器,Control是表單的控制者。
下面是各控件和頁面之間的繼承關(guān)系圖:
圖中的FormControl的FormApply等表單動作方法都是虛方法,真正的業(yè)務(wù)邏輯是在Editor和Viewer中覆寫的。FormControl中GetShowMode方法默認(rèn)申請、修改、重新申請頁面為載入Editor控件,其他頁面載入Viewer控件。若其他頁面需載入Editor控件,則可以在每個表單的Control類中覆寫GetShowMode方法。
#p#
下面是幾個主要類別的代碼:
- public class FormControl : BaseControl
- {
- /// < summary>
- /// 取得頁面顯示模型?
- /// < /summary>
- /// < param name="pageType">頁面類型< /param>
- /// < param name="signStep">審核步驟< /param>
- /// < returns>< /returns>
- public virtual string GetShowMode(Type pageType, string signStep)
- {
- if (pageType == typeof(Web.FormApply)
- || pageType == typeof(Web.FormReturn)
- || pageType == typeof(Web.FormModify))
- {
- return "Editor";
- }
- else
- {
- return "Viewer";
- }
- }
- public virtual void FormApply()
- {
- throw new NotImplementedException();
- }
- public virtual void FormReturn()
- {
- throw new NotImplementedException();
- }
- public virtual void FormModify()
- {
- throw new NotImplementedException();
- }
- public virtual void FormCancel(string cancelReason)
- {
- throw new NotImplementedException();
- }
- public virtual void FormSign(bool isAgree, string signComments)
- {
- throw new NotImplementedException();
- }
- public virtual void FormAssign()
- {
- throw new NotImplementedException();
- }
- public virtual void FormProcess()
- {
- throw new NotImplementedException();
- }
- public virtual void FormRevoke(string revokeType, string revokeReason)
- {
- throw new NotImplementedException();
- }
- public virtual void FormClose(string closeType)
- {
- throw new NotImplementedException();
- }
- }
- public class FormControlFactory
- {
- public static FormControl GetControl(string type)
- {
- string className = String.Format("Known.Booking.{0}Control", type);
- Type controlType = Type.GetType(className);
- if (controlType == null)
- {
- return new FormControl();
- //throw new Exception(String.Format("找不到{0}表單控件類型!", type));
- }
- FormControl control = Activator.CreateInstance(controlType) as FormControl;
- if (control == null)
- {
- throw new Exception(String.Format("找不到{0}表單控件類型!", type));
- }
- return control;
- }
- }
- public class FormPage : BasePage
- {
- public string FormNo
- {
- get { return Request.QueryString["formno"]; }
- }
- public string SignStep
- {
- get { return Request.QueryString["step"]; }
- }
- public string SignRole
- {
- get { return Request.QueryString["role"]; }
- }
- public string FormType
- {
- get
- {
- string formType = Request.QueryString["type"];
- if (String.IsNullOrEmpty(formType))
- {
- formType = Request.QueryString["type"];
- }
- return formType;
- }
- }
- public string FormName
- {
- get { return FormType + "申請單"; }
- }
- private FormControl formControl;
- public FormControl FormControl
- {
- get
- {
- if (formControl == null)
- {
- FormControl form = FormControlFactory.GetControl(FormType);
- string format =
- SysSetting.Instance.FormModelPath.EndsWith("/")
- ? "{0}{1}/{2}.ascx"
- : "{0}/{1}/{2}.ascx";
- string modelPath = String.Format(format, SysSetting.Instance.FormModelPath, FormType, form.GetShowMode(PageType, SignStep));
- formControl = LoadControl(modelPath) as FormControl;
- if (formControl == null)
- {
- throw new Exception(String.Format("請確認(rèn){0}是否繼承自FormControl!", modelPath));
- }
- }
- return formControl;
- }
- }
- }
- public class Test1Control : FormControl
- {
- public override string GetShowMode(Type pageType, string signStep)
- {
- if (pageType == typeof(FormSign) && signStep == "AL")
- {
- return "Editor";
- }
- return base.GetShowMode(pageType, signStep);
- }
- }
4.這樣開發(fā)優(yōu)劣在哪里?
優(yōu)勢:新增一個表單時,只需要在UControls下面添加像Test1一樣的控件,表單的內(nèi)容和邏輯都在控件中實現(xiàn),這樣寫不需要為每個表單增加相應(yīng)的頁面,維護時也不用改動多個頁面,這是集中開發(fā),集中維護。
劣勢:這樣寫就將一切邏輯都集中在一起,造成代碼很長,這點局限也可以讓它有所改變,就是將表單對象數(shù)據(jù)的組裝和呈現(xiàn)寫在控件中,將業(yè)務(wù)處理抽出來寫在Control類中。
5.結(jié)束語
這個系統(tǒng)的設(shè)計,我可能沒有講的太透徹,但是原理是很簡單的,就是動態(tài)加載用戶控件,我們把控件看成是一個表單對象,表單對象的動作就在控件中實現(xiàn),頁面中的功能按鈕事件響應(yīng)控件中的方法。關(guān)鍵的一個類就是FormControl,一切表單的動作都定義在此類中。
以上就介紹了多表單系統(tǒng)的設(shè)計思路。本文來自閣老的博客:《多表單系統(tǒng)框架設(shè)計》。
【編輯推薦】