朱云杰
(中博信息技術研究院有限公司,江蘇 南京 210012)
電信寬帶和ETV業務已經覆蓋了大量的酒店,為了更好的服務廣大酒店客戶,提高客戶對電信業務粘合度,基于天翼云推出了一款SaaS產品(翼云居智慧酒店管理系統,簡稱翼云居系統)。目前很多酒店使用單獨的門鎖系統,公安登記系統,餐飲系統,充值卡系統,或者簡易單機版的酒店管理系統。用戶辦理不同業務需要切換到不同的系統,使用極不便。而翼云居系統將這些功能單一的系統作為子模塊集成到一個系統,為用戶提供統一服務,方便用戶操作,同時新增了OTA直連、包天影視房、翼支付聚合支付等新業務。翼云居是基于互聯網上的云服務,酒店客戶端采用Web瀏覽器,瀏覽器頁面無法直接使用客戶端本地資源。
如:門鎖制卡器、身份證讀卡器、充值卡讀寫器和打印機。通常的方法是采用ActiveX控件,但只能用在IE瀏覽器,兼容性較差,雖然瀏覽器也提供了打印功能,但是無法控制打印參數,如:行間距、頁邊距、字體等,Javascript代碼的瀏覽器兼容性也差。綜合考慮,翼云居采用在酒店客戶端安裝翼云居前置服務解決設備接入問題。
酒店客戶端使用的全部是WINDOWS系統,有早期的32位XP系統,也有當前流行的64位WIN10系統,考慮操作系統兼容性、部署方便性,前置服務采用32位C++/MFC庫開發[1]。
前置服務分為三個主要部分:Http接口服務、主程序和設備服務模塊,設備服務涵蓋門卡操作服務、身份證閱讀服務、充值卡操作服務和打印服務等,如圖1所示。

圖1 翼云居前置服務主體結構
前置實現Http接口服務,翼云居Web客戶端通過調用前置服務的Http接口使用酒店客戶端硬件資源。翼云居Web客戶端調用前置服務的URL格式為:http://localhost:port/服務類型編碼+操作參數(Json格式)。Http接口提供了Get、Post兩種方法,Post用于參數較長的調用。返回結果的基本數據有:錯誤代碼和結果內容,返回數據UTF-8編碼轉換后發送到瀏覽器頁面[2]。
服務類型采用4位數字編碼,定義前置服務各種接口功能,如“0000”標識讀取身份證信息、“9999” 標識讀取客戶端機器碼信息、“0200”標識充值卡讀寫服務、“1002”標識必達門鎖免密型制卡服務等。
前置主程序基于MFC對話框設計,程序啟動時讀取前置配置文件,獲取門鎖接口信息、身份證讀卡器接口信息、菜單配置信息、充值卡接口信息和Http服務端口等。創建Http服務線程,根據前置配置的設備接口信息加載設備接口庫、創建設備接口服務對象[3]。
主程序采用MFC基于對話框的托盤程序,啟動后顯示主對話框,點擊關閉自動隱藏到任務欄,右擊任務欄圖標彈出菜單,點擊“退出”并確認后退出前置服務程序,防止用戶誤操作關閉前置服務,主程序生命周期如圖2。

圖2 前置服務主程序
(1)程序啟動后讀取前置配置文件,配置數據有:Http服務端口,充值卡讀寫器配置參數(設備類型、端口、起始讀寫扇區),身份證閱讀器配置參數(設備類型、端口),門鎖讀寫器配置參數(門鎖類型、門鎖接口配置文件名、加密數據),菜單配置參數。門鎖配置參數中涉及到酒店密碼、接口授權碼等敏感數據必須進行加密。
(2)判斷Http服務端口是否可用,若不可用,前置服務啟動報錯后退出。
(3)前置數據庫采用sqlite內存數據庫,數據庫表有:房間門鎖信息、房卡操作日志、打印模板、打印任務,數據庫初始化主要是清空房間門鎖信息、房卡操作日志和打印任務,數據庫預置了打印模板。
(4)準備設備接口文件,前置服務涉及的設備類型數以百計,不同類型的設備接口文件名會重復,因此將不同類型的設備接口文件放在相應的文件目錄下。前置服務配置文件中配置當前使用的設備類型,前置服務啟動時,通過設備類型參數,將對應設備類型目錄下的接口文件拷貝到主程序運行目錄。
(5)創建設備接口服務對象,對于門鎖制卡器涉及不同的接口類型各不相同,因此我們設計了門鎖虛基類,類中定義純虛函數作為門卡操作的基本方法,每種門鎖接口服務對象繼承該虛基類,采用該門鎖的接口實現父類中定義的虛函數。定義門鎖基類對象全局指針變量,主程序啟動時根據前置配置的門鎖類型創建對應的接口服務類對象,并將對象地址賦給門鎖基類全局對象指針變量。
(6)啟動Http服務線程,并在前置配置的Http端口上進行偵聽來自翼云居Web客戶端的設備連接請求。
(7)創建并顯示主對話框,用以測試身份證閱讀器。
(8)主對話框退出時,主程序釋放相關資源,如:關閉Http服務線程、卸載接口庫等,最后退出前置服務主程序。
前置服務窗口基于MFC對話框設計,功能界面有:主界面(測試身份證閱讀)、門鎖配置、預制卡、打印模板配置和打印參數配置。主界面生命周期即為前置服務生命周期,點擊對話框的“關閉”隱藏界面,雙擊托盤圖標顯示界面。其他界面通過右擊托盤圖標彈出的菜單即時創建模態對話框,點擊對話框的“退出”或“關閉”即銷毀對話框。托盤上彈出菜單項除“退出”,其他菜單項通過前置配置文件設置是否可選[3]。
主界面通過點擊“讀身份證”按鈕,調用身份證閱讀器接口服務對象的基本方法,完成設備連接、尋卡、讀卡、設備斷開,并在界面上顯示操作結果:身份證基本信息(身份證號、姓名、性別、出生日期、地址、簽發機關、有效期起始和有效期終止)和照片。
門鎖配置界面實現對門鎖接口配置文件和前置數據庫的讀寫。根據前置服務配置中的門鎖接口文件名打開門鎖接口配置文件,配置門鎖接口參數,如:串口號、酒店密碼、接口授權碼等,點擊“保存”將數據寫到配置文件并加密敏感數據,并斷開制卡器,重新使用新的接口參數初始化門鎖接口。房間門鎖信息配置包括:房號、門鎖號、房間名、樓號、層號、區域號,采用全覆蓋保存,批量保存前先清空數據庫房間門鎖信息。
預制卡界面實現在客人入住前根據客人預訂入住時間進行制卡,主要服務于團隊客人,當入住人數較多時,方便快捷完成入住服務。
打印模板配置界面用于系統管理員創建、修改和刪除打印模板,并測試打印效果。在該窗口內對打印模板參數的配置實現打印模板的管理,打印模板參數有:模板名稱、打印機名稱、打印紙張定義、打印字體定義、打印分辨率、打印方向、打印邊距、行間距、列間距、打印樣式標簽定義。輸入打印測試樣本數據測試打印效果。
打印參數配置界面用于用戶對打印模板參數進行調整,優化打印效果,可調整的打印模板參數有:打印頁邊距、行間距、列間距、字體大小、字體樣式,并提供打印測試[4-5]。
該接口服務接收來自同一終端上的翼云居Web客戶端的請求,因此接口服務設計為單個Web客戶端連接,并未設計成多客戶端連接。該接口服務為單獨的線程,在前置服務主程序啟動時創建,在主界面退出時,關閉服務Socket并恢復事件狀態,觸發線程退出。接口服務設計流程圖(如圖3)。

圖3 Http 接口服務流程圖
(1)線程啟動時創建事件,初始狀態為未觸發,線程等待該事件變為觸發狀態后,結束服務線程。
(2)創建Http接口服務Socket,并綁定到服務端口,在該端口上偵聽來自翼云居Web客戶端連接請求。當Socket被關閉,接收客戶端請求異常,結束服務線程。
(3)接收該連接上的請求數據,采用HTTP協議格式解析請求數據,URL解碼后解析URL參數得到請求的服務類型和請求參數。
(4)根據解析出的服務類型調用相應的設備接口服務對象的方法,并傳遞請求參數,操作酒店終端上的硬件設備,得到操作結果,使用HTTP協議格式封裝后返回翼云居Web客戶端。
(5)斷開客戶端Socket連接,繼續偵聽。
接入翼云居系統的酒店設備主要有:門鎖、身份證、充值卡和打印機。除門鎖設備外,公安部對身份證閱讀器制定了標準接口,基于打印機驅動使用MFC打印API,充值卡設備接口基本一致,而門鎖設備種類繁多,接口方式眾多,因此設計了門鎖虛基類,定義了基本的純虛函數,各種門鎖接口服務類均繼承該門鎖虛基類,實現基類中的純虛函數。
定義的純虛函數主要有:讀取房間門鎖信息、門鎖接口初始化、釋放接口資源、離線制卡、在線制卡、贈卡、讀卡、清卡。
(1)接口初始化,讀取門鎖接口配置文件相關參數。接口方式若基于DLL,導入門鎖接口動態庫文件,獲取接口函數地址;若基于COM,注冊COM組件,創建組件實例;若基于SOCKET,創建SOCKET客戶端;若基于SOAP,啟動網絡鎖回調SOAP服務,向網絡鎖平臺注冊應答回調地址;若基于HTTP,登錄門鎖網絡平臺獲取令牌TOKEN,作為調用其他接口的參數,放在COOKIE或者URL參數中。
(2)離線制卡,是指通過門鎖制卡器給房卡寫數據,制卡的數據包括:門鎖號、區域號、樓號、樓層號、公共門號、卡片序號、是否允許開反鎖、入住時間和預離時間。其中房號、是否允許開反鎖和預離時間由翼云居Web客戶端請求參數得到,再通過房號查詢門鎖信息表得到門鎖號、區域號、樓號、樓層號、公共門號,程序計數器計算得出卡片序號。制卡的入住時間使用終端機器時間,刷卡之后將頂替掉之前該房間所有房卡,制卡成功后將房號、門鎖號、入住時間、預離時間和卡ID保存到前置數據庫操作日志表。
(3)在線制卡,房間的門鎖是聯網的,制卡數據不是寫入房卡,而是讀取房卡ID,連同門鎖號、入住時間、預離時間等數據通過網絡發送到對應房間的門鎖上。客人開門刷卡時讀取房卡ID,對比門鎖上寫入的房卡ID、入住時間、預離時間決定能否開門。制卡成功同樣保存操作日志。密碼鎖也是一種在線制卡,雖然脫離了房卡和制卡器,這里前置服務隨機生成密碼,連同入住時間、預離時間通過門鎖網絡平臺發送到對應房間門鎖。
(4)贈卡,是指給同一房間發多張門卡。在做贈卡(副卡)時,入住時間必須和主卡相同,該時間在前置數據庫中通過房號查詢最近的制卡日志表得到。
(5)讀卡,讀出房卡對應的房號、入住和預離時間,提供兩種方式,一種是使用門鎖接口讀出房卡里面的制卡數據(鎖號、入住時間和預離時間),再由鎖號反查出房號;另一種是使用門鎖接口查出卡ID,再由卡ID查詢前置數據庫制卡日志,得到制卡數據。
(6)清卡,離線制卡方式提供兩種方式清卡,一種是使用門鎖接口的清卡函數,還有就是使用門鎖接口的寫卡函數寫入臟數據(比如過期的時間);對于在線制卡方式,通過門鎖網絡平臺接口刪除門鎖上的制卡數據完成清卡。
翼云居打印功能有兩種實現方式,一種是利用瀏覽器自帶打印功能,另一種是使用前置服務提供的打印接口。瀏覽器打印存在的缺陷是明顯的:由于前端打印功能是通過Javascript腳本實現的,而Javascript存在瀏覽器兼容性問題,所以打印功能也存在瀏覽器兼容性問題;打印格式就是頁面顯示格式,所以定制一個打印格式需要編寫一套Javascript腳本,相當不方便;打印字體大小、樣式,頁面的打印邊距、行間距無法靈活修改調整。使用前置打印服務不存在瀏覽器兼容性問題,前置打印采用先進的基于打印樣式模板的方法,通過配置打印模板快速定制打印格式,通過修改打印模板中的打印參數靈活修改字體、頁面邊距和行間距,只要給打印數據標注上打印樣式模板中的樣式標簽,發送給前置打印接口即可完成打印功能。
這里的表格型數據是指需要打印的數據可分為若干打印行,每一行又可以分為若干打印項。打印樣式模板提供兩種類型的樣式標簽:行樣式標簽和合并項標簽。行樣式標簽定義的屬性有:行高(非必選項,默認為0)、行邊框(包括線條樣式),以及該行每個打印項屬性,包括:打印項類型(文本、線條、頁碼、二維碼等)、打印項邊框、字體大小、字體樣式、對齊方式;合并項標簽定義需要合并的打印項,標簽屬性與打印項屬性相同。打印算法根據定義的模板基本參數(頁面大小、頁面邊距和行間距)和標注上打印樣式標簽的打印數據源,計算出所有打印項(包括合并項)的打印坐標和所有打印線段的位置坐標,并實現自動分頁和頁首頁尾,調用MFC打印API進行打印。
打印服務提供的接口有:獲取終端上所有打印機列表、獲取打印機屬性、下載打印模板、打印預覽和打印。打印預覽輸出為JPG圖片,方便在Web頁面上展示。Web客戶端調用打印接口服務進行數據打印的步驟如下:
(1)利用模板打印樣式標簽將打印測試數據
標簽化,并標注打印模板名和版本、頁首行、頁體行和頁尾行;
(2)前置服務接收到打印請求,檢查前置數據庫是否存在所需的打印模板,若沒有則須從翼云居服務端下載該版本的打印模板并保存到本地數據庫;
(3)前置打印服務使用該打印模板自動計算出打印數據的位置,并實現打印分頁,調用MFC打印API進行打印。
目前翼云居前置服務已接入數十種品牌的身份證閱讀器。市場上數以百計的大小品牌門鎖,均能通過本文設計軟件架構快速完成門鎖對接。創新的打印方法簡化了復雜的Web前端打印開發,通過標簽化配置快速完成酒店各種打印格式票據的定制化需求。