自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。

菜单的刷新策略

创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

自定义菜单接口可实现多种类型按钮,如下

  1. click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
  2. view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
  

 

封装菜单和按钮实体类

新建菜单按钮基类BasicButton.java

import lombok.Data;

/**
 * @auther: zqtao
 * @description: 菜单按钮基类
 * 所有一级菜单、二级菜单都共有相同的属性,name type
 * @version: 1.0
 */

新建click类型的按钮类ClickButton.java

import lombok.Data;

/**
 * @auther: zqtao
 * @Description: click类型的按钮(有type、name、key3个属性)
 * @version: 1.0
 */

新建view类型的按钮类ViewButton.java

import lombok.Data;

/**
 * @auther: zqtao
 * @description: view类型的按钮(有type 、 name 、 url三个属性)
 * @version: 1.0
 */

新建复合类型的按钮类ComplexButton.java

/**
 * @auther: zqtao
 * @description: 复合类型的按钮(含有子菜单的一级菜单)
 * @version: 1.0
 */
@Data
public class ComplexButton extends BasicButton {
    private BasicButton[] sub_button;
}

新建菜单类Menu.java

import lombok.Data;

/**
 * @auther: zqtao
 * @description: 整个菜单对象的封装
 * 菜单对象包含多个菜单项(最多只能有3个)
 * 这些菜单项即可以是子菜单项(不含二级菜单的一级菜单),也可以是父菜单项(包含二级菜单的菜单项)
 * @version: 1.0
 */

新建微信官方提供的功能性接口常量类WechatInterface.java

/**
 * @auther: zqtao
 * @description: 微信官方提供的功能性接口常量
 * @version: 1.0
 */

public class WechatInterface {
    /**
     * 获取access_token的接口地址(GET) 限200(次/天)
     */
    public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

    /**
     * 自定义菜单删除接口
     */
    public static final String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";

    /**
     * 自定义菜单的创建接口
     */
    public static final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

    /**
     * 自定义菜单的查询接口
     */
    public static final String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
}

菜单和按钮等实体封装好,接下来是如何进行菜单的创建,删除操作。

1、根据AppId和AppSecret,以https get方式获取访问特殊接口所必须的凭证access_token;

2、根据access_token,将json格式的菜单数据通过https post方式提交。

封装通用的请求方法

创建菜单需要调用https请求接口。所以首要问题是实现HTTPS 请求方法

https请求,需要一个证书信任管理器,这个管理器类需要自己定义,实现X509TrustManager接口

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * @auther: zqtao
 * @description: 证书管理器
 * @version: 1.0
 */
public class MyX509TrustManagerUtil implements X509TrustManager {
    // 证书管理器的作用就是让它信任我们指定的证书,下面的代码意味着信任所有证书,不管是否权威机构颁发
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

封装HTTPS请求工具HttpRequestUtil

import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;

/**
 * @auther: zqtao
 * @description: 公众接口http请求发起通用工具类
 * 封装通用的请求方法
 * 自定义菜单需要调用的接口,都是https请求,而非http,同时需要一个证书信任管理器类 MyX509TrustManagerUtil
 *
 * 1)支持HTTPS请求;
 *
 * 2)支持GET、POST两种方式;
 *
 * 3)支持参数提交,也支持无参数的情况;
 * @version: 1.0
 */

新建公众平台AccessToken 获取工具类AccessTokenUtil

封装获取凭证access_token的方法

import lombok.extern.slf4j.Slf4j;
import me.zqt.wx.constant.WechatInterface;
import me.zqt.wx.model.AccessToken;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;

/**
 * @auther: zqtao
 * @description: 公众平台AccessToken 获取工具类
 * @version: 1.0
 */

新建微信自定义菜单核心服务实现类MenuManagerServiceImpl

import lombok.extern.slf4j.Slf4j;
import me.zqt.wx.constant.LogConstant;
import me.zqt.wx.constant.WechatInterface;
import me.zqt.wx.model.menu.Menu;
import me.zqt.wx.service.MenuManagerService;
import me.zqt.wx.utils.HttpRequestUtil;
import net.sf.json.JSONObject;


/**
 * @auther: zqtao
 * @description: 微信自定义菜单核心服务实现类
 * @version: 1.0
 */

菜单管理器类MenuManagerDriverMain: 初始化自定义菜单

import lombok.extern.slf4j.Slf4j;
import me.zqt.wx.constant.LogConstant;
import me.zqt.wx.constant.SignatureConstant;
import me.zqt.wx.model.AccessToken;
import me.zqt.wx.model.menu.*;
import me.zqt.wx.service.MenuManagerService;
import me.zqt.wx.service.impl.MenuManagerServiceImpl;
import me.zqt.wx.utils.AccessTokenUtil;

/**
 * @auther: zqtao
 * @description: 菜单管理器类: 初始化自定义菜单
 * @version: 1.0
 */
@Slf4j
public class MenuManagerDriverMain {

    public static void main(String[] args) {
        log.info(LogConstant.LOG_INFO.replace("INFO", "开始初始化自定义菜单"));
        // 第三方用户唯一凭证
        String appId = SignatureConstant.APP_ID;
        // 第三方用户唯一凭证密钥
        String appSecret = SignatureConstant.APP_SECRET;

        // 调用接口获取access_token
        AccessToken at = AccessTokenUtil.getAccessToken(appId, appSecret);
        log.info(LogConstant.LOG_INFO.replace("INFO", "AccessToken is :" + at.getToken()));

        if (at != null) {
            MenuManagerService menuManagerService = new MenuManagerServiceImpl();
            // 调用接口创建菜单
            int result = menuManagerService.createMenu(getMenu(), at.getToken());
            // 调用接口删除菜单
//            int result = menuManagerService.deleteMenu(at.getToken());
            // 判断菜单创建结果
            if (0 == result)
                log.info(LogConstant.LOG_INFO.replace("INFO", "菜单操作成功!"));
            else
                log.info(LogConstant.LOG_INFO.replace("INFO", "菜单操作失败,错误码:" + result));
        }
        log.info(LogConstant.LOG_INFO.replace("ERROR", "结束初始化自定义菜单"));
    }

    /**
     * 组装菜单数据
     */
    private static Menu getMenu() {

        // 子按钮(菜单)
        ClickButton btn11 = new ClickButton();
        btn11.setName("开发工具");
        btn11.setType("click");
        btn11.setKey("11");

        ViewButton btn12 = new ViewButton();
        btn12.setName("资源合集");
        btn12.setType("view");
        btn12.setUrl("https://www.baidu.com/");

        ViewButton btn21 = new ViewButton();
        btn21.setName("知乎");
        btn21.setType("view");
        btn21.setUrl("https://www.zhihu.com/people/zqtao23/activities");

        ViewButton btn22 = new ViewButton();
        btn22.setName("简书");
        btn22.setType("view");
        btn22.setUrl("https://www.jianshu.com/u/7110a2ba6f9e");

        ViewButton btn31 = new ViewButton();
        btn31.setName("资源屋");
        btn31.setType("view");
        btn31.setUrl("http://www.baidu.com");

        ViewButton btn32 = new ViewButton();
        btn32.setName("Github");
        btn32.setType("view");
        btn32.setUrl("https://github.com/zqtao2332");

        ViewButton btn33 = new ViewButton();
        btn33.setName("博客");
        btn33.setType("view");
        btn33.setUrl("http://www.zqtaotao.cn");


        // 一级菜单
        ComplexButton mainBtn1 = new ComplexButton();
        mainBtn1.setName("开发助手");
        mainBtn1.setSub_button(new BasicButton[]{btn11, btn12});

        ComplexButton mainBtn2 = new ComplexButton();
        mainBtn2.setName("知识驿站");
        mainBtn2.setSub_button(new BasicButton[]{btn21, btn22});

        ComplexButton mainBtn3 = new ComplexButton();
        mainBtn3.setName("更多体验");
        mainBtn3.setSub_button(new BasicButton[]{btn31, btn32, btn33});

        Menu menu = new Menu();
        menu.setButton(new BasicButton[]{mainBtn1, mainBtn2, mainBtn3});
        return menu;
    }
}

运行MenuManagerDriverMain.java 即可创建自定义的菜单

删除自定义菜单只需要开放删除操作

// int result = menuManagerService.createMenu(getMenu(), at.getToken());
// 调用接口删除菜单
int result = menuManagerService.deleteMenu(at.getToken());

 转自:https://www.jianshu.com/p/7d10c933852e