使用 IBM Bluemix 上的 IoT 及 Push 服務(wù)發(fā)送推送消息到手機(jī)
Bluemix是 IBM 最新的云服務(wù),是企業(yè)和開(kāi)發(fā)人員可以快速輕松地創(chuàng)建,部署和管理應(yīng)用程序的云平臺(tái)。Bluemix 提供企業(yè)級(jí)的服務(wù),可以輕松地與云應(yīng)用程序集成。它目前提供了 100 多種服務(wù),覆蓋了認(rèn)知、移動(dòng)、運(yùn)維、web 和應(yīng)用程序、網(wǎng)絡(luò)、集成、數(shù)據(jù)與分析、安全、存儲(chǔ)、業(yè)務(wù)分析、物聯(lián)網(wǎng)、及 API 等。
本文引用地址:http://www.ex-cimer.com/article/201611/340672.htm本文就是使用了 Bluemix 上的移動(dòng),物聯(lián)網(wǎng)及存儲(chǔ)等相關(guān)的服務(wù),基于 Node-RED 快速構(gòu)建了一個(gè)推送消息給手機(jī),并且可以查看歷史數(shù)據(jù)的應(yīng)用。該應(yīng)用主要架構(gòu)如下:
圖 1 架構(gòu)圖
主要工作流程:
1.設(shè)備(在這里是從命令行)發(fā)消息給 IoT 平臺(tái)
2.IoT 平臺(tái)轉(zhuǎn)發(fā)消息給 Node-RED
3.當(dāng)收到消息時(shí),Node-RED 調(diào)用 Push 服務(wù)
4.Push 服務(wù)發(fā)推送消息給手機(jī)端
5.Node-RED 收到消息時(shí)將數(shù)據(jù)也存入 Cloudant 數(shù)據(jù)庫(kù)
6.手機(jī)客戶端從 Cloudant 數(shù)據(jù)庫(kù)讀取歷史消息
為實(shí)現(xiàn)以下應(yīng)用程序需要哪些準(zhǔn)備:
·Bluemix 賬號(hào)
·基本的 Java 技能
·基本的 Swift 技能
下面我們分步驟詳細(xì)介紹如何實(shí)現(xiàn)這些功能。
第一步:創(chuàng)建應(yīng)用程序
1.用 IBM ID 登錄到 Bluemix,選擇目錄(CATALOG),選擇 Internet of Things Platform Starter 模板。
圖 2 Internet of Things Platform Starter 模板位置
2.在打開(kāi)的頁(yè)面上,左邊欄是關(guān)于 Internet of Things Platform Starter 的概要信息,比如版本,類型,以及詳細(xì)文檔的鏈接;中間欄是當(dāng)前版所包含的服務(wù),當(dāng)前版本包含 SDK for Node.js, Cloudant NoSQL DB,及 Internet of Things Platform 服務(wù),那么我們?cè)诤筮吘筒恍枰兕~外添加這些服務(wù),此外,還列出了其主要功能及如何收費(fèi)等信息;右邊欄是要配置的信息,輸入 Name,Host 字段會(huì)根據(jù) Name 的值自動(dòng)生成,保持其他字段不變,點(diǎn)擊創(chuàng)建(CREATE)。
圖 3 Internet of Things Platform Starter 模板頁(yè)面
第二步:?jiǎn)⒂靡苿?dòng)應(yīng)用程序
點(diǎn)擊概述(Overview),找到啟用移動(dòng)應(yīng)用程序(Enable App for Mobile),并點(diǎn)擊。啟用移動(dòng)應(yīng)用程序的對(duì)話框會(huì)彈出,顯示如下:要啟用您的移動(dòng)應(yīng)用程序,Bluemix 將新增 Mobile Client Access 提供的應(yīng)用程序安全性功能。要將 Mobile Client Access 服務(wù)添加到您的應(yīng)用程序嗎?選擇添加(ADD)。在我們的例子中,移動(dòng)端的安全問(wèn)題并不是重點(diǎn),但是這是啟用移動(dòng)應(yīng)用程序的必要條件,我們必須添加這個(gè)服務(wù)。隨后選擇重新編譯打包應(yīng)用程序(Restage Application)。當(dāng)應(yīng)用程序重新啟動(dòng)結(jié)束,在概述(Overview)頁(yè)面的右上角,會(huì)出現(xiàn)一個(gè)移動(dòng)選項(xiàng)(Mobile Options),點(diǎn)擊它,會(huì)出現(xiàn)路徑(Route)和應(yīng)用程序 GUID 兩個(gè)字段,
圖 4 查看 Route 和 App GUID
這兩個(gè)字段很重要,在將來(lái)我們實(shí)現(xiàn)移動(dòng)端程序時(shí),會(huì)有如下字樣的代碼,需要指定 Route 和 App GUID,才能連上 Bluemix 里的應(yīng)用程序。
IMFClient.sharedInstance().initializeWithBackendRoute("http://iot-push.mybluemix.net", backendGUID:"27c9eb7b-0df6-43ac-b338-4678c926e936")
第三步:添加推送(Push)服務(wù)
推送服務(wù)能幫助我們發(fā)送推送消息到 Android 或者 iOS 設(shè)備,這些設(shè)備能作為標(biāo)記(tag)或者設(shè)備唯一標(biāo)識(shí)符的目標(biāo),我們使用簡(jiǎn)單而統(tǒng)一的 REST API 以配置,訂閱,發(fā)送和監(jiān)控推送消息到 Android 和 iOS 設(shè)備。
1.添加推送服務(wù)
仍然是回到概述頁(yè)面,點(diǎn)擊添加服務(wù)或者 API(Add a Service Or API),在 Services 的列表中選擇 Mobile,然后找到 IBM Push Notification 服務(wù),并點(diǎn)擊之。
圖 5 Push Notification 目錄
在右邊欄找到 Service name 字段輸入服務(wù)名稱,當(dāng)然,你也可以使用它的默認(rèn)名稱,點(diǎn)擊創(chuàng)建(CREATE)按鈕。在隨后彈出的窗口中,選擇重新編譯打包(RESTAGE)。因?yàn)樵搼?yīng)用程序新添加了服務(wù),必須重新編譯打包才能使此服務(wù)生效。至此,推送服務(wù)添加成功。
2.配置證書(shū)
在應(yīng)用程序的概述頁(yè)面找到 Push Notification 服務(wù),并點(diǎn)擊以進(jìn)入該服務(wù)頁(yè)面。
圖 6 概述頁(yè)面的 Push Notification 服務(wù)
在打開(kāi)的頁(yè)面上會(huì)有提示:你還沒(méi)有配置推送!在其下方,有一個(gè)按鈕配置推送(Setup Push),點(diǎn)擊進(jìn)入配置頁(yè)面。
圖 7 配置推送證書(shū)
因?yàn)榇死又校覀兪鞘褂玫?/span> iOS 客戶端,所以我們只配置蘋(píng)果推送認(rèn)證(Apple Push Certificate)。選擇你的證書(shū)所用的類型(請(qǐng)務(wù)必跟你的證書(shū)的類型相同,如果你的證書(shū)是生產(chǎn)環(huán)境下的,那么選擇 Production,否則選擇 Sandbox。如果類型不匹配,可能手機(jī)收不到推送消息。),選擇文件位置,填寫(xiě)密碼,然后點(diǎn)擊保存(Save)。證書(shū)配置如下,說(shuō)明已經(jīng)配置成功。
圖 8 成功配置推送證書(shū)
第四步:配置 Cloudant 數(shù)據(jù)庫(kù)
1.創(chuàng)建數(shù)據(jù)庫(kù)
回到應(yīng)用程序的概述頁(yè)面,找到 Cloudant NoSQL 服務(wù),點(diǎn)擊以打開(kāi)這個(gè)服務(wù),在右上角找到 LAUNCH 按鈕 ,并點(diǎn)擊。在新打開(kāi)的 Cloudant 控制臺(tái)頁(yè)面中,在右上角找到按鈕 – 創(chuàng)建數(shù)據(jù)庫(kù)(Create Database),點(diǎn)擊之,在彈出的對(duì)話框里,填上數(shù)據(jù)庫(kù)的名字,在這里我們填上 demodb,并點(diǎn)擊創(chuàng)建(Create),隨后頁(yè)面上有數(shù)據(jù)庫(kù)創(chuàng)建成功的字樣出現(xiàn),并且自動(dòng)切入到已創(chuàng)建好的數(shù)據(jù)庫(kù)中。至此,我們的數(shù)據(jù)庫(kù)創(chuàng)建工作已經(jīng)完成。
圖 9 在 Cloudant 里創(chuàng)建數(shù)據(jù)庫(kù)
2.設(shè)置權(quán)限
仍然是在 Cloudant 控制臺(tái)頁(yè)面中,在數(shù)據(jù)庫(kù)列表中點(diǎn)擊剛剛建好的 demodb,進(jìn)入其子菜單,找到權(quán)限(Permissions),為其他用戶勾選上_reader 選項(xiàng)。
圖 10 Cloudant 數(shù)據(jù)庫(kù)的權(quán)限設(shè)置
3.獲取 API 連接
獲取數(shù)據(jù)庫(kù)里所有數(shù)據(jù)的 API 為如下格式:https://<host>/<db_name>/_all_docs?include_docs=true
在應(yīng)用程序的概述頁(yè)面(Overview),找到 Cloudant NoSQL DB,點(diǎn)擊 Show Credentials,在打開(kāi)的窗口中找到 host,替換上面的相應(yīng)字段;在 db_name 中用我們的數(shù)據(jù)庫(kù)的名字 demodb 替換;設(shè)置 include_docs 為 true,是為返回列表中的數(shù)據(jù),如果沒(méi)有這個(gè)參數(shù),或者設(shè)為 false,則只會(huì)返回?cái)?shù)據(jù)項(xiàng)的 ID 和 key 信息,沒(méi)有數(shù)據(jù)信息。
圖 11 Cloudant 的憑證信息
點(diǎn)擊查看最終該 API 呈現(xiàn)形式,在移動(dòng)端可以使用該 API 獲取歷史數(shù)據(jù)。
第五步:配置 IoT 服務(wù)
1.回到應(yīng)用程序的概述頁(yè)面,找到 Internet of Things Platform 服務(wù),點(diǎn)擊進(jìn)入該服務(wù)的頁(yè)面,找到 Launch dashboard 按鈕,并點(diǎn)擊之。
2.在新打開(kāi)的標(biāo)簽頁(yè)中,在左側(cè)豎邊欄上懸停,在打開(kāi)的菜單欄上,找到設(shè)備 DEVICES,并點(diǎn)擊之。
圖 12 IoT 菜單
3.在新打開(kāi)的設(shè)備列表中,目前還沒(méi)有設(shè)備。點(diǎn)擊右上角的 Add Device, 開(kāi)始添加一個(gè)設(shè)備。首先要選擇設(shè)備類型,在下拉列表里,暫時(shí)還沒(méi)有設(shè)備類型。所以先點(diǎn)擊 Create device type 按鈕創(chuàng)建一個(gè)設(shè)備類型。
圖 13 添加設(shè)備
4.在彈出的頁(yè)面上,仍然選擇 Create device type.
圖 14 創(chuàng)建設(shè)備類型
5.接下來(lái),在頁(yè)面上輸入類型名稱(Name),這個(gè)是必填項(xiàng),然后點(diǎn)擊右下角的下一步(Next),期間有一些信息要填,但都不是必填項(xiàng),可以留空,一直點(diǎn)擊 Next,直到最后點(diǎn)擊創(chuàng)建(Create)。
6.在選擇設(shè)備類型(Choose Device Type)里選擇剛剛創(chuàng)建的設(shè)備類型,點(diǎn)擊右下角的下一步。
7.在設(shè)備信息(Device Info)頁(yè)面,填寫(xiě)設(shè)備 ID,這是唯一必填的信息。點(diǎn)擊額外的字段(+Additional fields)會(huì)有更多詳細(xì)的字段可以填寫(xiě),如序列號(hào),制造商,模型,類別等等,這些字段會(huì)根據(jù)在設(shè)備類型中設(shè)置的屬性值自動(dòng)填充,也可以填上這些字段的值以覆蓋設(shè)備類型中的值,此外設(shè)備類型中沒(méi)有定義的屬性,也可以增加。然后點(diǎn)擊右下角的下一步。
8.元數(shù)據(jù)(Metadata)這頁(yè),可以留空,點(diǎn)擊下一步。
9.在安全(Security)這頁(yè),主要是獲取安全口令。有兩個(gè)選擇,一是使用自動(dòng)生成的口令:會(huì)為你自動(dòng)生成認(rèn)證口令。該口令是一個(gè) 18 位長(zhǎng),包含數(shù)字、字母,以及符號(hào)的混合體,它將在注冊(cè)流程的最后返回給你;另一個(gè)種是由你為該設(shè)備提供認(rèn)證口令。在這種情況下,該口令是 8 到 36 位長(zhǎng),可以包括大小寫(xiě)字母,數(shù)字及符號(hào)(連字號(hào),下劃線及句點(diǎn))。如果你要使用自定義的口令,在請(qǐng)?zhí)峁┛诹睿?/span>Provide a token)后邊的橫線上填寫(xiě),然后點(diǎn)擊下一步。
10.在概要信息(Summary)這頁(yè),主要是顯示之前所填的信息,請(qǐng)核對(duì)為該設(shè)備所提交信息是否正確。如果無(wú)誤,請(qǐng)點(diǎn)擊添加(Add)。
11.在設(shè)備憑證(Your Device Credentials)頁(yè),這個(gè)設(shè)備已經(jīng)被注冊(cè)到了這個(gè)組織。要想連接到這個(gè)組織,需要把如下信息添加到你的設(shè)備上。請(qǐng)牢記認(rèn)證口令(Authentication Token),由于安全方面的原因,這個(gè)口令一旦被忘記,將沒(méi)有辦法獲取。
圖 15 設(shè)備憑證信息
12.點(diǎn)擊右上角的按鈕 ,關(guān)閉當(dāng)前頁(yè)面。在設(shè)備列表里,即有剛剛創(chuàng)建的設(shè)備。至此,設(shè)備的創(chuàng)建已經(jīng)完成。
第六步:在 Node-RED 里創(chuàng)建工作流
點(diǎn)擊儀表板(DASHBOARD),在應(yīng)用程序列表里找到剛剛創(chuàng)建的應(yīng)用程序,點(diǎn)擊圖標(biāo)。
圖 16 找到 Node-RED 入口圖標(biāo)
在打開(kāi)的 Node-RED 頁(yè)面,找到 Go to your Node-RED flow editor 按鈕,點(diǎn)擊以進(jìn)入流編輯器。接下來(lái),我們將使用 Node-RED 流編輯器來(lái)構(gòu)建一個(gè)工作流。 讀者也可以 點(diǎn)擊這里打開(kāi) Node-RED 流編輯器。
1.在左側(cè)調(diào)色板中,找到 input 區(qū)域,拖拽 ibmiot 節(jié)點(diǎn) 拖到畫(huà)布上,雙擊以打開(kāi)編輯頁(yè)面進(jìn)行配置。首先要選擇認(rèn)證(Authentication)的方式:此下拉框有 3 個(gè)值 Quickstart, API Key, Bluemix Service. Quickstart 是用于快速入門(mén)的案例學(xué)習(xí)用的;API Key 選項(xiàng)需要輸入 API Key 和 API token,多用于 IoT Platform 與 Node-RED 不在同一個(gè) app 時(shí);Bluemix Service 是內(nèi)部的服務(wù)調(diào)用,并不需要輸入?yún)?shù),主要用于 Node-RED 與 IoT 在同一 App 時(shí)。因?yàn)槲覀兊?nbsp;App 里邊有 IoT Platform 服務(wù),我們可以選擇 Bluemix Service 直接連接。選定認(rèn)證方式后,需要選擇輸入類型(Input Type),這里輸入類型有 Device Event, Device Command, Device Status, Application Status, 從名稱上我們即可知道他們 主要檢測(cè)的重點(diǎn)。我們?cè)诖诉x擇 Device Event。至于設(shè)備類型(Device Type),設(shè)備標(biāo)識(shí)(Device Id),事件(Event),格式(Format)等選擇 All 即可,再填寫(xiě)節(jié)點(diǎn)名稱(Name),點(diǎn)擊 OK。
圖 17 ibmiot 節(jié)點(diǎn)的編輯頁(yè)面
2.在 function 區(qū)域找到 json 節(jié)點(diǎn) ,并將其拖拽到畫(huà)布上。我們使用此節(jié)點(diǎn)是為了解析 msg.payload,把一個(gè) javascript 對(duì)象轉(zhuǎn)化為 json 字符串。
3.在 function 區(qū)域找到 function 節(jié)點(diǎn) ,并將其拖拽到畫(huà)布上。雙擊該節(jié)點(diǎn),打開(kāi)編輯頁(yè)面。在 Name 字段輸入該節(jié)點(diǎn)名稱(建議填上該字段,否則節(jié)點(diǎn)顯示名為空),在 Function 區(qū)域已有一句 return msg; 在此之前加入如下代碼:
var message = JSON.parse(msg.payload).d ;
msg.payload ={
"message":{“alert": message.content }
};
return msg;
圖 18 function 節(jié)點(diǎn)的編輯頁(yè)面
4.在 output 區(qū)域找到 ibmpush 節(jié)點(diǎn) ,將其拖拽至畫(huà)布。雙擊該節(jié)點(diǎn),打開(kāi)編輯頁(yè)面。因?yàn)楫?dāng)前節(jié)點(diǎn)所屬的應(yīng)用程序有 Push 服務(wù),所以對(duì)該節(jié)點(diǎn)初始化時(shí)會(huì)自動(dòng)去讀取該應(yīng)用程序 ID 和 push 服務(wù)的 appSecret 字段。編輯頁(yè)面上的可編輯的推送屬性只有 2 個(gè):模式(Mode)和類型(Type)。
模式有沙箱(Sandbox)和生產(chǎn)(Production)模式,由于我們的證書(shū)是生產(chǎn)模式下的,所以我們選擇生產(chǎn)模式。
類型一共有 5 種:
·廣播(Broadcast):可以向所有設(shè)備發(fā)送
·通過(guò)標(biāo)記(By Tags)
·通過(guò)設(shè)備唯一標(biāo)識(shí)符(By DeviceIds)
·僅 Android 設(shè)備(Only Android devices)
·僅 iOS 設(shè)備(Only iOS devices)
由于我們 demo 的移動(dòng)端程序僅支持 iOS 設(shè)備,所以我們選擇 Only iOS devices.
圖 19 ibmpush 節(jié)點(diǎn)編輯頁(yè)面
5.在 storage 區(qū)域找到 cloudant 節(jié)點(diǎn) ,將其拖拽至畫(huà)布。雙擊節(jié)點(diǎn),打開(kāi)編輯頁(yè)面。Service 字段已經(jīng)自動(dòng)定位到當(dāng)前應(yīng)用程序里附帶的 Cloudant 數(shù)據(jù)庫(kù);Database 字段輸入我們前面已經(jīng)創(chuàng)建好的數(shù)據(jù)庫(kù)名;Operation 字段表明要執(zhí)行的操作是插入(insert)還是刪除(remove),因?yàn)槲覀円娌橄⑦M(jìn)數(shù)據(jù)庫(kù),所以這里選擇 insert;勾上 Only store msg.payload object,將只存儲(chǔ) msg.payload 里的數(shù)據(jù),否則會(huì)存儲(chǔ)整個(gè) msg 對(duì)象的值。最終,我們配置如下:
將各節(jié)點(diǎn)用線連接起來(lái),形成如下所示的流:
圖 20 最終的工作流
在所有節(jié)點(diǎn)都修改之后,點(diǎn)擊右上角的 Deploy 按鈕來(lái)部署此工作流。當(dāng)屏幕中間彈出成功部署(Successfully deployed)時(shí),說(shuō)明此流已經(jīng)部署成功。
你也可以通過(guò)導(dǎo)入功能導(dǎo)入以上工作流。先找到菜單->導(dǎo)入->剪切板。
圖 21 Node-RED 的導(dǎo)入功能
然后把以下代碼粘貼到剪切板上,點(diǎn)擊 Ok 按鈕。當(dāng)鼠標(biāo)在畫(huà)布上點(diǎn)擊時(shí),工作流即會(huì)落到畫(huà)布上,然后部署即可。
[{"id":"1b156a6f.f4bd96","type":"ibmiot","z":"2282255c.190b3a","name":"my
api key"},{"id":"1007c76.d968b39","type":"ibmiot
in","z":"2282255c.190b3a","authentication":"boundService","apiKey":"1b156a6f.f4bd96","inputType":"evt","deviceId":"","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"IBM
IoT","service":"registered","allDevices":true,"allApplications":"","allDeviceTypes":true,"allEvents":true,"allCommands":"","allFormats":true,"x":212,"y":139,"wires":[["ba7c7e78.984a9"]]},{"id":"ba7c7e78.984a9","type":"json","z":"2282255c.190b3a","name":"","x":334,"y":139.5,"wires":[["13ed7f07.efa891"]]},{"id":"13ed7f07.efa891","type":"function","z":"2282255c.190b3a","name":"Message","func":"var
message = JSON.parse(msg.payload).d
;/nmsg.payload=message.content;/nreturn
msg;","outputs":"1","noerr":0,"x":466.5,"y":140,"wires":[["1b7a992d.274b47","27c244f9.52993c"]]},{"id":"1b7a992d.274b47","type":"cloudant
out","z":"2282255c.190b3a","name":"","cloudant":"","database":"demodb","service":"iot-push-cloudantNoSQLDB","payonly":true,"operation":"insert","x":613.5,"y":165.5,"wires":[]},{"id":"27c244f9.52993c","type":"ibmpush","z":"2282255c.190b3a","name":"","ApplicationID":"","identifiers":"","notification":"ios","mode":"PRODUCTION","x":612,"y":115,"wires":[]}]
第七步:發(fā)送 MQTT 消息
我們用 Java 實(shí)現(xiàn)一段程序,以發(fā)送 MQTT 消息給 IoT 平臺(tái)。其中,我們引用了如下包:
com.ibm.iotf.client.2016.4.21.jar, gson-2.2.4.jar,httpclient-4.3.6.jar, httpcore-4.3.3.jar,
org.eclipse.paho.client.mqttv3-1.0.3-20150818.040635-202.jar
核心代碼及解析如下:
publicclassDeviceMessage{
DeviceClient deviceClient =null;
publicDeviceMessage(){
//設(shè)置連接屬性,用我們已經(jīng)創(chuàng)建好的設(shè)備的憑證,連接到我們的 IoT 服務(wù)。
Properties options =newProperties();
options.setProperty("org","6bnaci");
options.setProperty("type","Type1");
options.setProperty("id","Device1");
options.setProperty("auth-method","token");
options.setProperty("auth-token","12345678");
try{
deviceClient =newDeviceClient(options);
}catch(Exception e){
e.printStackTrace();
}
try{
//連接到 IoT 服務(wù),參數(shù)設(shè)成 false,如果連接失敗,不自動(dòng)重新連接;如果設(shè)置成 true,那么程序會(huì)一直重試直到連接成功。
deviceClient.connect(false);
}catch(MqttException e){
e.printStackTrace();
System.out.println("network connection error !");
}
}
publicvoid sendMessage(String[] msg){
//創(chuàng)建一個(gè)對(duì)象 event
JsonObjectevent=newJsonObject();
//如果此類被執(zhí)行時(shí)有一個(gè)參數(shù)傳入,我們即把它作為屬性賦給 event,否則即賦值“This is one push message.”
if(msg!=null& msg.length==1){
event.addProperty("content", msg[0]);
}else{
event.addProperty("content","This is one push message.");
}
//發(fā)送消息到 IoT 平臺(tái)
deviceClient.publishEvent("status",event);
//從 IoT 平臺(tái)斷開(kāi)連接
deviceClient.disconnect();
}
publicstaticvoid main(String args[]){
DeviceMessage dm =newDeviceMessage();
dm.sendMessage(args);
}
}
我們通過(guò)命令行(當(dāng)然,也可以引入這個(gè) java 項(xiàng)目,在 IDE 里如 Eclipse 運(yùn)行該程序)運(yùn)行,
Java-jar Push.jar "This is the message from the command line."
圖 22 在命令行發(fā)送 MQTT 消息
第八步:手機(jī)裝上相應(yīng)的客戶端程序
將應(yīng)用程序發(fā)布到蘋(píng)果的 App Store 或者其他 store(如某些公司內(nèi)部有自己 App Store 供開(kāi)發(fā)測(cè)試用),并用手機(jī)安裝,打開(kāi)應(yīng)用程序,按第七步:發(fā)送 MQTT 消息在命令行發(fā)送一條命令,手機(jī)端即可接收推送消息了,同時(shí)也能查看歷史記錄。詳細(xì)源代碼已經(jīng)附在后邊,請(qǐng)大家自行查看。
評(píng)論