基於JavaEE——微信網頁(六)微信訊息的接收處理
上篇文章我們提到了微信訊息的接收
那麼在開始開發之前,我們需要在微信配置一些引數
URL為我們用於接收微信訊息的網路地址,微信出於安全性與保密性的考慮,設定了訊息加密(可選)和配置口令效驗,下面的Token便是效驗口令,這個隨意設定,只需要在程式裡進行效驗操作時一致即可(這裡的配置需要公網ip或公網域名,沒有的朋友們可以選擇團購一個雲伺服器)
微信的口令效驗官方文件裡有,我這裡就不一一闡述說明了
進行了一個簡單的模板程式碼處理
import java.io.*; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.edt.service.RedisService; import com.edt.wechat.WeChatMsgFactory; import com.edt.wechat.bean.Msg; import com.edt.wechat.bean.event.Event; import com.edt.wechat.bean.event.LocationEvent; import com.edt.wechat.bean.normalmsg.RecognitionMsg; import com.iceutils.weixin.security.IceWeiXinTokenUtils; import org.dom4j.DocumentException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import static com.edt.common.SysInit.DICTIONARY_CONFIG; /** * * 接收微信引數,分發轉向 * @author: 苟開唯 * @date: 2017年12月12日 下午2:44:15 * */ @Controller @RequestMapping("/WechatMsgReceive") public class WechatMsgController { @Resource private RedisService redisService; /** * 接收微信傳送的訊息,根據請求方式分發轉向(儲存資訊) * @param request, response * @return java.lang.String * @author 苟開唯 * @date 2018/1/10 13:46 */ @RequestMapping("MsgReceive") @ResponseBody public String msgReceive(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { String method = request.getMethod(); response.setContentType("text/html; charset=UTF-8"); request.setCharacterEncoding("utf-8"); System.out.println("微信中轉"); /** * ================================ * 判斷訊息傳輸方式,若是get 則進行微信口令驗證 * 若是post,進行訊息處理 * ================================ */ PrintWriter out = null; try { out = response.getWriter(); } catch (IOException e) { e.printStackTrace(); } if("get".equalsIgnoreCase(method)){ System.out.println("微信效驗"); String signature = request.getParameter("signature"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String echostr = request.getParameter("echostr"); String weiXinTokenSign = IceWeiXinTokenUtils.getWeiXinTokenSign(DICTIONARY_CONFIG.get("wechat_token"), timestamp, nonce); if (signature.equals(weiXinTokenSign)) { out.write(echostr); System.out.println("效驗成功"); } else { System.out.println("效驗失敗"); } }else{ /** * ============= * post,進行訊息處理 * ============= */ try { StringBuffer sb = new StringBuffer(); BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream())); String inputLine; while ( (inputLine = in.readLine()) != null) { sb.append(inputLine); } System.out.println("接收微信訊息:" + sb.toString()); Msg msg = WeChatMsgFactory.msgHanding(sb.toString());//將資料封裝為bean實體類 //判斷資料若不符合規範,則返回空資料 ,停止執行 if(msg != null){ String rec = msgController(msg);//執行業務邏輯Controller out.write(rec);//返回資料 } } catch (DocumentException e ) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 普通訊息業務邏輯 * 根據訊息型別分發轉向 * @param msg msg * @return java.lang.String * @author 苟開唯 * @date 2018/1/10 9:01 */ private String msgController(Msg msg){ String rec = "success"; if(msg instanceof Msg ){ //判斷訊息型別 switch (msg.getMsgType()) { case "text": //TODO 文字訊息 break; case "image": //TODO 圖片訊息 break; case "voice": //判斷是否開通語音轉換功能 if(msg instanceof RecognitionMsg){ //TODO 開通了語音 }else{ //TODO 未開通語音轉換 } break; case "video": //TODO 視訊訊息 break; case "shortvideo": //TODO 視訊訊息 break; case "location": //TODO 地理訊息 break; case "link": //TODO 連結訊息 break; case "event": Event event = (Event)msg; //判斷事件型別 rec = eventController(event, rec);//不能確認事件訊息是否有返回值 break; default: //TODO 訊息異常處理 break; } }else{ //TODO 不是一個訊息型別 } return rec ; } /** * 事件訊息型別業務邏輯 * 根據事件型別分發轉向 * @param event, rec * @return java.lang.String * @author 苟開唯 * @date 2018/1/10 9:02 */ private String eventController(Event event, String rec){ switch (event.getEvent()) { case "LOCATION"://使用者地理位置事件 /* 使用者上報地理位置事件 * 1.獲取使用者地理資訊--封裝 * 2.將資訊儲存起來 */ LocationEvent location = (LocationEvent)event; redisService.opsValue_set("userLocation_"+location.getFromUserName(),location,1, TimeUnit.HOURS ); break; case "subscribe"://使用者關注事件 break; case "unsubscribe"://使用者取消關注事件 break; case "SCAN"://使用者掃描二維碼事件 break; case "CLICK"://使用者點選自定義選單事件(使用者點選自定義選單後,微信會把點選事件推送給開發者,請注意,點選選單彈出子選單,不會產生上報。) break; case "VIEW"://使用者點選選單跳轉連結時的事件推送 break; default: break; } return rec; } }
這裡 我進行了訊息的封裝
整理了一下實體類的繼承關係的結構圖,可以參考此圖自行封裝
這些基本的訊息型別可以滿足我們的日常的開發和使用。如果大家感興趣,可以跳轉到微信開發者文件進行了解。
經過分析,我們只需要在接收到的xml資訊的MsgType欄位進行判斷,檢視訊息型別,若不是event,則進行普通訊息封裝,若是event,則進行時間訊息處理(區別很小,只是欄位與訊息內容不一樣)
package com.xwu.wechat.msg; import com.xwu.wechat.msg.bean.Msg; import com.xwu.wechat.msg.bean.event.Event; import com.xwu.wechat.msg.bean.event.EventWithKey; import com.xwu.wechat.msg.bean.event.LocationEvent; import com.xwu.wechat.msg.bean.event.SubscribeEvent; import com.xwu.wechat.msg.bean.normalmsg.*; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import java.io.Serializable; /** * * 微信訊息工廠 * @author: 愛尚實訓 * @date: 2017年12月12日 下午3:32:05 * */ public class WeChatMsgFactory implements Serializable { private static final long serialVersionUID = -4365145153558799142L; /** * 根據訊息型別(MsgType欄位)進行分發轉向 * @param xmlString * @return com.edt.entity.wechat.Msg * @author 愛尚實訓 * @date 2018/1/9 17:36 */ public static Msg msgHanding(String xmlString) throws DocumentException{ if(xmlString == null || "".equals(xmlString.trim())){ return null; } Document document = DocumentHelper.parseText(xmlString); Msg msg = null; Element root = document.getRootElement(); String msgType = root.element("MsgType").getText(); System.out.println("訊息型別:" + msgType); switch (msgType) { case "text"://文字訊息 msg = textMsgParse(root); break; case "image"://圖片訊息 msg = pictureMsgParse(root); break; case "voice"://語音訊息 //判斷是否擁有語音轉換功能 if(root.element("Recognition") == null){ msg = voiceMsgParse(root); }else{ msg = recognitionMsgParse(root); } break; case "shortvideo"://小視訊訊息 msg = videoMsgParse(root); break; case "video"://視訊訊息 msg = videoMsgParse(root); break; case "location"://地理位置訊息 msg = locationMsgParse(root); break; case "link"://連結訊息 msg = linkMsgParse(root); break; case "event"://事件訊息 msg = getEvent(root); default: //TODO 訊息異常處理 break; } return msg; } /** * 生成事件型別訊息 * @param root * @return com.edt.entity.wechat.event.Event * @author 愛尚實訓 * @date 2018/1/9 17:36 */ private static Event getEvent(Element root){ Event rec = null; Element event = root.element("Event"); String eventType = event.getText(); switch (eventType) { case "LOCATION"://地理位置事件 System.out.println("LOCATION"); rec = locationEventParse(root); break; case "subscribe"://訂閱事件 if(root.element("EventKey") == null){ rec = eventParse(root); }else{ rec = subscribeEventParse(root); } break; case "unsubscribe"://取消訂閱事件 rec = eventParse(root); break; case "SCAN"://掃描二維碼事件 rec = subscribeEventParse(root); break; case "CLICK"://自定義選單事件 rec = eventWithKeyParse(root); break; case "VIEW"://自定義選單事件 rec = eventWithKeyParse(root); break; default: //TODO 異常事件訊息處理 break; } return rec; } //-------------------------普通訊息型別----------------------- /** * 生成文字資訊實體類 * @param root * @return com.edt.entity.wechat.normalmsg.TextMsg * @author 愛尚實訓 * @date 2018/1/9 17:36 */ private static TextMsg textMsgParse(Element root){ TextMsg msg = new TextMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setContent(root.element("Content").getText()); return msg; } /** * 生成視訊資訊實體類 * @param root * @return com.xwu.entity.wechat.normalmsg.VideoMsg * @author 愛尚實訓 * @date 2018/1/9 17:37 */ private static VideoMsg videoMsgParse(Element root){ VideoMsg msg = new VideoMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setMediaId(root.element("MediaId").getText()); msg.setThumbMediaId(root.element("ThumbMediaId").getText()); return msg; } /** * 生成圖片資訊實體類 * @param root * @return com.xwu.entity.wechat.normalmsg.PictureMsg * @author 愛尚實訓 * @date 2018/1/9 17:38 */ private static PictureMsg pictureMsgParse(Element root){ PictureMsg msg = new PictureMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setMediaId(root.element("MediaId").getText()); msg.setPicUrl(root.element("PicUrl").getText()); return msg; } /** * 生成聲音資訊實體類 * @param:WeChatMsgFactory * @return:VoiceMsg * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:34:01 * */ private static VoiceMsg voiceMsgParse(Element root){ VoiceMsg msg = new VoiceMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setMediaId(root.element("MediaId").getText()); msg.setFormat(root.element("Format").getText()); return msg; } /** * 生成地理資訊實體類 * @param:WeChatMsgFactory * @return:LocationMsg * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:34:20 * */ private static LocationMsg locationMsgParse(Element root){ LocationMsg msg = new LocationMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setLocation_X(Float.parseFloat(root.element("Location_X").getText())); msg.setLocation_Y(Float.parseFloat(root.element("Location_Y").getText())); msg.setScale(Integer.parseInt(root.element("Scale").getText())); msg.setLabel(root.element("Label").getText()); return msg; } /** * 生成連結資訊實體類 * @param:WeChatMsgFactory * @return:LinkMsg * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:34:42 * */ private static LinkMsg linkMsgParse(Element root){ LinkMsg msg = new LinkMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setTitle(root.element("Title").getText()); msg.setDescription(root.element("Description").getText()); msg.setUrl(root.element("Url").getText()); return msg; } /** * 生成語音識別資訊實體類 * @param:WeChatMsgFactory * @return:VoiceMsg * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:34:01 * */ private static RecognitionMsg recognitionMsgParse(Element root){ RecognitionMsg msg = new RecognitionMsg(); msg.setToUserName(root.element("ToUserName").getText()); msg.setFromUserName(root.element("FromUserName").getText()); msg.setCreateTime(root.element("CreateTime").getText()); msg.setMsgType(root.element("MsgType").getText()); msg.setMsgId(Long.parseLong(root.element("MsgId").getText())); msg.setMediaId(root.element("MediaId").getText()); msg.setFormat(root.element("Format").getText()); msg.setRecognition(root.element("Recognition").getText()); return msg; } //-------------------------------------------------------- //-------------------------事件訊息型別----------------------- /** * 生成地理資訊實體類 * @param: xml根節點 * @return:LocationEvent * @author: 愛尚實訓 * @date: 2017年12月12日 下午3:58:21 * */ private static LocationEvent locationEventParse(Element root){ LocationEvent locationEvent = new LocationEvent(); locationEvent.setToUserName(root.element("ToUserName").getText()); locationEvent.setFromUserName(root.element("FromUserName").getText()); locationEvent.setCreateTime(root.element("CreateTime").getText()); locationEvent.setMsgType(root.element("MsgType").getText()); locationEvent.setEvent(root.element("Event").getText()); locationEvent.setLatitude(root.element("Latitude").getText()); locationEvent.setLongitude(root.element("Longitude").getText()); locationEvent.setPrecision(root.element("Precision").getText()); return locationEvent; } /** * 生成Event實體類 * @param:WeChatMsgFactory * @return:Event * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:12:53 * */ private static Event eventParse(Element root){ Event event = new Event(); event.setToUserName(root.element("ToUserName").getText()); event.setFromUserName(root.element("FromUserName").getText()); event.setCreateTime(root.element("CreateTime").getText()); event.setMsgType(root.element("MsgType").getText()); event.setEvent(root.element("Event").getText()); return event; } /** * * 生成EventWithKey實體類 * @param:WeChatMsgFactory * @return:EventWithKey * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:13:39 * */ private static EventWithKey eventWithKeyParse(Element root){ EventWithKey event = new EventWithKey(); event.setToUserName(root.element("ToUserName").getText()); event.setFromUserName(root.element("FromUserName").getText()); event.setCreateTime(root.element("CreateTime").getText()); event.setMsgType(root.element("MsgType").getText()); event.setEvent(root.element("Event").getText()); event.setEventKey(root.element("EventKey").getText()); return event; } /** * 生成SubscribeEvent實體類 * @param:WeChatMsgFactory * @return:SubscribeEvent * @author: 愛尚實訓 * @date: 2018年1月8日 上午9:14:06 * */ private static SubscribeEvent subscribeEventParse(Element root){ SubscribeEvent event = new SubscribeEvent(); event.setToUserName(root.element("ToUserName").getText()); event.setFromUserName(root.element("FromUserName").getText()); event.setCreateTime(root.element("CreateTime").getText()); event.setMsgType(root.element("MsgType").getText()); event.setEvent(root.element("Event").getText()); String text = root.element("EventKey").getText(); //判斷是否為掃描事件還是掃描關注事件 if(text.indexOf("qrscene_") != -1){ text = text.substring(8, text.length()); } event.setEventKey(text); //event.setTicket(root.element("Ticket").getText()); return event; } //-------------------------------------------------------- }
程式碼邏輯:將微信傳送的資料進行工廠解析,會返回給我們一個msg型別的物件,可能也都有想到了,它是一個抽象父類,從工廠取出訊息物件後,再次進行訊息型別判斷,根據不同的業務邏輯型別,去做相應的業務邏輯。
整體來說,接收微信訊息,完全可以理解為帶著引數來訪問我們的controller。而最繁瑣的地方,只是對微信的訊息型別稍微混亂了一些,如果看不懂我的程式碼,首先把微信說明文件中的訊息型別進行整理,查詢公共欄位,進行優化封裝。其次統一管理,進行業務邏輯判斷,並返回給微信“success”即可,即代表接收成功,否則微信則認為傳送失敗,5秒為一次請求,超過5秒將會再次發出請求,最多嘗試請求三次,將停止傳送(為了防止微信與我們的程式浪費效能,所以最好在這裡進行回覆處理)
微信訊息的處理到此告一段落,這裡可以進行被動回覆訊息(後續會說到),有興趣的小夥伴可以先嚐試一下!