微信公衆號菜單欄開發 微信公衆號接口開發個性化菜單 菜單事件開發

10萬+IT人都在關注,史上最全面的微信開發實戰教程:包含公衆號,小程序,微信支付等開發案例

歡迎關注筆者個人博客:http://blogs.chenyunkeji.com/

首先,直接上圖,看效果,如下,有三個根菜單,每個菜單上有不同類型的子菜單,點擊子菜單可以實現用戶和公衆號實時交互

http://blogs.chenyunkeji.com

本案例技術棧:springboot,mysql,logback,mybatis

菜單創建請求接口:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

首先是菜單項(按鈕)的基類,所有一級菜單、二級菜單都有一個相同的屬性,那就是name。菜單項基類的封裝代碼如下:

public class Button {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

接着是子菜單項的封裝。這裏對子菜單是這樣定義的:底部根菜單的二級菜單。這類子菜單項一定會包含三個屬性:type、name和key,封裝的代碼如下

public class CommandButton extends Button{

    private String type;
    private String key;
    
    public String getType() {
	  return type;
    }
    public void setType(String type) {
	  this.type = type;
    }
    public String getKey() {
	  return key;
    }
    public void setKey(String key) {
	  this.key = key;
    }
}

 接着封裝父菜單項。對父菜單項的定義:包含有二級菜單項的一級菜單。這類菜單項包含有二個屬性:name和sub_button,而sub_button是一個子菜單項數組。父菜單項的封裝代碼如下:

public class ComplexButton extends Button{

    private Button[] sub_button;

    public Button[] getSub_button() {
        return sub_button;
    }

    public void setSub_button(Button[] sub_button) {
        this.sub_button = sub_button;
    }
}

最後是整個菜單對象的封裝,菜單對象包含多個根菜單項(最多只能有3個),這些菜單項即可以是子菜單項(不含二級菜單的一級菜單),也可以是父菜單項(包含二級菜單的菜單項),如果能明白上面所講的,再來看封裝後的代碼就很容易理解了:

public class Menu {
    private Button[] button;
    public Button[] getButton() {
        return button;
    }

    public void setButton(Button[] button) {
        this.button = button;
    }
}

 最後再封裝一個鏈接類型的菜單,根據此處的名字也很好理解,不再贅述

public class ViewButton extends Button{

    private String type;
    private String url;
    
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
}

關於菜單實體類以及菜單對象的封裝就介紹完了,下面根據微信接口創建自定義菜單。

一、封裝菜單創建工具類

public class WeiXinMenuUtil {

    private static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";        private static Logger log = LoggerFactory.getLogger(WeiXinMenuUtil.class);
    public static int createMenu(Menu menu, String accessToken) {
        int result = 0;
        // 拼接創建菜單的url
        String url = menu_create_url .replace("ACCESS_TOKEN", accessToken);
        // 將菜單對象轉換成json字符串
        String jsonMenu = JSON.toJSONString(menu);
        // 調用接口創建菜單
        JSONObject jsonObject = WeiXinUtil.httpPost(url, "POST", jsonMenu);
        if (null != jsonObject) {
            if (0 != (Integer)jsonObject.get("errcode")) {
                result = (Integer)jsonObject.get("errcode");
                log.error("創建菜單失敗 errcode:{} errmsg:{}", jsonObject.get("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return result;
    }
    
    public static Menu initMenu(){
        //1.創建菜單
        Menu menu = new Menu();
        // 主菜單1
        ComplexButton cb1 = new ComplexButton();
        cb1.setName("技術乾貨");
        
        // 主菜單2
        ComplexButton cb2 = new ComplexButton();
        cb2.setName("交流合作");
        
        // 主菜單3
        ComplexButton cb3 = new ComplexButton();
        cb3.setName("演示功能");
        
        // 主菜單1下面的子菜單1
        ViewButton cb01 = new ViewButton();
        cb01.setName("微服務教程連載");
        cb01.setType("view");
        cb01.setUrl("https://blog.csdn.net/guobinhui/article/category/8739270");
        
        // 主菜單1下面的子菜單2
        ViewButton cb02 = new ViewButton();
        cb02.setName("公衆號開發教程");
        cb02.setType("view");
        cb02.setUrl("https://blog.csdn.net/guobinhui/article/category/8534361");
        
        ViewButton cb03 = new ViewButton();
        cb03.setName("小程序開發教程");
        cb03.setType("view");
        cb03.setUrl("https://blog.csdn.net/guobinhui/article/category/7763266");
        
        ViewButton cb04 = new ViewButton();
        cb04.setName("JavaEE基礎");
        cb04.setType("view");
        cb04.setUrl("https://blog.csdn.net/guobinhui");
        
        ViewButton cb05 = new ViewButton();
        cb05.setName("筆者博客");
        cb05.setType("view");
        cb05.setUrl("http://blogs.chenyunkeji.com/");
        cb1.setSub_button(new ViewButton[]{cb01,cb02,cb03,cb04,cb05});
        
        // 主菜單2下面的子菜單1
        CommandButton cb11 = new CommandButton();
        cb11.setType("click");
        cb11.setKey("聯繫我");
        cb11.setName("聯繫筆者");
        
        CommandButton cb12 = new CommandButton();
        cb12.setName("技術交流");
        cb12.setType("click");
        cb12.setKey("18629374628");
        cb2.setSub_button(new CommandButton[]{cb11,cb12});
        
        CommandButton cb21 = new CommandButton();
        cb21.setType("scancode_waitmsg");
        cb21.setKey("rselfmenu_0_0");
        cb21.setName("掃碼帶提示");
        
        CommandButton cb22 = new CommandButton();
        cb22.setType("pic_sysphoto");
        cb22.setKey("rselfmenu_1_0");
        cb22.setName("系統拍照發圖");
        
        CommandButton cb23 = new CommandButton();
        cb23.setType("pic_photo_or_album");
        cb23.setKey("rselfmenu_1_1");
        cb23.setName("拍照或者相冊發圖");
        
        CommandButton cb24 = new CommandButton();
        cb24.setType("pic_weixin");
        cb24.setKey("rselfmenu_1_2");
        cb24.setName("微信相冊發圖");
        
        CommandButton cb25 = new CommandButton();
        cb25.setType("location_select");
        cb25.setKey("rselfmenu_2_0");
        cb25.setName("發送地理位置");
        cb3.setSub_button(new CommandButton[]{cb21, cb22,cb23, cb24,cb25});
        menu.setButton(new Button[] { cb1,cb2, cb3});
        return menu;        
    }
}

二、微信接口憑證access_token的獲取以及緩存實現

access_token可以用各種緩存插件或者線程,定時任務,寫入數據庫等多種方式對access_token進行緩存7200秒,開發者可以依據自己的喜好選擇其一,本案例策略採用IO流定時寫入文件的方法緩存。首先獲取access_token:

public static JSONObject getToken()throws IOException{

 private String GET_ACCESS_TOKEN= "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
 String url = GET_ACCESS_TOKEN.replace("APPID",WeixinConstant.APPID).replace("APPSECRET",WeixinConstant.APPSECRET);
   JSONObject tokenObj = HttpGet(url);
   return tokenObj;
}

接着緩存access_token,也就是每隔7200秒生成一次access_token,生成的緩存7200秒,這樣其餘用到access_token的從文件中讀取的就是最新的,不會過期。

public static  Map <String,Object> cacheToken() throws IOException {
      Gson gson = new Gson();
      Map <String,Object> map = new HashMap <String,Object> ();
      String token = null;
      JSONObject tokenObj = null; //需要獲取的access_token對象;
      String filePath = System.getProperty("user.dir")+"/src/main/resources/static/token.txt";
      File file = new File(filePath);//Access_token保存的位置
      if (!file.exists())
        file.createNewFile();
      // 如果文件大小等於0,說明第一次使用,存入Access_token
      if (file.length() == 0) {
        tokenObj = WeiXinUtil.getToken();
        token = (String)tokenObj.get("access_token");
        FileOutputStream fos = new FileOutputStream(filePath, false);// 不允許追加
        tokenObj.put("expires_in",System.currentTimeMillis()/1000+"");
        String json = gson.toJson(tokenObj);
        fos.write(json.getBytes());
        fos.close();
      }else {
        //讀取文件內容
        @SuppressWarnings("resource")
        FileInputStream fis = new FileInputStream(file);
        byte[] b = new byte[2048];
        int len = fis.read(b);
        String jsonAccess_token = new String(b, 0, len);// 讀取到的文件內容
        JSONObject access_token = gson.fromJson(jsonAccess_token,JSONObject.class);
        if (access_token.get("expires_in") != null) {
          String lastSaveTime = (String)access_token.get("expires_in");
          long nowTime = System.currentTimeMillis()/1000;
          long remianTime = nowTime - Long.valueOf(lastSaveTime);
          if (remianTime < WeixinConstant.EXPIRESIN_TIME) {
              JSONObject access = gson.fromJson(jsonAccess_token,JSONObject.class);
              token = (String)access.get("access_token");
          } else {
              tokenObj = WeiXinUtil.getToken();
              FileOutputStream fos = new FileOutputStream(file, false);// 不允許追加
              tokenObj.put("expires_in",System.currentTimeMillis()/1000+"");
              String json = gson.toJson(tokenObj);
              fos.write((json).getBytes());
              fos.close();
          }
      }
      }
      map.put("access_token",token);
      return map;
    }

最後在項目的啓動入口文件的main方法調用菜單創建方法進行初始化,那麼項目在啓動的時候,公衆號的菜單就進行了初始化,用戶就能看到公衆號的菜單。

@SpringBootApplication
@MapperScan(basePackages = "com.chenyun.cloud.dao")
public class HelloServiceApplication {
    private final static Logger logger= LoggerFactory.getLogger(HelloServiceApplication.class);
    
    public static void main(String[] args) {
         SpringApplication.run(HelloServiceApplication.class, args);
         Map<String, Object> map;
        try {
            map = WeiXinUtil.cacheToken();
             String accessToken = (String)map.get("access_token");
             logger.info("accessToken:"+accessToken);
             Menu menu = WeiXinMenuUtil.initMenu();
             System.out.println(JSON.toJSONString(menu));
             int result  = WeiXinMenuUtil.createMenu(menu,accessToken);
             if (0 == result){
                  logger.info("菜單創建成功!");
              }else{
                  logger.info("菜單創建失敗,錯誤碼:" + result);
              }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }    
}

啓動項目後,打印的JSON格式的日誌打就顯示了創建的菜單結構。

下節內容爲大家分享公衆號菜單欄的各種點擊事件開發案例,更多JavaEE資料請關注下面公衆號,歡迎廣大開發者朋友一起交流。更多微信公衆號功能演示請掃碼體驗,筆者電話(微信):18629374628

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章