微信公众号实现跟关注用户进行消息的交互功能(2)实现交互功能

一、解析微信服务器传来的消息

因为微信服务器发送过来的是xml格式的消息,所以我们可以采用 开源框架dom4j去解析xml 。

所用到的jar 包是:dom4j-1.6.1.jar  

官网下载地址:http://www.dom4j.org/dom4j-1.6.1/

二、将响应消息转换成xml返回给微信服务器

如何将响应消息转换成xml返回的问题,这里我们将  采用开源框架xstream来实现Java类到xml的转换

所用到的jar包为:xstream-1.3.1.jar

 

由于是maven工程,所以在pom.xml添加以下依赖

 <!--wechat微信  -->
		<dependency>
		    <groupId>dom4j</groupId>
		    <artifactId>dom4j</artifactId>
		    <version>1.6.1</version>
		</dependency>
		<dependency>
		    <groupId>xstream</groupId>
		    <artifactId>xstream</artifactId>
		    <version>1.2.2</version>
		</dependency>
		<dependency>
		    <groupId>commons-httpclient</groupId>
		    <artifactId>commons-httpclient</artifactId>
		    <version>3.1</version>
		</dependency>

封装消息处理工具:

package cn.com.comit.appointment.modules.wechat.utils;



import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;

import cn.com.comit.appointment.modules.wechat.message.resp.Article;
import cn.com.comit.appointment.modules.wechat.message.resp.ImageMessage;
import cn.com.comit.appointment.modules.wechat.message.resp.MusicMessage;
import cn.com.comit.appointment.modules.wechat.message.resp.NewsMessage;
import cn.com.comit.appointment.modules.wechat.message.resp.TextMessage;
import cn.com.comit.appointment.modules.wechat.message.resp.VideoMessage;
import cn.com.comit.appointment.modules.wechat.message.resp.VoiceMessage;

/**
* 类名: MessageUtil </br>
* 描述: 消息处理工具类</br>
 */
public class MessageUtil {
    // 请求消息类型:文本
    public static final String REQ_MESSAGE_TYPE_TEXT = "text";
    // 请求消息类型:图片
    public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
    // 请求消息类型:语音
    public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
    // 请求消息类型:视频
    public static final String REQ_MESSAGE_TYPE_VIDEO = "video";
    // 请求消息类型:小视频
    public static final String REQ_MESSAGE_TYPE_SHORTVIDEO = "shortvideo";
    // 请求消息类型:地理位置
    public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
    // 请求消息类型:链接
    public static final String REQ_MESSAGE_TYPE_LINK = "link";

    // 请求消息类型:事件推送
    public static final String REQ_MESSAGE_TYPE_EVENT = "event";

    // 事件类型:subscribe(订阅)
    public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
    // 事件类型:unsubscribe(取消订阅)
    public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
    // 事件类型:scan(用户已关注时的扫描带参数二维码)
    public static final String EVENT_TYPE_SCAN = "scan";
    // 事件类型:LOCATION(上报地理位置)
    public static final String EVENT_TYPE_LOCATION = "LOCATION";
    // 事件类型:CLICK(自定义菜单)
    public static final String EVENT_TYPE_CLICK = "CLICK";

    // 响应消息类型:文本
    public static final String RESP_MESSAGE_TYPE_TEXT = "text";
    // 响应消息类型:图片
    public static final String RESP_MESSAGE_TYPE_IMAGE = "image";
    // 响应消息类型:语音
    public static final String RESP_MESSAGE_TYPE_VOICE = "voice";
    // 响应消息类型:视频
    public static final String RESP_MESSAGE_TYPE_VIDEO = "video";
    // 响应消息类型:音乐
    public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
    // 响应消息类型:图文
    public static final String RESP_MESSAGE_TYPE_NEWS = "news";

    /**
     * 解析微信发来的请求(XML)
     * 
     * @param request
     * @return Map<String, String>
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map<String, String> map = new HashMap<String, String>();

        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();

        // 遍历所有子节点
        for (Element e : elementList)
            map.put(e.getName(), e.getText());

        // 释放资源
        inputStream.close();
        inputStream = null;

        return map;
    }

    /**
     * 扩展xstream使其支持CDATA
     */
    private static XStream xstream = new XStream(new XppDriver() {
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                // 对所有xml节点的转换都增加CDATA标记
                boolean cdata = true;

                @SuppressWarnings("unchecked")
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }

                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });

    /**
     * 文本消息对象转换成xml
     * 
     * @param textMessage 文本消息对象
     * @return xml
     */
    public static String messageToXml(TextMessage textMessage) {
        xstream.alias("xml", textMessage.getClass());
        return xstream.toXML(textMessage);
    }

    /**
     * 图片消息对象转换成xml
     * 
     * @param imageMessage 图片消息对象
     * @return xml
     */
    public static String messageToXml(ImageMessage imageMessage) {
        xstream.alias("xml", imageMessage.getClass());
        return xstream.toXML(imageMessage);
    }

    /**
     * 语音消息对象转换成xml
     * 
     * @param voiceMessage 语音消息对象
     * @return xml
     */
    public static String messageToXml(VoiceMessage voiceMessage) {
        xstream.alias("xml", voiceMessage.getClass());
        return xstream.toXML(voiceMessage);
    }

    /**
     * 视频消息对象转换成xml
     * 
     * @param videoMessage 视频消息对象
     * @return xml
     */
    public static String messageToXml(VideoMessage videoMessage) {
        xstream.alias("xml", videoMessage.getClass());
        return xstream.toXML(videoMessage);
    }

    /**
     * 音乐消息对象转换成xml
     * 
     * @param musicMessage 音乐消息对象
     * @return xml
     */
    public static String messageToXml(MusicMessage musicMessage) {
        xstream.alias("xml", musicMessage.getClass());
        return xstream.toXML(musicMessage);
    }

    /**
     * 图文消息对象转换成xml
     * 
     * @param newsMessage 图文消息对象
     * @return xml
     */
    public static String messageToXml(NewsMessage newsMessage) {
        xstream.alias("xml", newsMessage.getClass());
        xstream.alias("item", new Article().getClass());
        return xstream.toXML(newsMessage);
    }
}

所用到的实体类可以看微信公众号实现跟关注用户进行消息的交互功能(1)封装基本类

在控制层进行消息的接受与响应:

/**
 * 微信公众号控制器
 * @author ChenXiHua
 * @version 2018-11-2
 *
 */
@Controller
@RequestMapping("/wechat")
public class WechatController implements IWechatController {
	Logger log=LoggerFactory.getLogger(WechatController.class);
	
	@Autowired
	private IAppointmentRecordService appointmentRecordService;
	@Autowired
	private IWechatService wechatService;
	
	/**
	 * 验证微信
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@RequestMapping(value = "/toAppoin", method = RequestMethod.GET)
	public void wechat(HttpServletRequest request, HttpServletResponse response) 
			 throws IOException { 
			 System.out.println("接口测试开始!!!"); 
			 //微信加密签名 
			 String signature = request.getParameter("signature"); 
			 //时间戳 
			 String timestamp = request.getParameter("timestamp"); 
			 //随机数 
			 String nonce = request.getParameter("nonce"); 
			 //随机字符串 
			 String echostr = request.getParameter("echostr"); 
			  
			 PrintWriter out = response.getWriter(); 
			 //通过校验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 
			 if(CheckoutUtil.checkSignature(signature,timestamp,nonce)){ 
				 System.out.println("cs----------"+echostr);
			        out.print(echostr); 
			 } 
			 out.close(); 
			 out = null; 
			  
			  
			 } 
	
	 /**
     * 处理微信服务器发来的消息
     */
	@RequestMapping(value = "/toAppoin", method = RequestMethod.POST)
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws  IOException {
        // 消息的接收、处理、响应
        // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println("请求进入-----");
        // 调用核心业务类接收消息、处理消息
        System.out.println("req--->"+request);
        String respXml = CoreServiceImpl.processRequest(request);
        System.out.println("RESP---toAppointPost-->"+respXml);
        // 响应消息
        PrintWriter out = response.getWriter();
        out.print(respXml);
        out.close();
    }
	

/toAppoin方法的get是配置微信公众号的验证方法,详情可以看微信公众号开发需要准备的一些资料以及如何搭建一个本地测试服务环境

/toAppoin方法的post是用户和公众号交互功能的处理方法

在服务service层实现交互功能:

package cn.com.comit.appointment.modules.wechat.service.impl;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import cn.com.comit.appointment.modules.wechat.message.resp.Article;
import cn.com.comit.appointment.modules.wechat.message.resp.NewsMessage;
import cn.com.comit.appointment.modules.wechat.message.resp.TextMessage;
import cn.com.comit.appointment.modules.wechat.utils.MessageUtil;


/**
* 类名: CoreService </br>
* 描述: 核心服务类 </br>
 */
public class CoreServiceImpl {
    /**
     * 处理微信发来的请求
     * @param request
     * @return xml
     */
    public static String processRequest(HttpServletRequest request) {
        // xml格式的消息数据
        String respXml = null;
        // 默认返回的文本消息内容
        String respContent = "未知的消息类型!";
        try {
            // 调用parseXml方法解析请求消息
            Map<String, String> requestMap = MessageUtil.parseXml(request);
            // 发送方帐号
            String fromUserName = requestMap.get("FromUserName");
            // 开发者微信号
            String toUserName = requestMap.get("ToUserName");
            // 消息类型
            String msgType = requestMap.get("MsgType");

            // 回复文本消息
            TextMessage textMessage = new TextMessage();
            textMessage.setToUserName(fromUserName);
            textMessage.setFromUserName(toUserName);
            textMessage.setCreateTime(new Date().getTime());
            textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);

            // 文本消息
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
                respContent = "您发送的是文本消息!";
            }
            // 图片消息
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
                respContent = "您发送的是图片消息!";
            }
            // 语音消息
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
                respContent = "您发送的是语音消息!";
            }
            // 视频消息
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VIDEO)) {
                respContent = "您发送的是视频消息!";
            }
            // 视频消息
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_SHORTVIDEO)) {
                respContent = "您发送的是小视频消息!";
            }
            // 地理位置消息
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
                respContent = "您发送的是地理位置消息!";
            }
            // 链接消息
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
                respContent = "您发送的是链接消息!";
            }
            // 事件推送
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
                // 事件类型
                String eventType = requestMap.get("Event");
                // 关注
                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
                    respContent = "谢谢您的关注!";
                }
                // 取消关注
                else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
                    // TODO 取消订阅后用户不会再收到公众账号发送的消息,因此不需要回复
                }
                // 扫描带参数二维码
                else if (eventType.equals(MessageUtil.EVENT_TYPE_SCAN)) {
                    // TODO 处理扫描带参数二维码事件
                }
                // 上报地理位置
                else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION)) {
                    // TODO 处理上报地理位置事件
                }
                // 自定义菜单
                else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
                	String eventKey = requestMap.get("EventKey");  
                    // TODO 处理菜单点击事件
                	if (eventKey.equals("11")) {  
                        respContent = "天气预报菜单项被点击!";  
                    } else if (eventKey.equals("12")) {  
                        respContent = "公交查询菜单项被点击!";  
                    } else if (eventKey.equals("13")) {  
                        respContent = "周边搜索菜单项被点击!";  
                    } else if (eventKey.equals("14")) {  
                        respContent = "历史上的今天菜单项被点击!";  
                    } else if (eventKey.equals("21")) {  
                        respContent = "歌曲点播菜单项被点击!";  
                    } else if (eventKey.equals("22")) {  
                        respContent = "经典游戏菜单项被点击!";  
                    } else if (eventKey.equals("23")) {  
                        respContent = "美女电台菜单项被点击!";  
                    } else if (eventKey.equals("24")) {  
                        respContent = "人脸识别菜单项被点击!";  
                    } else if (eventKey.equals("25")) {  
                        respContent = "聊天唠嗑菜单项被点击!";  
                    } else if (eventKey.equals("31")) {  
                        respContent = "Q友圈菜单项被点击!";  
                    } else if (eventKey.equals("32")) {  
                        respContent = "电影排行榜菜单项被点击!";  
                    } else if (eventKey.equals("33")) {  
                    	
                    	
                        respContent = "幽默笑话菜单项被点击!"; 
                     // 创建图文消息(回复用)
                        NewsMessage newsMessage = new NewsMessage();
                        newsMessage.setToUserName(fromUserName);
                        newsMessage.setFromUserName(toUserName);
                        newsMessage.setCreateTime(new Date().getTime());
                        newsMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS);
     
                        List<Article> articleList = new ArrayList<Article>();
     
                        Article article = new Article();
                        article.setTitle("违章预约处理");
                        //article.setDescription("我不是高手,我不会武功。"); 
                        article.setPicUrl("http://pic.cnblogs.com/avatar/466668/20150530175722.png");
                        article.setUrl("http://grefwolf.free.idcfengye.com/appointment/wechat/toAppoin/toAppointInit");
                        articleList.add(article);
                        // 设置图文消息个数
                        newsMessage.setArticleCount(articleList.size());
                        // 设置图文消息包含的图文集合
                        newsMessage.setArticles(articleList);
                        // 将图文消息对象转换成xml字符串
                        respXml = MessageUtil.messageToXml(newsMessage);
                        return respXml;
                        
                        
                        
                    }  
                }
            }
            // 设置文本消息的内容
            textMessage.setContent(respContent);
            // 将文本消息对象转换成xml
            respXml = MessageUtil.messageToXml(textMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return respXml;
    }
}

 

效果图:

微信公众号实现跟关注用户进行消息的交互功能(2)实现交互功能

 

我的座右铭:不会,我可以学;落后,我可以追赶;跌倒,我可以站起来;我一定行。