基于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秒将会再次发出请求,最多尝试请求三次,将停止发送(为了防止微信与我们的程序浪费性能,所以最好在这里进行回复处理)


微信消息的处理到此告一段落,这里可以进行被动回复消息(后续会说到),有兴趣的小伙伴可以先尝试一下!














展开阅读全文

没有更多推荐了,返回首页