diff --git a/CHANGE.md b/CHANGE.md
index ad6fec0d..e35d6293 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -263,4 +263,8 @@
* 2015-04-29
- + released 1.4
\ No newline at end of file
+ + released 1.4
+
+* 2015-04-30
+
+ + 新增`media_id`和`view_limited`两种菜单类型
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java
index e6604270..d51122e8 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/Button.java
@@ -43,7 +43,7 @@ public class Button implements Serializable {
*
* 使用API设置的自定义菜单:
* click、scancode_push、scancode_waitmsg、pic_sysphoto、pic_photo_or_album
- * 、 pic_weixin、location_select:保存值到key;view:保存链接到url
+ * 、 pic_weixin、location_select:保存为key;view:保存为url;media_id、view_limited:保存为media_id
*
*
*/
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/ButtonType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/ButtonType.java
index dcd6e8ba..5d696260 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/ButtonType.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/ButtonType.java
@@ -1,10 +1,11 @@
package com.foxinmy.weixin4j.type;
/**
- * 自定义菜单类型
- * scancode_push 到 location_selec
- * 仅支持微信iPhone5.4.1以上版本,和Android5 .4以上版本的微信用户 旧版本微信用户点击后将没有回应
- * 开发者也不能正常接收到事件推送。
+ * 自定义菜单类型 请注意,scancode_push到location_select的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户
+ * ,旧版本微信用户点击后将没有回应
+ * ,开发者也不能正常接收到事件推送。media_id和view_limited,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型
+ * ,它们是没有事件推送的,能力相对受限,其他类型的公众号不必使用。
*
* @className ButtonType
* @author jy
@@ -51,5 +52,15 @@ public enum ButtonType {
* 弹出地理位置选择器用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,
* 随后可能会收到开发者下发的消息。
*/
- location_select;
+ location_select,
+ /**
+ * 下发消息(除文本消息):用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、
+ * 音频、视频、图文消息。 请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
+ */
+ media_id,
+ /**
+ * 跳转图文消息URL:用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,
+ * 永久素材类型只支持图文消息。 请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
+ */
+ view_limited;
}
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java
index 269e615c..58fd5333 100644
--- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java
+++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java
@@ -9,7 +9,6 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
-import com.alibaba.fastjson.serializer.NameFilter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Button;
@@ -118,65 +117,63 @@ public class HelperApi extends MpApi {
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_get_selfmenu_uri,
token.getAccessToken()));
-
JSONObject result = response.getAsJson();
+
JSONArray buttons = result.getJSONObject("selfmenu_info").getJSONArray(
"button");
- List _buttons = new ArrayList();
-
+ List buttonList = new ArrayList(buttons.size());
JSONObject buttonObj = null;
- Object subButton = null;
for (int i = 0; i < buttons.size(); i++) {
buttonObj = buttons.getJSONObject(i);
- subButton = buttonObj.remove("sub_button");
- if (subButton != null) {
- buttonObj.put("sub_button",
- ((JSONObject) subButton).getJSONArray("list"));
+ if (buttonObj.containsKey("sub_button")) {
+ JSONPath.set(buttonObj, "$.sub_button", buttonObj
+ .getJSONObject("sub_button").getJSONArray("list"));
}
- Button button = JSON.parseObject(
- JSON.toJSONString(buttonObj, ArticleNameFilter.global),
- Button.class, NewsExtraProcessor.global);
- _buttons.add(button);
+ buttonList.add(JSON.parseObject(buttonObj.toJSONString(),
+ Button.class, ButtonExtraProcessor.global));
}
- return new MenuSetting(result.getBooleanValue("is_menu_open"), _buttons);
+ return new MenuSetting(result.getBooleanValue("is_menu_open"),
+ buttonList);
}
- private static final class NewsExtraProcessor implements ExtraProcessor {
- private static NewsExtraProcessor global = new NewsExtraProcessor();
+ private static final class ButtonExtraProcessor implements ExtraProcessor {
+ private static ButtonExtraProcessor global = new ButtonExtraProcessor();
- private NewsExtraProcessor() {
+ private ButtonExtraProcessor() {
}
@Override
public void processExtra(Object object, String key, Object value) {
if (key.equals("news_info")) {
- List news = JSON
- .parseArray(((JSONObject) value).getString("list"),
- MpArticle.class);
- JSONPath.set(object, "$.content", news);
+ JSONArray news = ((JSONObject) value).getJSONArray("list");
+ List newsList = new ArrayList(news.size());
+ for (int i = 0; i < news.size(); i++) {
+ newsList.add(JSON.parseObject(news.getString(i),
+ MpArticle.class, ArticleExtraProcessor.global));
+ }
+ JSONPath.set(object, "$.content", newsList);
+ } else {
+ JSONPath.set(object, "$.content", value);
}
}
};
- private static final class ArticleNameFilter implements NameFilter {
- private static final ArticleNameFilter global = new ArticleNameFilter();
+ private static final class ArticleExtraProcessor implements ExtraProcessor {
+ private static final ArticleExtraProcessor global = new ArticleExtraProcessor();
- private ArticleNameFilter() {
+ private ArticleExtraProcessor() {
}
@Override
- public String process(Object object, String name, Object value) {
- if (name.equals("url") || name.equals("key")) {
- return "content";
+ public void processExtra(Object object, String key, Object value) {
+ MpArticle mpArticle = (MpArticle) object;
+ if (key.equals("show_cover")) {
+ mpArticle.setShowCoverPic(value.equals("1"));
}
- if (name.equals("show_cover")) {
- return "show_cover_pic";
+ if (key.equals("source_url")) {
+ mpArticle.setSourceUrl(value.toString());
}
- if (name.equals("source_url")) {
- return "content_source_url";
- }
- return name;
}
}
@@ -194,13 +191,13 @@ public class HelperApi extends MpApi {
Response response = request.get(String.format(
autoreply_setting_get_uri, token.getAccessToken()));
- JSONObject obj = response.getAsJson();
- AutoReplySetting replySetting = JSON.toJavaObject(obj,
+ JSONObject result = response.getAsJson();
+ AutoReplySetting replySetting = JSON.toJavaObject(result,
AutoReplySetting.class);
List ruleList = null;
- if (obj.containsKey("keyword_autoreply_info")) {
- JSONArray keywordList = obj.getJSONObject("keyword_autoreply_info")
- .getJSONArray("list");
+ if (result.containsKey("keyword_autoreply_info")) {
+ JSONArray keywordList = result.getJSONObject(
+ "keyword_autoreply_info").getJSONArray("list");
ruleList = new ArrayList(keywordList.size());
JSONObject keywordObj = null;
JSONArray replyList = null;
@@ -215,10 +212,9 @@ public class HelperApi extends MpApi {
for (int j = 0; j < replyList.size(); j++) {
replyObj = replyList.getJSONObject(j);
if (replyObj.getString("type").equals("news")) {
- entryList.add(JSON.parseObject(JSON.toJSONString(
- replyObj, ArticleNameFilter.global),
+ entryList.add(JSON.parseObject(replyObj.toJSONString(),
AutoReplySetting.Entry.class,
- NewsExtraProcessor.global));
+ ButtonExtraProcessor.global));
} else {
entryList.add(JSON.toJavaObject(replyObj,
AutoReplySetting.Entry.class));
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java
index 0b2b2d0b..52014311 100644
--- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java
+++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java
@@ -1,9 +1,14 @@
package com.foxinmy.weixin4j.mp.api;
+import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONPath;
+import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
+import com.alibaba.fastjson.parser.deserializer.ParseProcess;
import com.alibaba.fastjson.serializer.NameFilter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
@@ -52,8 +57,13 @@ public class MenuApi extends MpApi {
public String process(Object object, String name,
Object value) {
if (object instanceof Button && name.equals("content")) {
- if (((Button) object).getFormatType() == ButtonType.view) {
+ ButtonType buttonType = ((Button) object)
+ .getFormatType();
+ if (buttonType == ButtonType.view) {
return "url";
+ } else if (buttonType == ButtonType.media_id
+ || buttonType == ButtonType.view_limited) {
+ return "media_id";
} else {
return "key";
}
@@ -80,19 +90,20 @@ public class MenuApi extends MpApi {
Response response = request.get(String.format(menu_get_uri,
token.getAccessToken()));
- String text = JSON.toJSONString(
- response.getAsJson().getJSONObject("menu")
- .getJSONArray("button"), new NameFilter() {
- @Override
- public String process(Object object, String name,
- Object value) {
- if (name.equals("url") || name.equals("key")) {
- return "content";
- }
- return name;
- }
- });
- return JSON.parseArray(text, Button.class);
+ JSONArray buttons = response.getAsJson().getJSONObject("menu")
+ .getJSONArray("button");
+ List buttonList = new ArrayList(buttons.size());
+ ParseProcess processor = new ExtraProcessor() {
+ @Override
+ public void processExtra(Object object, String key, Object value) {
+ JSONPath.set(object, "$.content", value);
+ }
+ };
+ for (int i = 0; i < buttons.size(); i++) {
+ buttonList.add(JSON.parseObject(buttons.getString(i), Button.class,
+ processor));
+ }
+ return buttonList;
}
/**
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/model/OauthToken.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/model/OauthToken.java
index 4550d615..ac9efd9c 100644
--- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/model/OauthToken.java
+++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/model/OauthToken.java
@@ -18,9 +18,13 @@ public class OauthToken extends Token {
private static final long serialVersionUID = 1L;
/**
- * 用户的openi
+ * 用户的openid
*/
private String openid;
+ /**
+ * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段
+ */
+ private String unionid;
/**
* 刷新token时的凭证
@@ -38,6 +42,14 @@ public class OauthToken extends Token {
this.openid = openid;
}
+ public String getUnionid() {
+ return unionid;
+ }
+
+ public void setUnionid(String unionid) {
+ this.unionid = unionid;
+ }
+
public String getRefreshToken() {
return refreshToken;
}
@@ -56,9 +68,10 @@ public class OauthToken extends Token {
@Override
public String toString() {
- return "OauthToken [openid=" + openid + ", refreshToken="
- + refreshToken + ", scope=" + scope + ", getAccessToken()="
- + getAccessToken() + ", getExpiresIn()=" + getExpiresIn()
- + ", getTime()=" + getTime() + "]";
+ return "OauthToken [openid=" + openid + ", unionid=" + unionid
+ + ", refreshToken=" + refreshToken + ", scope=" + scope
+ + ", getAccessToken()=" + getAccessToken()
+ + ", getExpiresIn()=" + getExpiresIn() + ", getTime()="
+ + getTime() + "]";
}
}
diff --git a/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java b/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java
index 3c33578d..8a1db412 100644
--- a/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java
+++ b/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/WeixinProxy.java
@@ -402,9 +402,11 @@ public class WeixinProxy {
* 通过员工授权获取到的code,每次员工授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
* @param agentid
* 跳转链接时所在的企业应用ID
+ * @see com.foxinmy.weixin4j.qy.model.User
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员对象
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#getUser(String)}
+ * @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#getUserIdByCode(String,int)}
* @see 企业获取code
* @see 企业获取code
+ * @see 根据code获取成员信息
+ * @throws WeixinException
+ */
+ public JSONObject getUserIdByCode(String code, int agentid)
+ throws WeixinException {
+ return userApi.getUserIdByCode(code, agentid);
+ }
+
/**
* 获取部门成员
*
diff --git a/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/MenuApi.java b/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/MenuApi.java
index 572c745c..05e9a5da 100644
--- a/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/MenuApi.java
+++ b/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/MenuApi.java
@@ -1,9 +1,14 @@
package com.foxinmy.weixin4j.qy.api;
+import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONPath;
+import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
+import com.alibaba.fastjson.parser.deserializer.ParseProcess;
import com.alibaba.fastjson.serializer.NameFilter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
@@ -55,8 +60,13 @@ public class MenuApi extends QyApi {
public String process(Object object, String name,
Object value) {
if (object instanceof Button && name.equals("content")) {
- if (((Button) object).getFormatType() == ButtonType.view) {
+ ButtonType buttonType = ((Button) object)
+ .getFormatType();
+ if (buttonType == ButtonType.view) {
return "url";
+ } else if (buttonType == ButtonType.media_id
+ || buttonType == ButtonType.view_limited) {
+ return "media_id";
} else {
return "key";
}
@@ -85,19 +95,20 @@ public class MenuApi extends QyApi {
Response response = request.get(String.format(menu_get_uri,
token.getAccessToken(), agentid));
- String text = JSON.toJSONString(
- response.getAsJson().getJSONObject("menu")
- .getJSONArray("button"), new NameFilter() {
- @Override
- public String process(Object object, String name,
- Object value) {
- if (name.equals("url") || name.equals("key")) {
- return "content";
- }
- return name;
- }
- });
- return JSON.parseArray(text, Button.class);
+ JSONArray buttons = response.getAsJson().getJSONObject("menu")
+ .getJSONArray("button");
+ List buttonList = new ArrayList(buttons.size());
+ ParseProcess processor = new ExtraProcessor() {
+ @Override
+ public void processExtra(Object object, String key, Object value) {
+ JSONPath.set(object, "$.content", value);
+ }
+ };
+ for (int i = 0; i < buttons.size(); i++) {
+ buttonList.add(JSON.parseObject(buttons.getString(i), Button.class,
+ processor));
+ }
+ return buttonList;
}
/**
diff --git a/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java b/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java
index 46a498a5..995c3f72 100644
--- a/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java
+++ b/weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/UserApi.java
@@ -112,9 +112,11 @@ public class UserApi extends QyApi {
* 通过员工授权获取到的code,每次员工授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
* @param agentid
* 跳转链接时所在的企业应用ID
+ * @see com.foxinmy.weixin4j.qy.model.User
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员对象
* @see {@link com.foxinmy.weixin4j.qy.api.UserApi#getUser(String)}
+ * @see {@link com.foxinmy.weixin4j.qy.api.UserApi#getUserIdByCode(String,int)}
* @see 企业获取code
* @see 企业获取code
+ * @see 根据code获取成员信息
+ * @throws WeixinException
+ */
+ public JSONObject getUserIdByCode(String code, int agentid)
+ throws WeixinException {
String user_getid_uri = getRequestUri("user_getid_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_getid_uri,
token.getAccessToken(), code, agentid));
- return getUser(response.getAsJson().getString("UserId"));
+ return response.getAsJson();
}
/**