|
2013年3月13日
現在做互聯網產品的團隊都比較小,也可能沒有特別多運維人員。因此特別需要用一些系統或是工具來監控服務器或者是服務是否正常。之前比較直接的做法是自己搭建一套開源的監控系統,現在隨著云服務器的流行,也有越來越多的人用戶會使用云端的監控平臺。
從我的經驗來看,云服務器監控是有些特別的好處的:
1.自建的監控平臺,有一部分問題是沒辦法發出警報。比如,一般監控服務器也會在內部網絡中,如果出現外部網絡問題,監控服務是沒辦法通知到相關人員(因為發郵件或者是發短信的通路也會出現問題)。
2.自建的監控平臺,只能檢測到一個點的訪問情況。阿里云監控平臺可以檢測杭州和青島兩個節點(這是我的帳號看到的),可以比較有效地了解非監測點的一個訪問情況。
3.云監控平臺都有一套不錯的管理界面,可有效減少部署維護和使用成本。
下面我介紹一下,我使用過的一些云監控平臺,并對他們的優點和缺點進行比較。
阿里云監控
阿里的云服務器在市場中是做的很不錯的,我的很多朋友都購買了阿里的云服務器。以阿里的公司實力,做一款云監控的產品應該不會差到哪里去。
優點:
1.產品體驗好,進入住界面就能輕而易舉找到你想要的功能。
2.監控功能全,包括站點監控、服務器監控和自定義監控。
3.多點監控,可以在全國提供幾個點的監控。從用戶的角度了解服務是否可用。
4.站點監控功能也很全面,包含SMTP、POP3、FTP監控。
5.免費,且監控站點數沒限制。
缺點:
1.服務器監控和自定義監控,要求是云服務器,如果不是云服務器,只能使用站點檢測功能。
360網站服務監控
360的個人用戶產品很少使用,但是360的企業產品還真有不少做的不錯的。說說360監控的優缺點。
優點:
1.提供服務器監控。可以監控到服務器的磁盤,CPU,內存等情況。
2.UI和告警都設計的不錯。
3.免費。
缺點:
1.需要在服務器上開SNMP協議。(不過360提供很多腳本工具,可以一鍵安裝)
2.有20臺服務器的限制。(這個基本上夠用,超過20臺服務器,可以購買服務了)
監控寶
名字很專一,一看就知道是專業做監控的。
優點:
1.專注監控,界面設計還算可以。
2.監控功能全面,內網采集的方式較多。可以采集數據庫數據。
缺點:
1.免費用戶,服務器和網站監控都有限制,而且數量極少2臺服務器監控,5臺網站監控。
作者簡介: qiyadeng(www.qiyadeng.com)對互聯網技術、運營及市場領域有濃厚的興趣,喜愛思考、閱讀、討論;擅長Java開發及分布式技術。現專注于互聯網的創新產品– 老來寶(http://www.laolaibao.com),幫助年輕用戶獲得補充養老金,并提供養老金增值產品。
比較少參加這類大型的會議,進到會場的第一眼,發現會議室已經全部坐滿,后來主辦方發現站的人太多了,找來了一些小板凳,我快速找到一個小板凳坐下。坐下開始認真聽,非常開心地聽到廣告時間結束,和我計劃的時間完美一致。
回顧一下我比較關系的幾個主題
基于用戶畫像的大數據實例
演講嘉賓是聯通沃商店的大數據技術經理,該大數據實例主要是通過聯通營運商的數據和沃商店進行分析,通過繪制用戶畫像的形式,在其他應用場景,如廣告、游戲下載中為用戶推薦用戶喜歡的產品。可以看出來嘉賓技術實例及基礎功是十分不錯,至少是一個碩士畢業。近些年被大家掛在嘴邊的機器學習算法、推薦算法、語義分析都有部分介紹,實在是接受不過來;比較熟悉的還是我們當初碩士的專業方向推薦算法,看到了簡單的介紹覺得很親切。不過后來提問環節看,現場還是很多高人,有不少是做這個領域的。不過歸根是國有企業和類似研究機構,是否能產生非常大的價值,我表示懷疑,不過這些算法一羅列,對經費的分配還是很有好處的。
電商系統的心得分享
這又是一個國有企業,號稱是線上賣大力丸的人(國藥1健康)。從技術成長為總經理,有很多心得體會。感覺和我有那么一點像,有一些體會也迫不及待的分析給這些年輕的IT從業者,為人嚴肅,總是會把困難估計的充分一點(估計年輕也沒少教學費)。演講中說了構建系統中的四個原則
權限獨立,相互制約
非常務實的看到某些大型企業的,部門斗爭。從系統層面開始設計制約(這個應該非常符合老板心意)。這個對很多小型企業在成才過程中是非常有幫助的。
設計流程 減少犯錯
在電商行業非常清楚客服和倉庫的員工流動性,以及普遍受教育程度偏低,通過流程設計,而不是提高對用人的要求。這也是非常務實的方法。回顧之前在系統層面獨自設計支持中央預訂系統,設計出來的自動傳真(當時網絡不如現在易得)及新訂單提醒(感謝施總的支持,增加音響進行聲音)等等,簡直覺得找到了知音。
多了解一些財務知識。
談到的兩點是數據之間需要有勾稽關系和不能修改歷史數據,很驕傲我對財務的理解還是不錯,從未犯過這種不靠譜的錯誤。
跨平臺大型在線客服系統的技術構架
嘉賓談了的是一套客服系統,比較多的關鍵字是客服妹子,可以看出IT從業者苦中作樂的精神。給我的體會是,客服系統都可以做成這樣。從一個項目到一個產品,在云計算的世界,可以好一個客服的組件,也是有很大的價值。和我的理想事業很接近,可以花上一生中最精華的時間,做好一個有價值的小眾專業的行業。
阿里分布式數據庫服務實踐
阿里的人就是高調,上場就調戲京東雙11前系統崩潰。我也經歷過很多系統崩潰,簡直是開發人員的噩夢,也是IT人員信用受損的嚴重事件(因此我一直比較注意防止崩潰及崩潰后的快速恢復)。回到分布式數據庫,這個是收獲最大的一個演講。雖然這個演講看上去是再給阿里云的DRDS做宣傳,但是嘉賓演講的很進行,深入淺出地介紹了分布式數據庫和單機數據庫的區別。對分布式事務的重新認識是一個很大的收獲,以前一直把教程中的數據庫原理中的事務定義,作為分布式事務需要解決的問題,其實不是。需要更加務實,在淘寶阿里這類訂單處理系統中,有一類對分布式事務的模式(異步消息機制);在其他領域會有其他模式分布式的事務模型,這些分布式模型肯定都不滿足單機的事務模型,但是可以滿足和解決相應領域的問題。
平臺架構的服務器監控
一個APP的監控模型,猜測項目立項的原因,有兩個。一個是和競爭對手的數據比較(UPYUN的對手主要是七牛),一個其實可以真正從用戶的角度看,用戶的體檢速度如何,以及影響用戶體驗速度的真實原因。目前一般行業還不會做的這么細,因為UPYUN是技術支持公司,因此一定需要用這些數據去說服和支持用戶。我們現在做的比較多的服務器的可用性、性能和應用的可用性、性能監控。前端時間剛好再比較,發現互聯網上有不少好的監控平臺,一般的創業公司,可以無需自己搭建監控平臺,接入到相應的監控平臺即可。下次再開文進行討論。
作者簡介:qiyadeng(www.qiyadeng.com)對互聯網技術、運營及市場領域有濃厚的興趣,喜愛思考、閱讀、討論;擅長Java開發及分布式技術。現專注于互聯網的創新產品--老來寶(http://www.laolaibao.com),立志于幫助廣大鳳凰(diao)男(si)提供補充養老金管理平臺。
摘要: guava是Java的一個擴展類庫,在google的許多項目中使用過了,現在最為一個 開源的Java類庫廣泛使用(http://code.google.com/p/guava-libraries/)。
guava類庫擴展的主要是這些相關類:collections(集合類),concurrency(并發),primitives,reflection(反射),comparison,I/O,hashi... 閱讀全文
簡單介紹一下8個Java牛人,他們為Java社區,創建了框架(framework),產品或者是寫書,影響甚至改變了Java開發的方法(根據個人喜好排序)。 8.Tomcat創始人
James Duncan Davidson,是當時Sun公司的軟件工程師(1997-2001),創建了Java的Web服務器Tomcat,Tomcat廣泛應用于Java Web開發的各個領域。 7.測試驅動開發JUnit創始人
Kent Beck,極限編程和測試驅動開發方法的締造者。此外,他還創造了JUnit,JUnit目前一次成為Java開發測試的事實標準。基于測試驅動的開發方法和JUnit給Java開發的方法帶了巨大的變化。 6.Java Collections框架設計者
Joshua Bloch,領導設計了Java平臺的許多功能,包括Java 5.0 版本中飽受贊譽的Java Collections框架。2004年他離開Sun公司,成為Google的首席Java架構師,此外他的著作“Effective Java”基本上是學習Java的必讀之書。 5.JBoss創始人
Marc Fleury,在2001年創造了JBoss,JBoss是一個Java開源的應用服務器,也已經成為Java Web應用部署中的事實標準。后來他把JBoss買給了RedHat,之后繼續從事JBoss的開發工作。不過2007年他離開了RedHat去追求他的個人愛好。 4.Struts創始人
Craig Mcclanahan,創建了Struts,一個流行的基于Java的MVC開源框架,基本上很多Java開發者都知道如何開發Struts的應用程序。 3.Spring創始人
Rod Johnson,Spring框架的創始人,Spring Source的CEO。Spring是一個非常流行的Java應用程序開發的開源框架。此外,他的著作Expert One-to-One J2EE Design and Development,是J2EE最有影響力的一本書。 2.Hibernate創始人
Gavin King,Hibernate的創始人,一個流行的Java ORM解決方案;同時他也是Seam的創始人,此外他為EJB3.0和JPA也做出了突出的貢獻。 1.Java之父
James Gosling,1994年發明了Java語言,他創建了Java編譯器和虛擬機。在2010年,當Oracle收購Sun公司時,他離開了Sun公司。 原創文章,作者:qiyadeng,轉載請注明: 轉載自http://www.qiyadeng.com/ 本文鏈接地址: 你應該知道的8個Java牛人
1.申請開發者帳號 首先注冊百度的帳號,然后申請成為百度開發者(需要通過手機進行身份證驗證)。   2.新建應用 點擊菜單中的創建應用,我們目前選擇的是Web應用。   應用創建之后,選擇左邊菜單的云環境,環境類型需要選擇JAVA。  并新創建一個版本,輸入1作為版本號  3.在百度集成開發環境中開始開發 百度提供了基于Eclipse的插件,由于該插件不能支持最新的Eclipse版本。建議下載百度的一鍵安裝版本。百度文檔中介紹了如何使用開發環境,詳細請看集成開發環境使用。 打開百度集成開發環境,在Eclipse左下角點擊Login to Baidu,使用你的賬號登陸。然后點擊工具欄中百度Logo,選擇Import BAE Project,填入application和version  之后選擇Java作為Project Language。 4.解決項目錯誤 剛導入的BAE project,在Eclipse中會報錯。通過problems view可以看到是因為JRE環境配置不正確和Web運行環境設置不正確。 A.右鍵項目屬性--選擇JavaBuildPath,在Libraries中選擇Add Library,之后再選擇JRE System Library。  B.接下來把Java project轉換為Java Web Project(Eclipse中Java Project轉換為Java web Project),注意如果你的tomcat是6版本的話,請注意選擇Dynamic web Module的版本不超過2.5。 設置Web應用的運行環境,在servers view中新建一個tomcat服務器。  C.和A類似,在Java Build Path中加入 Server Runtime,選擇Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代碼 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把項目部署到tomcat中。
至此項目錯誤全部解決,應該可以看到運行結果。

5.新建Servert測試
新建一個Servlet,HomeServlet,Eclipse會自動在web.xml中加入配置信息,HomeServet.java和web.xml的部分代碼如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代碼
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 運行tomcat,可以看到如下運行成功,這樣你就可以像一般的Java Web Project一樣進行開發。

6.最后
BAE中Java環境中百度使用的Jetty,而不是tomcat,Jetty的好處是不需要頻繁的重啟,修改的代碼即時就可看到運行結果。
通過SVN提交代碼到BAE,如果有需要做小的修改,可以通過百度的在線編輯工具直接修改。

原創文章,轉載請注明: 轉載自http://www.qiyadeng.com/
本文鏈接地址: 百度開發者中心BAE新建Java應用
1.申請開發者帳號 首先注冊百度的帳號,然后申請成為百度開發者(需要通過手機進行身份證驗證)。
2.新建應用 點擊菜單中的創建應用,我們目前選擇的是Web應用。
應用創建之后,選擇左邊菜單的云環境,環境類型需要選擇JAVA。
并新創建一個版本,輸入1作為版本號
3.在百度集成開發環境中開始開發 百度提供了基于Eclipse的插件,由于該插件不能支持最新的Eclipse版本。建議下載百度的一鍵安裝版本。百度文檔中介紹了如何使用開發環境,詳細請看集成開發環境使用。 打開百度集成開發環境,在Eclipse左下角點擊Login to Baidu,使用你的賬號登陸。然后點擊工具欄中百度Logo,選擇Import BAE Project,填入application和version
之后選擇Java作為Project Language。 4.解決項目錯誤 剛導入的BAE project,在Eclipse中會報錯。通過problems view可以看到是因為JRE環境配置不正確和Web運行環境設置不正確。 A.右鍵項目屬性--選擇JavaBuildPath,在Libraries中選擇Add Library,之后再選擇JRE System Library。
B.接下來把Java project轉換為Java Web Project(Eclipse中Java Project轉換為Java web Project),注意如果你的tomcat是6版本的話,請注意選擇Dynamic web Module的版本不超過2.5。 設置Web應用的運行環境,在servers view中新建一個tomcat服務器。
C.和A類似,在Java Build Path中加入 Server Runtime,選擇Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代碼 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把項目部署到tomcat中。
至此項目錯誤全部解決,應該可以看到運行結果。
5.新建Servert測試
新建一個Servlet,HomeServlet,Eclipse會自動在web.xml中加入配置信息,HomeServet.java和web.xml的部分代碼如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代碼
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 運行tomcat,可以看到如下運行成功,這樣你就可以像一般的Java Web Project一樣進行開發。
6.最后
BAE中Java環境中百度使用的Jetty,而不是tomcat,Jetty的好處是不需要頻繁的重啟,修改的代碼即時就可看到運行結果。
通過SVN提交代碼到BAE,如果有需要做小的修改,可以通過百度的在線編輯工具直接修改。

空指針異常(Null Pointer Exception)是我們平時最容易碰到的,也是最令人討厭的異常。本文介紹如何避免出現空指針異常。 首先我們看如下的示例 private Boolean isFinished(String status) { if (status.equalsIgnoreCase("Finish")) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 如果status的值為空的話,那么將會出現空指針異常(本例第2行)。所以我們應該使用如下的方法
private Boolean isFinished(String status) { if ("Finish".equalsIgnoreCase(status)) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 這樣的話,如果status為空,也不會出現空指針異常。相信我們大多數朋友已經知道這樣的方法了,如果一個對象可能為null,那么不需要直接調用它的方法。
接下來我將接著提供幾種避免空指針的建議。
1.判斷Collection是否為空。
2.使用一些判斷方法。
3.assert關鍵字。
4.Assert類。
5.異常處理。
6.太多的點.操作語法。
7.使用StringUtils類
1.判斷Collection是否為空
Collection 為空是指Collection中沒有元素。一些開發者如果碰到Collection中沒有元素的時候,經常return null,更好的做法是,你應該return Collections.EMPTY_LIST,Collections.EMPTY_SET或者是Collections.EMPTY_MAP.
錯誤的代碼
public static List getEmployees() { List list = null; return list; }
正確的代碼
public static List getEmployees() { List list = Collections.EMPTY_LIST; return list; }
2.使用一些判斷方法
使用一些方法如contains(),indexOf(),isEmpty(),containsKey(),ContainsValue和hasNext()等來判斷,確保不存在空值。
示例:
String myName = "qiyadeng"; List list = Collections.EMPTY_LIST; boolean exist = list.contains(myName); int index = list.indexOf(myName); boolean isEmpty =list.isEmpty(); Map map =Collections.EMPTY_MAP; exist=map.containsKey(myName); exist=map.containsValue(myName); isEmpty=map.isEmpty(); Set set=Collections.EMPTY_SET; exist=set.contains(myName); isEmpty=set.isEmpty(); Iterator iterator; exist = iterator.hasNext();
3.assert關鍵字
在Java1.4版本之后,提供了斷言assert來確定你的代碼中的假設。使用的語法如下:
expression1是一個boolean表達式,如果expression1返回的false,系統將會拋出AssertError(沒有詳細信息)。
另外一種使用方法
assert expression1:expression2 如果expression1返回false,那么系統將會拋出AssertError,并且詳細信息為expression2。
示例:
public static String getManager(String employeeId) { assert (employeeId != null) : "employeeId must be not null"; return "qiyadeng"; } 我使用getManager(null)來調用getManger方法,最后運行的結果是"java.lang.AssertionError:employeedId must be not null"
注意記得使用java選項中加入-enableassertion開啟assertion功能。
4.Assert類
Assert類在com.bea.core.repackaged.springframework.util包中,有許多方法可以用于斷言。
public static String getManager(String employeeId) { Assert.notNull(employeeId, "employeeId must be not null"); Assert.hasLength(employeeId, "employeeId must has length greater than 0"); return "qiyadeng"; } 當我同樣使用getManager(null)來調用getManager方法,將獲得信息"java.lang.IllegalArgumentException: employeeId must be not null"。
5.異常處理
使用try catch處理異常或是檢查變量是否為空。
public static String getManager(String employeeId) { return null; } 如上代碼,我使用下面方法調用
String managerId = getManager("A015"); System.out.println(managerId.toString()); 將會發生"java.lang.NullPointerException",為了處理這個異常,我們應該使用try catch來處理異常或者是檢查變量是否為null。
try-catch方法
String managerId = getManager("A015"); try { System.out.println(managerId.toString()); } catch (NullPointerException npe) { //write your code here } 或者是對變量進行檢查
String managerId = getManager("A015"); if (managerId != null) { System.out.println(managerId.toString()); } else { //write your code here }
6.不要太多的點.操作語法
一些開發者使用太多的這樣的方法來減少代碼,但是這個對后面的維護和異常處理都是不太好的。
錯誤的寫法
String attrValue = (String)findViewObject("VO_NAME").getCurrentRow().getAttribute("Attribute_NAME"); 正確的寫法
ViewObject vo = findViewObject("VO_NAME"); Row row = vo.getCurrentRow(); String attrValue = (String)row.getAttribute("Attribute_NAME");
7.使用StringUtils類
StringUtil是org.apache.commns.lang包中的類,我們可以使用該類來避免空指針異常。
例如 StringUtils.isEmpty(),StringUtils.isBlank,StringUtils.equals()等等,更多的你可以參考文檔。
為了不出現空指針異常,在寫代碼的過程中需要時刻檢查你的代碼是否會拋出NullPointerException,如果你沒有時間及時調整的話,使用//TODO標記,便于你后面解決問題。
現在經常需要根據用戶提供的位置,提供一些和位置相關的信息。有時可以直接確定用戶的經度和緯度,有時不一定可以確定用戶的經度和緯度信息,用戶是 通過輸入一些路名、標志性建筑或是商場名等位置,但是我們的數據庫可能并沒有存法用戶可能輸入的這些位置信息的經度緯度,這時候可以使用一些地圖提供的 API來確定,用戶所輸入的位置信息的經度和緯度。 我們使用百度地圖提供的GeoCoding API實現從位置信息到經度緯度的轉換,詳細的使用說明可以參考 GeoCoding API。我們這里做一個簡單的演示 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//百度返回的經度緯度信息xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 使用JUnit進行測試 @Test public void testGeoCode() throws Exception { BaiduMapService bms = new BaiduMapService(); String response = bms.getGeoCode("上地十街十號"); BaiduGeoCodeResponse res = BaiduGeoCodeResponse.getBaiduGeoCode(response);//解析xml System.out.println(res.toString()); } 輸出的結果 <GeocoderSearchResponse> <status>OK</status> <result> <location> <lat>40.057098</lat> <lng>116.307175</lng> </location> <precise>1</precise> <confidence>80</confidence> <level>道路</level> </result> </GeocoderSearchResponse> BaiduGeoCodeResponse [lat=40.057098, lng=116.307175]
到了一個較陌生的環境,經常會在周邊找一些基礎設施,比如銀行,商場,餐廳等(還有一種更急切的是找廁所)。通過百度提供的地圖API,可以在你的應用中簡單做到,詳情可閱讀 Place API。我們以查找周邊銀行作為示例,需確定的參數至少有三個,要查找的位置的經度和緯度,需要查找的內容的類型或是關鍵字。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//位置xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } Junit測試 @Test public void testGetBaiduPlace() throws Exception{ BaiduMapService bms = new BaiduMapService(); String response = bms.getPalace("銀行", "39.915", "116.404"); List<BaiduPlaceResponse> list = BaiduPlaceResponse.getBaiduPlace(response); for(BaiduPlaceResponse res:list){ System.out.println(res.toString()); } } 輸出內容(省略部分內容) <?xml version="1.0" encoding="utf-8" ?> <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中國工商銀行東長安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>東城區東長安街1號東方廣場西三辦公樓1樓</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi</detail_url> <tag>銀行,王府井/東單</tag> </result> </results> </PlaceSearchResponse> BaiduPlaceResponse [name=中國工商銀行東長安街支行, telephone=null, address=東城區東長安街1號東方廣場西三辦公樓1樓, lat=39.915891, lng=116.41867, tag=null, detailUrl=http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&output=html&source=placeapi]
位置識別這是實際應用經常應用的消息,特別是很多商家,通過了解用戶位置,給用戶提供特別的產品或是商場的推薦。其中用戶可能發送兩種類型的消息: 1.微信地理位置信息 2.路名、標志性建筑或是商場名稱 1.微信地理位置消息認識一下,微信地理位置消息,包含一些什么信息 <xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1351776360</CreateTime> <MsgType><![CDATA[location]]></MsgType> <Location_X>23.134521</Location_X> <Location_Y>113.358803</Location_Y> <Scale>20</Scale> <Label><![CDATA[位置信息]]></Label> <MsgId>1234567890123456</MsgId> </xml>
包含的主要信息有經度緯度和Label的位置。可以根據label中描述的位置信息,提供給用戶對應的服務。也可根據用戶的經度緯度信息,提供你最近的產品或是有地域性的產品。
首先根據微信的地理位置信息,定義WeChatLocationMessage類,并能把Xml轉換為WeChatLocationMessage對象 public class WeChatLocationMessage { private String toUserName; private String fromUserName; private String createTime; private String msgType; private String locationx; private String localtiony; private String scale; private String label; private String msgId; public static WeChatLocationMessage getWeChatLocationMessage(String xml){ XStream xstream = new XStream(new DomDriver()); WeChatLocationMessage message = null; xstream.alias("xml", WeChatLocationMessage.class); xstream.aliasField("ToUserName", WeChatLocationMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatLocationMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatLocationMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatLocationMessage.class, "msgType"); xstream.aliasField("Location_X", WeChatLocationMessage.class, "locationx"); xstream.aliasField("Location_Y", WeChatLocationMessage.class, "localtiony"); xstream.aliasField("Scale", WeChatLocationMessage.class, "scale"); xstream.aliasField("Label", WeChatLocationMessage.class, "label"); xstream.aliasField("MsgId", WeChatLocationMessage.class, "msgId"); message = (WeChatLocationMessage)xstream.fromXML(xml); return message; } //getter and setter } 本文利用百度的地圖API,查找最近的銀行做為示例。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 輸出的結果 <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中國工商銀行東長安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>東城區東長安街1號東方廣場西三辦公樓1樓</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi </detail_url> <tag>銀行,王府井/東單</tag>
</result> </results> </PlaceSearchResponse> 接下來,把百度地圖反映出來的最近位置信息,以圖文消息的格式展示給微信用戶 public static String getWeChatReplyNewsMessageByBaiduPlace(List<BaiduPlaceResponse> placeList, double lat, double lng,String userName, int size){ WeChatReplyNewsMessage newsMessage = new WeChatReplyNewsMessage(); List<Item> items = new ArrayList<Item>(); StringBuffer strBuf = new StringBuffer(); logger.log(Level.INFO,"placeList count="+placeList.size()); newsMessage.setItems(items); if(placeList.size()>size){ newsMessage.setArticleCount(size); } else{ newsMessage.setArticleCount(placeList.size()); } logger.log(Level.INFO,"article count="+newsMessage.getArticleCount()); newsMessage.setCreateTime(new Date().getTime()+""); newsMessage.setMsgType("news"); newsMessage.setFuncFlag("0"); newsMessage.setToUserName(userName); newsMessage.setFromUserName(WeChatConstant.FROMUSERNAME); for(int i = 0;i <newsMessage.getArticleCount();i++){ BaiduPlaceResponse place = placeList.get(i); Double distance = GeoUtil.DistanceOfTwoPoints(Double.valueOf(place.getLng()), Double.valueOf(place.getLat()), lng, lat, GaussSphere.Beijing54); Item item = new Item(); item.setTitle(place.getName()+"["+distance+"米]"+"\n"+place.getAddress()+"\n"+place.getTelephone()); item.setPicUrl(""); item.setUrl(place.getDetailUrl()); item.setDescription(""); items.add(item); } logger.log(Level.INFO,"newMessage="+newsMessage.toString()); strBuf = strBuf.append(getWeChatNewsMessage(newsMessage)); return strBuf.toString(); } public static String getWeChatNewsMessage(WeChatReplyNewsMessage newsMessage){ XStream xstream = new XStream(new DomDriver()); xstream.alias("xml", WeChatReplyNewsMessage.class); xstream.aliasField("ToUserName", WeChatReplyNewsMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatReplyNewsMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatReplyNewsMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatReplyNewsMessage.class, "msgType"); xstream.aliasField("ArticleCount", WeChatReplyNewsMessage.class, "articleCount"); xstream.aliasField("Content", WeChatReplyNewsMessage.class, "content"); xstream.aliasField("FuncFlag", WeChatReplyNewsMessage.class, "funcFlag"); xstream.aliasField("Articles", WeChatReplyNewsMessage.class, "items"); xstream.alias("item", Item.class); xstream.aliasField("Title", Item.class, "title"); xstream.aliasField("Description", Item.class, "description"); xstream.aliasField("PicUrl", Item.class, "picUrl"); xstream.aliasField("Url", Item.class, "url"); return xstream.toXML(newsMessage); } 2.路名、標志性建筑或是商場名稱對路名、標志性建筑等信息,方法還是通過第三方地圖信息,確定輸入的位置信息的經度緯度。 本文使用百度地圖API,確定所查找的位置的經度和緯度。 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 確定了經度和緯度,問題就變成和第1種消息類型一致了,根據經度緯度去做相應處理。
3.源代碼本文的代碼較長,提供源代碼下載。 WeChatDemo下載
本文介紹,如果把Java Project轉換為Java Web Project,應該在多數的Eclipse的版本都類似。 1.Java Project一個Java Projec,在Eclipse中顯示的是一個“J”的藍色文件夾。
2.Project Facets右鍵項目屬性Properties,右側選擇菜單Project Facets,點擊converted to faceted form...
勾選dynamic web module
選擇下面的further configuration available
項目中的Web目錄和設置保持一致。
3.Java Web Project這樣你就轉換到了Java Web Project。 
1.新建Project 新建Java Project,并把mongo-java-driver驅動加入到項目bulid path中,如果你使用的是maven增加依賴。 <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.10.1</version> </dependency> 2.連接上MongoDB //>2.10版本 MongoClient mongo = new MongoClient( "localhost" , 27017 );
//老版本 Mongo mongo = new Mongo("localhost", 27017); 如果需要驗證,需要輸入用戶名和密碼 MongoClient mongoClient = new MongoClient(); DB db = mongoClient.getDB("database name"); boolean auth = db.authenticate("username", "password".toCharArray()); 3.MongoDB數據庫 得到MongoDB中的數據庫,如果數據庫名不存在,MongoDB會自動創建 DB db = mongo.getDB("database name"); 顯示所有的數據庫 List<String> dbs = mongo.getDatabaseNames(); for(String db : dbs){ System.out.println(db); } 4.MongoDB Collection(MongoDB表) 得到數據庫中的表 DB db = mongo.getDB("testdb"); DBCollection table = db.getCollection("user"); 顯示數據庫中的所有表 DB db = mongo.getDB("testdb"); Set<String> tables = db.getCollectionNames(); for(String coll : tables){ System.out.println(coll); } 5.插入、查找、更新、刪除操作 插入數據,向Collection(表)中插入一個Document DBCollection table = db.getCollection("user"); BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); 更新Document中的name="qiyadeng.com" DBCollection table = db.getCollection("user");
BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng");
BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com");
BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); 從Collection中查找name="qiyadeng.com"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } 刪除name="qiyadeng"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); table.remove(searchQuery);
6.完整的例子 package com.qiyadeng.mongodb;
import java.util.Date;
import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.MongoClient;
public class MongoDBSample {
public static void main(String[] args) throws Exception{ /**** Connect to MongoDB ****/ //2.10.0后,使用MongoClient MongoClient mongo = new MongoClient("localhost", 27017); /**** Get database ****/ // if database doesn't exists, MongoDB will create it for you DB db = mongo.getDB("testdb"); /**** Get collection / table from 'testdb' ****/ // if collection doesn't exists, MongoDB will create it for you DBCollection table = db.getCollection("user"); /**** Insert ****/ // create a document to store key and value BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); /**** Find and display ****/ BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } /**** Update ****/ // search document where name="qiyadeng" and update it with new values BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng"); BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com"); BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); /**** Find and display ****/ BasicDBObject searchQuery2 = new BasicDBObject().append("name", "qiyadeng.com"); DBCursor cursor2 = table.find(searchQuery2); while (cursor2.hasNext()) { System.out.println(cursor2.next()); } } }
輸出 { "_id" : { "$oid" : "51444c88874c79654063356b"} , "name" : "qiyadeng" , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"}} { "_id" : { "$oid" : "51444c88874c79654063356b"} , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"} , "name" : "qiyadeng.com"}
使用mongo驗證創建的數據庫testdb,collection user是否存在。
本文介紹如何安裝在windows 7中安裝MongoDB。 注:MongoDB并不像Windows上安裝其他軟件,只需要下載Zip包并解壓,然后配置數據存放目錄并啟動即可。 1.下載MongoDB從MongoDB官方網站,根據你的平臺選擇對應的windows的壓縮包并解壓,本文解壓到D:\mongodb\。
注:如果需要在命令行中快速使用MongoDB bin目錄下的命令,可以將D:\mongoDB\bin加入到Window環境變量。 2.配置數據文件在D:\mongodb\創建mongo.config文件,如下(并在d:\mongodb目錄下新建data,log文件夾) ##數據存儲的位置 dbpath=D:\mongodb\data ##所有的輸出位置 logpath=D:\mongodb\log\mongo.log ##日志讀寫操作 diaglog=3 3.運行MongoDB Server在命令控制行,切換到d:\mongodb\bin目錄下,使用命令mongod.exe --config d:\mongdb\mongo.config啟動MongoDb Server。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config all output going to: D:\mongodb\log\mongo.log 4.連接MongoDB新開啟一個命令行控制窗口,使用mongo.exe連接MongoDB Server. 5.設置MongoDB為Windows服務在命令行控制窗口,加入--install選項可以把MongoDB安裝為Windows服務。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config 啟動MongoDB的命令為:net start MongoDB 停車MongODB的命令為:net stop MongoDB 刪除MongoDB的命令為:mongod --remove
1.設置成為開發者模式
登錄微信工作平臺,選擇高級功能-進入開發模式,成為開發者。需要做如下圖配置。URL配置的信息是指,微信的后臺服務器把您的用戶消息發送到該URL處理。Token是你和微信之間的一個密碼,用來驗證消息是否是從微信的服務發送而來,而不是其他來攻擊你的系統。
現在你還不能設置,在設置時微信會GET請求你設置的URL,已檢測接口是否可以使用。只有等你準備好GET方法之后才可以進行設置。
2.實現GET方法
從文檔中知道,我們需要實現POST和GET方法,GET方法用于驗證微信和你的通訊驗證,POST用于消息處理。
新建Servlet HelloWeChat,先實現其中的GET方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 為了簡單起見,先不對消息來源進行校驗
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
可以在本地使用http://localhost:8080/QiyadengWeb/HelloWeChat?echostr=hello中文,先進行測試,如果沒有問題,可以部署到服務器上,然后在微信公眾平臺進行設置了。
3.實現POST方法
POST方法首先接收到微信公眾平臺傳送過來的XML,從中提取消息發送人和消息內容。更加消息發送內容,你可以增加自己的處理邏輯,最后拼裝成回復消息XML,返回給微信公眾平臺。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的處理邏輯,這里只是簡單重復消息
replyMsg.append("您給我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暫時看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
關于調試,這里推薦一個工具Fiddler,你可以模擬微信的POST消息到你的本地,而不必每次部署到服務器上進行調試。關于Fiddler的POST數據使用方法,可以參考下圖標注內容。
4.部署并測試
完成第一步,并和你的公眾帳號好進行對話,回復消息沒有問題的話,那就恭喜你了 。
5.依賴庫
使用maven的同學,添加以下依賴即可。非maven用戶,找到這些庫添加到buider path中即可。
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.3</version>
</dependency>
6.完整的代碼
package com.qiyadeng.wechat;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* Servlet implementation class HelloWeChat
*/
public class HelloWeChat extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloWeChat() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 為了簡單起見,先不對消息來源進行校驗
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的處理邏輯,這里只是簡單重復消息
replyMsg.append("您給我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暫時看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
private WeChatTextMessage getWeChatTextMessage(String xml){
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatTextMessage.class);
xstream.aliasField("ToUserName", WeChatTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatTextMessage.class, "content");
xstream.aliasField("MsgId", WeChatTextMessage.class, "msgId");
WeChatTextMessage wechatTextMessage = (WeChatTextMessage)xstream.fromXML(xml);
return wechatTextMessage;
}
private String getReplyTextMessage(String content, String weChatUser){
WeChatReplyTextMessage we = new WeChatReplyTextMessage();
we.setMessageType("text");
we.setFuncFlag("0");
we.setCreateTime(new Long(new Date().getTime()).toString());
we.setContent(content);
we.setToUserName(weChatUser);
we.setFromUserName("shanghaiweather");//TODO 你的公眾帳號微信號
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatReplyTextMessage.class);
xstream.aliasField("ToUserName", WeChatReplyTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatReplyTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatReplyTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatReplyTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatReplyTextMessage.class, "content");
xstream.aliasField("FuncFlag", WeChatReplyTextMessage.class, "funcFlag");
String xml =xstream.toXML(we);
return xml;
}
}
開始微信公眾平臺的開發,我們首先要了解微信平臺可以幫助我們做哪些事情? 使用您的公眾賬號登陸http://mp.weixin.qq.com/,選擇菜單--高級功能-開發模式--查看文檔,即能看到微信公眾平臺目前所能開發的功能。 一、通訊機制 公眾平臺的主要內容是 - 接受用戶發送給您公眾賬號的消息
- 給您的用戶回復消息
需要特別說明的是,發送消息和回復消失是一個連貫的過程,只能在一個對話中完成。也就是說您的用戶不找您說話,您是不能主動發送消息給你的客戶(群發是另外一種情況,有次數限制。你也可以申請付費使用微信CRM平臺)。所有的發送消息和接受消息,都需要微信平臺進行中轉。 二、消息類型 下面介紹用戶能給您發送的消息類型,也就是目前接受到的消息類型。 1.接受消息類型 1.1文本消息: 這也是我們平時碰到最多的,可以根據文本中提到的一些關鍵字,進行判斷,判斷用戶的含義,并進行回復。 1.2圖片消息: 目前通過圖片理解用戶想表達的意思,還是有較大難度,因此多數的公眾賬號,會選擇忽略圖片信息或選擇由人工來處理。只能說一句:圖片很美,但是我看不懂。 1.3地理位置消息: 用戶把他的位置發給您,這對大多數公眾賬號來說,是一個重要的信息。可以提供一些基于位置信息的服務,比如酒店預訂公眾賬號,可以給你推薦你周邊的酒店。 另外一個補充是,可以在文本消息中分析出位置信息,并加以利用。比如用戶輸入“南京路步行街”,可以提供用戶南京路步行街的相關商戶。 1.4鏈接消息: 目前還沒有看到開發模式中特別有效的使用方法。使用比較多的可能會是購物時或是咨詢時,對所談論的對象進行明確。 1.5事件推送消息: 當用戶進入到和你對話的過程中,可以先和用戶打招呼等。這個消息目前只支持4.5版本,且暫時還沒有開發。后續可想想的空間很大,比如用戶進入到會話之后,搖一搖會發生什么呢? 2.回復消息類型 2.1文本消息 這是我們平時發送最多的一類消息,當只需要簡單的文字即可回答用戶的消息時,可用文本消息。文本消息中可以帶有鏈接地址。 2.2圖文消息 圖文消息,這是我們在推送消息中經常看到的消息格式。每項內容可以點擊查看更詳細信息(當然你也可以把鏈接設置為空,使其不能跳轉) 2.3音樂消息 在你的答復中給用戶一個語音消息或是音樂,可以獲得不少用戶的親睞。 了解了公眾平臺的通訊機制和消息類型,接下來,我們開始準備開發環境了。
在類似PHP的項目中,經常會碰到項目開發目錄和運行目錄不是一個目錄的情況。 在window7下面有一個非常好的工具,可以做到自動同步。 如: mklink /J D:\wamp\www\shanghaisales C:\Users\Admin\git\ShanghaiSales\ShanghaiSales
|