1. 程式人生 > >基於JavaEE——微信網頁(六)微信訊息的接收處理

基於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秒將會再次發出請求,最多嘗試請求三次,將停止傳送(為了防止微信與我們的程式浪費效能,所以最好在這裡進行回覆處理)

微信訊息的處理到此告一段落,這裡可以進行被動回覆訊息(後續會說到),有興趣的小夥伴可以先嚐試一下!