微信公众号开发(三)

自定义菜单

资料:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html

请注意:

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

创建菜单接口调用请求说明

请求地址: https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

请求方式:POST

删除菜单接口调用请求说明

请求地址:https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN

请求方式:GET

查询菜单接口调用请求说明

请求地址:https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN

请求方式: GET

示例:

// 引入request-promist-native
const rp = require(‘request-promise-native‘);
// 引入fs方法
const {writeFile, readFile} = require(‘fs‘);

var app = function () {
    return {
        /**
         * 用来获取access_token
         * */
        async getAccessToken() {
            // 定义请求父地址
            const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx4cac0ae3b7cb88dc&secret=e895c1174c407d88bed2bf34e705c56b`
            /*
            * 发送请求
            * request
            * request-promist-native
            * 下载:npm i request request-promise-native
            * */
            return new Promise((resolve, reject) => {
                rp({method: ‘GET‘, url, json: true}).then(res => {
                    // {   //获取到的凭证
                    //     access_token: ‘34_4CwWFJkJN8yAvRqm4q4eqH_SozmhKqXqDDJ1gJ9HLWhD9FFnPX-sp5dxTlbhIhsmUFtFb471mCWkWjQREVmVpwewmLW45DD2MMD3tWXEjIWbsnqvB1f5ZN-d7IiXQ_UVgcOKlsx89cdUwVsgSQHgAGAZXO‘,
                    //     expires_in: 7200 // 过期时间
                    // }
                    // 设置access_token的过期时间
                    res.expires_in = Date.now() + (res.expires_in - 5 * 60) * 1000;
                    //    将promise对象状态改为成功的状态
                    resolve(res)
                }).catch(err => {
                    reject(‘getAccessToken方法出了问题:‘ + err)
                })
            })
        },

        /**
         * 用来保存access_token的方法
         * @param accessToken 要保存的
         * */
        saveAccessToken(accessToken) {
            // 保存
            return new Promise((resolve, reject) => {
                writeFile(‘./accessToken‘, JSON.stringify(accessToken), err => {
                    if (!err) {
                        resolve()
                    } else {
                        reject(‘saveAccessToken方法出了问题‘ + err)
                    }
                })
            })
        },

        /**
         * 用来读取access_token的方法
         * */
        readAccessToken() {
            // 保存
            return new Promise((resolve, reject) => {
                readFile(‘./accessToken‘, (err, date) => {
                    if (!err) {
                        resolve(JSON.parse(date))
                    } else {
                        reject(‘saveAccessToken方法出了问题‘ + err)
                    }
                })
            })
        },

        /**
         * 检查 access_token 是否是有效的
         * @param data
         * */
        isValidAccessToken(data) {
            // 检查传入的参数是否是有效的
            if (!data && !data.access_token && !data.expires_in) {
                return false //    代表access_token无效的
            }
            return data.expires_in > Date.now();
        },

        /**
         * 用来获取没有过期的access_token
         * */
        async fetchAccessToken() {
            if (this.access_token && this.expires_in && this.isValidAccessToken(this)) {
                // 说明之前保存过access_token,并且它是有效的,直接使用
                return Promise.resolve({
                    access_token: this.access_token,
                    expires_in: this.expires_in
                })
            }
            return this.readAccessToken()
                .then(async res => {
                    // 本地文件
                    // 判断是否过期
                    if (this.isValidAccessToken(res)) {
                        return Promise.resolve(res);
                    } else {
                        // 无效的:发送请求获取access_token
                        const res = await this.getAccessToken()
                        // 保存下来,直接使用
                        await this.saveAccessToken(res);
                        // 将请求回来的access_token返回出去
                        return Promise.resolve(res);
                    }
                }).catch(async err => {
                        // 没有本地文件
                        // 发送请求获取access_token
                        const res = await this.getAccessToken()
                        //    保存下来,直接使用
                        await this.saveAccessToken(res);
                        // 将请求回来的access_token返回出去
                        return Promise.resolve(res);
                    }
                ).then(res => {
                    // 将access_token挂载到this中
                    this.access_token = res.access_token;
                    this.expires_in = res.expires_in;
                    // 返回res包装了一层promise对象
                    // 是this.readAccessToken()最终的返回值
                    return Promise.resolve(res);
                })
        },
        /*
        * 创建自定义菜单
        * */
        createMenu(menu) {
            return new Promise(async (resolve, reject) => {
                try {
                    // 获取access_token
                    const data = await this.fetchAccessToken();
                    // 定义请求地址
                    const url = `https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${data.access_token}`
                    const result = rp({method: ‘POST‘,url,json:true,body:menu});
                    resolve(result)
                } catch (e) {
                    reject(‘createMenu方法:‘ + e)
                }
            })
        },
        /*
        * 删除自定义菜单
        * */
        deleteMenu() {
            return new Promise(async (resolve, reject) => {
                try {
                    const data = await this.fetchAccessToken();
                    const url = `https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=${data.access_token}`;
                    const result =await rp({method:‘GET‘,url,json:true});
                    resolve(result)
                }catch (e) {
                    reject(‘deleteMenu:‘+ e)
                }
            })
        },
        /*
        * 查询自定义菜单
        * */
        getMenu() {
            return new Promise(async (resolve, reject) => {
                try {
                    const data = await this.fetchAccessToken();
                    const url = `https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=${data.access_token}`;
                    const result =await rp({method:‘GET‘,url,json:true});
                    resolve(result)
                }catch (e) {
                    reject(‘deleteMenu:‘+ e)
                }
            })
        }
    }
};


// 菜单
const menu = {
    "button":[
        {
            "name":"点击事件",
            "sub_button":[
                {
                    "type":"click",
                    "name":"功能说明",
                    "key":"click事件"
                },
                {
                    "type":"click",
                    "name":"菜单模式1",
                    "key":"click1事件"
                },
                {
                    "type":"click",
                    "name":"菜单模式2",
                    "key":"click2事件"
                }
            ]
        },
        {
            "name":"菜单",
            "sub_button":[
                {
                    "type":"view",
                    "name":"搜索",
                    "url":"http://www.soso.com/"
                },
                {
                    "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"
                },
            ]
        },
    ]
};
(async () => {
    // 删除之前的自定义菜单
    let result = await app().deleteMenu();
    console.log(result)
    // 创建新的菜单
    result = await app().createMenu(menu);
    console.log(result)
    // 查询菜单
    result = await app().getMenu();
    console.log(result)
})();

 右击执行,重新关注测试号

微信公众号开发(三)

 

微信公众号开发(三)

上一篇:PHP实现微信模板消息发送给指定用户


下一篇:Apache Kafka源码分析 - ReplicaStateMachine