公众平台的自定义菜单主要分三种:
官网的开发文档中,对自定义菜单的一些基本概念和特点都讲的很详细了,这里不再赘述,直接进入正题。
下面代码片段都是接上一篇,代码均是写在WeixinGZHService.java类
@Value("${weixinGZH.appid}")
private String WEIXIN_GZH_APPID;
@Value("${weixinGZH.secret}")
private String WEIXIN_GZH_SECRET;
public static final String WEIXIN_ACCESS_TOKEN_KEY = "weixin_access_token";
public static final String KEY_I_HAVE_TIME = "V1001_I_have_time";
/**
* 生成菜单列表
*/
public void createMenuList() throws IOException, ParseException {
httpClient = new HttpClient(get_create_menu_list(getAcessToken()));
httpClient.setHttps(true);
//定义自定义菜单集合
List<WeixinMenu> weixinMenus = new ArrayList<>();
//一级菜单
WeixinMenu menu1 = new WeixinMenu();
menu1.setName("我有空");
menu1.setType(ButtonType.click);
menu1.setKey(KEY_I_HAVE_TIME);
weixinMenus.add(menu1);
WeixinMenu menu2 = new WeixinMenu();
menu2.setName("任务中心");
menu2.setType(ButtonType.view);
menu2.setUrl(SERVER_ROOT_URL + "/redirect/redirecttogoingorder");
weixinMenus.add(menu2);
WeixinMenu menu3 = new WeixinMenu();
menu3.setName("我的帐户");
menu3.setSub_button(new ArrayList<>());
menu3.setType(null);
WeixinMenu subMenu3_0 = new WeixinMenu();
subMenu3_0.setName("查看余额");
subMenu3_0.setType(ButtonType.view);
subMenu3_0.setUrl(SERVER_ROOT_URL + "/redirect/redirecttocashout");
menu3.getSub_button().add(subMenu3_1);
WeixinMenu subMenu3_1 = new WeixinMenu();
subMenu3_1.setName("本月战绩");
subMenu3_1.setType(ButtonType.view);
subMenu3_1.setUrl(SERVER_ROOT_URL + "/redirect/redirecttomonthscore");
menu3.getSub_button().add(subMenu3_1);
weixinMenus.add(menu3);
WeixinMenu subMenu3_2 = new WeixinMenu();
subMenu3_2.setName("注册视频教程");
subMenu3_2.setType(ButtonType.view);
subMenu3_2.setUrl("http://www.baidu.com");
menu3.getSub_button().add(subMenu3_2);
Map map = new HashMap();
map.put("button", weixinMenus);
System.out.println("菜单转json对象:" + JSONObject.toJSONString(map));
httpClient.setXmlParam(JSONObject.toJSONString(map));
httpClient.post();
System.out.println("======创建菜单:" + httpClient.getContent());
}
/**
* 获取创建菜单的url
*
* @return
*/
public String get_create_menu_list(String accessToken) {
return "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + accessToken;
}
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取access_token。
* access_token之所以采用缓存到redis,是因为这个token在微信官方中指明只有2个小时时效,超过2小时,要重新获取
* 所以在这里,将直接借助Redis来设置生成的token时效110分钟,这样目的是既可以防止频繁调用公众号获取token接口又能保存程序自动获取token
*/
public String getAcessToken() {
Object accessToken = redisTemplate.opsForValue().get(WEIXIN_ACCESS_TOKEN_KEY);
if (accessToken == null) {
//重新调用接口查询access_token
httpClient = new HttpClient(get_access_token(WEIXIN_GZH_APPID, WEIXIN_GZH_SECRET));
try {
httpClient.get();
String content = httpClient.getContent();
JSONObject jsonObject = JSONObject.parseObject(content);
accessToken = jsonObject.get("access_token");
if (accessToken != null) {
redisTemplate.opsForValue().set(WEIXIN_ACCESS_TOKEN_KEY, accessToken.toString(), 110, TimeUnit.MINUTES);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return accessToken == null ? null : accessToken.toString();
}
上面的代码示例,我用到了3种菜单跳转类型:
click:在点击菜单时,处理业务逻辑
view:
跳转界面,不需要获取当前微信用户信息
跳转界面,需要获取当前点击菜单的微信用户信息
在程序中,实现click点击,那就需要在我上一篇中讲联调公众平台时写的那个WechatCallbackApi.java类中完成
//在doGet()方法中,通过下面的方法来进行判断
/发送模板消息时,也会向公众号返回一条消息,但此消息的数据类型与 click事件的返回类型不同。需要判断eventKey的值
if (weixinResult != null && StringUtils.isNotBlank(weixinResult.getEventKey())) {
switch (weixinResult.getEventKey()) {
case KEY_I_HAVE_TIME:
//这里处理点这个【我有空】菜单时的业务逻辑代码
//当然,这里的业务代码没必要写在这个类中,你可以再写一个component,然后通过@Autowired注入后调用封装后的方法
break;
}
}
界面跳转并认证流程
官方的这个流程听起来就很晕了,如果你是个新手,可能你已经完全没勇气往下走了,因为这里如果出现什么异常,其实是很难定位的。
所以接下直接上代码,按照代码的编写过程边复制代码边理解,这样就算中间出现什么问题,你也能有解决问题的方向。
WeixinMenu subMenu3_0 = new WeixinMenu();
subMenu3_0.setName("查看余额");
subMenu3_0.setType(ButtonType.view);
subMenu3_0.setUrl(SERVER_ROOT_URL + "/redirect/redirecttocashout");
配置的URL是:SERVER_ROOT_URL + “/redirect/redirecttocashout”。
SERVER_ROOT_URL :这是访问你服务器根路径的变量
现在问题来了,微信后台如何能知道我们服务器的根URL是多少呢?
所以在这里,你需要再到微信公众后台配置一个访问你服务器的根路径信息
点击【修改】,进行访问根URL配置
将图片中第3步的文件下载来,放到你的后台工程中 /resources/static 目录下。
域名1:配置外网访问你服务器的域名或是外网IP
如何确定文件放对了位置:访问工程URL+第3步中的文件名。如果可以成功访问,则说明成功。
如果你在访问时,报401之类错误,则说明被你的权限框架拦截了,配置文件URL放行即可。
关于公众号view界面跳转带授权的逻辑将都会写在WeixinRedirectController.java类中
/redirect/redirecttocashout
URL逻辑//公众号view跳转并获取openid的跳转操作
@Controller
@RequestMapping("/redirect")
public class WeixinRedirectController {
@Value("${weixinGZH.appid}")
private String WEIXIN_GZH_APPID;
@Value("${weixinGZH.secret}")
private String WEIXIN_GZH_SECRET;
@Value("${SERVER_ROOT_URL}")
private String SERVER_ROOT_URL;
/**
* 重定向到【我的余额】
*
* @param
* @return java.lang.String
* @author caiwen
*/
@RequestMapping("/redirecttocashout")
public String redirectToCashout() {
return "redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid="
+ WEIXIN_GZH_APPID + "&redirect_uri=" + SERVER_ROOT_URL
+ "/redirect/weixinoauth&response_type=code&scope=snsapi_base&state=" + STATE_CASHOUT + "#wechat_redirect";
}
}
关键参数state
,这个值是用于区分后面oauth认证成功后,到底是点的哪个菜单。
@RequestMapping("/weixinoauth")
public String weixinOauth(String code, String state) {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
+ WEIXIN_GZH_APPID + "&secret=" + WEIXIN_GZH_SECRET + "&code=" + code + "&grant_type=authorization_code";
HttpClient httpClient = new HttpClient(url);
String content = "";
try {
httpClient.get();
content = httpClient.getContent();
} catch (Exception e) {
e.printStackTrace();
}
String openid = JSONObject.parseObject(content).getString("openid");
LoggerFactory.getLogger(getClass()).error("================content=" + content + ",openid=" + openid);
String redirect = "";
if (StringUtils.isBlank(openid)) {
return null;
}
//根据state的值,决定在拿到了用户openid的情况下往哪里跳
switch (state) {
case STATE_CASHOUT:
redirect = "真正要跳转到你自己页面的URL,可以是一个后台请求URL也可以直接是一个界面地址。"
break;
}
return "redirect:" + redirect;
}
到此,通过自定义菜单跳转个人界面,同时获取当前点击按钮的微信用户的信息(openid)
下一篇将会介绍 微信支付 充值功能实现