前面处理方法中MessageUtil.parseXml代码如下,解析来自微信服务器的消息XML格式(Dom4J),这个消息是用户发送来的。
MessageUtil.messageToXml代码如下,由消息生成XML(XStream),是公众号服务器发送给微信服务器然后发送给用户的消息。
这两种消息的格式详见微信开发者文档。
/**
* 消息工具类
*
* @author 熊诗言
* @date 2015-09-05
*/
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(订阅)
*/
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
/**
* 事件类型:unsubscribe(取消订阅)
*/
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
/**
* 事件类型:CLICK(自定义菜单点击事件)
*/
public static final String EVENT_TYPE_CLICK = "CLICK";
/**
* 事件类型:场景二维码扫描事件
*/
public static final String EVENT_TYPE_SCAN = "SCAN";
/**
* 事件类型:发送用户地理位置
*/
public static final String EVENT_TYPE_LOCATION = "LOCATION";
/**
* 解析微信发来的请求(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 musicMessage 音乐消息对象
* @return xml
*/
public static String musicMessageToXml(MusicMessage musicMessage) {
xstream.alias("xml", musicMessage.getClass());
return xstream.toXML(musicMessage);
}
/**
* 图文消息对象转换成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 块
*
*/
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);
}
}
};
}
});
public static String messageToXml(BaseMessage message) {
if (message instanceof NewsMessage) {
return newsMessageToXml((NewsMessage)message);
}else if(message instanceof MusicMessage) {
return musicMessageToXml((MusicMessage)message);
}else{
return textMessageToXml((TextMessage)message);
}
}
}
大的责任链中有这些消息处理器
TextMessageHandler、EventMessageHandler、ImageMessageHandler、VoiceMessageHandler、LinkTextMessageHandler、LocationMessageHandler、FinalMessageHandler
先说TextMessageHandler
/**
* 处理文本消息作为一个大的分支,又是一个责任链,作为一个大的总入口
* @author 熊诗言
*
*/
public class TextMessageHandler extends DefaultMessageHandler {
/**
* 由于在语音消息中可能要根据语音识别结果给出回复,那么就用到文本处理器,
* 而系统中只需要一个文本处理器,所以设计成单例模式*/
DefaultMessageHandler first = null;
private static final TextMessageHandler _instance = new TextMessageHandler();
private TextMessageHandler(){
/*
questionTextHandler = new QuestionTextHandler();
TranslateTextHandler translateTextHandler = new TranslateTextHandler();
TodayInHisTextHandler todayInHisTextHandler = new TodayInHisTextHandler();
QQFaceTextHandler qqFaceTextHandler = new QQFaceTextHandler();
QueryMusicTextHandler queryMusicTextHandler = new QueryMusicTextHandler();
JokeTextHandler jokeTextHandler = new JokeTextHandler();
TalkRobot talkRobot = new TalkRobot();
*/
/*questionTextHandler.setNextMessageHandler(translateTextHandler);
translateTextHandler.setNextMessageHandler(todayInHisTextHandler);
todayInHisTextHandler.setNextMessageHandler(qqFaceTextHandler);
qqFaceTextHandler.setNextMessageHandler(queryMusicTextHandler);
queryMusicTextHandler.setNextMessageHandler(jokeTextHandler);
jokeTextHandler.setNextMessageHandler(talkRobot);*/
InputStream is= this.getClass().getClassLoader().getResourceAsStream("txthandler.txt");
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader(is));
List<DefaultMessageHandler> handlers = new ArrayList<DefaultMessageHandler>();
String className = "";
while ((className = br.readLine())!=null) {
//把所有的handler保存起来
handlers.add((DefaultMessageHandler) Class.forName(className).newInstance());
}
for (int i = handlers.size()-1; i > 0; i--) {
//构造责任链
handlers.get(i-1).setNextMessageHandler(handlers.get(i));
}
//第一个
first = handlers.get(0);
} catch (Exception e) {
e.printStackTrace();
}
}
public static TextMessageHandler sharedTextMessageHandler() {
return _instance;
}
@Override
public boolean canDo(Map<String, String> requestMap) {
// 消息类型
String msgType = requestMap.get("MsgType");
return msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT);
}
@Override
public BaseMessage handleByMe(Map<String, String> requestMap) {
return first.handleMessage(requestMap);
//txthandler2.properties
//简单工厂模式通过配置文件的内容:handler获取handler的handleByMe就不必每次都跑一个链
}
}
txthandler.txt
com.xsy.weixin.service.handler.texthandler.QuestionTextHandler
com.xsy.weixin.service.handler.texthandler.TranslateTextHandler
com.xsy.weixin.service.handler.texthandler.WeatherTextHandler
com.xsy.weixin.service.handler.texthandler.ExpressQueryTextHandler
com.xsy.weixin.service.handler.texthandler.TodayInHisTextHandler
com.xsy.weixin.service.handler.texthandler.QQFaceTextHandler
com.xsy.weixin.service.handler.texthandler.QueryMusicTextHandler
com.xsy.weixin.service.handler.texthandler.JokeTextHandler
com.xsy.weixin.service.handler.texthandler.TalkRobot
关于这些消息处理器后面详说,涉及Webservice的调用。
EventMessageHandler
/**
* 处理推送消息
* @author 熊诗言
*
*/
public class EventMessageHandler extends DefaultMessageHandler {
@Override
public boolean canDo(Map<String, String> requestMap) {
String msgType = requestMap.get("MsgType");
return msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT);//处理推送事件类型
}
private SubscribleEventHandler subscribleEventHandler = null;
public EventMessageHandler() {
subscribleEventHandler = new SubscribleEventHandler();
MenuClickEventHandler menuClickEventHandler = new MenuClickEventHandler();
ScanCodeEventHandler scanCodeEventHandler = new ScanCodeEventHandler();
LocationEventHandler locationEventHandler = new LocationEventHandler();
UnSubscribleEventHandler unSubscribleEventHandler = new UnSubscribleEventHandler();
subscribleEventHandler.setNextMessageHandler(menuClickEventHandler);
menuClickEventHandler.setNextMessageHandler(scanCodeEventHandler);
scanCodeEventHandler.setNextMessageHandler(locationEventHandler);
locationEventHandler.setNextMessageHandler(unSubscribleEventHandler);
}
@Override
public BaseMessage handleByMe(Map<String, String> requestMap) {
return subscribleEventHandler.handleMessage(requestMap);
}
}
ImageMessageHandler
public class ImageMessageHandler extends DefaultMessageHandler {
@Override
public boolean canDo(Map<String, String> requestMap) {
// 消息类型
String msgType = requestMap.get("MsgType");
return msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE);
}
@Override
public BaseMessage handleByMe(Map<String, String> requestMap) {
// 取得图片地址
String picUrl = requestMap.get("PicUrl");
// 人脸检测
String detectResult = FaceService.detect(picUrl);
return MessageFactory.createTextMessage(fromUserName, toUserName, detectResult);
}
}
VoiceMessageHandler
/**
* 处理音频类型的消息
* @author 熊诗言
*
*/
public class VoiceMessageHandler extends DefaultMessageHandler {
@Override
public boolean canDo(Map<String, String> requestMap) {
// 消息类型
String msgType = requestMap.get("MsgType");
return msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE);
}
@Override
public BaseMessage handleByMe(Map<String, String> requestMap) {
//腾讯在这里坑死人不偿命啊,在介绍中说字段是Recongnition,害我弄一下午
/**
* Unicode 编码并不只是为某个字符简单定义了一个编码,而且还将其进行了归类。
/pP 其中的小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。
大写 P 表示 Unicode 字符集七个字符属性之一:标点字符。
其他六个是
L:字母;
M:标记符号(一般不会单独出现);
Z:分隔符(比如空格、换行等);
S:符号(比如数学符号、货币符号等);
N:数字(比如阿拉伯数字、罗马数字等);
C:其他字符
上面这七个是属性,七个属性下还有若干个子属性,用于更进一步地进行细分。
*/
String recongnition = requestMap.get("Recognition").trim().replaceAll("\\pP|\\pS", "");
if(null!=recongnition){
requestMap.put("MsgType", MessageUtil.REQ_MESSAGE_TYPE_TEXT);
requestMap.put("Content", recongnition);//因为文本处理器都是把content取出来处理
return TextMessageHandler.sharedTextMessageHandler().handleMessage(requestMap);
}else{
return MessageFactory.createTextMessage(fromUserName, toUserName, "您发送的是音频消息,目前不能识别,对不起");
}
}
}
LinkTextMessageHandler
public class LinkTextMessageHandler extends DefaultMessageHandler {
@Override
public boolean canDo(Map<String, String> requestMap) {
// 消息类型
String msgType = requestMap.get("MsgType");
return msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK);
}
@Override
public BaseMessage handleByMe(Map<String, String> requestMap) {
return MessageFactory.createTextMessage(fromUserName, toUserName, "您发送的是链接消息!");
}
}
LocationMessageHandler
public class LocationMessageHandler extends DefaultMessageHandler {
@Override
public boolean canDo(Map<String, String> requestMap) {
// 消息类型
String msgType = requestMap.get("MsgType");
return msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION);
}
@Override
public BaseMessage handleByMe(Map<String, String> requestMap) {
String latitude = requestMap.get("Location_X");//纬度
String longitude = requestMap.get("Location_Y");//经度
int random = new Random().nextInt(4);
/**
* 目前只是测试功能,以后加上数据库之后根据用户的选择进行处理
*/
if(random==0){
//测距,以后可以提供免费送货服务,在距离范围内
int distance = BaiduDistanceService.getDistance(latitude,longitude);
return MessageFactory.createTextMessage(fromUserName, toUserName, "您距离我们大约"+distance+"米");
}else if(random==1){
//周边搜索
String zhoubian = BaiduZhoubianSearchService.searchZhoubian(latitude, longitude, "酒店");
return MessageFactory.createTextMessage(fromUserName, toUserName, zhoubian);
}else if(random==2){
//导航
List<Article> articles = new ArrayList<Article>();
Article article = new Article();
article.setTitle("导航");
article.setDescription("点击此链接导航到我公司");
article.setPicUrl("");//以后放一张公司的宣传照
//高德导航String urlString = "http://mo.amap.com/?from={$latitude},{$longitude}(你的位置)&to=23.378341,116.706653(我的公司)&type=0&opt=1&dev=1";
String url = "http://api.map.baidu.com/direction?origin=latlng:"+latitude+","+longitude+"|name:你的位置&destination=latlng:41.136646,122.066261|name:我的公司&mode=driving®ion=盘锦&output=html&src=yourCompanyName|yourAppName";
article.setUrl(url);
articles.add(article);
return MessageFactory.createNewsMessage(fromUserName, toUserName, articles);
}else{
//百度静态地图导航
String url = "http://api.map.baidu.com/staticimage?width=400&height=300¢er="+longitude+","+latitude+"&zoom=14&markers=122.066261,41.136646|"+longitude+","+latitude+"&markerStyles=l,0|l,1";
List<Article> articles = new ArrayList<Article>();
Article article = new Article();
article.setTitle("导航");
article.setDescription("按地图标注导航到我公司");
article.setPicUrl("");//以后放一张公司的宣传照
article.setUrl(url);
articles.add(article);
return MessageFactory.createNewsMessage(fromUserName, toUserName, articles);
}
}
@Test
public void testHandleMessage(){
Map<String, String> requestMap = new HashMap<String, String>();
requestMap.put("FromUserName", "dfgsd");
requestMap.put("ToUserName", "dfsfd");
requestMap.put("MsgType", MessageUtil.REQ_MESSAGE_TYPE_LOCATION);
requestMap.put("Location_X", "41");
requestMap.put("Location_Y", "122");
BaseMessage message = new LocationMessageHandler().handleMessage(requestMap);
System.out.println(MessageUtil.messageToXml(message));
}
}