于浩佳++陳波
摘要:為方便患者掛號就醫(yī)并緩解醫(yī)院排隊掛號的擁擠問題,設(shè)計并實現(xiàn)了基于微信平臺的醫(yī)院信息動態(tài)查詢與掛號系統(tǒng)。介紹了基于微信平臺的網(wǎng)頁開發(fā),基于PHP實現(xiàn)的動態(tài)網(wǎng)頁生成和網(wǎng)頁信息抓取和以Web代理為核心的網(wǎng)頁移植技術(shù)。建立了一個微信公眾號開放給患者進行方便的查詢和掛號等操作,同時考慮到移動端設(shè)備的多樣性將系統(tǒng)設(shè)計成響應(yīng)式布局。該服務(wù)系統(tǒng)具有靈活性高、適應(yīng)性強、時效性強等特點。
關(guān)鍵詞:微信平臺;Web代理;掛號預(yù)約;動態(tài)網(wǎng)頁
中圖分類號:TP319 文獻(xiàn)標(biāo)識碼:A DOI: 10.3969/j.issn.1003-6970.2015.10.025
引言
手機即時通訊應(yīng)用“微信”給我們帶來了一種全新的“微生活”方式。2012年8月推出的微信公眾平臺,由于提供多媒體信息大規(guī)模推送、定向推送(可按性別、地區(qū)、分組等指標(biāo)定向推送)、一對一互動、多樣化開發(fā)和智能回復(fù)等功能,成為繼微博之后又一重要的運營平臺。當(dāng)前,微信公眾平臺有3萬認(rèn)證賬號,其中超過七成的賬號為企業(yè)賬號,主要服務(wù)于媒體、營銷、客服、公共服務(wù)等應(yīng)用。目前國內(nèi)也有許多學(xué)者和企業(yè)在進行微信方面的研究和開發(fā)。
隨著移動設(shè)備的普及,近年來移動醫(yī)療產(chǎn)業(yè)也日漸發(fā)達(dá),市場規(guī)模已從2012年的不到2億元發(fā)展到了如今的28億元,甚至有望在2017年突破125億元,移動醫(yī)療具有廣闊的發(fā)展前景?;谖⑿艑崿F(xiàn)移動醫(yī)療服務(wù),能夠拓展微信平臺應(yīng)用領(lǐng)域,使其服務(wù)大眾、便利大眾。
南京12320衛(wèi)生網(wǎng)2012年開始提供網(wǎng)上在線預(yù)約掛號服務(wù),本文工作是基于微信官方為公眾號提供的相關(guān)接口和服務(wù),設(shè)計與開發(fā)面向移動端用戶的醫(yī)院預(yù)約掛號系統(tǒng),使用戶可以在移動端通過關(guān)注微信號就能便捷的實現(xiàn)預(yù)約掛號等操作,并通過微信公眾號的群發(fā)功能為用戶推送最新的可預(yù)約專家信息以及第一手健康咨詢。
接下來本文首先介紹預(yù)約掛號服務(wù)與功能設(shè)計,然后再詳細(xì)介紹各個功能模塊實現(xiàn)的技術(shù)細(xì)節(jié)。
1 系統(tǒng)設(shè)計
我們首先進行了調(diào)研分析,確定用戶掛號流程。南京衛(wèi)生網(wǎng)站www.nj12320.org提供的掛號方式比較靈活,用戶可以從網(wǎng)站上醫(yī)院、科室、醫(yī)生等任一瀏覽位置開始選擇掛號。但這種方式并不便捷,而且也不符合移動端用戶的操作習(xí)慣,因此我們決定采用一種適用于移動端的固定掛號流程:讓用戶首先選擇醫(yī)院,然后選擇該醫(yī)院的具體科室,再選擇醫(yī)生,最后再選擇看病具體時間,用戶在最終決定選擇預(yù)約掛號后才需要登錄完成整個預(yù)約掛號操作。
由此確定系統(tǒng)功能模塊如圖1所示。在公眾號中有兩個菜單選項,預(yù)約掛號和用戶管理。在預(yù)約掛號功能下,共有6個核心功能模塊。索引模塊提供不同頁面的索引,作為主頁;選擇醫(yī)院、科室、醫(yī)生模塊分別是進行對應(yīng)項的選擇,全部選擇完畢后進入登錄模塊進行登錄,登錄成功后確認(rèn)預(yù)約,完成全部操作。在用戶管理菜單中,可以查詢預(yù)約結(jié)果,或者取消已完成的預(yù)約,其中具體取消規(guī)則要按照衛(wèi)生網(wǎng)相關(guān)協(xié)議進行。
如圖2所示,預(yù)約掛號服務(wù)系統(tǒng)由4部分構(gòu)成,分別是用戶微信客戶端、微信公眾平臺服務(wù)器、南京衛(wèi)生網(wǎng)服務(wù)器和預(yù)約掛號服務(wù)系統(tǒng)所在服務(wù)器。4部分之間按照箭頭關(guān)系分別進行消息交互。用戶發(fā)出的信息請求部分交由微信官方服務(wù)器處理,例如用戶發(fā)送的文字信息會通過微信官方服務(wù)器傳輸,經(jīng)過上傳部署好的本服務(wù)系統(tǒng)代碼進行處理進行相應(yīng)的自動回復(fù);還有部分信息直接交由本預(yù)約服務(wù)系統(tǒng)處理,例如用戶選擇預(yù)約服務(wù)菜單發(fā)出的請求。本服務(wù)系統(tǒng)提供給用戶的所有信息都來自于南京12320衛(wèi)生網(wǎng),從而確保用戶獲取信息的實時性。另外,整個預(yù)約掛號服務(wù)過程中的消息交互對用戶都是透明的。
本服務(wù)系統(tǒng)各模塊之間的工作流程如圖3所示。用戶通過點擊菜單向微信服務(wù)器發(fā)起訪問請求,微信服務(wù)器把請求轉(zhuǎn)交給系統(tǒng)服務(wù)器,系統(tǒng)服務(wù)器把請求網(wǎng)頁返回給用戶,用戶在網(wǎng)頁上進行相關(guān)查詢操作,系統(tǒng)服務(wù)器則動態(tài)響應(yīng)這些操作,從衛(wèi)生網(wǎng)服務(wù)器請求用戶所需信息并予以返回。最后,用戶確認(rèn)預(yù)約請求,衛(wèi)生網(wǎng)服務(wù)器接受到請求后如預(yù)約成功,則直接向用戶手機發(fā)送短信進行通知。
2 系統(tǒng)開發(fā)
2.1 系統(tǒng)開發(fā)環(huán)境
(1)硬件環(huán)境
服務(wù)器端:英特爾酷睿I5處理器,WIN7專業(yè)版64位,4G內(nèi)存,500G硬盤。
客戶端設(shè)備:裝有微信5.0以上版本的智能手機。
(2)軟件環(huán)境
服務(wù)器端:裝有IIS的Windows操作系統(tǒng);裝有PHP5。
客戶端:裝有微信5.0以上版本。
2.2 微信公眾平臺對用戶文字消息的處理
為了把掛號系統(tǒng)移植到微信平臺,首先要注冊一個微信公眾號。微信提供了服務(wù)號、訂閱號、企業(yè)號和測試號給用戶使用,其中測試號可以免費試用所有的接口和功能,本文為了敘述清晰和開發(fā)過程重現(xiàn),選擇注冊微信測試號。注冊完成后會需要填寫URL和Token,驗證開發(fā)者是否擁有自己的服務(wù)器資源,并進行Token的驗證,此處填寫的URL需要正確響應(yīng)微信發(fā)送的Token驗證。具體操作是從微信官方網(wǎng)站下載wx_sample.php文件,用Dreamweaver打開編輯,將代碼中的define(”TOKEN”,”weixin”);中的”weixm”改成白定義Token值,要注意在“修改”一“頁面設(shè)置”中將代碼的編碼改為UTF-8,勾選“不包括BOM”,否則Token無法驗證。代碼修改完成后將其部署到SAE,把代碼部署位置的路徑填寫到URL處,并點擊進行驗證。
完成上述操作后,首先編寫代碼實現(xiàn)基本文字消息的處理。接口配置已經(jīng)完成,對剛才使用的驗證代碼進行修改,來實現(xiàn)一些簡單的消息回復(fù)功能。
將“$wechatObj->valid();改成$wechatObj->responseMsg();”然后對responseMsg()函數(shù)進行修改。在“$msgType=”text”;”后添加一個switch語句,通過對$keyword的匹配來進行關(guān)鍵詞回復(fù)。例如:
case”你好”;
$contentStr=”你好!”;
break:
除此之外,微信公眾號還能自動回復(fù)類型豐富的消息類型,例如:圖片消息、語音消息、視頻消息、音樂消息、圖文消息等等。微信的官方文檔為不同的消息類型提供了對應(yīng)格式,按照官方格式在代碼中修改$textTpl變量即可回復(fù)對應(yīng)消息類型。
2.3 用戶端菜單設(shè)計與實現(xiàn)
除了各種類型消息的處理,本系統(tǒng)要求公眾號必須擁有白定義菜單來為用戶提供進入系統(tǒng)的方便的入口。
根據(jù)微信官方提供的文檔,目前自定義菜單最多包括三個一級菜單,每個一級菜單最多包含5個二級菜單。一級菜單最多4個漢字,二級菜單最多7個漢字,多出部分將以”……”代替。創(chuàng)建白定義菜單后,微信客戶端將延遲一段時間后才展現(xiàn)出來。用戶創(chuàng)建自定義菜單時,用注冊帳號時系統(tǒng)分配的appID和appsecret同微信服務(wù)器以POST方式發(fā)送一個HTTP請求,從而獲得一個ACCESS—TOKEN值,獲得這個值后就能根據(jù)官方文檔提供的創(chuàng)建自定義菜單的格式創(chuàng)建屬于自己的白定義菜單了。發(fā)起第一次請求的代碼如下:
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false):
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST.false):
curl_setopt($ch,CURLOPT_RETURNTRANSFER.1):
$a=curl_exec($ch);
$strjson=json_decode($a);
$token=$strjson->access_token;
至此完成一次POST請求并保存獲得的ACCESS_TOKEN值。完成驗證后,就可以創(chuàng)建自己的自定義菜單了。創(chuàng)建白定義菜單的代碼格式由微信官方給出,此處不再贅述,但要注意在用PHP寫這段代碼時要在引號前加轉(zhuǎn)義符,例如\”button\”,否則在發(fā)送這個創(chuàng)建請求時服務(wù)器將不會識別,并返回一個錯誤碼。還要注意編碼必須為UTF-8,否則也會報錯。在寫完自定義菜單的代碼后,再次調(diào)用cURL函數(shù)發(fā)送一次POST請求,代碼如下:
$url=”https://api.weixin.qq.com/cgi-bin/menu/create?access_token={$token)”;
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$post):
curl_exec($ch);
curl_close($ch);
當(dāng)返回{”errcode”:0,”errmsg”:”ok”}時候,證明請求成功,在等待一段時間后即可在微信客戶端看到白定義菜單的效果。如圖4所示。
2.4 系統(tǒng)各主要模塊實現(xiàn)
(1)索引模塊
索引模塊就是默認(rèn)主頁index,本系統(tǒng)的主頁是一個靜態(tài)網(wǎng)頁,提供進入各個模塊的鏈接,是一個導(dǎo)航頁面。因為這是系統(tǒng)開發(fā)過程中的第一個網(wǎng)頁,因此要先進行網(wǎng)頁設(shè)計,把樣式表CSS做好,以實現(xiàn)響應(yīng)式布局。此處的網(wǎng)頁設(shè)計是下載了開源的CSS框架進行修改,之后的各個模塊都將采用這個框架。
其中,各個鏈接都設(shè)置成標(biāo)題hl,并居中:
(2)醫(yī)院選擇模塊
在前期的調(diào)研分析中發(fā)現(xiàn),南京衛(wèi)生網(wǎng)提供的信息是經(jīng)常更新的,并且未來可能會有更多的醫(yī)院加入這個系統(tǒng),因此這個模塊要設(shè)計成動態(tài)更新的網(wǎng)頁。用戶每次打開網(wǎng)頁前,后臺運行一段程序,向南京12320網(wǎng)發(fā)起一次http請求,并通過正則表達(dá)式匹配獲得當(dāng)前所有醫(yī)院信息。獲得這些信息后再生成網(wǎng)頁展示給用戶,如此避免用戶選擇一個無法掛號的醫(yī)院或用戶找不到想要去的醫(yī)院。
獲得醫(yī)院信息的重點在于正則表達(dá)式的匹配,此處為:
preg_match_all(/HospitalDesc\(\(\d+)\\)\”\>(\W+)\
其中(\d+)是要匹配的醫(yī)院名,(\W+)是要匹配的醫(yī)院id,這個id是下一步選擇科室模塊要發(fā)起http請求的目的url地址的重要元素。將醫(yī)院名和醫(yī)院id分別保存在兩個數(shù)組中,然后將醫(yī)院名展示在網(wǎng)頁上,醫(yī)院id則作為option標(biāo)簽的值保留下來。
在具體實現(xiàn)時,還發(fā)現(xiàn)了一個問題。在獲得醫(yī)院信息的url地址中,一個地址只顯示10個醫(yī)院的信息,要更多的醫(yī)院信息則需要訪問更多的url地址。如果一個個的枚舉顯然效率太過低下。在觀察url地址后發(fā)現(xiàn),url地址中有一個“start={}”,只要修改大括號中的數(shù)字就能實現(xiàn)對不同地址的訪問并得到所有醫(yī)院信息。由于具體醫(yī)院數(shù)量是未知的,因此調(diào)用foreach函數(shù)遍歷一下即可,主要代碼如下:
for($n=0;$n<=50;$n+=10){
$url=”http://www.nj12320.org/weixin/wx/qdefault.php?__op=oho_online&i_f_djklfe_d=s6057240&show_num=lO&start= {$n)”;
preg_match_all(/HospitalDesc\(\(\d+)\\)\”\>(\W+)\
foreach($matches as $hid){$a口=$hid;)
foreach($matches as $hname){$b口=$11name;}}
for($i_0;$i print””;}/*用于動態(tài)生成網(wǎng)頁信息*/ (3)科室選擇模塊 科室選擇模塊中,若把所有可掛號的科室全部顯示出來顯然太多了,不利于用戶選擇,因此本系統(tǒng)的科室選擇模塊做在醫(yī)院選擇模塊之后,在用戶選好醫(yī)院后,再根據(jù)這個醫(yī)院的id向?qū)?yīng)url地址發(fā)送一次http請求,獲得當(dāng)前該醫(yī)院可掛號科室的信息并展示給用戶。 具體代碼實現(xiàn)和醫(yī)院選擇相似,此處的正則表達(dá)式為: preg_match_all(/onclick\=\“keshi\((\d+),\(\W+)\\)\”\>/i,$f,$matches); 同樣的既要保存科室名顯示,也要保存科室id做下一步工作。 $hos=$_POST[”hospital”]; $ke=$_POST[”zjzk”]; $url=”http://www.nj12320.org/weixin/wx/qdefault.php?mod=yy_{$ke}&id={$hos}&hospital=”; $f=fle_get_contents($url); preg_match_all(/onclick\=\”keshi\((\d+),\(\W+)\\)\”\>/i,$f,$matches); foreach($matches as $keid){$a口=$keid;) foreach($matches as $kename){$b口=$kename;, for( $i=0; $i (4)醫(yī)生選擇模塊 此模塊僅展示醫(yī)生的名字,并不顯示具體就診時間和掛號費,主要是為了給尋找特定專家的用戶提供服務(wù),對普通用戶的意義并不太大。具體代碼實現(xiàn)和醫(yī)院選擇相似。此處匹配所用的正則表達(dá)式為: preg_match_all(/data-docid\=\”(\d+)\”\sdata-docname\=\”(\W+)\”/i,$f,$matches); (5)登錄模塊 南京12320衛(wèi)生網(wǎng)的登錄功能是通過javascript函數(shù)實現(xiàn)的,由于本系統(tǒng)的代碼不是部署在其服務(wù)器上,因此如果也采用javascript函數(shù)進行登錄會出現(xiàn)跨域訪問的問題,根據(jù)互聯(lián)網(wǎng)相關(guān)安全協(xié)議,無法發(fā)出登錄的http請求,所以本系統(tǒng)采用的方法是用PHP重寫登錄函數(shù),進行第三方登。 首先用Wireshark抓取了官方網(wǎng)站的登錄報文,如圖6所示??梢钥吹酱颂幍膸ぬ?、密碼和驗證碼是明文傳輸?shù)?。再記錄下http數(shù)據(jù)包的其他頭部信息,用PHP構(gòu)造一個完全相同的數(shù)據(jù)包。 $data=”username=$u&password=$p&verifyCode=$v”;/*用戶名,密碼,驗證碼*/ $time=number_format(microtime(true),3,”,”);/*時間戳*/ $url=”http://www.nj12320.org/njres/indexjson/login.do?timestamp=$time&ajax=true”; curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_HTTPHEADER,array(/*頭部信息*/). curl_setopt($ch,CURLOPT_POSTFIELDS,$data): curl_setopt($ch,CURLOPT_COOKIEFILE,$cookie_jar); 代碼中的$cookiejar是在驗證碼獲取時服務(wù)器返回的一個cookie值,這個cookie必須保存下來,然后在向服務(wù)器遞交用戶名密碼和驗證碼時把這個cookie添加到http請求的頭部才能成功登錄。因為把驗證碼從服務(wù)器下載后展示給用戶,而直接在驗證碼所在的img處添加url地址無法保存cookie,為了解決這個問題,本系統(tǒng)采用的方法是先發(fā)起請求將驗證碼和cookie保存在系統(tǒng)服務(wù)器上,然后在顯示驗證碼時將img標(biāo)簽的src屬性填寫成本地地址。 $cookiejar=tempnam(”./temp”,”cookie”);/*創(chuàng)建臨時文件,保存cookie*/ $f=fopen(”yzm.jpg”,”w”);/*創(chuàng)建圖片文件,之后把驗證碼圖片以數(shù)據(jù)流寫入其中*/ $url=http://www.nj12320.org/njres/authhng.do:
curl_setopt($ch,CURLOPT_FILE, $f);/*在選項中加入CURLOPT_FILE來將驗證碼寫入*/
(6)確認(rèn)預(yù)約模塊
預(yù)約模塊是本系統(tǒng)的重中之重。首先,預(yù)約界面的顯示和前面幾個模塊一樣是實時更新、動態(tài)生成的,此處用了三個正則表達(dá)式:
preg_match_all(/javascript\:toconfirm\((\d+)\)\”i,$f,$matches0);
preg_match_all(/出診時間:([\W\W]*?)/,$f,$matchesl):
preg_match_all(/掛號費:([\w\W]*?)n>/, $f,$matches2):
分別用于匹配本次預(yù)約的代號,出診時間和掛號費。如果沒有匹配到任何信息,則輸出“對不起,暫無可預(yù)約時間。”
在用戶點擊確認(rèn)預(yù)約后,系統(tǒng)跳轉(zhuǎn)到登錄界面進行登錄,登錄成功后系統(tǒng)會自動開始預(yù)約操作,若掛號成功則用戶會在一段時間內(nèi)收到12320的短信通知。如果登錄失敗,則提示失敗信息并3秒后返回登錄界面。
此處實現(xiàn)登錄和實現(xiàn)預(yù)約的程序分屬不同文件,而在這兩個功能之間需要進行cookie的傳遞,故涉及到了PHP中超級全局變量的問題。本系統(tǒng)采用session來進行文件間的變量傳遞。首先,在登錄處用session—start函數(shù)初始化session,然后創(chuàng)建一個臨時文件用于保存之后產(chǎn)生的cookie,把指向這個文件的變量$cookiejar保存到session中,最后在完成預(yù)約的文件中用session start初始化session后就能訪問這個跨文件變量了。
由于是第三方訪問,因此稍有不慎就會被官方服務(wù)器判定為不安全行為,所以必須把http請求模擬的和官方自己發(fā)送的請求一模一樣才行。實現(xiàn)Web代理的同時存在著一定的安全隱患,但由于本系統(tǒng)無法獲得南京衛(wèi)生網(wǎng)的服務(wù)器資源,且僅作學(xué)習(xí)研究用,因此這些安全問題暫不予考慮。
3 系統(tǒng)運行實例
圖7a是系統(tǒng)登錄界面,對于沒有帳號的用戶還可點擊注冊新賬號進入帳號注冊界面,如圖7b所示。這兩個界面運用了大量的htm15+css3的新特性,力求做到簡潔美觀。
圖8是醫(yī)院選擇界面,此處要同時選擇醫(yī)院和專家/??坪笤龠M入下一步,若不選擇專家/專科,系統(tǒng)將自動默認(rèn)選擇專家號,因為絕大多數(shù)用戶都會選擇專家號。選好醫(yī)院并點擊選擇科室后,系統(tǒng)會提交此表單。
在選擇醫(yī)院后,還要分別選擇科室和醫(yī)生。選擇科室列表中出現(xiàn)的信息是根據(jù)用戶選擇的醫(yī)院來顯示的,同樣,醫(yī)生信息也是根據(jù)科室來顯示的,這樣就避免了用戶接受不需要的冗余信息。實際效果如圖9a和9b所示。
在所有選項都選擇完成后,會顯示可預(yù)約的醫(yī)生信息,包括可預(yù)約時間和掛號費。(其余信息都是用戶自己選擇的,因此不再顯示)如圖10所示。如果當(dāng)前用戶選擇的醫(yī)生沒有可預(yù)約的時間,則會顯示“對不起,暫無可預(yù)約時間”。用戶需返回重新選擇別的醫(yī)生。
4 結(jié)論
本文設(shè)計實現(xiàn)的基于微信平臺的醫(yī)院預(yù)約掛號系統(tǒng)能夠為用戶就醫(yī)帶來極大的方便。首先基于微信平臺,用戶不需要再去下載額外的APP占用手機資源;其次,通過手機預(yù)約,可以做到真正的隨時、隨地;最后,微信發(fā)展速度快,擴展性強,方便未來隨時升級此系統(tǒng)。
本系統(tǒng)還可以擴展許多功能,例如:自動識別驗證碼,在線支付掛號費,預(yù)約時間鬧鐘提醒,醫(yī)院查詢,醫(yī)生查詢和電子病歷等,可以此為用戶提供一整套的移動端醫(yī)療服務(wù)。