美文网首页
微信开发之自定义菜单以及自定义菜单推送事件

微信开发之自定义菜单以及自定义菜单推送事件

作者: 喊我小王吧 | 来源:发表于2019-04-02 16:00 被阅读0次

    微信开发之自定义菜单以及自定义菜单推送事件

    自定义菜单创建接口

    微信提供2种机制生成菜单

    机制一

    是在公众平台官网通过网站功能发布菜单 ,这种方式接入后台服务器之后菜单会失效。

    在这里插入图片描述

    机制二

    是通过API调用设置的菜单

    微信官方文档是这样子描述:

    1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
    2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
    3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
    

    自定义菜单接口类型按钮,如下10种:

    1、click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
    2、view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
    3、scancode_push:扫码推事件用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。
    4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
    5、pic_sysphoto:弹出系统拍照发图用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
    6、pic_photo_or_album:弹出拍照或者相册发图用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
    7、pic_weixin:弹出微信相册发图器用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
    8、location_select:弹出地理位置选择器用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
    9、media_id:下发消息(除文本消息)用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
    10、view_limited:跳转图文消息URL用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
    

    请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。9和10,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型,它们是没有事件推送的,能力相对受限,其他类型的公众号不必使用。

    接口调用请求说明

    http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

    click和view的请求示例

     {
         "button":[
         {    
              "type":"click",
              "name":"今日歌曲",
              "key":"V1001_TODAY_MUSIC"
          },
          {
               "name":"菜单",
               "sub_button":[
               {    
                   "type":"view",
                   "name":"搜索",
                   "url":"http://www.soso.com/"
                },
                {
                     "type":"miniprogram",
                     "name":"wxa",
                     "url":"http://mp.weixin.qq.com",
                     "appid":"wx286b93c14bbf93aa",
                     "pagepath":"pages/lunar/index"
                 },
                {
                   "type":"click",
                   "name":"赞一下我们",
                   "key":"V1001_GOOD"
                }]
           }]
     }
    

    其他新增按钮类型的请求示例

    {
        "button": [
            {
                "name": "扫码", 
                "sub_button": [
                    {
                        "type": "scancode_waitmsg", 
                        "name": "扫码带提示", 
                        "key": "rselfmenu_0_0", 
                        "sub_button": [ ]
                    }, 
                    {
                        "type": "scancode_push", 
                        "name": "扫码推事件", 
                        "key": "rselfmenu_0_1", 
                        "sub_button": [ ]
                    }
                ]
            }, 
            {
                "name": "发图", 
                "sub_button": [
                    {
                        "type": "pic_sysphoto", 
                        "name": "系统拍照发图", 
                        "key": "rselfmenu_1_0", 
                       "sub_button": [ ]
                     }, 
                    {
                        "type": "pic_photo_or_album", 
                        "name": "拍照或者相册发图", 
                        "key": "rselfmenu_1_1", 
                        "sub_button": [ ]
                    }, 
                    {
                        "type": "pic_weixin", 
                        "name": "微信相册发图", 
                        "key": "rselfmenu_1_2", 
                        "sub_button": [ ]
                    }
                ]
            }, 
            {
                "name": "发送位置", 
                "type": "location_select", 
                "key": "rselfmenu_2_0"
            },
            {
               "type": "media_id", 
               "name": "图片", 
               "media_id": "MEDIA_ID1"
            }, 
            {
               "type": "view_limited", 
               "name": "图文消息", 
               "media_id": "MEDIA_ID2"
            }
        ]
    }
    

    参数说明

    参数 是否必须 说明
    button 一级菜单数组,个数应为1~3个
    sub_button 二级菜单数组,个数应为1~5个
    type 菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型
    name 菜单标题,不超过16个字节,子菜单不超过60个字节
    key click等点击类型必须 菜单KEY值,用于消息接口推送,不超过128字节
    url view、miniprogram类型必须 网页 链接,用户点击菜单可打开链接,不超过1024字节。 type为miniprogram时,不支持小程序的老版本客户端将打开本url。
    media_id media_id类型和view_limited类型必须 调用新增永久素材接口返回的合法media_id
    appid miniprogram类型必须 小程序的appid(仅认证公众号可配置)
    pagepath miniprogram类型必须 小程序的页面路径

    返回结果

    正确时的返回JSON数据包如下:

    {"errcode":0,"errmsg":"ok"}
    

    错误时的返回JSON数据包如下(示例为无效菜单名长度):

    {"errcode":40018,"errmsg":"invalid button name size"}
    

    代码示例:

    1 简单的一级菜单可以直接发送json字符串 ,调用接口即可。

    可以查看此博客 https://www.jianshu.com/p/6eee9f99ec56

    2 复杂的二级菜单

    顶级菜单基类
    @Data
    public class BasicButton {
        private String name;
    
        private String type;
    
        /**
         * 二级菜单的数组标签 为 sub_button
         */
        private BasicButton []sub_button;
    
    }
    
    点击事件菜单实体
    @Data
    public class ClickButton extends BasicButton {
    
        private String key;
    }
    
    
    跳转链接菜单实体
    @Data
    public class ViewButton extends  BasicButton {
        private String url ;
    }
    
    
    设置菜单
    public final static String CREAT_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";
        public static void creatOption() {
            
            String url = CREAT_OPTION_URL + AccessTokenTool.getToken();
            String data = JSON.toJSONString(getMenu());
            log.info("发送的菜单json数据为: " + data);
            String s = HttpUtil.sendHttpByPost(url, data);
            log.info("返回的菜单json数据为: " + s);
            JSONObject jsonObject = JSONObject.parseObject(s);
            if (jsonObject.getInteger("errcode") == 0) {
                log.info("设置自定义菜单成功。");
    
            } else {
                log.error("设置自定义菜单失败。");
            }
        }
    
    
    
        /**
         * 组装菜单数据
         *
         * @return
         */
        private static Menu getMenu() {
            ClickButton btn11 = new ClickButton();
            btn11.setName("点击事件11");
            btn11.setType("click");
            btn11.setKey("11");
    
            ClickButton btn12 = new ClickButton();
            btn12.setName("点击事件12");
            btn12.setType("click");
            btn12.setKey("12");
    
            ClickButton btn13 = new ClickButton();
            btn13.setName("点击事件13");
            btn13.setType("click");
            btn13.setKey("13");
    
            ViewButton btn14 = new ViewButton();
            btn14.setName("view类型事件14");
            btn14.setType("view");
            btn14.setUrl("https://www.baidu.com"); //需要跳转的url
    
            ViewButton btn21 = new ViewButton();
            btn21.setName("view类型事件21");
            btn21.setType("view");
            btn21.setUrl("需要跳转的url"); //需要跳转的url
    
            ViewButton btn22 = new ViewButton();
            btn22.setName("view类型事件22");
            btn22.setType("view");
            btn22.setUrl("需要跳转的url"); //需要跳转的url
    
            ClickButton btn31 = new ClickButton();
            btn31.setName("点击事件31");
            btn31.setType("click");
            btn31.setKey("31");
    
            ViewButton btn32 = new ViewButton();
            btn32.setName("view类型事件32");
            btn32.setType("view");
            btn32.setUrl("/find"); //需要跳转的url
    
            ClickButton btn33 = new ClickButton();
            btn33.setName("点击事件33");
            btn33.setType("click");
            btn33.setKey("33");
    
            ViewButton btn34 = new ViewButton();
            btn34.setName("view类型事件34");
            btn34.setType("view");
            btn34.setUrl( "https://www.baidu.com"); //需要跳转的url
    
            BasicButton mainBtn1 = new BasicButton();
            mainBtn1.setName("一级菜单1");
            mainBtn1.setSub_button(new BasicButton[]{ btn11, btn12, btn13 ,btn14});
    
            BasicButton mainBtn2 = new BasicButton();
            mainBtn2.setName("一级菜单2");
            mainBtn2.setSub_button(new BasicButton[] { btn21, btn22});
    
            BasicButton mainBtn3 = new BasicButton();
            mainBtn3.setName("一级菜单3");
            mainBtn3.setSub_button(new BasicButton[] { btn31, btn32, btn33,btn34 });
    
            /**
             * 这是公众号 “ 程序员日常锦集 ” 目前的菜单结构 ,每个一级菜单都有二级菜单项<br>
             *
             * 在某个一级菜单下没有二级菜单的情况,menu该如何定义呢?<br>
             * 比如,第三个一级菜单项不是“点击事件31”,而直接是“view类型事件32”,那么menu应该这样定义:<br>
             * menu.setButton(new Button[] { mainBtn1, mainBtn2, btn32 });
             */
            Menu menu = new Menu();
            menu.setButton(new BasicButton[] { mainBtn1, mainBtn2, mainBtn3 });
    
            return menu;
        }
    
    

    自定义菜单事件推送

    微信会将点击事件推送给开发者,也就是我们填写的服务器地址!

    这里请注意:

    请注意,点击菜单弹出子菜单,不会产生上报。请注意,第3个到第8个的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。

    点击菜单拉取消息时的事件推送

    推送XML数据包示例:

    <xml>
    <ToUserName><![CDATA[toUser]]></ToUserName>
    <FromUserName><![CDATA[FromUser]]></FromUserName>
    <CreateTime>123456789</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[CLICK]]></Event>
    <EventKey><![CDATA[EVENTKEY]]></EventKey>
    </xml>
    

    参数说明:

    事件类型Event为:CLICK

    EventKey 就是我们自定义菜单时候所填写的key值,根据这个key值区分不同的菜单。

    点击菜单,解析微信推送给我们的xml数据:

    判断菜单:
        //这里xml转为 map类型了 
        public static BaseMsg handleClick(Map<String, String> xmlData) {
            String eventKey = xmlData.get("EventKey");
            BaseMsg bm = null;
    
            switch (eventKey) {
    
                case "11":
                    bm = new TextMsg(xmlData, "菜单11");
                    break;
    
                case "12":
    
                    bm = new TextMsg(xmlData,"菜单11" );;
                    break;
    
                case "13":
                    bm = new TextMsg(xmlData, "");
                    break;
    
                case "31":
                    bm =  new TextMsg(xmlData, "更多信息,敬请期待!");
                    break;
    
                case "33":
                    //返回图文消息
                    bm =  ArticlesMessageTool.getAiticlesMessage(xmlData, "url")
    
                        //其他的消息类型自己定义即可
                    break;
    
                default:
                    bm = new TextMsg(xmlData, " 欢迎。。。。 ");
            }
    
            return bm;
        }
    
    消息基类
    @XStreamAlias("xml") //设置根节点名
    @Data
    public class BaseMsg {
    
        //置顶别名首字母大写
        @XStreamAlias("ToUserName")
        private String toUserName;//开发者微信号
        private String FromUserName;//发送方帐号(一个OpenID)
        private String CreateTime;//消息创建时间 (整型)
        private String MsgType;//MsgType 文本类型
    
    
        public BaseMsg(Map<String, String> map) {
            this.CreateTime = System.currentTimeMillis() / 1000 + "";
            this.FromUserName = map.get("ToUserName");
            this.toUserName = map.get("FromUserName");
        }
    
    }
    
    
    文本消息类:
    @XStreamAlias("xml")
    @Data
    public class TextMsg extends BaseMsg {
    
        private String Content;//文本消息内容
    
        public TextMsg(Map<String, String> map, String Content) {
            super(map);
            this.Content = Content;
            this.setMsgType("text");
        }
    
    }
    
    
    图文消息类:
    @XStreamAlias("xml") //设置根节点名
    @Data
    public class ImageMsg extends BaseMsg {
    
    
        private String ArticleCount;// 是  图文消息个数;当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息
        private List<ArticlesItem> Articles;// 是  图文消息信息,注意,如果图文数超过限制,则将只发限制内的
    
    
        public ImageMsg() {
        }
    
        public ImageMsg(Map<String, String> map) {
            super(map);
            this.setMsgType("news");
        }
    
    
    
    
    获取图文消息工具:
        /**
         * 获取图文消息
         *
         * @param custermName
         * @param serverName
         * @param createTime
         * @param xmlData
         * @return
         */
        public static ImageMsg getAiticlesMessage(Map<String, String> xmlData, String url) {
            ImageMsg imageMsg = new ImageMsg(xmlData);
            List<ArticlesItem> list = new ArrayList<ArticlesItem>();
            ArticlesItem item = new ArticlesItem();
            String title = "欢迎使用公众号!";
            String description = "点击图文进入";
            //图片路径
            String picurl = "自己的服务器地址" + "/img/008.jpg";
    
            item.setDescription(AirPortConfig.description);
            item.setTitle(AirPortConfig.title);
            item.setPicUrl(picurl);
            item.setUrl(url);
            list.add(item);
            
            // 多个可以继续设置.....
    
            imageMsg.setArticleCount("1");
            imageMsg.setMsgType("news");
    
            imageMsg.setArticles(list);
    
            return imageMsg;
        }
    
    xml转为map
    public static Map<String, String> getXmlData(InputStream inputStream) {
        Map<String, String> map = new HashMap<>();
        //截取xml
        SAXReader reader = new SAXReader();
        try {
    
            Document document = reader.read(inputStream);
            Element rootElement = document.getRootElement(); //获取根节点
            List<Element> elements = rootElement.elements(); // h获取所有的节点
            for (Element e : elements) {
                map.put(e.getName(), e.getStringValue());
            }
    
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
    
    实体对象输出xml
        public static String bean2Xml(BaseMsg baseMsg) {
            XStream xStream = new XStream();
            //若没有这句,xml中的根元素会是<包.类名>;或者说:注解根本就没生效,所以的元素名就是类的属性
            xStream.processAnnotations(BaseMsg.class);
            xStream.processAnnotations(TextMsg.class);
            xStream.processAnnotations(ImageMsg.class);
            String xml = xStream.toXML(baseMsg);
            log.info("返回的xml = " + xml);
            return xml;
        }
    
    maven依赖
          <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
            <dependency>
                <groupId>dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>1.6.1</version>
            </dependency>
    
    
            <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
            <dependency>
                <groupId>com.thoughtworks.xstream</groupId>
                <artifactId>xstream</artifactId>
                <version>1.4.11.1</version>
            </dependency>
    

    Java学习扣 群 :731690200

    相关文章

      网友评论

          本文标题:微信开发之自定义菜单以及自定义菜单推送事件

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