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 Articles;
public int getArticleCount() {
return ArticleCount;
}
public void setArticleCount(int articleCount) {
ArticleCount = articleCount;
}
public List getArticles() {
return Articles;
}
public void setArticles(List 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 parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map map = new HashMap();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List 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(");
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 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 articleList = new ArrayList();
// 接收文本消息内容
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 = "百度主页";
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|/:|/: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|/:|/:jump|/:shake|/:|/: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、做完以上,相信你在测试号上能够实现相应的消息回复功能了。下一篇讲自定义菜单的操作。