zoukankan      html  css  js  c++  java
  • 对柳峰博主的微信公众号后台示例的部分重构

    柳峰博主的专栏(http://blog.csdn.net/column/details/wechatmp.html)和王信平博主的专栏(http://www.cnblogs.com/wangshuo1/)对微信公众号开发已经做了比较详尽的阐述,基本上照搬,就可以做出第一个微信公众号后台应用。但是在『照搬』的过程中,发现有些地方总是觉得别扭,由着完美主义者的性格使然,对以下这几个地方做个小优化吧。

    一、区别消息和响应

    原来的消息类(用户发给后台)和响应类(后台回给用户)有这些:

    我改成了这样的:

    我把『消息Message』和『响应Response』在命名上分开了。这样在编程时不会总是想着要去区别,这个『Message』究竟是用户发给后台的『消息』呢,还是后台回给用户的『响应』。

    二、给消息和响应添加构造函数

    原来消息和响应都只是使用缺省构造函数(详见http://blog.csdn.net/lyq8479/article/details/8949088),实际上是把这两个类当做了『结构体』来看待。基于面向对象的思想,还是觉得需要有构造函数。

    2.1 BaseMessage类的构造函数

     1    public BaseMessage(String toUserName, String fromUserName, String createTime, String msgType, String msgId){
     2         this.ToUserName=toUserName;
     3         this.FromUserName=fromUserName;
     4         this.CreateTime=Long.parseLong(createTime);
     5         this.MsgType=msgType;
     6         this.MsgId=Long.parseLong(msgId);
     7     }
     8 
     9     public BaseMessage(Map<String, String> mapMessage){
    10         try {
    11             // 开发者微信号
    12             String toUserName = mapMessage.get("ToUserName");
    13             // 发送方帐号(open_id)
    14             String fromUserName = mapMessage.get("FromUserName");
    15             // 消息类型
    16             String msgType = mapMessage.get("MsgType");
    17             // 建立时间
    18             String createTime = mapMessage.get("CreateTime");
    19             // 消息ID
    20             String msgId = mapMessage.get("MsgId");
    21 
    22             this.ToUserName = toUserName;
    23             this.FromUserName = fromUserName;
    24             this.CreateTime = Long.parseLong(createTime);
    25             this.MsgType = msgType;
    26             this.MsgId = Long.parseLong(msgId);
    27         }
    28         catch (Exception e) {
    29             e.printStackTrace();
    30         }
    31     }
    View Code

    2.2 ImageMessage类的构造函数

     1     public ImageMessage(String toUserName,
     2                         String fromUserName,
     3                         String createTime,
     4                         String msgType,
     5                         String msgId,
     6                         String picUrl) {
     7         super(toUserName,fromUserName,createTime,msgType,msgId);
     8         assert (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE));
     9         this.PicUrl=picUrl;
    10     }
    11 
    12     public ImageMessage(Map<String, String> mapMessage){
    13         super(mapMessage);
    14         assert (this.getMsgType().equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE));
    15         this.PicUrl=mapMessage.get("PicUrl");
    16     }
    View Code

    2.3 TextMessage类的构造函数

     1     public TextMessage(String toUserName,
     2                        String fromUserName,
     3                        String createTime,
     4                        String msgType,
     5                        String msgId,
     6                        String content) {
     7         super(toUserName,fromUserName,createTime,msgType,msgId);
     8         assert (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT));
     9         this.Content=content;
    10 
    11     }
    12 
    13     public TextMessage(Map<String, String> mapMessage){
    14         super(mapMessage);
    15         assert (this.getMsgType().equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT));
    16         this.Content=mapMessage.get("Content");
    17     }
    View Code

    这里只列举了消息类的基类、图片消息、文本消息三个构造函数,其余的位置消息、声音消息、短视频、视频、链接消息的构造函数类似。

    2.4 BaseResponse类的构造函数(这个有点意思)

     1     public BaseResponse(BaseMessage baseMessage){
     2         this.ToUserName=baseMessage.getFromUserName(); //返回的目标是当初的发送源
     3         this.FromUserName=baseMessage.getToUserName(); //发送的来源是当初的发送目标
     4         this.MsgType= MessageUtil.RESP_MESSAGE_TYPE_TEXT; // 默认返回消息为文本
     5         this.CreateTime=new Date().getTime(); //默认为当前时间
     6         this.FuncFlag=0; //默认为非星标
     7     }
     8 
     9     public BaseResponse(String toUserName,String fromUserName, String msgType){
    10         this.ToUserName=toUserName;
    11         this.FromUserName=fromUserName;
    12         this.MsgType=msgType;
    13         this.CreateTime=new Date().getTime(); //默认为当前时间
    14         this.FuncFlag=0; //默认为非星标
    15     }

    响应类的基类的构造函数,我使用了消息类的基类作为参数,是因为响应消息的返回目标,就是当初的发送源(用户),响应消息的发送源,其实就是当初的发送目标(微信后台程序)。

    2.5 TextResponse类的构造函数

     1     public TextResponse(String toUserName,String fromUserName, String msgType,String content){
     2         super(toUserName,fromUserName,msgType);
     3         this.Content=content;
     4     }
     5     public TextResponse(BaseMessage baseMessage,
     6                          String content){
     7         super(baseMessage);
     8         this.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
     9         this.Content=content;
    10     }
    View Code

    2.6 Article类的构造函数

    1     public Article(String title, String description, String picUrl, String url) {
    2         this.Title=title;
    3         this.Description=description;
    4         this.PicUrl=picUrl;
    5         this.Url=url;
    6     }
    View Code

    2.7 NewsResponse类的构造函数

     1 public class NewsResponse extends BaseResponse {
     2     // 图文消息个数,限制为10条以内
     3     private int ArticleCount;
     4     // 多条图文消息信息,默认第一个item为大图
     5     private List<Article> Articles;
     6 
     7     public NewsResponse(String toUserName,String fromUserName, String msgType){
     8         super(toUserName,fromUserName,msgType);
     9         ArticleCount=0;
    10         this.Articles = new ArrayList<Article>();
    11     }
    12     public NewsResponse(BaseMessage baseMessage){
    13         super(baseMessage);
    14         this.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS);
    15         ArticleCount=0;
    16         this.Articles = new ArrayList<Article>();
    17     }
    18 
    19     //如果已经达到10篇文章,图文消息不可以再增加新的文章。返回FALSE;
    20     public boolean addArticle(Article article) {
    21         if (ArticleCount>=10){
    22             return false;
    23         }
    24         Articles.add(article);
    25         ArticleCount++;
    26         return true;
    27     }
    28     public Article getArticle(int articleIndex){
    29         return Articles.get(articleIndex);
    30     }
    31 
    32     public int getArticleCount() {
    33         return ArticleCount;
    34     }
    35 
    36 //    public void setArticleCount(int articleCount) {
    37 //        ArticleCount = articleCount;
    38 //    }
    39 
    40     public List<Article> getArticles() {
    41         return Articles;
    42     }
    43 
    44 //    public void setArticles(List<Article> articles) {
    45 //        Articles = articles;
    46 //    }
    47 }
    View Code

    这个NewsResponse类也要说一下,原来的设计也够奇葩的,居然直接把List结构暴露出来对其读写,List里的Article的数量,可以单独赋值,呵呵。于是果断注释掉计数变量的读写方法,新增了addArticle和getArticle这两个方法,用以从List中添加和获取Article,计算变量,应该自动累加,不应该对其手工赋值。

    MusicResponse类的改造类似,不细说了。

    三、归一化命名

    原来的响应消息转成XML有三个函数,分别是textMessageToXml、musicMessageToXml、newsMessageToXml(详见http://blog.csdn.net/lyq8479/article/details/8949088),我把它们的命名归一为responseToXml,调用的时候,也不用区别传进去的是啥响应消息来选择不同名字的函数,省心。

     1     /**
     2      * 文本消息对象转换成xml
     3      *
     4      * @param textMessage 文本消息对象
     5      * @return xml
     6      */
     7     public static String responseToXml(TextResponse textMessage) {
     8         xstream.alias("xml", textMessage.getClass());
     9         return xstream.toXML(textMessage);
    10     }
    11 
    12     /**
    13      * 音乐消息对象转换成xml
    14      *
    15      * @param musicMessage 音乐消息对象
    16      * @return xml
    17      */
    18     public static String responseToXml(MusicResponse musicMessage) {
    19         xstream.alias("xml", musicMessage.getClass());
    20         return xstream.toXML(musicMessage);
    21     }
    22 
    23     /**
    24      * 图文消息对象转换成xml
    25      *
    26      * @param newsMessage 图文消息对象
    27      * @return xml
    28      */
    29     public static String responseToXml(NewsResponse newsMessage) {
    30         xstream.alias("xml", newsMessage.getClass());
    31         xstream.alias("item", newsMessage.getArticle(0).getClass());
    32         return xstream.toXML(newsMessage);
    33     }
    View Code

    四、构造消息路由

    原来的消息处理的主函数(详见http://blog.csdn.net/lyq8479/article/details/9393195),在一个超级巨大的processRequest函数中对消息进行处理,一堆if else if 下来,眼都花了,所以我简化了processRequest函数,只在这个函数里面构造消息路由,针对不同类型的消息,转去不同的处理逻辑中。

     1    //定义一个logger,用于发送日志,可以在console或者服务器上看到。存储目录在log4j.properties中定义
     2     private static final Logger logger = Logger.getLogger(CoreService.class);
     3 
     4     //处理请求
     5     public static String processRequest(HttpServletRequest request){
     6         String returnMsg=null;
     7         try{
     8             Map<String, String> requestMap = MessageUtil.parseXml(request); //解析XML
     9             String msgType = requestMap.get("MsgType"); //消息类型
    10             logger.info("***step1 msgType=" + msgType);
    11             switch (msgType){
    12                 case MessageUtil.REQ_MESSAGE_TYPE_TEXT:
    13                     returnMsg=HandleTextMsg(new TextMessage(requestMap));
    14                     break;
    15                 case MessageUtil.REQ_MESSAGE_TYPE_IMAGE:
    16                     returnMsg=HandleImageMsg(new ImageMessage(requestMap));
    17                     break;
    18                 case MessageUtil.REQ_MESSAGE_TYPE_LINK:
    19                     returnMsg=HandleLinkMsg(new LinkMessage(requestMap));
    20                     break;
    21                 case MessageUtil.REQ_MESSAGE_TYPE_LOCATION:
    22                     returnMsg=HandleLocationMsg(new LocationMessage(requestMap));
    23                     break;
    24                 case MessageUtil.REQ_MESSAGE_TYPE_SHORT_VIDEO:
    25                     returnMsg=HandleShortVideoMsg(new ShortVideoMessage(requestMap));
    26                     break;
    27                 case MessageUtil.REQ_MESSAGE_TYPE_VIDEO:
    28                     returnMsg=HandleVideoMsg(new VideoMessage(requestMap));
    29                     break;
    30                 case MessageUtil.REQ_MESSAGE_TYPE_VOICE:
    31                     returnMsg=HandleVoiceMsg(new VoiceMessage(requestMap));
    32                     break;
    33                 case MessageUtil.REQ_MESSAGE_TYPE_EVENT:
    34                     // 事件类型
    35                     String eventType = requestMap.get("Event");
    36                     switch (eventType){
    37                         case MessageUtil.EVENT_TYPE_SUBSCRIBE:
    38                             returnMsg=HandleSubscribeEvent(new BaseMessage(requestMap));
    39                             break;
    40                         case MessageUtil.EVENT_TYPE_UNSUBSCRIBE:
    41                             returnMsg=HandleUnsubscribeEvent(new BaseMessage(requestMap));
    42                             break;
    43                         case MessageUtil.EVENT_TYPE_CLICK:
    44                             returnMsg=HandleClickEvent(new BaseMessage(requestMap));
    45                             break;
    46                         default:
    47                             returnMsg=HandleDefaultMsgOrEvent(new BaseMessage(requestMap));
    48                     }
    49                     break;
    50                 default:
    51                     returnMsg=HandleDefaultMsgOrEvent(new BaseMessage(requestMap));
    52             }
    53         }catch (Exception e) {
    54             e.printStackTrace();
    55         }
    56         return returnMsg;
    57     }

    下面是对各种消息的handle函数

      1     public static String HandleTextMsg(TextMessage textMessage)
      2     {
      3         String respContent = "您发送的是文本消息!";
      4         if (textMessage.getContent().indexOf("/:") > -1)
      5             respContent="你"+textMessage.getContent()+"什么?";
      6         TextResponse textResponse=new TextResponse(textMessage, respContent);
      7         String respMsg=MessageUtil.responseToXml(textResponse);
      8         logger.info("***step2 Response=" + respMsg);
      9         return respMsg;
     10     }
     11     public static String HandleImageMsg(ImageMessage imageMessage)
     12     {
     13         String title="鉴定结果";
     14         String picUrl=imageMessage.getPicUrl();
     15         String forwardUrl= HttpTools.imagePlusPlusPath+picUrl;
     16         logger.info("***step2 image++ Url=" + forwardUrl);
     17 
     18         //从image++网站识别上传的图片的拍摄地点、拍摄物体。
     19         String imagePlusPlusReturn=getHttpResponse(forwardUrl);
     20         logger.info("***step3 image++ Response=" + imagePlusPlusReturn);
     21         String description = ReadJSONString(imagePlusPlusReturn);
     22         logger.info("***step4 image++ Response=" + description);
     23 
     24         Article article=new Article(title,description,picUrl,forwardUrl);
     25         NewsResponse newsResponse=new NewsResponse(imageMessage);
     26         newsResponse.addArticle(article);
     27         String respMsg=MessageUtil.responseToXml(newsResponse);
     28         logger.info("***step5  Weixin Response=" + respMsg);
     29         return respMsg;
     30     }
     31     public static String HandleLinkMsg(LinkMessage linkMessage)
     32     {
     33         String respContent = "您发送的是链接!";
     34         TextResponse textResponse=new TextResponse(linkMessage, respContent);
     35         String respMsg=MessageUtil.responseToXml(textResponse);
     36         logger.info("***step2 Response=" + respMsg);
     37         return respMsg;
     38     }
     39     public static String HandleLocationMsg(LocationMessage locationMessage)
     40     {
     41         String respContent = "您发送的是地理位置!";
     42         TextResponse textResponse=new TextResponse(locationMessage, respContent);
     43         String respMsg=MessageUtil.responseToXml(textResponse);
     44         logger.info("***step2 Response=" + respMsg);
     45         return respMsg;
     46     }
     47     public static String HandleShortVideoMsg(ShortVideoMessage shortVideoMessage)
     48     {
     49         String respContent = "您发送的是小视频!";
     50         TextResponse textResponse=new TextResponse(shortVideoMessage, respContent);
     51         String respMsg=MessageUtil.responseToXml(textResponse);
     52         logger.info("***step2 Response=" + respMsg);
     53         return respMsg;
     54     }
     55     public static String HandleVideoMsg(VideoMessage videoMessage)
     56     {
     57         String respContent = "您发送的是视频!";
     58         TextResponse textResponse=new TextResponse(videoMessage, respContent);
     59         String respMsg=MessageUtil.responseToXml(textResponse);
     60         logger.info("***step2 Response=" + respMsg);
     61         return respMsg;
     62     }
     63     public static String HandleVoiceMsg(VoiceMessage voiceMessage)
     64     {
     65         String respContent = "您发送的是语音!";
     66         TextResponse textResponse=new TextResponse(voiceMessage, respContent);
     67         String respMsg=MessageUtil.responseToXml(textResponse);
     68         logger.info("***step2 Response=" + respMsg);
     69         return respMsg;
     70     }
     71     public static String HandleSubscribeEvent(BaseMessage baseMessage)
     72     {
     73         String respContent = "欢迎订阅!";
     74         TextResponse textResponse=new TextResponse(baseMessage, respContent);
     75         String respMsg=MessageUtil.responseToXml(textResponse);
     76         logger.info("***step2 Response=" + respMsg);
     77         return respMsg;
     78     }
     79     public static String HandleUnsubscribeEvent(BaseMessage baseMessage)
     80     {
     81         String respContent = "再见,希望您再来!";
     82         TextResponse textResponse=new TextResponse(baseMessage, respContent);
     83         String respMsg=MessageUtil.responseToXml(textResponse);
     84         logger.info("***step2 Response=" + respMsg);
     85         return respMsg;
     86     }
     87     public static String HandleClickEvent(BaseMessage baseMessage)
     88     {
     89         String respContent = "您点击了自定义菜单!";
     90         TextResponse textResponse=new TextResponse(baseMessage, respContent);
     91         String respMsg=MessageUtil.responseToXml(textResponse);
     92         logger.info("***step2 Response=" + respMsg);
     93         return respMsg;
     94     }
     95     public static String HandleDefaultMsgOrEvent(BaseMessage baseMessage)
     96     {
     97         String respContent = "请求不可识别,请稍候尝试!";
     98         TextResponse textResponse=new TextResponse(baseMessage, respContent);
     99         String respMsg=MessageUtil.responseToXml(textResponse);
    100         logger.info("***step2 Response=" + respMsg);
    101         return respMsg;
    102     }
    View Code

    五、应用:试试图像识别

    我的第一个微信公众号后台应用,是识别用户上传的图片,是什么东西,在哪里拍摄的。调用的是image++网站的api,核心代码如下:

     1 /*
     2 读取一个网址的所有返回内容
     3  */
     4     public static String getHttpResponse(String connectUrl) {
     5         BufferedReader in = null;
     6         StringBuffer result = null;
     7         try {
     8             URI uri = new URI(connectUrl);
     9             URL url = uri.toURL();
    10             URLConnection connection = url.openConnection();
    11             connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    12             connection.setRequestProperty("Charset", "utf-8");
    13             connection.connect();
    14 
    15             result = new StringBuffer();
    16             //读取URL的响应
    17             in = new BufferedReader(new InputStreamReader(
    18                     connection.getInputStream()));
    19             String line;
    20             while ((line = in.readLine()) != null) {
    21                 result.append(line);
    22             }
    23 
    24             return result.toString();
    25 
    26         } catch (Exception e) {
    27             e.printStackTrace();
    28         }finally {
    29             try {
    30                 if (in != null) {
    31                     in.close();
    32                 }
    33             } catch (Exception e2) {
    34                 e2.printStackTrace();
    35             }
    36         }
    37         return null;
    38     }
    39 
    40     /*
    41     从JSON字符串中读取某些关键信息
    42     JSON文件来自:http://www.imageplusplus.com/doc
    43      */
    44     public static String ReadJSONString(String jSonString){
    45         try {
    46             int imageConfidenceIndex=jSonString.indexOf("confidence",jSonString.indexOf("image"));
    47             int imageConfidenceIndexEnd=jSonString.indexOf(",",imageConfidenceIndex);
    48             String imageConfidence=jSonString.substring(imageConfidenceIndex+12,imageConfidenceIndexEnd);
    49             int imageConfidencePercent=(int)(Float.parseFloat(imageConfidence)*100);
    50             int imageValueIndex=jSonString.indexOf("value",jSonString.indexOf("image"));
    51             int imageValueIndexEnd=jSonString.indexOf("}",imageValueIndex)-1;
    52             String imageValue=jSonString.substring(imageValueIndex+8,imageValueIndexEnd).trim();
    53 
    54             int sceneConfidenceIndex=jSonString.indexOf("confidence",jSonString.indexOf("scene"));
    55             int sceneConfidenceIndexEnd=jSonString.indexOf(",",sceneConfidenceIndex);
    56             String sceneConfidence=jSonString.substring(sceneConfidenceIndex+12,sceneConfidenceIndexEnd);
    57             int sceneConfidencePercent=(int)(Float.parseFloat(sceneConfidence)*100);
    58             int sceneValueIndex=jSonString.indexOf("value",jSonString.indexOf("scene"));
    59             int sceneValueIndexEnd=jSonString.indexOf("}",sceneValueIndex)-1;
    60             String sceneValue=jSonString.substring(sceneValueIndex+8,sceneValueIndexEnd).trim();
    61 
    62             String result="这张照片有【"+imageConfidencePercent+"%】的可能是【"+imageValue+"】," +
    63                     "有【"+sceneConfidencePercent+"%】的可能是在【"+sceneValue+"】拍摄的。";
    64             return result;
    65         } catch (Exception e) {
    66             e.printStackTrace();
    67         }
    68         return "服务器秀逗了,请重试!";
    69     }
    70 }
    View Code

    全部的源代码,我放在了这里:20161013微信公众号后台应用示例src.zip

    示例源码中用到几个外部jar lib,去官网下载,然后扔进项目里面就好。

  • 相关阅读:
    Codeforces 651 A. Joysticks
    Codeforces 538 C. Tourist's Notes
    Codeforces 538 B. Quasi Binary
    Codeforces 538 A. Cutting Banner-substr()函数字符串拼接
    Codeforces 626 C. Block Towers-二分 (8VC Venture Cup 2016-Elimination Round)
    Codeforces 626 B. Cards (8VC Venture Cup 2016-Elimination Round)
    hdu 4825 Xor Sum trie树
    Codeforces Round #358 (Div. 2) C. Alyona and the Tree dfs
    Codeforces Round #357 (Div. 2) 优先队列+模拟
    2016 湘潭邀请赛
  • 原文地址:https://www.cnblogs.com/eXcel/p/5956098.html
Copyright © 2011-2022 走看看