美文网首页微信公众号开发教程微信公众号开发
微信公众号开发教程(四)自定义菜单

微信公众号开发教程(四)自定义菜单

作者: 叩丁狼教育 | 来源:发表于2018-04-09 18:30 被阅读196次

    作者:陈惠,叩丁狼教育高级讲师。原创文章,转载请注明出处。

    本篇文章,我们来做一个最常见的,也是用户最喜欢使用的功能——自定义菜单。
    因为菜单只需要点一下就可以获取需要的信息,无需用户手动输入关键字,用户体验相对来说比较好。

    菜单效果:
    图片.png

    打开开发文档,选择"自定义菜单"的"菜单创建接口"。

    图片.png
    注意:
    1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
    2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
    3、创建自定义菜单后,微信是有一定的菜单刷新策略,不会立马看到效果,尽量尝试先取消关注后再次关注,即可以看到创建后的效果。
    

    菜单的类型非常多,文档中列了10种类型,这里不一一说明。

    举两个比较常见的类型:
    1、click:点击推事件类型,即点击之后微信服务器会推送一个事件类型的消息到我们的URL上,与之前的关注/取消关注类似。
    2、view:跳转URL类型,即点击之后可以跳转到指定的网页地址。
    
    现在我们利用click类型的按钮来实现需求:

    用户点击"开班信息"按钮后,公众号马上推送最近开班信息,点击"校区地址"按钮后,马上推送地址信息,不需要用户再手动输入关键字。

    实现细节:

    1.用户点击click类型按钮后,微信服务器会推送一个事件类型的消息到URL上,也就是意味着需要使用之前的MsgType判断是否为event类型。
    2.需要判断是什么事件,我们之前判断的是关注事件subscribe,但现在是click类型,所以Event属性我们需要判断是否为click。
    3.我们可能会有很多个click类型的按钮,所以需要根据不同的按钮来回复不同的内容,微信为每个click类型的按钮提供了key属性,不同的按钮设置不同的key值,根据key值即可知道当前需要处理的是哪个按钮。

    创建菜单:

    那么接下来,我们就需要创建自定义菜单了。

    开发文档中,已经提供创建菜单的接口地址了,我们需要把菜单转换为json数据传递过去。

    下图是官方提供的菜单具体参数:


    图片.png

    先把我们的要创建的自定义菜单对应的json数据准备好:

    {
         "button":[
         {    
              "type":"click",
              "name":"开班信息",
              "key":"classinfo"
          },
         {    
              "type":"click",
              "name":"校区地址",
              "key":"address"
          },
          {
               "name":"学科介绍",
               "sub_button":[
               {    
                   "type":"view",
                   "name":"Java课程",
                   "url":"http://www.wolfcode.cn/zt/java/index.html"
                },
               {    
                   "type":"view",
                   "name":"Python课程",
                   "url":"http://www.wolfcode.cn/zt/python/index.html"
                }]
           }]
     }
    

    这里我是创建了2个click类型的一级菜单,并且有对应的key值,还创建了1个有子菜单的一级菜单, 里面的2个子菜单都是view类型的,点击后直接链接到课程介绍页面。

    access_token介绍

    接下来,我们注意看这个接口的地址:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

    地址最后有一个access_token的参数,对于该参数的介绍,在开发文档的"首页"有这么一句话。

    公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,也就意味着它就是一个门票,请求接口时需要带上这个门票,如果没有就无法获取需要的信息,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。

    这句话说明了我们在调用接口之前,必须先获取到access_token这么一个凭据。

    获取access_token

    打开文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

    文档告诉我们,access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效,而且也有获取的次数限制,所以我们应该把它缓存起来,2个小时内重复使用,另外, 如果access_token要保存在数据库中,至少要保留512个字符空间。

    通过文档介绍可知,我们请求指定的接口地址,并且把我们之前分配到的appid与secret作为参数传递过去,即可以获取到有效的access_token。

    图片.png

    代码:

    public class WeChatUtil {
        //URL验证时使用的token
        public static final String TOKEN = "wolfcode";
        //appid
        public static final String APPID = "wx59687be81dd3d388";
        //secret
        public static final String SECRET = "d4624c36b6795d1d99dcf0547af5443d";
            //创建菜单接口地址
        public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
        //获取access_token的接口地址
        public static final String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        //缓存的access_token
        private static String accessToken;
        //access_token的失效时间
        private static long expiresTime;
    
        /**
         * 获取accessToken
         * @return
         */
        public static String getAccessToken(){
            //判断accessToken是否已经过期,如果过期需要重新获取
            if(accessToken==null||expiresTime < new Date().getTime()){
                //发起请求获取accessToken
                String result = HttpUtil.get(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));
                //把json字符串转换为json对象
                JSONObject json = JSON.parseObject(result);
                //缓存accessToken
                accessToken = json.getString("access_token");
                //设置accessToken的失效时间
                long expires_in = json.getLong("expires_in");
                //失效时间 = 当前时间 + 有效期(提前一分钟)
                expiresTime = new Date().getTime()+ (expires_in-60) * 1000;
            }
            return accessToken;
        }
    }
    

    代码中的HttpUtil是发起http请求的工具类,网上可以找到很多,这里就不贴出来了。

    通过getAccessToken方法我们就可以获取到需要的accessToken凭据了。

    接下来我们就可以调用创建自定义菜单的接口了,使用main方法直接调用createMenu方法,把菜单的json数据传入即可。当然,想要更灵活的设置菜单,需要写菜单管理的相关页面使用可视化的方式来编辑菜单。

        /**
         * 创建自定义菜单
         * @param menu
         */
        public static void createMenu(String menu){
            String result = HttpUtil.post(CREATE_MENU_URL.replace("ACCESS_TOKEN", getAccessToken()),menu);
            System.out.println(result);
        }
    

    接口响应的结果为:{"errcode":0,"errmsg":"ok"},代表菜单创建成功,这时候就可以打开公众号测试了,如果不能及时看到新的菜单,应先取消关注再重新关注公众号。

    如果响应的结果是其他,代表创建过程出错了,这时候应该对照错误码,查找错误原因。

    图片.png

    上面列出的是比较常见的错误,如果上面找不到,需要在全局返回码中搜索。
    https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234

    菜单效果:


    图片.png

    菜单已经创建成功,我们最后一步就是做业务处理了。

    1.InMsgEntity类添加EventKey属性

    @Setter
    @Getter
    @XmlRootElement(name="xml")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class InMsgEntity {
    
        ....省略不相关的属性...
    
        /**
         * 消息类型
         * text 文本消息
         * image 图片消息
         * voice 语音消息
         * video 视频消息
         * music 音乐消息
         * event 事件推送
         */
        protected String MsgType;
        /**
         * 事件类型
         * subscribe(订阅)
         * unsubscribe(取消订阅)
         * LOCATION(上报地理位置)
         * CLICK(点击普通的菜单)
         * VIEW(点击跳转链接的菜单)
         */
        private String Event;
        //菜单按钮的key值
        private String EventKey;
    }
    

    2.判断菜单key值

        /**
         * 微信消息处理
         */
        @RequestMapping(value = "/weChat", method = RequestMethod.POST)
        @ResponseBody
        public Object handleMessage(@RequestBody InMsgEntity msg) {
            //创建消息响应对象
            OutMsgEntity out = new OutMsgEntity();
            //把原来的发送方设置为接收方
            out.setToUserName(msg.getFromUserName());
            //把原来的接收方设置为发送方
            out.setFromUserName(msg.getToUserName());
            //获取接收的消息类型
            String msgType = msg.getMsgType();
            //设置消息创建时间
            out.setCreateTime(new Date().getTime());
            //公众号回复的内容
            String outContent = null;
            //根据类型设置不同的消息数据
            if("text".equals(msgType)){
               //.....
            }else if("image".equals(msgType)){
               //.....
            }else if("event".equals(msgType)){
                //判断关注事件
                if("subscribe".equals(msg.getEvent())){
                    out.setContent("欢迎关注![愉快]");
                    //设置消息的响应类型
                    out.setMsgType("text");
                }else if("click".equals(msg.getEvent())){
                    //获取菜单的key值
                    String key = msg.getEventKey();
                    if("classinfo".equals(key)){
                        outContent = "上海Java基础班第05期于2018/05/10开班\n" +
                                "广州Java基础班第24期于2018/04/02开班";
                    }else if("address".equals(key)){
                        outContent = "北京校区:北京昌平区沙河镇万家灯火装饰城2楼8077号\n" +
                                "广州校区:广州市天河区棠下涌东路大地工业区D栋六楼\n" +
                                "上海校区:上海市青浦区华新镇华隆路1777号E通世界商务园华新园A座4楼402";
                    }
                    //设置消息的响应类型
                    out.setMsgType("text");
                    out.setContent(outContent);
                }
            }
            return out;
        }
    
    

    菜单效果演示:

    7a825b948d2d2954d0875a5e06cf60b6.gif

    相关文章

      网友评论

        本文标题:微信公众号开发教程(四)自定义菜单

        本文链接:https://www.haomeiwen.com/subject/skzihftx.html