微信公众号开发 - 创建菜单

微信公众号开发文章目录

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接口,调用结果如下图所示
图1.创建菜单调用结果图
再用手机看一下公众号
图2.公众号菜单效果图
说明公众号菜单创建成功!

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页