基於springboot+mybatis的微信公眾號開發第三篇-訊息的接收與回覆
阿新 • • 發佈:2019-02-09
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、做完以上,相信你在測試號上能夠實現相應的訊息回覆功能了。下一篇講自定義選單的操作。