1. 程式人生 > >基於springboot+mybatis的微信公眾號開發第三篇-訊息的接收與回覆

基於springboot+mybatis的微信公眾號開發第三篇-訊息的接收與回覆

1、在寫處理訊息的方法前,我們得把相關的model類寫好。
在model包下建立message(req與resp),具體建立如圖所示:
這裡寫圖片描述

BaseMessage類

/**
 * 訊息基類(普通使用者 -> 公眾帳號)
 *
 */
public class BaseMessage {
    // 開發者微訊號
    private String ToUserName;
    // 傳送方帳號(一個OpenID)
    private String FromUserName;
    // 訊息建立時間 (整型)
    private long CreateTime;
    // 訊息型別(text/image/location/link)
private String MsgType; // 訊息id,64位整型 private long MsgId; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public
void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public long getCreateTime() { return CreateTime; } public void setCreateTime(long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public
void setMsgType(String msgType) { MsgType = msgType; } public long getMsgId() { return MsgId; } public void setMsgId(long msgId) { MsgId = msgId; } }

ImageMessage類

/**
 * 圖片訊息
 *
 */
public class ImageMessage extends BaseMessage {
    // 圖片連結
    private String PicUrl;

    public String getPicUrl() {
        return PicUrl;
    }

    public void setPicUrl(String picUrl) {
        PicUrl = picUrl;
    }
}

LinkMessage類

/**
 * 連結訊息
 *
 */
public class LinkMessage extends BaseMessage {
    // 訊息標題
    private String Title;
    // 訊息描述
    private String Description;
    // 訊息連結
    private String Url;

    public String getTitle() {
        return Title;
    }

    public void setTitle(String title) {
        Title = title;
    }

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        Description = description;
    }

    public String getUrl() {
        return Url;
    }

    public void setUrl(String url) {
        Url = url;
    }
}

LocationMessage類

/**
 * 地理位置訊息
 *
 */
public class LocationMessage extends BaseMessage {
    // 地理位置維度
    private String Location_X;
    // 地理位置經度
    private String Location_Y;
    // 地圖縮放大小
    private String Scale;
    // 地理位置資訊
    private String Label;

    public String getLocation_X() {
        return Location_X;
    }

    public void setLocation_X(String location_X) {
        Location_X = location_X;
    }

    public String getLocation_Y() {
        return Location_Y;
    }

    public void setLocation_Y(String location_Y) {
        Location_Y = location_Y;
    }

    public String getScale() {
        return Scale;
    }

    public void setScale(String scale) {
        Scale = scale;
    }

    public String getLabel() {
        return Label;
    }

    public void setLabel(String label) {
        Label = label;
    }
}

MenuMessage類

public class MenuMessage extends BaseMessage {
    private String EventKey;

    public String getEventKey() {
        return EventKey;
    }

    public void setEventKey(String eventKey) {
        EventKey = eventKey;
    }
}

TextMessage類

/**
 * 文字訊息
 *
 */
public class TextMessage extends BaseMessage {
    // 訊息內容
    private String Content;

    public String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }
}

VoiceMessage類

/**
 * 音訊訊息
 *
 */
public class VoiceMessage extends BaseMessage {
    // 媒體ID
    private String MediaId;
    // 語音格式
    private String Format;

    public String getMediaId() {
        return MediaId;
    }

    public void setMediaId(String mediaId) {
        MediaId = mediaId;
    }

    public String getFormat() {
        return Format;
    }

    public void setFormat(String format) {
        Format = format;
    }
}

Article類

/**
 * 圖文model
 *
 */
public class Article {
    // 圖文訊息名稱
    private String Title;
    // 圖文訊息描述
    private String Description;
    // 圖片連結,支援JPG、PNG格式,較好的效果為大圖640*320,小圖80*80,限制圖片連結的域名需要與開發者填寫的基本資料中的Url一致
    private String PicUrl;
    // 點選圖文訊息跳轉連結
    private String Url;

    public String getTitle() {
        return Title;
    }

    public void setTitle(String title) {
        Title = title;
    }

    public String getDescription() {
        return null == Description ? "" : Description;
    }

    public void setDescription(String description) {
        Description = description;
    }

    public String getPicUrl() {
        return null == PicUrl ? "" : PicUrl;
    }

    public void setPicUrl(String picUrl) {
        PicUrl = picUrl;
    }

    public String getUrl() {
        return null == Url ? "" : Url;
    }

    public void setUrl(String url) {
        Url = url;
    }

}

BaseMessage類

/**
 * 訊息基類(公眾帳號 -> 普通使用者)
 *
 */
public class BaseMessage {
    // 接收方帳號(收到的OpenID)
    private String ToUserName;
    // 開發者微訊號
    private String FromUserName;
    // 訊息建立時間 (整型)
    private long CreateTime;
    // 訊息型別(text/music/news)
    private String MsgType;
    // 位0x0001被標誌時,星標剛收到的訊息
    private int FuncFlag;

    public String getToUserName() {
        return ToUserName;
    }

    public void setToUserName(String toUserName) {
        ToUserName = toUserName;
    }

    public String getFromUserName() {
        return FromUserName;
    }

    public void setFromUserName(String fromUserName) {
        FromUserName = fromUserName;
    }

    public long getCreateTime() {
        return CreateTime;
    }

    public void setCreateTime(long createTime) {
        CreateTime = createTime;
    }

    public String getMsgType() {
        return MsgType;
    }

    public void setMsgType(String msgType) {
        MsgType = msgType;
    }

    public int getFuncFlag() {
        return FuncFlag;
    }

    public void setFuncFlag(int funcFlag) {
        FuncFlag = funcFlag;
    }
}

NewsMessage類

/**
 * 文字訊息
 *
 */
public class NewsMessage extends BaseMessage {
    // 圖文訊息個數,限制為10條以內
    private int ArticleCount;
    // 多條圖文訊息資訊,預設第一個item為大圖
    private List<Article> Articles;

    public int getArticleCount() {
        return ArticleCount;
    }

    public void setArticleCount(int articleCount) {
        ArticleCount = articleCount;
    }

    public List<Article> getArticles() {
        return Articles;
    }

    public void setArticles(List<Article> articles) {
        Articles = articles;
    }
}

TextMessage類

/**
 * 文字訊息
 *
 */
public class TextMessage extends BaseMessage {
    // 回覆的訊息內容
    private String Content;

    public String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }
}

2、model類建立好了之後,在util包中建立一個MessageUtil類來解析和響應訊息。
a、接收訊息時是xml,所以需要解析,這裡我們藉助於開源框架dom4j去解析xml(這裡使用的是dom4j-1.6.1.jar)
b、而響應訊息要把java類轉換成xml,這裡我們將採用開源框架xstream來實現兩者的轉換(這裡使用的是xstream-1.3.1.jar)

package com.util;

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 com.model.message.response.Article;
import com.model.message.response.NewsMessage;
import com.model.message.response.TextMessage;
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;

/**
 * 訊息工具類
 *
 */
public class MessageUtil {

    /**
     * 返回訊息型別:文字
     */
    public static final String RESP_MESSAGE_TYPE_TEXT = "text";

    /**
     * 返回訊息型別:音樂
     */
    public static final String RESP_MESSAGE_TYPE_MUSIC = "music";

    /**
     * 返回訊息型別:圖文
     */
    public static final String RESP_MESSAGE_TYPE_NEWS = "news";

    /**
     * 請求訊息型別:文字
     */
    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_LINK = "link";

    /**
     * 請求訊息型別:地理位置
     */
    public static final String REQ_MESSAGE_TYPE_LOCATION = "location";

    /**
     * 請求訊息型別:音訊
     */
    public static final String REQ_MESSAGE_TYPE_VOICE = "voice";

    /**
     * 請求訊息型別:推送
     */
    public static final String REQ_MESSAGE_TYPE_EVENT = "event";

    /**
     * 事件型別:subscribe(訂閱)and 未關注群體掃描二維碼
     */
    public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

    /**
     * 事件型別:已關注群體掃描二維碼
     */
    public static final String EVENT_TYPE_SCAN="SCAN";
    /**
     * 事件型別:unsubscribe(取消訂閱)
     */
    public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

    /**
     * 事件型別:CLICK(自定義選單點選事件)
     */
    public static final String EVENT_TYPE_CLICK = "CLICK";
    /**
     * 事件型別:VIEW(點選自定義選單跳轉連結時的事件)
     */
    public static final String EVENT_TYPE_VIEW = "VIEW";

    /**
     * 事件型別:transfer_customer_service(把訊息推送給客服)
     */
    public static final String EVENT_TYPE_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";


    /**
     * 解析微信發來的請求(XML)
     *
     * @param request
     * @return
     * @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;
    }

    /**
     * 文字訊息物件轉換成xml
     *
     * @param textMessage 文字訊息物件
     * @return xml
     */
    public static String textMessageToXml(TextMessage textMessage) {
        xstream.alias("xml", textMessage.getClass());
        return xstream.toXML(textMessage);
    }


    /**
     * 圖文訊息物件轉換成xml
     *
     * @param newsMessage 圖文訊息物件
     * @return xml
     */
    public static String newsMessageToXml(NewsMessage newsMessage) {
        xstream.alias("xml", newsMessage.getClass());
        xstream.alias("item", new Article().getClass());
        return xstream.toXML(newsMessage);
    }

    /**
     * 擴充套件xstream,使其支援CDATA塊
     *
     * @date 2013-05-19
     */
    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);
                    }
                }
            };
        }
    });
}

3、在CoreServiceImpl.java中完善processRequest的邏輯

注:
這裡加入了預設回覆訊息,qq懷舊錶情回覆(最新的無法識別),單、多圖文回覆等等。

package com.service.core;

import com.model.message.response.Article;
import com.model.message.response.NewsMessage;
import com.model.message.response.TextMessage;
import com.util.MessageUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 核心服務類
 */
@Service("coreService")
public class CoreServiceImpl implements CoreService {

    private static Logger log = LoggerFactory.getLogger(CoreServiceImpl.class);

    /**
     * 處理微信發來的請求(包括事件的推送)
     *
     * @param request
     * @return
     */
    public  String processRequest(HttpServletRequest request) {

        String respMessage = null;
        try {
            // 預設返回的文字訊息內容
            String respContent = "請求處理異常,請稍候嘗試!";
            // xml請求解析
            Map<String, String> requestMap = MessageUtil.parseXml(request);
            // 傳送方帳號(open_id)
            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);
            textMessage.setFuncFlag(0);


            // 建立圖文訊息
            NewsMessage newsMessage = new NewsMessage();
            newsMessage.setToUserName(fromUserName);
            newsMessage.setFromUserName(toUserName);
            newsMessage.setCreateTime(new Date().getTime());
            newsMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS);
            newsMessage.setFuncFlag(0);

            List<Article> articleList = new ArrayList<Article>();
            // 接收文字訊息內容
            String content = requestMap.get("Content");
            // 自動回覆文字訊息
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {

                //如果使用者傳送表情,則回覆同樣表情。
                if (isQqFace(content)) {
                    respContent = content;
                    textMessage.setContent(respContent);
                    // 將文字訊息物件轉換成xml字串
                    respMessage = MessageUtil.textMessageToXml(textMessage);
                } else {
                    //回覆固定訊息
                    switch (content) {

                        case "1": {
                            StringBuffer buffer = new StringBuffer();
                            buffer.append("您好,我是小8,請回複數字選擇服務:").append("\n\n");
                            buffer.append("11 可檢視測試單圖文").append("\n");
                            buffer.append("12  可測試多圖文傳送").append("\n");
                            buffer.append("13  可測試網址").append("\n");

                            buffer.append("或者您可以嘗試傳送表情").append("\n\n");
                            buffer.append("回覆“1”顯示此幫助選單").append("\n");
                            respContent = String.valueOf(buffer);
                            textMessage.setContent(respContent);
                            respMessage = MessageUtil.textMessageToXml(textMessage);
                            break;
                        }
                        case "11": {
                            //測試單圖文回覆
                            Article article = new Article();
                            article.setTitle("微信公眾帳號開發教程Java版");
                            // 圖文訊息中可以使用QQ表情、符號表情
                            article.setDescription("這是測試有沒有換行\n\n如果有空行就代表換行成功\n\n點選圖文可以跳轉到百度首頁");
                            // 將圖片置為空
                            article.setPicUrl("http://www.sinaimg.cn/dy/slidenews/31_img/2016_38/28380_733695_698372.jpg");
                            article.setUrl("http://www.baidu.com");
                            articleList.add(article);
                            newsMessage.setArticleCount(articleList.size());
                            newsMessage.setArticles(articleList);
                            respMessage = MessageUtil.newsMessageToXml(newsMessage);
                            break;
                        }
                        case "12": {
                            //多圖文傳送
                            Article article1 = new Article();
                            article1.setTitle("緊急通知,不要撿這種錢!湛江都已經傳瘋了!\n");
                            article1.setDescription("");
                            article1.setPicUrl("http://www.sinaimg.cn/dy/slidenews/31_img/2016_38/28380_733695_698372.jpg");
                            article1.setUrl("http://mp.weixin.qq.com/s?__biz=MjM5Njc2OTI4NQ==&mid=2650924309&idx=1&sn=8bb6ae54d6396c1faa9182a96f30b225&chksm=bd117e7f8a66f769dc886d38ca2d4e4e675c55e6a5e01e768b383f5859e09384e485da7bed98&scene=4#wechat_redirect");

                            Article article2 = new Article();
                            article2.setTitle("湛江誰有這種女兒,請給我來一打!");
                            article2.setDescription("");
                            article2.setPicUrl("http://www.sinaimg.cn/dy/slidenews/31_img/2016_38/28380_733695_698372.jpg");
                            article2.setUrl("http://mp.weixin.qq.com/s?__biz=MjM5Njc2OTI4NQ==&mid=2650924309&idx=2&sn=d7ffc840c7e6d91b0a1c886b16797ee9&chksm=bd117e7f8a66f7698d094c2771a1114853b97dab9c172897c3f9f982eacb6619fba5e6675ea3&scene=4#wechat_redirect");

                            Article article3 = new Article();
                            article3.setTitle("以上圖片我就隨意放了");
                            article3.setDescription("");
                            article3.setPicUrl("http://www.sinaimg.cn/dy/slidenews/31_img/2016_38/28380_733695_698372.jpg");
                            article3.setUrl("http://mp.weixin.qq.com/s?__biz=MjM5Njc2OTI4NQ==&mid=2650924309&idx=3&sn=63e13fe558ff0d564c0da313b7bdfce0&chksm=bd117e7f8a66f7693a26853dc65c3e9ef9495235ef6ed6c7796f1b63abf1df599aaf9b33aafa&scene=4#wechat_redirect");

                            articleList.add(article1);
                            articleList.add(article2);
                            articleList.add(article3);
                            newsMessage.setArticleCount(articleList.size());
                            newsMessage.setArticles(articleList);
                            respMessage = MessageUtil.newsMessageToXml(newsMessage);
                            break;
                        }

                        case "13": {
                            //測試網址回覆
                            respContent = "<a href=\"http://www.baidu.com\">百度主頁</a>";
                            textMessage.setContent(respContent);
                            // 將文字訊息物件轉換成xml字串
                            respMessage = MessageUtil.textMessageToXml(textMessage);
                            break;
                        }

                        default: {
                            respContent = "(這是裡面的)很抱歉,現在小8暫時無法提供此功能給您使用。\n\n回覆“1”顯示幫助資訊";
                            textMessage.setContent(respContent);
                            // 將文字訊息物件轉換成xml字串
                            respMessage = MessageUtil.textMessageToXml(textMessage);
                        }
                    }
                }
            }
        // 圖片訊息
        else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
            respContent = "您傳送的是圖片訊息!";
            textMessage.setContent(respContent);
            // 將文字訊息物件轉換成xml字串
            respMessage = MessageUtil.textMessageToXml(textMessage);
        }
        // 地理位置訊息
        else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
            respContent = "您傳送的是地理位置訊息!";
            textMessage.setContent(respContent);
            // 將文字訊息物件轉換成xml字串
            respMessage = MessageUtil.textMessageToXml(textMessage);
        }
        // 連結訊息
        else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
            respContent = "您傳送的是連結訊息!";textMessage.setContent(respContent);
            // 將文字訊息物件轉換成xml字串
            respMessage = MessageUtil.textMessageToXml(textMessage);

        }
        // 音訊訊息
        else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
            respContent = "您傳送的是音訊訊息!";
            textMessage.setContent(respContent);
            // 將文字訊息物件轉換成xml字串
            respMessage = MessageUtil.textMessageToXml(textMessage);
        }
    } catch (Exception e) {
        e.printStackTrace();
        }

        return respMessage;
        }
/**
 * 判斷是否是QQ表情
 *
 * @param content
 * @return
 */
public static boolean isQqFace(String content) {
        boolean result = false;

        // 判斷QQ表情的正則表示式
        String qqfaceRegex = "/::\\)|/::~|/::B|/::\\||/:8-\\)|/::<|/::$|/::X|/::Z|/::'\\(|/::-\\||/::@|/::P|/::D|/::O|/::\\(|/::\\+|/:--b|/::Q|/::T|/:,@P|/:,@-D|/::d|/:,@o|/::g|/:\\|-\\)|/::!|/::L|/::>|/::,@|/:,@f|/::-S|/:\\?|/:,@x|/:,@@|/::8|/:,@!|/:!!!|/:xx|/:bye|/:wipe|/:dig|/:handclap|/:&-\\(|/:B-\\)|/:<@|/:@>|/::-O|/:>-\\||/:P-\\(|/::'\\||/:X-\\)|/::\\*|/:@x|/:8\\*|/:pd|/:<W>|/:beer|/:basketb|/:oo|/:coffee|/:eat|/:pig|/:rose|/:fade|/:showlove|/:heart|/:break|/:cake|/:li|/:bome|/:kn|/:footb|/:ladybug|/:shit|/:moon|/:sun|/:gift|/:hug|/:strong|/:weak|/:share|/:v|/:@\\)|/:jj|/:@@|/:bad|/:lvu|/:no|/:ok|/:love|/:<L>|/:jump|/:shake|/:<O>|/:circle|/:kotow|/:turn|/:skip|/:oY|/:#-0|/:hiphot|/:kiss|/:<&|/:&>";
        Pattern p = Pattern.compile(qqfaceRegex);
        Matcher m = p.matcher(content);
        if (m.matches()) {
        result = true;
        }
        return result;
        }
}

因為這裡只是一個demo,所有東西都是寫死的,在後期擴充套件時,把寫好的資料庫的回覆模板加入即可。

4、做完以上,相信你在測試號上能夠實現相應的訊息回覆功能了。下一篇講自定義選單的操作。