请注意:
1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
newMenu2 类,创建菜单主类
public class BaseMenu2 {
private static Log log = LogFactory.getLog(BaseMenu2.class);
private final String appid="",appSecret="",domainName="";
public int newMenu2() {
// 调用接口获取access_token
AccessToken at = WeixinUtil.getAccessToken(appid,
appSecret);
int result = -1;
if (null != at) {
// 调用接口创建菜单
result = WeixinUtil.createMenu(getMenu, at.getToken());
// 判断菜单创建结果
if (0 == result)
log.info("菜单创建成功!");
else
log.info("菜单创建失败,错误码:" + result);
} else {
log.info("菜单创建失败,获取AccessToken 失败!");
}
return result;
}
public static void main(String[] args) {
System.out.println(new BaseMenu2().newMenu2());
}
/**
* 组装菜单数据
*
* @return
*/
private static Menu getMenu() {
CommonButton btn11 = new CommonButton();
btn11.setName("天气预报");
btn11.setType("click");
btn11.setKey("11");
CommonButton btn12 = new CommonButton();
btn12.setName("公交查询");
btn12.setType("click");
btn12.setKey("12");
CommonButton btn13 = new CommonButton();
btn13.setName("个人信息");
btn13.setType("click");
btn13.setKey("13");
CommonButton btn14 = new CommonButton();
btn14.setName("历史上的今天");
btn14.setType("click");
btn14.setKey("14");
CommonButton btn15 = new CommonButton();
btn15.setName("车辆管理");
btn15.setType("click");
btn15.setKey("32");
//第二个菜单下22222
CommonButton btn21 = new CommonButton();
btn21.setName("最新电影");
btn21.setType("click");
btn21.setKey("21");
CommonButton btn22 = new CommonButton();
btn22.setName("音乐推荐");
btn22.setType("click");
btn22.setKey("22");
CommonButton btn23 = new CommonButton();
btn23.setName("旅游景点");
btn23.setType("click");
btn23.setKey("23");
CommonButton btn24 = new CommonButton();
btn24.setName("人脸识别");
btn24.setType("click");
btn24.setKey("24");
CommonButton btn25 = new CommonButton();
btn25.setName("百度一下");
btn25.setType("click");
btn25.setKey("25");
//33333第三个菜单下
CommonButton btn31 = new CommonButton();
btn31.setName("Q友圈");
btn31.setType("click");
btn31.setKey("31");
CommonButton btn33 = new CommonButton();
btn33.setName("幽默笑话");
btn33.setType("click");
btn33.setKey("33");
CommonButton btn34 = new CommonButton();
btn34.setName("用户反馈");
btn34.setType("click");
btn34.setKey("34");
CommonButton btn35 = new CommonButton();
btn35.setName("关于我们");
btn35.setType("click");
btn35.setKey("35");
ViewButton btn32 = new ViewButton();
btn32.setName("使用帮助");
btn32.setType("view");
btn32.setUrl("http://liufeng.gotoip2.com/xiaoqrobot/help.jsp");
ComplexButton mainBtn1 = new ComplexButton();
mainBtn1.setName("生活助手");
mainBtn1.setSub_button(new Button[] { btn11, btn12, btn13, btn14, btn15 });
ComplexButton mainBtn2 = new ComplexButton();
mainBtn2.setName("附件饭店");
mainBtn2.setSub_button(new Button[] { btn21, btn22, btn23, btn24, btn25 });
ComplexButton mainBtn3 = new ComplexButton();
mainBtn3.setName("更多");
mainBtn3.setSub_button(new Button[] { btn31, btn33, btn34, btn35, btn32 });
/**
* 这是公众号xiaoqrobot目前的菜单结构,每个一级菜单都有二级菜单项<br>
*
* 在某个一级菜单下没有二级菜单的情况,menu该如何定义呢?<br>
* 比如,第三个一级菜单项不是“更多体验”,而直接是“幽默笑话”,那么menu应该这样定义:<br>
* menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 });
*/
Menu menu = new Menu();
menu.setButton(new Button[] { mainBtn1, mainBtn2, btn32 });
return menu;
}
}
AccessToken类
/**
* 微信通用接口凭证
*
*
*
*/
public class AccessToken {
// 获取到的凭证
private String token;
// 凭证有效时间,单位:秒
private int expiresIn;
private long createTime;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public long getCreateTime() {
return createTime;
}
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
}
Button类
/**
* 按钮的基类
*
*
*
*/
public class Button {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
CommonButton 类
/**
* 普通按钮(子按钮)
*
*
*
*/
public class CommonButton 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;
}
}
ComplexButton 类
/**
* 复杂按钮(父按钮)
*
*
*
*/
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;
}
}
Menu 类
/**
* 菜单
*
*
*
*/
public class Menu {
private Button[] button;
public Button[] getButton() {
return button;
}
public void setButton(Button[] button) {
this.button = button;
}
}
ViewButton
/**
* view类型的菜单
*
*
*
*/
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;
}
}
WeixinUtil类
/**
* 公众平台通用接口工具类
*
*
*
*/
public class WeixinUtil {
private static Log log = LogFactory.getLog(WeixinUtil.class);
// 获取access_token的接口地址(GET) 限200(次/天)
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
private static Map<String,AccessToken> tokenMap = new HashMap<String,AccessToken>();
/**
* 获取access_token
*
* @param appid 凭证
* @param appsecret 密钥
* @param flag 是否重新获取
* @return
*/
public static AccessToken getAccessToken(String appid, String appsecret,boolean flag) {
AccessToken accessToken = null;
log.info("=================getAccessToken-start");
if(!flag){
accessToken = tokenMap.get(appid);
}
long now = System.currentTimeMillis();
if(accessToken != null && !flag){
long lastTime = accessToken.getCreateTime();
//当accessToken还在有效期内
if(now <= (lastTime + accessToken.getExpiresIn() * 1000) ){
log.info("读取缓存accessToken=="+accessToken);
return accessToken;
}
}
String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
// 如果请求成功
if (null != jsonObject) {
try {
accessToken = new AccessToken();
accessToken.setCreateTime(now);
accessToken.setToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInt("expires_in") - 300);
log.info("重新获取accessToken=="+accessToken);
} catch (JSONException e) {
accessToken = null;
// 获取token失败
log.error("获取token失败 errcode:"+jsonObject.getInt("errcode")+" errmsg:{"+ jsonObject.getString("errmsg")+"}" );
}
}
log.info("=================getAccessToken-end");
tokenMap.put(appid, accessToken);
return accessToken;
}
/**
* 获取access_token
*
* @param appid 凭证
* @param appsecret 密钥
* @return
*/
public static AccessToken getAccessToken(String appid, String appsecret ) {
log.info("getAccessToken:重新获取AccessToken");
return getAccessToken(appid, appsecret, true);
}
// 菜单创建(POST) 限100(次/天)
public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
/**
* 创建菜单
*
* @param menu 菜单实例
* @param accessToken 有效的access_token
* @return 0表示成功,其他值表示失败
*/
public static int createMenu(Menu menu, String accessToken) {
int result = 0;
// 拼装创建菜单的url
String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);
// 将菜单对象转换成json字符串
String jsonMenu = JSONObject.fromObject(menu).toString();
// 调用接口创建菜单
JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
result = jsonObject.getInt("errcode");
log.error("创建菜单失败 errcode:"+jsonObject.getInt("errcode")+" errmsg:{"+ jsonObject.getString("errmsg")+"}" );
}
}
return result;
}
public static String JS_API_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
/**
*
* @param accessToken
* @return
*/
public static String getJsapi_ticket(String accessToken){
log.info("=====================getJsapi_ticket:start");
String url = JS_API_URL.replace("ACCESS_TOKEN", accessToken);
JSONObject json = httpRequest(url, "GET", null);
log.info("getJsapi_ticket:"+json);
String jsapi_ticket = "";
try {
jsapi_ticket = json.getString("ticket");
} catch (Exception e) {
log.error("=====================getJsapi_ticket:error"+json);
e.printStackTrace();
}
log.info("=====================getJsapi_ticket:end");
return jsapi_ticket;
}
//JS SDK 中需要的相关的数据
private static Map<String,String > JsSDKMap = new HashMap<String, String>();
public static String getJsapi_ticket_cache(String accessToken ){
String jsapi = JsSDKMap.get(accessToken);
if(jsapi != null){
log.info("读取jsapi_ticket缓存"+jsapi );
return jsapi;
}else{
jsapi = getJsapi_ticket(accessToken);
log.info("刷新Jsapi_ticket:"+jsapi+"===="+accessToken );
JsSDKMap.put(accessToken, jsapi);
}
return jsapi;
}
//获得jsapi_ticket,noncestr,timestamp
public static Map<String ,String> getJSSDKData(String requestUrl,String appid,String appsecret){
log.info("=================getJSSDKData-start");
//1)获取access_token
AccessToken accessToken = getAccessToken(appid, appsecret,false);
String token = accessToken.getToken();
log.info("accessToken:"+accessToken);
//2)获取jsapi_ticket(有效期7200秒
String jsapi_ticket = getJsapi_ticket_cache(token);
log.info("jsapi_ticket:"+jsapi_ticket);
//3)获取//随机字符串
String noncestr = Double.toString(Math.random()).substring(2, 15);//随机字符串
//4)获取 随机时间戳
String timeStamp =((int)(new Date().getTime()/1000))+"";//随机时间戳
//5)获取签名
String str = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp="+ timeStamp +"&url=" + requestUrl;
log.info("str:" + str);
String signature = SHA1Util.Sha1(str);
Map<String ,String> map = new HashMap<String, String>();
map.put("ticket", jsapi_ticket);
map.put("noncestr", noncestr);
map.put("timestamp", timeStamp);
map.put("signature", signature);
map.put("url", requestUrl);
map.put("appid", appid);
log.info("=================getJSSDKData-end");
return map;
}
/**
* 通过code换取网页授权access_token
*
* @Title: getAccess_token
* @Description: 获得ACCESS_TOKEN
* @param appid
* 公众号的唯一标识
* @param secret
* 公众号的appsecret
* @param code
* 填写第一步获取的code参数
* @return String 返回类型
* @throws
*/
public static JSONObject getOAuthJason(String appId, String appSecret, String code)
{
JSONObject jsonObject = null;
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code";
jsonObject=httpRequest(url,"GET","");
return jsonObject;
}
/**
* 发起https请求并获取结果
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
}