微信公众号开发(三):消息处理器(大的责任链)

    前面处理方法中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&region=盘锦&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&center="+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));
    }
}

你可能感兴趣的:(微信公众号开发(三):消息处理器(大的责任链))