10萬+IT人都在關注,史上最全面的微信開發實戰教程:包含公衆號,小程序,微信支付等開發案例
歡迎關注筆者個人博客: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