微信公眾平台開發系列


http://mobile.51cto.com/aprogram-389672.htm

http://www.qiyadeng.com/category/open-platform/

開始微信公眾平台的開發,我們首先要了解微信平台可以幫助我們做哪些事情?
使用您的公眾賬號登陸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音樂消息
   在你的答復中給用戶一個語音消息或是音樂,可以獲得不少用戶的親睞。
了解了公眾平台的通訊機制和消息類型,接下來,我們開始准備開發環境了

 

1.設置成為開發者模式

登錄微信工作平台,選擇高級功能-進入開發模式,成為開發者。需要做如下圖配置。URL配置的信息是指,微信的后台服務器把您的用戶消息發送到該URL處理。Token是你和微信之間的一個密碼,用來驗證消息是否是從微信的服務發送而來,而不是其他來攻擊你的系統。

現在你還不能設置,在設置時微信會GET請求你設置的URL,已檢測接口是否可以使用。只有等你准備好GET方法之后才可以進行設置。

2.實現GET方法

從文檔中知道,我們需要實現POST和GET方法,GET方法用於驗證微信和你的通訊驗證,POST用於消息處理。

新建Servlet HelloWeChat,先實現其中的GET方法

  1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  2.         // TODO 為了簡單起見,先不對消息來源進行校驗 
  3.         response.setContentType("text/html;charset=UTF-8"); 
  4.         PrintWriter pw = response.getWriter(); 
  5.         String echo = request.getParameter("echostr"); 
  6.         echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8"); 
  7.         pw.println(echo); 
  8.     } 

可以在本地使用http://localhost:8080/QiyadengWeb/HelloWeChat?echostr=hello中文,先進行測試,如果沒有問題,可以部署到服務器上,然后在微信公眾平台進行設置了。

3.實現POST方法

POST方法首先接收到微信公眾平台傳送過來的XML,從中提取消息發送人和消息內容。更加消息發送內容,你可以增加自己的處理邏輯,最后拼裝成回復消息XML,返回給微信公眾平台。

  1. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  2.         response.setContentType("text/html;charset=UTF-8"); 
  3.         PrintWriter pw = response.getWriter(); 
  4.         String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8"); 
  5.         WeChatTextMessage textMsg = null
  6.         try { 
  7.             textMsg = getWeChatTextMessage(wxMsgXml); 
  8.         } catch (Exception e) { 
  9.             e.printStackTrace(); 
  10.         } 
  11.         StringBuffer replyMsg = new StringBuffer(); 
  12.         if(textMsg != null){ 
  13.             //增加你所需要的處理邏輯,這里只是簡單重復消息 
  14.             replyMsg.append("您給我的消息是:"); 
  15.             replyMsg.append(textMsg.getContent()); 
  16.         } 
  17.         else
  18.             replyMsg.append(":)不是文本的消息,我暫時看不懂"); 
  19.         } 
  20.         String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName()); 
  21.         pw.println(returnXml); 
  22.     } 

關於調試,這里推薦一個工具Fiddler,你可以模擬微信的POST消息到你的本地,而不必每次部署到服務器上進行調試。關於Fiddler的POST數據使用方法,可以參考下圖標注內容。

4.部署並測試

完成第一步,並和你的公眾帳號好進行對話,回復消息沒有問題的話,那就恭喜你了

5.依賴庫

使用maven的同學,添加以下依賴即可。非maven用戶,找到這些庫添加到buider path中即可。

  1. <dependency> 
  2.     <groupId>joda-time</groupId> 
  3.     <artifactId>joda-time</artifactId> 
  4.     <version>2.2</version> 
  5. </dependency> 
  6. <dependency> 
  7.     <groupId>org.apache.commons</groupId> 
  8.     <artifactId>commons-io</artifactId> 
  9.     <version>1.3.2</version> 
  10. </dependency> 
  11. <dependency> 
  12.     <groupId>com.thoughtworks.xstream</groupId> 
  13.     <artifactId>xstream</artifactId> 
  14.     <version>1.4.3</version> 
  15. </dependency> 

6.完整的代碼

  1. package com.qiyadeng.wechat; 
  2. import java.io.IOException; 
  3. import java.io.PrintWriter; 
  4. import java.util.Date; 
  5. import javax.servlet.ServletException; 
  6. import javax.servlet.http.HttpServlet; 
  7. import javax.servlet.http.HttpServletRequest; 
  8. import javax.servlet.http.HttpServletResponse; 
  9. import org.apache.commons.io.IOUtils; 
  10. import com.thoughtworks.xstream.XStream; 
  11. import com.thoughtworks.xstream.io.xml.DomDriver; 
  12. /** 
  13.  * Servlet implementation class HelloWeChat 
  14.  */ 
  15. public class HelloWeChat extends HttpServlet { 
  16.     private static final long serialVersionUID = 1L; 
  17.     /** 
  18.      * @see HttpServlet#HttpServlet() 
  19.      */ 
  20.     public HelloWeChat() { 
  21.         super(); 
  22.     } 
  23.     /** 
  24.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 
  25.      */ 
  26.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  27.         // TODO 為了簡單起見,先不對消息來源進行校驗 
  28.         response.setContentType("text/html;charset=UTF-8"); 
  29.         PrintWriter pw = response.getWriter(); 
  30.         String echo = request.getParameter("echostr"); 
  31.         echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8"); 
  32.         pw.println(echo); 
  33.     } 
  34.     /** 
  35.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 
  36.      */ 
  37.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  38.         response.setContentType("text/html;charset=UTF-8"); 
  39.         PrintWriter pw = response.getWriter(); 
  40.         String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8"); 
  41.         WeChatTextMessage textMsg = null
  42.         try { 
  43.             textMsg = getWeChatTextMessage(wxMsgXml); 
  44.         } catch (Exception e) { 
  45.             e.printStackTrace(); 
  46.         } 
  47.         StringBuffer replyMsg = new StringBuffer(); 
  48.         if(textMsg != null){ 
  49.             //增加你所需要的處理邏輯,這里只是簡單重復消息 
  50.             replyMsg.append("您給我的消息是:"); 
  51.             replyMsg.append(textMsg.getContent()); 
  52.         } 
  53.         else
  54.             replyMsg.append(":)不是文本的消息,我暫時看不懂"); 
  55.         } 
  56.         String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName()); 
  57.         pw.println(returnXml); 
  58.     } 
  59.     private WeChatTextMessage getWeChatTextMessage(String xml){ 
  60.         XStream xstream = new XStream(new DomDriver()); 
  61.         xstream.alias("xml", WeChatTextMessage.class); 
  62.         xstream.aliasField("ToUserName", WeChatTextMessage.class"toUserName"); 
  63.         xstream.aliasField("FromUserName", WeChatTextMessage.class"fromUserName"); 
  64.         xstream.aliasField("CreateTime", WeChatTextMessage.class"createTime"); 
  65.         xstream.aliasField("MsgType", WeChatTextMessage.class"messageType"); 
  66.         xstream.aliasField("Content", WeChatTextMessage.class"content"); 
  67.         xstream.aliasField("MsgId", WeChatTextMessage.class"msgId"); 
  68.         WeChatTextMessage wechatTextMessage = (WeChatTextMessage)xstream.fromXML(xml);  
  69.         return wechatTextMessage; 
  70.     } 
  71.     private String getReplyTextMessage(String content, String weChatUser){ 
  72.         WeChatReplyTextMessage we = new WeChatReplyTextMessage(); 
  73.         we.setMessageType("text"); 
  74.         we.setFuncFlag("0"); 
  75.         we.setCreateTime(new Long(new Date().getTime()).toString()); 
  76.         we.setContent(content); 
  77.         we.setToUserName(weChatUser); 
  78.         we.setFromUserName("shanghaiweather");//TODO 你的公眾帳號微信號 
  79.         XStream xstream = new XStream(new DomDriver());  
  80.         xstream.alias("xml", WeChatReplyTextMessage.class); 
  81.         xstream.aliasField("ToUserName", WeChatReplyTextMessage.class"toUserName"); 
  82.         xstream.aliasField("FromUserName", WeChatReplyTextMessage.class"fromUserName"); 
  83.         xstream.aliasField("CreateTime", WeChatReplyTextMessage.class"createTime"); 
  84.         xstream.aliasField("MsgType", WeChatReplyTextMessage.class"messageType"); 
  85.         xstream.aliasField("Content", WeChatReplyTextMessage.class"content"); 
  86.         xstream.aliasField("FuncFlag", WeChatReplyTextMessage.class"funcFlag"); 
  87.         String xml =xstream.toXML(we); 
  88.         return xml; 
  89.     } 

 

 

 

位置識別這是實際應用經常應用的消息,特別是很多商家,通過了解用戶位置,給用戶提供特別的產品或是商場的推薦。其中用戶可能發送兩種類型的消息:

1.微信地理位置信息

2.路名、標志性建築或是商場名稱

1.微信地理位置消息

認識一下,微信地理位置消息,包含一些什么信息

  1. <xml> 
  2. <ToUserName><![CDATA[toUser]]></ToUserName> 
  3. <FromUserName><![CDATA[fromUser]]></FromUserName> 
  4. <CreateTime>1351776360</CreateTime> 
  5. <MsgType><![CDATA[location]]></MsgType> 
  6. <Location_X>23.134521</Location_X> 
  7. <Location_Y>113.358803</Location_Y> 
  8. <Scale>20</Scale> 
  9. <Label><![CDATA[位置信息]]></Label> 
  10. <MsgId>1234567890123456</MsgId> 
  11. </xml>  

包含的主要信息有經度緯度和Label的位置。可以根據label中描述的位置信息,提供給用戶對應的服務。也可根據用戶的經度緯度信息,提供你最近的產品或是有地域性的產品。

首先根據微信的地理位置信息,定義WeChatLocationMessage類,並能把Xml轉換為WeChatLocationMessage對象

  1. public class WeChatLocationMessage { 
  2.     private String toUserName; 
  3.     private String fromUserName; 
  4.     private String createTime; 
  5.     private String msgType; 
  6.     private String locationx; 
  7.     private String localtiony; 
  8.     private String scale; 
  9.     private String label; 
  10.     private String msgId; 
  11.     public static WeChatLocationMessage getWeChatLocationMessage(String xml){ 
  12.         XStream xstream = new XStream(new DomDriver()); 
  13.         WeChatLocationMessage  message = null
  14.         xstream.alias("xml", WeChatLocationMessage.class); 
  15.         xstream.aliasField("ToUserName", WeChatLocationMessage.class"toUserName"); 
  16.         xstream.aliasField("FromUserName", WeChatLocationMessage.class"fromUserName"); 
  17.         xstream.aliasField("CreateTime", WeChatLocationMessage.class"createTime"); 
  18.         xstream.aliasField("MsgType", WeChatLocationMessage.class"msgType"); 
  19.         xstream.aliasField("Location_X", WeChatLocationMessage.class"locationx"); 
  20.         xstream.aliasField("Location_Y", WeChatLocationMessage.class"localtiony"); 
  21.         xstream.aliasField("Scale", WeChatLocationMessage.class"scale"); 
  22.         xstream.aliasField("Label", WeChatLocationMessage.class"label"); 
  23.         xstream.aliasField("MsgId", WeChatLocationMessage.class"msgId"); 
  24.         message = (WeChatLocationMessage)xstream.fromXML(xml); 
  25.         return message; 
  26.     } 
  27. //getter and setter 

本文利用百度的地圖API,查找最近的銀行做為示例。

  1. public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ 
  2.     HttpClient httpClient = new DefaultHttpClient(); 
  3.     String url = palceRequestUrl(query,lat,lng); 
  4.     logger.log(Level.INFO, url); 
  5.     HttpGet httpget = new HttpGet(url); 
  6.     ResponseHandler<String> responseHandler = new BasicResponseHandler(); 
  7.     String responseBody = httpClient.execute(httpget, responseHandler); 
  8.     logger.log(Level.INFO,"baidu response:"+responseBody); 
  9.     return responseBody; 
  10.  
  11. public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { 
  12.     String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" 
  13.             + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; 
  14.     return url; 

輸出的結果

  1. <PlaceSearchResponse> 
  2.     <status>OK</status> 
  3.     <results> 
  4.         <result> 
  5.             <name>中國工商銀行東長安街支行</name> 
  6.             <location> 
  7.                 <lat>39.915891</lat> 
  8.                 <lng>116.41867</lng> 
  9.             </location> 
  10.             <address>東城區東長安街1號東方廣場西三辦公樓1樓</address> 
  11.             <uid>a025683c73033c35a21de987</uid> 
  12.             <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;amp;output=html&amp;amp;source=placeapi 
  13.             </detail_url> 
  14.             <tag>銀行,王府井/東單</tag> 
  15.         </result> 
  16.       </results> 
  17. </PlaceSearchResponse> 

接下來,把百度地圖反映出來的最近位置信息,以圖文消息的格式展示給微信用戶

  1.     public static String getWeChatReplyNewsMessageByBaiduPlace(List<BaiduPlaceResponse> placeList, double lat, double lng,String userName, int size){ 
  2.         WeChatReplyNewsMessage newsMessage = new WeChatReplyNewsMessage(); 
  3.         List<Item> items = new ArrayList<Item>(); 
  4.         StringBuffer strBuf = new StringBuffer(); 
  5.         logger.log(Level.INFO,"placeList count="+placeList.size()); 
  6.         newsMessage.setItems(items); 
  7.         if(placeList.size()>size){ 
  8.             newsMessage.setArticleCount(size); 
  9.         } 
  10.         else
  11.             newsMessage.setArticleCount(placeList.size()); 
  12.         } 
  13.         logger.log(Level.INFO,"article count="+newsMessage.getArticleCount()); 
  14.         newsMessage.setCreateTime(new Date().getTime()+""); 
  15.         newsMessage.setMsgType("news"); 
  16.         newsMessage.setFuncFlag("0"); 
  17.         newsMessage.setToUserName(userName); 
  18.         newsMessage.setFromUserName(WeChatConstant.FROMUSERNAME); 
  19.         for(int i = 0;i <newsMessage.getArticleCount();i++){ 
  20.             BaiduPlaceResponse place = placeList.get(i); 
  21.             Double distance = GeoUtil.DistanceOfTwoPoints(Double.valueOf(place.getLng()), Double.valueOf(place.getLat()), lng, lat, GaussSphere.Beijing54); 
  22.             Item item = new Item(); 
  23.             item.setTitle(place.getName()+"["+distance+"米]"+"\n"+place.getAddress()+"\n"+place.getTelephone()); 
  24.             item.setPicUrl(""); 
  25.             item.setUrl(place.getDetailUrl()); 
  26.             item.setDescription(""); 
  27.             items.add(item); 
  28.         } 
  29. logger.log(Level.INFO,"newMessage="+newsMessage.toString()); 
  30.         strBuf = strBuf.append(getWeChatNewsMessage(newsMessage)); 
  31.         return strBuf.toString(); 
  32.     } 
  33.     public static String getWeChatNewsMessage(WeChatReplyNewsMessage newsMessage){ 
  34.         XStream xstream = new XStream(new DomDriver()); 
  35.         xstream.alias("xml", WeChatReplyNewsMessage.class); 
  36.         xstream.aliasField("ToUserName", WeChatReplyNewsMessage.class"toUserName"); 
  37.         xstream.aliasField("FromUserName", WeChatReplyNewsMessage.class"fromUserName"); 
  38.         xstream.aliasField("CreateTime", WeChatReplyNewsMessage.class"createTime"); 
  39.         xstream.aliasField("MsgType", WeChatReplyNewsMessage.class"msgType"); 
  40.         xstream.aliasField("ArticleCount", WeChatReplyNewsMessage.class"articleCount"); 
  41.         xstream.aliasField("Content", WeChatReplyNewsMessage.class"content"); 
  42.         xstream.aliasField("FuncFlag", WeChatReplyNewsMessage.class"funcFlag"); 
  43.         xstream.aliasField("Articles", WeChatReplyNewsMessage.class"items"); 
  44.         xstream.alias("item", Item.class); 
  45.         xstream.aliasField("Title", Item.class"title"); 
  46.         xstream.aliasField("Description", Item.class"description"); 
  47.         xstream.aliasField("PicUrl", Item.class"picUrl"); 
  48.         xstream.aliasField("Url", Item.class"url"); 
  49.         return xstream.toXML(newsMessage); 
  50.     } 
  51. 2.路名、標志性建築或是商場名稱

    對路名、標志性建築等信息,方法還是通過第三方地圖信息,確定輸入的位置信息的經度緯度。

    本文使用百度地圖API,確定所查找的位置的經度和緯度。

    1. public String getGeoCode(String query) throws ClientProtocolException, IOException{ 
    2.         HttpClient httpClient = new DefaultHttpClient(); 
    3.         String url = geoCodeRequestUrl(query); 
    4.         logger.log(Level.INFO, url); 
    5.         HttpGet httpget = new HttpGet(url); 
    6.         ResponseHandler<String> responseHandler = new BasicResponseHandler(); 
    7.         String responseBody = httpClient.execute(httpget, responseHandler); 
    8.         logger.log(Level.INFO,"baidu response:"+responseBody); 
    9.         return responseBody; 
    10.     } 
    11.     public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ 
    12.         String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" 
    13.                 + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; 
    14.         return url; 
    15.     } 

    確定了經度和緯度,問題就變成和第1種消息類型一致了,根據經度緯度去做相應處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM