微信公衆号開發文章目錄
1.微信公衆号開發 - 環境搭建
2.微信公衆号開發 - 配置表設計以及接入公衆号接口開發
3.微信公衆号開發 - token擷取(保證同一時間段内隻請求一次)
4.微信公衆号開發 - 菜單按鈕bean封裝
5.微信公衆号開發 - 建立菜單
6.微信公衆号開發 - 事件處理和回複消息
7.微信公衆号開發 - 發送Emoji表情
項目完整代碼請通路github:https://github.com/liaozq0426/wx.git
在上篇文章中完成了菜單、按鈕bean的封裝,現在來實作微信公衆号菜單的建立
向wx_cfg配置表中插入若幹菜單記錄
INSERT INTO `wx_cfg` (`id`, `type`, `name`, `value`, `parent_id`, `sort`, `platform`, `wx_type`, `enabled`, `desc`) VALUES ('4', 'app_url', 'appUrl', 'http://www.gavin.com', NULL, NULL, 'gavin', 'service', '1', '公衆号域名');
INSERT INTO `wx_cfg` (`id`, `type`, `name`, `value`, `parent_id`, `sort`, `platform`, `wx_type`, `enabled`, `desc`) VALUES ('5', 'menu', 'view1', '/transfer/index.html', NULL, '1', 'gavin', 'service', '1', '學習資料');
INSERT INTO `wx_cfg` (`id`, `type`, `name`, `value`, `parent_id`, `sort`, `platform`, `wx_type`, `enabled`, `desc`) VALUES ('6', 'menu', 'view2', '/transfer/index.html', NULL, '2', 'gavin', 'service', '1', '線上題庫');
INSERT INTO `wx_cfg` (`id`, `type`, `name`, `value`, `parent_id`, `sort`, `platform`, `wx_type`, `enabled`, `desc`) VALUES ('7', 'menu', 'click1', '/transfer/index.html', NULL, '3', 'gavin', 'service', '1', '關于');
INSERT INTO `wx_cfg` (`id`, `type`, `name`, `value`, `parent_id`, `sort`, `platform`, `wx_type`, `enabled`, `desc`) VALUES ('8', 'menu', 'view11', '/transfer/index.html', '5', '1', 'gavin', 'service', '1', 'JAVA');
INSERT INTO `wx_cfg` (`id`, `type`, `name`, `value`, `parent_id`, `sort`, `platform`, `wx_type`, `enabled`, `desc`) VALUES ('9', 'menu', 'view12', '/transfer/index.html', '5', '2', 'gavin', 'service', '1', 'MySQL');
編寫代碼
建立WxMenuService接口
package com.gavin.service;
import com.gavin.pojo.Menu;
public interface WxMenuService {
public Menu makeMenu(String platform) throws Exception;
}
建立實作類WxMenuServiceImpl,代碼如下
package com.gavin.service.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.gavin.cfg.WxCfgEnum;
import com.gavin.pojo.AccessToken;
import com.gavin.pojo.Button;
import com.gavin.pojo.ClickButton;
import com.gavin.pojo.ComplexButton;
import com.gavin.pojo.Menu;
import com.gavin.pojo.ViewButton;
import com.gavin.pojo.WxApiCfg;
import com.gavin.pojo.WxCfg;
import com.gavin.service.WxCfgService;
import com.gavin.service.WxMenuService;
import com.gavin.service.WxTokenService;
import com.gavin.util.EncryptionUtil;
import com.gavin.util.WxUtil;
import com.google.gson.Gson;
@Service
public class WxMenuServiceImpl implements WxMenuService{
private Logger logger = Logger.getLogger(this.getClass());
@Autowired
private WxTokenService wxTokenService;
@Autowired
private WxCfgService wxCfgService;
/**
* @title 根據平台建立微信公衆号菜單
* @author gavin
* @date 2019年5月23日
* @param platform
* @return
* @throws Exception
*/
@Override
public Menu makeMenu(String platform) throws Exception {
// 查詢一級菜單
System.out.println(platform);
WxCfg parentParam = new WxCfg();
parentParam.setType(WxCfgEnum.MENU.getType());
parentParam.setEnabled(true);
parentParam.setPlatform(platform);
parentParam.setOrderBy("sort asc");
List<WxCfg> cfgList = this.wxCfgService.select(parentParam);
if (cfgList.size() > 0) {
Menu menu = new Menu();
List<Button> buttons = new ArrayList<Button>();
for (WxCfg cfg : cfgList) {
WxCfg subParam = new WxCfg();
subParam.setType(WxCfgEnum.MENU.getType());
subParam.setParentId(cfg.getId());
subParam.setEnabled(true);
subParam.setPlatform(platform);
// 查詢二級菜單
List<WxCfg> subCfgList = this.wxCfgService.select(subParam);
if (subCfgList.size() > 0) {
// 建構多級按鈕
List<Button> subButtons = new ArrayList<Button>();
for (WxCfg subCfg : subCfgList) {
Button sub = createButton(subCfg);
subButtons.add(sub);
}
ComplexButton complexButton = new ComplexButton();
complexButton.setName(cfg.getDesc());
complexButton.setSub_button(subButtons.toArray(new Button[subButtons.size()]));
buttons.add(complexButton);
} else {
Button button = createButton(cfg);
buttons.add(button);
}
}
// 擷取accessToken
AccessToken token = this.wxTokenService.readAccessToken(AccessToken.TYPE_ACCESS_TOKEN, platform);
menu.setButton(buttons.toArray(new Button[buttons.size()]));
Gson gson = new Gson();
String menuJson = gson.toJson(menu);
System.out.println(menuJson);
Map<String , Object> message = WxUtil.createMenu(menu, token.getAccess_token());
logger.info(message.toString());
if (message.get("errcode") != null
&& (Integer)message.get("errcode") == 0
&& "ok".equalsIgnoreCase((String)message.get("errmsg"))) {
logger.info("微信菜單建立成功!");
return menu;
}else {
logger.error("微信菜單建立失敗");
}
}
return null;
}
/**
* @title 根據配置資訊建立button,這裡隻實作了最常用的click和view兩種類型的按鈕
* @author gavin
* @date 2019年5月23日
* @param cfg
* @return
* @throws Exception
*/
private Button createButton(WxCfg cfg) throws Exception {
String btnType = cfg.getName();
if (btnType.startsWith(Button.TYPE_CLICK)) {
// 普通點選按鈕
ClickButton click = new ClickButton();
click.setName(cfg.getDesc());
click.setType(Button.TYPE_CLICK);
// key可以根據自己的需要設定,這裡我設定為[name_desc]
click.setKey(cfg.getName() + "_" + cfg.getDesc());
return click;
} else if (btnType.startsWith(Button.TYPE_VIEW)) {
// view按鈕
ViewButton view = new ViewButton();
view.setName(cfg.getDesc());
view.setType(Button.TYPE_VIEW);
// 動态生成url
WxCfg cfgParam = new WxCfg();
cfgParam.setPlatform(cfg.getPlatform());
cfgParam.setType(WxCfgEnum.WX_KEY_APPID.getType());
cfgParam.setName(WxCfgEnum.WX_KEY_APPID.getName());
WxCfg queryCfg = this.wxCfgService.selectOne(cfgParam);
if(queryCfg != null) {
String appId = EncryptionUtil.base64Decode(queryCfg.getValue());
String wxType = cfg.getWxType();
String url = null;
if(WxCfgEnum.WX_TYPE_SERVICE.getType().equals(wxType)) {
// 如果是服務号,生成網頁授權的格式url
String redirectUrl = cfg.getValue();
if(!StringUtils.isBlank(redirectUrl)) {
redirectUrl = redirectUrl + "?state=" + cfg.getName() + "&platform=" + cfg.getPlatform();
}
url = WxUtil.makeViewUrl(appId, WxApiCfg.SCOPE_BASE, redirectUrl,
cfg.getName());
}else if(WxCfgEnum.WX_TYPE_SUBSCRIBE.getType().equals(wxType)) {
// 如果是訂閱号,生成普通url格式
url = cfg.getValue();
if(!StringUtils.isBlank(url))
url = url + "?state=" + cfg.getName() + "&platform=" + cfg.getPlatform();
}
logger.info(url);
view.setUrl(url);
return view;
}
}
return null;
}
}
上述代碼中的幾個要點
1)在查詢資料庫配置中,首先查詢了
parent_id
為空的記錄,也就是一級菜單
2)查詢出一級菜單後,周遊一級菜單集合,用一級菜單記錄的
id
作為
parent_id
參數去查詢二級菜單
3)createButton為建立按鈕對象方法,建立時會判斷name字段的值,如果name值的字首是click,則建立成ClickButton,如果name值得字首是view,則建立成ViewButton。
4)如果建立的按鈕是ViewButton,會進一步判斷wx_type字段(公衆号類型),如果是服務号,url會建立成網頁授權的格式,因為網頁授權格式才能在後續擷取openid和使用者基本資訊;如果是訂閱号,則建立成普通格式的url。
接下來在WxController中新增一個方法,調用service
@GetMapping("wx/menu")
public Menu getWxMenu(String platform) {
try {
return this.wxMenuService.makeMenu(platform);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
測試
最後,用postman測試一下wx/menu接口,調用結果如下圖所示

再用手機看一下公衆号
說明公衆号菜單建立成功!