天天看點

JS腳本與伺服器互動完成業務處理系統實作

由于醫保是目前與HIS系統唯一需要接口的業務,且由于各地區的醫保接口不相同,也不相通,但是主要的業務的處理模型基本上是相通的,即都需要進行收費明細處理.為滿足能夠靈活的支援各種醫保接口且減少我們自己的HIS系統與醫保接口的強藕合,則需要對醫保的業務處理進行必要的抽象模組化.

但是,由于B/S系統需要考慮互動上的問題,則對這個業務的需求就需要使用JS來完成.JS有一定的面向對象的機制,但不是很全面,目前的了解應該可以采用變通的方式實作抽象.

JS腳本與伺服器互動完成業務處理系統實作

這裡,采用了類圖的描述方式,但是實作的方式需要采用變通的方法

1.         OCXBase:OCX完成與對本地醫保的接口調用封裝,使JS可以與醫保接口互動,每一種醫保類型需要一個具體的實作,OCX對外暴露的接口允許變化,這是因為每種醫保都有各自的業務規則且提供的接口形式也不盡相同,這個規則的變動隻需要具體的JS業務類知道,針對于各個醫保的JS對其變化進行了封裝.

2.         伺服器端業務:這個可以以WebService的形式或者頁面來實作,主要是針對各種不同的醫保傳回的資料進行處理,例如需要儲存各種醫保的傳回資料,以為将來的報表查詢提供原始資料.另外,在這個業務處理過程還要處理我們自身的業務.

3.         JS業務類:因為是JS的實作,是以不可能将OCX和伺服器端的業務類以引用的形式傳給它,限于技術上的限制,這裡對OCX和伺服器端均需要采用字元串用來辨別它們.JS業務類需要保持對象的對外暴露的接口,在其内部,它可以根據業務的需要來協調調用本地OCX控件與醫保接口的接互,和對伺服器端業務類的遠端異步調用來完成醫保資料與我們自身業務資料的處理.

JS腳本與伺服器互動完成業務處理系統實作

調用首先由用戶端頁面來觸發,JS得到其所需要的業務資料,然後将資料發送給OCX控件用來與醫保接口互動和調用伺服器端業務類進行資料的處理,伺服器端的業務類即要處理醫保的業務又要處理我們自身的業務.

在這裡,調用OCX對象和異步遠端調用伺服器的業務類是可以由JS業務類來決定調用的順序的,因為這裡可能存在的變化點是根據醫保接口的特點其處理的過程可能會是以而發生變化,那麼也就是說,在JS的這個業務處理的過程中将變化點封裝在它的内部,而不需要暴露給頁面的代碼進行處理.

最後,如果全部處理成功,則需要異步回調目前頁面,對目前頁面的某些控件進行重新整理,或者執行列印報表一類的操作.

JS腳本與伺服器互動完成業務處理系統實作

現有的方案是頁面與醫保的接口進行的強耦合,即由頁面級的代碼來差別各種醫保類型然後加以處理,由于各種醫保接口的不盡相同,則勢必在頁面級别編寫各種類型的JS與各種類型的OCX進行互動,頁面上會存在很多判斷來确定是何種醫保類型,然後才去決定應該進行何種的業務處理,如果醫保的接口變動,或者新增醫保類型,那麼在頁面上進行代碼的修改将會變得很麻煩.另外,由于B/S的特殊性,動态執行某些功能時需要經常性的向頁面注冊腳本也會使處理過程變的更加複雜.

而采用與醫保處理分離的方式,則可以頁面與醫保的處理獨立進行,減小依賴,便于擴充和修改.

從上面的分析可以确定,用戶端的JS類和伺服器端的業務類應該彼此了解對方的處理細節,它們之間是有很強的依賴性的,是以在實作具體的JS用戶端業務類與具體的伺服器端業務類時可以互相約定一些調用過程的細節.這裡指的具體的含義是某種醫保的業務類型,這就會形成某個業務類,會分别有具體的JS業務對象和伺服器端的業務對象.

JS用戶端業務對象需要調用伺服器端的業務處理邏輯,這裡用戶端的需要異步請求以不重新整理頁面,那麼這個方案自然是要采用XMLHttpRequest的,那麼伺服器端的業務處理以何種方式暴露給JS用戶端業務對象呢?可選的方案有WebService,我們可以使用XMLHttpRequest可以傳遞Post或者Get參數,然後在WebService進行判斷再處理.

但是這裡存在一個問題,就是這裡的業務類是需要處理我們自身業務的,即需要我們自身業務處理所需要的參數.我們倒着來演繹一下這個參數是從哪來的:

1.         伺服器業務處理的這些參數從哪來呢?由JS業務類傳入?

2.         JS從哪裡擷取業務處理所需的資料呢?在調用JS業務類的方法時傳進來.

3.         在調用JS業務類的方法時如何傳進來?觸發業務時組織資料傳入.

4.         觸發業務時如何組織資料呢?這就回到原始的資料點了.

JS腳本與伺服器互動完成業務處理系統實作

從功能上可以得知,觸發事件的點是在用戶端的頁面中,那麼擷取資料可以通過DOM或者回伺服器組織資料.通過DOM的方式,則需要與頁面進行強藕合,且有可能組織不全處理所需的參數.回伺服器組織參數,則需要背景的頁類來處理,而背景的頁面又如何知道具體的業務需要哪些資料呢?這就又造成了強藕合.即使通過伺服器的業務類來組織資料,但是讓一大堆參數資料傳來傳去也不是很爽,與現有的模型沖突太大.

那麼有沒有一種方案,能夠做到讓伺服器端的頁面完成完整的生命周期, 然後我們就可以讀取各個控件的值了?有的,因為了解了AJAX的原理和ASP.Net2.0的異常回調模型後,就會發現這種方案是完全可行的,分析Asp.net2.0的異常回調模型是一個很大的議題,我在這裡就不讨論它了.我将微軟的JS代碼修改後封裝到WebForm_DoCallback中,使用這個函數進行送出的時候可以将ViewState和Form裡的值送出回去,這樣就會使背景頁面的IsPostback為真,可以正常讀取控件的值,然後再在頁面級進行檢查後,再調用業務類進行業務處理,參數傳遞自然也變成了一件簡單的事情.

消息格式采用XML格式,如下所示:

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

Code

<?xml version="1.0"?>

<Message>

 <Class></Class>

 <Command></Command>

 <ClientParams></ClientParams>

 <ClientReturn></ClientReturn>

 <ServerParams></ServerParams>

 <ServerReturn></ServerReturn>

</Message>

l         Class:辨別了調用功能的JS的業務類标志.

l         Command:指令字辨別

l         ClientParams:調用用戶端JS函數時的傳入參數.

l         ClientReturn:調用用戶端JS與OCX互動時産生的傳回值.

l         ServerParams:需要調用伺服器的功能的參數

l         ServerReturn:伺服器端的傳回值,這個主要是儲存上次調用時的傳回值.

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

//<summary>構造函數</summary>

//<param name="param">顯示錯誤資訊的容器</param>

//<returns></returns>

function MedicareBase(errContainer)

{

    this.ErrContainer=errContainer;

    //保留屬性定義

    this._MessagePacketFlag="<?xml version=\"1.0\"?>";

    this._BeginMessageFlag="<Message>";

    this._EndMessageFlag="</Message>";

    this._BeginClassFlag="<Class>";

    this._EndClassFlag="</Class>";

    this._BeginNoTransCommand="<Command Transaction='No'>";

    this._BeginBeginTransCommand="<Command Transaction='Begin'>";

    this._BeginCommitTransCommand="<Command Transaction='Commit'>";

    this._BeginRollTransCommand="<Command Transaction='Rollback'>";

    this._EndCommandFlag="</Command>";

    this._BeginClientParamsFlag="<ClientParams>";

    this._EndClientParamsFlag="</ClientParams>";

    this._BeginClientReturnFlag="<ClientReturn>";

    this._EndClientReturnFlag="</ClientReturn>";

    this._BeginServerParamsFlag="<ServerParams>";

    this._EndServerParamsFlag="</ServerParams>";

    this._BeginServerReturnFlag="<ServerReturn>";

    this._EndServerReturnFlag="</ServerReturn>";

    this._JSClientCommand="JSClientCommand";

}

//<summary>擷取以0或-1開頭的真假值</summary>

//<param name="param"></param>

//<returns>true/false</returns>

MedicareBase.prototype._GetBoolean=function(str)

    try

    {

        if (str.substring(0,1)=='0')

        {

            return true;

        }

        else

            return false;

    }

    catch(e)

        return false;

};

//<summary>去掉成功與否的标記,擷取Server的傳回值</summary>

MedicareBase.prototype._GetReturnString=function(str)

    var digits = "0123456789"; 

        if (str.length==0)

            return str;

        if (str.charAt(0) == "e" || str.charAt(0) == "s")

            return str.substring(1);

            for(i=0;i<str.length;i++)

            {

                if (str.substring(i,1)!='-' && digits.indexOf(str.substring(i,1))==-1)

                {

                    break;

                }

            }

        return str.substring(i);

        return str;

MedicareBase.prototype._GetReturnFlag=function(str)

        var i=0;

            return '-1';

        return str.substring(0,i-1);

//<summary>擷取首行</summary>

MedicareBase.prototype._GetFirstLine=function(str)

    var i=0;

    for(i=0;i<str.length;i++)

        if (str.substring(i,i+1)=='\r' || str.substring(i,i+1)=='\n')

            break;

    return str.substring(0,i);

//<summary>顯示成功後的資訊</summary>

//<param name="param">消息文本</param>

MedicareBase.prototype._ShowSuccessMsg=function(str)

    //判斷一下是否為空,為空則不顯示提示,将來确定何時來顯示這個框時可以在參數裡加标志

    if (str!="")

        alert(str);

//<summary>顯示失敗後的資訊</summary>

MedicareBase.prototype._ShowFailurMsg=function(str)

    if (typeof(errContainer)=="undefined")

    else

        var control=document.getElementById(errContainer);

        if (typeof(control)=="undefined")

            alert(str);

        {   

            control.value=str;

    //預設讀卡傳回真

MedicareBase.prototype.ReadCard=function()

    return true;

    //預設挂号傳回真

MedicareBase.prototype.Registration=function()

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

//構造函數 

    //objID:OCX控件ID 

    //remoteService:遠端背景服務  

    //callBack:目前為AjaxManagerID 

    //userName:操作員

    //errContainer:錯誤資訊的容器

    function SYMedicare(objID,callBack,userName,errContainer)

        MedicareBase.call(this,errContainer);                    //使屬性繼承

        this.ObjID=objID;

        this.UserName=userName;

        this.CallBack=callBack;

        //私有屬性

        this._personAccountInfo="";

//方法定義 SYMedicare.prototype=new MedicareBase();必需是定義方法前的第一行

    SYMedicare.prototype=new MedicareBase();

    //<summary>向伺服器發送資訊,将來可以根據是發送到WS還是背景頁面來做修改</summary>

    //<param name="param">發送的資訊</param>

    //<returns>[0,-1]|ReturnMessage</returns>

    SYMedicare.prototype._SendDataToServer=function(param)

        //使用ASP.net2.0的WebForm_DoCallback異步機制,使用修改後的函數

        var result=WebForm_DoCallback(param);

        return this._GetFirstLine(result);

    //<summary>重置狀态</summary>

    //<returns></returns>

    SYMedicare.prototype._Reset=function()

    //<summary>對發送的消息打包</summary>

    //<param name="clientParams">調用用戶端時的參數</param>

    //<param name="clientReturn">調用用戶端産生的傳回值</param>

    //<param name="serverParams">調用伺服器端需要的業務的參數</param>

    //<param name="serverReturn">上一次伺服器端的傳回值</param>

    //<param name="rollBack">是否為復原指令</param>

    //<param name="commandText">指令字</param>

    MedicareBase.prototype._PacketMessage=function(clientParams,clientReturn,

            serverParams,serverReturn,commandType,commandText)

        var msg=new StringBuilder();

        msg.append(this._MessagePacketFlag);

        msg.append(this._BeginMessageFlag);

        msg.append(this._BeginClassFlag);

        msg.append("SYMedicare");

        msg.append(this._EndClassFlag);

        msg.append(commandType);

        msg.append(commandText);

        msg.append(this._EndCommandFlag);

        msg.append(this._BeginClientParamsFlag);

        msg.append(clientParams= (typeof(clientParams)=="undefined")?"":clientParams);

        msg.append(this._EndClientParamsFlag);

        msg.append(this._BeginClientReturnFlag);

        msg.append(clientReturn= (typeof(clientReturn)=="undefined")?"":clientReturn);

        msg.append(this._EndClientReturnFlag);

        msg.append(this._BeginServerParamsFlag);

        msg.append(serverParams= (typeof(serverParams)=="undefined")?"":serverParams);

        msg.append(this._EndServerParamsFlag);

        msg.append(this._BeginServerReturnFlag);

        msg.append(serverReturn= (typeof(serverReturn)=="undefined")?"":serverReturn);

        msg.append(this._EndServerReturnFlag);

        msg.append(this._EndMessageFlag);

        return msg.toString();

    };

    //<summary>讀卡</summary>

    //<param name="param">數值型1~7或者x+y的形式</param>

    //<param name="callbackFlag">是否回調 true/false,預設為true</param>

    //<returns>讀卡産生的消息或者出錯資訊</returns>

    SYMedicare.prototype.ReadCard=function(param,callbackFlag,callServer)

        callbackFlag= (typeof(callbackFlag)=="undefined")?true:callbackFlag;

        callServer= (typeof(callServer)=="undefined")?true:callServer;

        var control=document.getElementById(this.ObjID);

        var objMsg="";

        //對參數要進行判斷,如果是a+b+c形式的,需要多次讀卡,并将讀出的資訊用符号~連接配接起來

        var readPara=String(param).split('+');

        for(var i=0;i<readPara.length;i++)

            var m=control.ReadCard(readPara[i]);

            if (control.GetBoolean(m))

                if (objMsg=="")

                    objMsg=m;

                else

                    objMsg+="~"+m;

            else

                if (callbackFlag==true)

                    this._ShowFailurMsg(control.GetReturnString(m));

                return m;

        if (callServer==true)

            var serverResult=false;

            //調用伺服器端業務邏輯處理讀卡

            var s=this._PacketMessage(param,objMsg,"","",this._BeginNoTransCommand,"ReadCard");

            var serverMsg=this._SendDataToServer(s);

            serverResult=this._GetBoolean(serverMsg);

            if (serverResult==true)

                this._ShowSuccessMsg(this._GetReturnString(serverMsg));

                this._ShowFailurMsg(this._GetReturnString(serverMsg));

            s=this._PacketMessage(param,objMsg,"",serverResult,this._BeginNoTransCommand,"ReadCard");

        if (callbackFlag==true)

            var managerName=this.CallBack.toString();

            window[managerName].AjaxRequest(this._JSClientCommand+','+s);

        return objMsg;

    //<summary>挂号服務</summary>

    //<param name="param">參數約定: MedType|BillNO|InHosNo|SysDate|DiseaseNO|DiseaseName</param>

    //<param name="transType">挂号類别</param>

    //<returns>挂号産生的消息或者出錯資訊</returns>

    SYMedicare.prototype._RegistrationAll=function(param,callbackFlag,commandText,transType)

        var strParam=new String(param);

        var paramArray=strParam.split("|");

        if (paramArray.length<6)

            this._ShowFailurMsg("挂号參數傳遞錯誤");

            return;

        objMsg=this.ReadCard(7,false,false);

        if (control.GetBoolean(objMsg)==false)

            this._ShowFailurMsg(control.GetReturnString(objMsg));

            return objMsg;

        this._personAccountInfo=control.GetReturnString(objMsg);

        objMsg=control.Registration(this._personAccountInfo,transType,

                        paramArray[0],paramArray[1],paramArray[2],paramArray[3],

                        paramArray[4],this.UserName,paramArray[5]);

        if (control.GetBoolean(objMsg)==true)

            //調用伺服器端業務邏輯處理挂号

            var s=this._PacketMessage(param,objMsg,"","",this._BeginBeginTransCommand,commandText);

            //如果伺服器端傳回成功,則送出本地

                objMsg=control.CommitTrans();

                //判斷接口是否送出成功,不成功則調用伺服器端撤銷業務

                if (control.GetBoolean(objMsg)==false)

                    s=this._PacketMessage(param,objMsg,"",serverMsg,this._BeginRollTransCommand,commandText);

                    this._SendDataToServer(s);

                    this._ShowFailurMsg(control.GetReturnString(objMsg));

                    return objMsg;

                    //這裡應該再次調用伺服器端确認送出資料,暫時未做

                    this._ShowSuccessMsg(this._GetReturnString(serverMsg));

                    return serverMsg;

                control.RollbackTrans();

                return serverMsg;

            //回調

            if (callbackFlag==true)

                window[this.CallBack].AjaxRequest('Registration,'+paramArray[0]+','+objMsg+","+serverMsg);

    //<summary>挂号</summary>

    //<param name="callbackFlag">是否回調 true/false</param>

    SYMedicare.prototype.Registration=function(param,callbackFlag)

        return this._RegistrationAll(param,callbackFlag,"Registration","1");

    //<summary>退号</summary>

    SYMedicare.prototype.UnRegistration=function(param)

        return this._RegistrationAll(param,callbackFlag,"UnRegistration","-1");

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

/// <summary>

/// JS醫保類的消息解析器,目前版本隻解析出來可用的文本,将來可能将解析出來的結果自動建立對應的類

/// 目前隻有各傳回值都隻有一個參數,如果有多參數,則用特殊符号分隔

/// </summary>

public class MsgParser

    /// <summary>

    /// 消息的指令的事務類型

    /// </summary>

    public enum MsgTransactionType

        /// <summary>

        /// 無事務

        /// </summary>

        NoTransaction,

        /// 開起事務

        BeginTransaction,

        /// 送出事務

        CommitTransaction,

        /// 復原事務

        RollbackTransaction

    /// 對消息指令的封裝

    public class MsgCommand

        private string _commandName;

        private MsgTransactionType _transType;

        /// 構造函數

        /// <param name="commandName">指令字</param>

        /// <param name="isRollback">復原操作标志</param>

        public MsgCommand(string commandName, MsgTransactionType transType)

            _commandName = commandName;

            _transType = transType;

        /// 指令名

        public string CommandName

            get

                return _commandName;

        /// 事務類型

        public MsgTransactionType TransactionType

                return _transType;

    private XmlDocument _doc = null;

    private MsgCommand _command = null;

    private string _className = null;

    private string _clientParams;

    private string _clientReturn;

    private string _serverParams;

    private string _serverReturn;

    /// 構造函數

    /// <param name="msg">消息的XML文本</param>

    public MsgParser(string msg)

        _doc = new XmlDocument();

        _doc.LoadXml(msg);

        //解析出類名

        _className = _doc.SelectSingleNode("Message/Class").InnerText;

        //解析出指令字

        XmlNode cmd = _doc.SelectSingleNode("Message/Command");

        MsgTransactionType tran = MsgTransactionType.NoTransaction;

        if (cmd.Attributes["Transaction"] != null)

            switch (cmd.Attributes["Transaction"].Value)

                case "No":

                    tran = MsgTransactionType.NoTransaction;

                case "Begin":

                    tran = MsgTransactionType.BeginTransaction;

                case "Commit":

                    tran = MsgTransactionType.CommitTransaction;

                case "Rollback":

                    tran = MsgTransactionType.RollbackTransaction;

                default:

                    throw new Exception(cmd.Attributes["Transaction"].Value + ",未知的指令事務類型");

        _command = new MsgCommand(cmd.InnerText, tran);

        _clientParams = _doc.SelectSingleNode("Message/ClientParams").InnerText;

        _clientReturn = _doc.SelectSingleNode("Message/ClientReturn").InnerText;

        _serverParams = _doc.SelectSingleNode("Message/ServerParams").InnerText;

        _serverReturn = _doc.SelectSingleNode("Message/ServerReturn").InnerText;

    /// 消息指令

    public MsgCommand Command

        get

            return _command;

    /// JS的類名

    public string ClassName

            return _className;

    /// 用戶端的調用時的參數

    public string ClientParams

            return _clientParams;

    /// 用戶端的傳回值

    public string ClientReturn

            return _clientReturn;

    /// 要求伺服器完成業務時的資料

    public string ServerParams

            return _serverParams;

    /// 上一次伺服器傳回的資料

    public string ServerReturn

            return _serverReturn;

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

///簡單工廠:根據參數建立相應的醫保業務類,将建立業務類集的邏輯封裝在一點

public class MedicareFactory

    private MedicareFactory()

    public static readonly MedicareFactory Instance = new MedicareFactory();

    /// 根據消息建立相應的醫保類

    /// <param name="parser"></param>

    /// <returns></returns>

    public MedicareBase GetMedicare(MsgParser parser)

        ReadCardType type;

        switch (parser.ClassName)

            case "SYMedicare":

                type= ReadCardType.市醫保卡;

                break;

            default:

                throw new Exception(parser.ClassName + "是目前系統尚未實作的醫保類型");

        switch (type)

            case ReadCardType.市醫保卡:

                return new Medicare_Sy();

                throw new NotImplementedException("尚未實作的醫保類");

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

/// 醫保基類

public abstract class MedicareBase

    #region 聲明一些有關醫保的常量

    /// JS用戶端的指令字首

    public const string JSCLIENTCOMMAND = "JSClientCommand";

    /// 讀卡指令

    public const string READCARD = "ReadCard";

    /// 挂号指令

    public const string REGISTRATION = "Registration";

    #endregion

    /// 檢驗類名

    /// <param name="className"></param>

    protected abstract void ValidateClass(string className);

    /// 讀卡擷取醫保病人資訊

    /// <param name="msg"></param>

    public abstract MedicarePatientBase ReadCard(MsgParser msg);

    /// 擷取挂号的附加資訊

    public abstract RegistrationInfoBase GetRegistrationInfo(MsgParser msg);

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

/// 市醫保業務類

public class Medicare_Sy : MedicareBase

    private char[] spliter1 = new char[] { '~' };

    private char[] spliter2 = new char[] { '|' };

    /// 讀出卡中的患者資訊,在沈陽的JS用戶端将卡内餘額附在ClientParams消息中,并且

    /// 是兩組消息,第一組為患者為7的,第二組為3的,兩組用~分開

    public override MedicarePatientBase ReadCard(MsgParser msg)

        ValidateClass(msg.ClassName);

        MedicarePatientBase patient = null;

        string[] ps = msg.ClientReturn.Split(spliter1);

        if (ps.Length == 2)

            patient = new MedicarePatientBase();

            //取卡号

                //1.去掉第一個|前的字元

            ps[0]=ps[0].Substring(1);

            ps[0] = ps[0].Trim(spliter2);

                //2.分解各個參數

            string[] paras = ps[0].Split(spliter2);

                //3.取醫保卡号

            patient.CardNO = paras[9];

            //取餘額

            ps[1] = ps[1].Substring(1);

            string balance = ps[1].Trim(spliter2);

            patient.Balance = Convert.ToDecimal(balance);

        return patient;

    /// 擷取挂号用戶端調用的參數字元串

    /// <param name="mediCategory">醫療類别</param>

    /// <param name="userName">登入使用者名</param>

    /// <param name="diseaseNO">疾病編碼</param>

    /// <param name="diseaseName">疾病名稱</param>

    public string GetRegistrationParams(string mediCategory,string userName,

                                        string diseaseNO,string diseaseName)

        string p = "";

        Guid newRegId = Guid.NewGuid();

        DateTime regDateTime = DateTime.Now;

        string optDate = regDateTime.ToString("yyyyMMddhhmmss");

        //生成一個住院号

        string inHosNO = newRegId.ToString().Substring(0, 4) + optDate;

        string billNO = inHosNO;

        p = string.Format("{0}|{1}|{2}|{3}|{4}|{5}",

            mediCategory, billNO, inHosNO, optDate, diseaseNO, diseaseName);

        //将挂号将使用的Guid和挂号時間附到消息末尾,待消息傳回後再取出,用來進行伺服器端業務處理

        p += string.Format("|{0}|{1}", newRegId, regDateTime.ToString());

        return p;

    /// 市醫保的要求将挂号資訊附加到用戶端的調用參數後面

    /// <param name="info"></param>

    /// <param name="registrationID"></param>

    /// <param name="registrationTime"></param>

    private string AttachRegistrationInfo(string info,Guid registrationID,DateTime registrationTime)

        return info + string.Format("|{0}|{1}", registrationID, registrationTime);

    /// 市醫保的從ClientParams中取得RegistrationInfoBase資訊

    public override RegistrationInfoBase GetRegistrationInfo(MsgParser msg)

        string[] paras = msg.ClientParams.Split(spliter2);

        RegistrationInfoBase r = new RegistrationInfoBase();

        r.RegistrationID = new Guid(paras[paras.Length - 2]);

        r.RegistrationTime = DateTime.Parse(paras[paras.Length - 1]);

        return r;

    protected override void ValidateClass(string className)

        if (className.ToLower() != "SYMedicare".ToLower())

            throw new Exception("醫保類消息檢驗錯誤");

因為使用的是ASP.Net2.0的異步回調模型,是以要實作ICallbackEventHandler接口.

1.組織功能調用:通過觸發用戶端的頁面上的送出事件(例如控鈕觸發),使伺服器端接收到請求,然後需要組織JS業務類方法中所需的參數,這裡需要Ajax技術的支援,以使頁面不被重新整理.

某按鈕中的代碼片斷如下:

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

                //醫療類别

                string YllbCode = iMdt.GetEntityByPrimaryKey(new Guid(drpYLLB.Value)).NormalCode;

                McsillnessCodeEntity entity = iMc.GetEntityByPrimaryKey(new Guid(drpYBTB.Value));

                string p=new Medicare_Sy().GetRegistrationParams(YllbCode,CurrentUser.UserName,

                    entity.NormalCode,entity.CnName);

                string str = string.Format("Registration('{0}',true,'{1}')",

                                    p,this.Label6.ClientID);

                new JsHelper(this).RegisterClientScriptBlock(str);

這裡需要根據頁面上的選擇來确定使用哪個醫保的伺服器端業務類,通過傳遞必要的參數後,由伺服器端業務組織成JS業務端所需要的參數,最後動态注冊JS腳本,使其傳回時被執行,JS業務端執行時可以中斷處理,然後産生一個對伺服器端業務的請求.

2.接收JS業務端産生的請求:JS業務類處理完成後需要通知伺服器進行處理,伺服器可以在ICallbackEventHandler的接口方法的RaiseCallbackEvent(string eventArgument)會被調用.在這裡就可以通過多态來屏蔽細節了.示例代碼如下:

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

    #region ICallbackEventHandler 成員

    private string _returnMsg = "";

    public string GetCallbackResult()

        return _returnMsg;

    public void RaiseCallbackEvent(string eventArgument)

        try

            MsgParser msg = new MsgParser(eventArgument);

            switch (msg.Command.CommandName)

                case MedicareBase.READCARD:

                    _returnMsg = "0";

                case MedicareBase.REGISTRATION:

                    if (msg.Command.TransactionType==MsgParser.MsgTransactionType.BeginTransaction)

                    {

                        string s = "";

                        ReadCardType cardType = JSMessageHelper.GetReadCardType(msg.ClassName);

                        //取時挂号的Guid和挂号時間

                        string[] p = msg.ClientParams.Split(new char[] { '|' });

                        DateTime regTime = DateTime.Parse(p[p.Length - 1]);

                        Guid regID = new Guid(p[p.Length - 2]);

                        if (Registration(out s, cardType, regID, regTime))

                        {

                            _returnMsg = JSMessageHelper.BuildSuccessMsg(s);

                        }

                        else

                            _returnMsg = JSMessageHelper.BuildFailurMsg(s);

                    }

                    _returnMsg = JSMessageHelper.BuildFailurMsg("未知的業務類型");

        catch (Exception ex)

            _returnMsg = JSMessageHelper.BuildFailurMsg(ex.Message);

其中_returnMsg是頁面級的變量,是供異步傳回時使用的.這個回調完成後,JS業務端得到傳回值用來判斷是否可以繼續處理,如果出錯則可以進行對用戶端的復原,如果成功則繼續處理,處理完畢後需要調用用戶端進行界面上的重新整理.

3.接收JS業務端請求,完成界面的重新整理:這裡需要Ajax的配合,目前我使用的是Rad的AjaxManager,它提供一個回調的方法,我可以在伺服器端接收到這個請求,示例代碼如下:

JS腳本與伺服器互動完成業務處理系統實作
JS腳本與伺服器互動完成業務處理系統實作

    protected void DawAjaxManager1_AjaxRequest(object sender, AjaxRequestEventArgs e)

            if (e.Argument.StartsWith(MedicareBase.JSCLIENTCOMMAND))

                //得到消息體

                string[] ps = e.Argument.Split(new char[] { ',' });

                MsgParser parser = new MsgParser(ps[1]);

                MedicareBase medicare = MedicareFactory.Instance.GetMedicare(parser);

                switch (parser.Command.CommandName)

                    case MedicareBase.READCARD:

                        MedicarePatientBase patient = medicare.ReadCard(parser);

                        txtCarNum.Text = patient.CardNO;

                        DawButton1_Click(null, null);

                        txtYe.Text = patient.Balance.ToString();

                        break;

                    case MedicareBase.REGISTRATION:

                        ShowSummary();

總結

通過使用這種模型,将業務處理邏輯配置設定到JS的業務類和伺服器端的業務類,減少了頁面對于業務處理的代碼,達到了一定的業務抽象,便于擴充其它的醫保類型.同時,對于JS的異步調用也解決了”中斷函數->同步請求服務->根據傳回值再繼續處理”的技術問題,簡化了與由于B/S處理這種與本地互動的而産生的複雜過程.

一點說明:為什麼在标題中要嵌入英文?原因是為了能夠讓國外的網友能查詢到這篇文章。平常在Google上查資料的時候,經常參考國外網友的部落格,幫助我解決了很多問題,是以我也想讓他們能夠參考我寫的内容。當然文中我不可能全部譯為英文,是以我盡量把代碼粘全,靠代碼說話吧。

繼續閱讀