公众号:调整二维码参数类 & 新增自定义菜单配置、自动回复配置查询接口

This commit is contained in:
jinyu 2015-04-16 11:30:42 +08:00
parent b6c2fe7d63
commit 8995196b8d
29 changed files with 817 additions and 230 deletions

View File

@ -242,3 +242,8 @@
+ **weixin4j-qy**: 新增WeixinTokenCreator与WeixinJSTicketCreator类 + **weixin4j-qy**: 新增WeixinTokenCreator与WeixinJSTicketCreator类
* 2015-04-16
+ **weixin4j-mp**: <font color="red">调整[二维码参数](./weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/QRParameter.java)类</font>
+ **weixin4j-mp**: 新增获取[自定义菜单配置、自动回复配置](./weixin4j-mp/weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java)接口

View File

@ -29,19 +29,25 @@ public class Button implements Serializable {
*/ */
private String name; private String name;
/** /**
* 菜单的响应动作类型 * 菜单类型 </br> <font color="red">
* 公众平台官网上能够设置的菜单类型有viewtextimgphotovideovoice </font>
* *
* @see com.foxinmy.weixin4j.type.ButtonType * @see com.foxinmy.weixin4j.type.ButtonType
*/ */
private ButtonType type; private String type;
/** /**
* 菜单KEY值用于消息接口推送不超过128字节 * 菜单KEY值,根据type的类型而定,用于消息接口推送,不超过128字节.
* <p>
* 官网上设置的自定义菜单</br> Text:保存文字到value Imgvoice保存mediaID到value
* Video保存视频下载链接到value</br> News保存图文消息到news_info View保存链接到url</br>
* <p>
* 使用API设置的自定义菜单</br>
* clickscancode_pushscancode_waitmsgpic_sysphotopic_photo_or_album
* </br> pic_weixinlocation_select保存值到keyview保存链接到url
* </p>
* </p>
*/ */
private String key; private Serializable content;
/**
* view类型必须 网页链接用户点击菜单可打开链接不超过256字节
*/
private String url;
/** /**
* 二级菜单数组个数应为1~5个 * 二级菜单数组个数应为1~5个
*/ */
@ -53,18 +59,18 @@ public class Button implements Serializable {
/** /**
* 创建一个菜单 * 创建一个菜单
* @param name 菜单显示的名称 *
* @param value 当buttonType为view时value设置为url,否则为key. * @param name
* @param buttonType 按钮类型 * 菜单显示的名称
* @param content
* 当buttonType为view时content设置为url,否则为key.
* @param buttonType
* 按钮类型
*/ */
public Button(String name, String value, ButtonType buttonType) { public Button(String name, String content, ButtonType buttonType) {
this.name = name; this.name = name;
this.type = buttonType; this.content = content;
if (buttonType == ButtonType.view) { this.type = buttonType.name();
this.url = value;
} else {
this.key = value;
}
} }
public String getName() { public String getName() {
@ -75,28 +81,25 @@ public class Button implements Serializable {
this.name = name; this.name = name;
} }
public ButtonType getType() { public String getType() {
return type; return type;
} }
public void setType(ButtonType type) { @JSONField(serialize = false, deserialize = false)
public ButtonType getFormatType() {
return ButtonType.valueOf(type);
}
public void setType(String type) {
this.type = type; this.type = type;
} }
public String getKey() { public Serializable getContent() {
return key; return content;
} }
public void setKey(String key) { public void setContent(Serializable content) {
this.key = key; this.content = content;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
} }
public List<Button> getSubs() { public List<Button> getSubs() {
@ -117,19 +120,7 @@ public class Button implements Serializable {
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); return "Button [name=" + name + ", type=" + type + ", content="
sb.append("[Button name=").append(name); + content + ", subs=" + subs + "]";
sb.append(" ,type=").append(type);
sb.append(" ,key=").append(key);
sb.append(" ,url=").append(url);
if (subs != null && !subs.isEmpty()) {
sb.append("{");
for (Button sub : subs) {
sb.append(sub.toString());
}
sb.append("}");
}
sb.append("]");
return sb.toString();
} }
} }

View File

@ -54,11 +54,11 @@ public final class Consts {
*/ */
public static final String MICROPAYURL = "https://api.mch.weixin.qq.com/pay/micropay"; public static final String MICROPAYURL = "https://api.mch.weixin.qq.com/pay/micropay";
/** /**
* 商户平台下native支付的url * V2支付下natvie支付的url
*/ */
public static final String NATIVEURLV2 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s&timestamp=%s&noncestr=%s"; public static final String NATIVEURLV2 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s&timestamp=%s&noncestr=%s";
/** /**
* V2支付下natvie支付的url * 商户平台(V3)下native支付的url
*/ */
public static final String NATIVEURLV3 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s"; public static final String NATIVEURLV3 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s";
} }

View File

@ -32,7 +32,7 @@ public class MpArticle implements Serializable {
* 在图文消息页面点击阅读原文后的页面 可为空 * 在图文消息页面点击阅读原文后的页面 可为空
*/ */
@JSONField(name = "content_source_url") @JSONField(name = "content_source_url")
private String url; private String sourceUrl;
/** /**
* 图文消息页面的内容支持HTML标签 非空 * 图文消息页面的内容支持HTML标签 非空
*/ */
@ -47,6 +47,18 @@ public class MpArticle implements Serializable {
@JSONField(name = "show_cover_pic") @JSONField(name = "show_cover_pic")
private String showCoverPic; private String showCoverPic;
/**
* 正文的URL 可为空
*/
@JSONField(name = "content_url")
private String contentUrl;
/**
* 封面图片的URL 可为空
*/
@JSONField(name = "cover_url")
private String coverUrl;
public MpArticle(String thumbMediaId, String title, String content) { public MpArticle(String thumbMediaId, String title, String content) {
this.thumbMediaId = thumbMediaId; this.thumbMediaId = thumbMediaId;
this.title = title; this.title = title;
@ -81,12 +93,12 @@ public class MpArticle implements Serializable {
this.title = title; this.title = title;
} }
public String getUrl() { public String getSourceUrl() {
return url; return sourceUrl;
} }
public void setUrl(String url) { public void setSourceUrl(String sourceUrl) {
this.url = url; this.sourceUrl = sourceUrl;
} }
public String getContent() { public String getContent() {
@ -113,16 +125,28 @@ public class MpArticle implements Serializable {
this.showCoverPic = showCoverPic ? "1" : "0"; this.showCoverPic = showCoverPic ? "1" : "0";
} }
public String getContentUrl() {
return contentUrl;
}
public void setContentUrl(String contentUrl) {
this.contentUrl = contentUrl;
}
public String getCoverUrl() {
return coverUrl;
}
public void setCoverUrl(String coverUrl) {
this.coverUrl = coverUrl;
}
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); return "MpArticle [thumbMediaId=" + thumbMediaId + ", author=" + author
sb.append("[MpArticle thumbMediaId=").append(thumbMediaId); + ", title=" + title + ", sourceUrl=" + sourceUrl
sb.append(", author=").append(author); + ", content=" + content + ", digest=" + digest
sb.append(", title=").append(title); + ", showCoverPic=" + showCoverPic + ", contentUrl="
sb.append(", url=").append(url); + contentUrl + ", coverUrl=" + coverUrl + "]";
sb.append(", content=").append(content);
sb.append(", digest=").append(digest);
sb.append(", showCoverPic=").append(showCoverPic).append("]");
return sb.toString();
} }
} }

View File

@ -68,8 +68,8 @@ weixin4j-mp
"token":"开放者的token",\ "token":"开放者的token",\
"encodingAesKey":"公众号设置了加密方式且为「安全模式」时需要填入",\ "encodingAesKey":"公众号设置了加密方式且为「安全模式」时需要填入",\
"mchId":"V3.x版本下的微信商户号",\ "mchId":"V3.x版本下的微信商户号",\
"partnerId":"财付通的商户号",\ "partnerId":"V2版本下的财付通的商户号",\
"partnerKey":"财付通商户权限密钥Key",\ "partnerKey":"V2版本下的财付通商户权限密钥Key",\
"version":"针对微信支付的版本号(2,3),如果不填则按照mchId非空与否来判断",\ "version":"针对微信支付的版本号(2,3),如果不填则按照mchId非空与否来判断",\
"paySignKey":"微信支付中调用API的密钥"} "paySignKey":"微信支付中调用API的密钥"}
@ -217,3 +217,9 @@ weixin4j-mp
+ **weixin4j-mp-api**: 新增WeixinTokenCreator与WeixinJSTicketCreator类 + **weixin4j-mp-api**: 新增WeixinTokenCreator与WeixinJSTicketCreator类
+ **weixin4j-mp-api**: 新增用户分组批量移动、删除组别接口 + **weixin4j-mp-api**: 新增用户分组批量移动、删除组别接口
* 2015-04-16
+ **weixin4j-mp**: <font color="red">调整[二维码参数](./weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/model/QRParameter.java)类</font>
+ **weixin4j-mp**: 新增获取[自定义菜单配置、自动回复配置](./weixin4j-mp-api/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java)接口

View File

@ -61,8 +61,8 @@ weixin.properties说明
"token":"开放者的token",\ "token":"开放者的token",\
"encodingAesKey":"公众号设置了加密方式且为「安全模式」时需要填入",\ "encodingAesKey":"公众号设置了加密方式且为「安全模式」时需要填入",\
"mchId":"V3.x版本下的微信商户号",\ "mchId":"V3.x版本下的微信商户号",\
"partnerId":"财付通的商户号",\ "partnerId":"V2版本下的财付通的商户号",\
"partnerKey":"财付通商户权限密钥Key",\ "partnerKey":"V2版本下的财付通商户权限密钥Key",\
"version":"针对微信支付的版本号(2,3),如果不填则按照mchId非空与否来判断",\ "version":"针对微信支付的版本号(2,3),如果不填则按照mchId非空与否来判断",\
"paySignKey":"微信支付中调用API的密钥"} "paySignKey":"微信支付中调用API的密钥"}
@ -192,3 +192,9 @@ weixin.properties说明
+ 新增WeixinTokenCreator与WeixinJSTicketCreator类 + 新增WeixinTokenCreator与WeixinJSTicketCreator类
+ 新增用户分组批量移动、删除组别接口 + 新增用户分组批量移动、删除组别接口
* 2015-04-16
+ **weixin4j-mp-api**: <font color="red">调整[二维码参数](./src/main/java/com/foxinmy/weixin4j/mp/model/QRParameter.java)类</font>
+ **weixin4j-mp-api**: 新增获取[自定义菜单配置、自动回复配置](./src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java)接口

View File

@ -21,6 +21,7 @@ import com.foxinmy.weixin4j.mp.api.TmplApi;
import com.foxinmy.weixin4j.mp.api.UserApi; import com.foxinmy.weixin4j.mp.api.UserApi;
import com.foxinmy.weixin4j.mp.message.NotifyMessage; import com.foxinmy.weixin4j.mp.message.NotifyMessage;
import com.foxinmy.weixin4j.mp.message.TemplateMessage; import com.foxinmy.weixin4j.mp.message.TemplateMessage;
import com.foxinmy.weixin4j.mp.model.AutoReplySetting;
import com.foxinmy.weixin4j.mp.model.CustomRecord; import com.foxinmy.weixin4j.mp.model.CustomRecord;
import com.foxinmy.weixin4j.mp.model.Following; import com.foxinmy.weixin4j.mp.model.Following;
import com.foxinmy.weixin4j.mp.model.Group; import com.foxinmy.weixin4j.mp.model.Group;
@ -28,6 +29,7 @@ import com.foxinmy.weixin4j.mp.model.KfAccount;
import com.foxinmy.weixin4j.mp.model.KfSession; import com.foxinmy.weixin4j.mp.model.KfSession;
import com.foxinmy.weixin4j.mp.model.MediaCounter; import com.foxinmy.weixin4j.mp.model.MediaCounter;
import com.foxinmy.weixin4j.mp.model.MediaRecord; import com.foxinmy.weixin4j.mp.model.MediaRecord;
import com.foxinmy.weixin4j.mp.model.MenuSetting;
import com.foxinmy.weixin4j.mp.model.QRParameter; import com.foxinmy.weixin4j.mp.model.QRParameter;
import com.foxinmy.weixin4j.mp.model.SemQuery; import com.foxinmy.weixin4j.mp.model.SemQuery;
import com.foxinmy.weixin4j.mp.model.SemResult; import com.foxinmy.weixin4j.mp.model.SemResult;
@ -1073,25 +1075,6 @@ public class WeixinProxy {
return qrApi.getQRData(parameter); return qrApi.getQRData(parameter);
} }
/**
* 生成带参数的二维码
*
* @param sceneId
* 场景值
* @param expireSeconds
* 过期秒数 如果小于等于0则 视为永久二维码
* @return byte数据包
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.QrApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#getQR(QRParameter)}
* @see <a
* href="http://mp.weixin.qq.com/wiki/18/28fc21e7ed87bec960651f0ce873ef8a.html">生成二维码</a>
*/
public byte[] getQRData(int sceneId, int expireSeconds)
throws WeixinException {
return qrApi.getQRData(sceneId, expireSeconds);
}
/** /**
* 生成带参数的二维码 * 生成带参数的二维码
* <p> * <p>
@ -1206,6 +1189,37 @@ public class WeixinProxy {
return helperApi.getcallbackip(); return helperApi.getcallbackip();
} }
/**
* 获取公众号当前使用的自定义菜单的配置如果公众号是通过API调用设置的菜单则返回菜单的开发配置
* 而如果公众号是在公众平台官网通过网站功能发布菜单则本接口返回运营者设置的菜单配置
*
* @return 菜单集合
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#getMenu()}
* @see <a
* href="http://mp.weixin.qq.com/wiki/17/4dc4b0514fdad7a5fbbd477aa9aab5ed.html">获取自定义菜单配置</a>
* @see com.foxinmy.weixin4j.model.Button
* @se com.foxinmy.weixin4j.mp.model.MenuSetting
* @see com.foxinmy.weixin4j.msg.model.MpArticle
* @see com.foxinmy.weixin4j.mp.api.HelperApi
* @throws WeixinException
*/
public MenuSetting getMenuSetting() throws WeixinException {
return helperApi.getMenuSetting();
}
/**
* 获取公众号当前使用的自动回复规则包括关注后自动回复消息自动回复60分钟内触发一次关键词自动回复
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting
* @see com.foxinmy.weixin4j.mp.api.HelperApi
* @see <a
* href="http://mp.weixin.qq.com/wiki/7/7b5789bb1262fb866d01b4b40b0efecb.html">获取自动回复规则</a>
* @throws WeixinException
*/
public AutoReplySetting getAutoReplySetting() throws WeixinException {
return helperApi.getAutoReplySetting();
}
/** /**
* 数据统计 * 数据统计
* *

View File

@ -53,7 +53,7 @@ public class CustomApi extends MpApi {
* @param endtime * @param endtime
* 查询结束时间 每次查询不能跨日查询 * 查询结束时间 每次查询不能跨日查询
* @param pagesize * @param pagesize
* 每页大小 每页最多拉取1000条 * 每页大小 每页最多拉取50条
* @param pageindex * @param pageindex
* 查询第几页 从1开始 * 查询第几页 从1开始
* @throws WeixinException * @throws WeixinException

View File

@ -1,15 +1,24 @@
package com.foxinmy.weixin4j.mp.api; package com.foxinmy.weixin4j.mp.api;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.alibaba.fastjson.TypeReference; 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.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response; import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.AutoReplySetting;
import com.foxinmy.weixin4j.mp.model.MenuSetting;
import com.foxinmy.weixin4j.mp.model.SemQuery; import com.foxinmy.weixin4j.mp.model.SemQuery;
import com.foxinmy.weixin4j.mp.model.SemResult; import com.foxinmy.weixin4j.mp.model.SemResult;
import com.foxinmy.weixin4j.msg.model.MpArticle;
import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenHolder;
/** /**
@ -90,4 +99,136 @@ public class HelperApi extends MpApi {
return JSON.parseArray(response.getAsJson().getString("ip_list"), return JSON.parseArray(response.getAsJson().getString("ip_list"),
String.class); String.class);
} }
/**
* 获取公众号当前使用的自定义菜单的配置如果公众号是通过API调用设置的菜单则返回菜单的开发配置
* 而如果公众号是在公众平台官网通过网站功能发布菜单则本接口返回运营者设置的菜单配置
*
* @return 菜单集合
* @see {@link com.foxinmy.weixin4j.mp.api.MenuApi#getMenu()}
* @see <a
* href="http://mp.weixin.qq.com/wiki/17/4dc4b0514fdad7a5fbbd477aa9aab5ed.html">获取自定义菜单配置</a>
* @see com.foxinmy.weixin4j.model.Button
* @see com.foxinmy.weixin4j.mp.model.MenuSetting
* @see com.foxinmy.weixin4j.msg.model.MpArticle
* @throws WeixinException
*/
public MenuSetting getMenuSetting() throws WeixinException {
String menu_get_selfmenu_uri = getRequestUri("menu_get_selfmenu_uri");
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<Button> _buttons = new ArrayList<Button>();
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"));
}
Button button = JSON.parseObject(
JSON.toJSONString(buttonObj, ArticleNameFilter.global),
Button.class, NewsExtraProcessor.global);
_buttons.add(button);
}
return new MenuSetting(result.getBooleanValue("is_menu_open"), _buttons);
}
private static final class NewsExtraProcessor implements ExtraProcessor {
private static NewsExtraProcessor global = new NewsExtraProcessor();
private NewsExtraProcessor() {
}
@Override
public void processExtra(Object object, String key, Object value) {
if (key.equals("news_info")) {
List<MpArticle> news = JSON
.parseArray(((JSONObject) value).getString("list"),
MpArticle.class);
JSONPath.set(object, "$.content", news);
}
}
};
private static final class ArticleNameFilter implements NameFilter {
private static final ArticleNameFilter global = new ArticleNameFilter();
private ArticleNameFilter() {
}
@Override
public String process(Object object, String name, Object value) {
if (name.equals("url") || name.equals("key")) {
return "content";
}
if (name.equals("show_cover")) {
return "show_cover_pic";
}
if (name.equals("source_url")) {
return "content_source_url";
}
return name;
}
}
/**
* 获取公众号当前使用的自动回复规则包括关注后自动回复消息自动回复60分钟内触发一次关键词自动回复
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting
* @see <a
* href="http://mp.weixin.qq.com/wiki/7/7b5789bb1262fb866d01b4b40b0efecb.html">获取自动回复规则</a>
* @throws WeixinException
*/
public AutoReplySetting getAutoReplySetting() throws WeixinException {
String autoreply_setting_get_uri = getRequestUri("autoreply_setting_get_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(
autoreply_setting_get_uri, token.getAccessToken()));
JSONObject obj = response.getAsJson();
AutoReplySetting replySetting = JSON.toJavaObject(obj,
AutoReplySetting.class);
List<AutoReplySetting.Rule> ruleList = null;
if (obj.containsKey("keyword_autoreply_info")) {
JSONArray keywordList = obj.getJSONObject("keyword_autoreply_info")
.getJSONArray("list");
ruleList = new ArrayList<AutoReplySetting.Rule>(keywordList.size());
JSONObject keywordObj = null;
JSONArray replyList = null;
JSONObject replyObj = null;
for (int i = 0; i < keywordList.size(); i++) {
keywordObj = keywordList.getJSONObject(i);
AutoReplySetting.Rule rule = JSON.toJavaObject(keywordObj,
AutoReplySetting.Rule.class);
replyList = keywordObj.getJSONArray("reply_list_info");
List<AutoReplySetting.Entry> entryList = new ArrayList<AutoReplySetting.Entry>(
replyList.size());
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),
AutoReplySetting.Entry.class,
NewsExtraProcessor.global));
} else {
entryList.add(JSON.toJavaObject(replyObj,
AutoReplySetting.Entry.class));
}
}
rule.setReplyList(entryList);
ruleList.add(rule);
}
}
replySetting.setKeywordReplyList(ruleList);
return replySetting;
}
} }

View File

@ -4,12 +4,14 @@ import java.util.List;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.NameFilter;
import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult; import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response; import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.ButtonType;
/** /**
* 菜单相关API * 菜单相关API
@ -31,7 +33,8 @@ public class MenuApi extends MpApi {
/** /**
* 自定义菜单 * 自定义菜单
* *
* @param btnList 菜单列表 * @param btnList
* 菜单列表
* @throws WeixinException * @throws WeixinException
* @see <a * @see <a
* href="http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html">创建自定义菜单</a> * href="http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html">创建自定义菜单</a>
@ -44,7 +47,20 @@ public class MenuApi extends MpApi {
obj.put("button", btnList); obj.put("button", btnList);
Response response = request.post( Response response = request.post(
String.format(menu_create_uri, token.getAccessToken()), String.format(menu_create_uri, token.getAccessToken()),
obj.toJSONString()); JSON.toJSONString(obj, new NameFilter() {
@Override
public String process(Object object, String name,
Object value) {
if (object instanceof Button && name.equals("content")) {
if (((Button) object).getFormatType() == ButtonType.view) {
return "url";
} else {
return "key";
}
}
return name;
}
}));
return response.getAsJsonResult(); return response.getAsJsonResult();
} }
@ -64,8 +80,18 @@ public class MenuApi extends MpApi {
Response response = request.get(String.format(menu_get_uri, Response response = request.get(String.format(menu_get_uri,
token.getAccessToken())); token.getAccessToken()));
String text = response.getAsJson().getJSONObject("menu") String text = JSON.toJSONString(
.getString("button"); 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); return JSON.parseArray(text, Button.class);
} }

View File

@ -150,7 +150,7 @@ public class OauthApi extends MpApi {
} }
/** /**
* oauth获取用户信息 * oauth获取用户信息(需scope为 snsapi_userinfo)
* *
* @param token * @param token
* 授权票据 * 授权票据

View File

@ -9,7 +9,6 @@ import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response; import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.QRParameter; import com.foxinmy.weixin4j.mp.model.QRParameter;
import com.foxinmy.weixin4j.mp.type.QRType;
import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil; import com.foxinmy.weixin4j.util.ConfigUtil;
@ -46,7 +45,7 @@ public class QrApi extends MpApi {
String qr_uri = getRequestUri("qr_ticket_uri"); String qr_uri = getRequestUri("qr_ticket_uri");
Response response = request.post( Response response = request.post(
String.format(qr_uri, token.getAccessToken()), String.format(qr_uri, token.getAccessToken()),
parameter.toJson()); parameter.getContent());
String ticket = response.getAsJson().getString("ticket"); String ticket = response.getAsJson().getString("ticket");
qr_uri = getRequestUri("qr_image_uri"); qr_uri = getRequestUri("qr_image_uri");
response = request.get(String.format(qr_uri, ticket)); response = request.get(String.format(qr_uri, ticket));
@ -54,25 +53,6 @@ public class QrApi extends MpApi {
return response.getBody(); return response.getBody();
} }
/**
* 生成带参数的二维码
*
* @param sceneId
* 场景值
* @param expireSeconds
* 过期秒数 如果小于等于0则 视为永久二维码
* @return byte数据包
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.mp.api.QrApi#getQR(QRParameter)}
* @see <a
* href="http://mp.weixin.qq.com/wiki/18/28fc21e7ed87bec960651f0ce873ef8a.html">生成二维码</a>
*/
public byte[] getQRData(int sceneId, int expireSeconds)
throws WeixinException {
QRParameter parameter = new QRParameter(sceneId, expireSeconds);
return getQRData(parameter);
}
/** /**
* 生成带参数的二维码 * 生成带参数的二维码
* <p> * <p>
@ -89,22 +69,22 @@ public class QrApi extends MpApi {
*/ */
public File getQR(QRParameter parameter) throws WeixinException { public File getQR(QRParameter parameter) throws WeixinException {
String qr_path = ConfigUtil.getValue("qr_path"); String qr_path = ConfigUtil.getValue("qr_path");
String filename = String.format("%s_%d_%d.jpg", parameter.getQrType() String filename = String.format("%s_%s_%d.jpg", parameter.getQrType()
.name(), parameter.getSceneId(), parameter.getExpireSeconds()); .name(), parameter.getSceneValue(), parameter
.getExpireSeconds());
File file = new File(qr_path + File.separator + filename); File file = new File(qr_path + File.separator + filename);
if (parameter.getQrType() == QRType.PERMANENCE && file.exists()) { if (parameter.getQrType().ordinal() > 0 && file.exists()) {
return file; return file;
} }
byte[] datas = getQRData(parameter); byte[] datas = getQRData(parameter);
OutputStream os = null; OutputStream os = null;
try { try {
boolean flag = file.exists() || file.createNewFile(); if (file.createNewFile()) {
if (flag) {
os = new FileOutputStream(file); os = new FileOutputStream(file);
os.write(datas); os.write(datas);
} else { } else {
throw new WeixinException(String.format( throw new WeixinException(String.format("create file fail:%s",
"create file fail:%s", file.getAbsolutePath())); file.getAbsolutePath()));
} }
} catch (IOException e) { } catch (IOException e) {
throw new WeixinException(e.getMessage()); throw new WeixinException(e.getMessage());

View File

@ -55,6 +55,8 @@ following_uri={api_cgi_url}/user/get?access_token=%s&next_openid=%s
menu_create_uri={api_cgi_url}/menu/create?access_token=%s menu_create_uri={api_cgi_url}/menu/create?access_token=%s
# \u67e5\u8be2\u83dc\u5355 # \u67e5\u8be2\u83dc\u5355
menu_get_uri={api_cgi_url}/menu/get?access_token=%s menu_get_uri={api_cgi_url}/menu/get?access_token=%s
# \u67e5\u8be2\u901a\u8fc7\u63a5\u53e3\u6216\u8005\u5728\u516c\u4f17\u5e73\u53f0\u4e0a\u8bbe\u7f6e\u7684\u83dc\u5355\u914d\u7f6e\u4fe1\u606f
menu_get_selfmenu_uri={api_cgi_url}/get_current_selfmenu_info?access_token=%s
# \u5220\u9664\u83dc\u5355 # \u5220\u9664\u83dc\u5355
menu_delete_uri={api_cgi_url}/menu/delete?access_token=%s menu_delete_uri={api_cgi_url}/menu/delete?access_token=%s
# \u4e0a\u4f20\u56fe\u6587 # \u4e0a\u4f20\u56fe\u6587
@ -164,6 +166,8 @@ material_media_del_uri={api_cgi_url}/material/del_material?access_token=%s
material_media_count_uri={api_cgi_url}/material/get_materialcount?access_token=%s material_media_count_uri={api_cgi_url}/material/get_materialcount?access_token=%s
# \u83b7\u53d6\u5a92\u4f53\u7d20\u6750\u5217\u8868 # \u83b7\u53d6\u5a92\u4f53\u7d20\u6750\u5217\u8868
material_media_list_uri={api_cgi_url}/material/batchget_material?access_token=%s material_media_list_uri={api_cgi_url}/material/batchget_material?access_token=%s
# \u81ea\u52a8\u56de\u590d\u89c4\u5219
autoreply_setting_get_uri={api_cgi_url}/get_current_autoreply_info?access_token=%s
# \u53d1\u653e\u4ee3\u91d1\u5238 # \u53d1\u653e\u4ee3\u91d1\u5238
coupon_send_uri={mch_base_url}/mmpaymkttransfers/send_coupon coupon_send_uri={mch_base_url}/mmpaymkttransfers/send_coupon

View File

@ -0,0 +1,260 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.mp.type.AutomatchMode;
import com.foxinmy.weixin4j.mp.type.AutoreplyMode;
/**
* 自动回复设置
*
* @className AutoReplySetting
* @author jy
* @date 2015年4月15日
* @since JDK 1.7
* @see
*/
public class AutoReplySetting implements Serializable {
private static final long serialVersionUID = 8164017927864497009L;
/**
* 关注后自动回复是否开启
*/
@JSONField(name = "is_add_friend_reply_open")
private boolean isAddFriendReplyOpen;
/**
* 消息自动回复是否开启
*/
@JSONField(name = "is_autoreply_open")
private boolean isAutoreplyOpen;
/**
* 关注后自动回复的信息
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting.Entry
*/
@JSONField(name = "add_friend_autoreply_info")
private Entry addFriendReply;
/**
* 默认自动回复的信息
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting.Entry
*/
@JSONField(name = "message_default_autoreply_info")
private Entry defaultReply;
/**
* 关键词自动回复的信息
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting.Rule
*/
private List<Rule> keywordReplyList;
public boolean isAddFriendReplyOpen() {
return isAddFriendReplyOpen;
}
public void setAddFriendReplyOpen(boolean isAddFriendReplyOpen) {
this.isAddFriendReplyOpen = isAddFriendReplyOpen;
}
public boolean isAutoreplyOpen() {
return isAutoreplyOpen;
}
public void setAutoreplyOpen(boolean isAutoreplyOpen) {
this.isAutoreplyOpen = isAutoreplyOpen;
}
public Entry getAddFriendReply() {
return addFriendReply;
}
public void setAddFriendReply(Entry addFriendReply) {
this.addFriendReply = addFriendReply;
}
public Entry getDefaultReply() {
return defaultReply;
}
public void setDefaultReply(Entry defaultReply) {
this.defaultReply = defaultReply;
}
public List<Rule> getKeywordReplyList() {
return keywordReplyList;
}
public void setKeywordReplyList(List<Rule> keywordReplyList) {
this.keywordReplyList = keywordReplyList;
}
@Override
public String toString() {
return "AutoReplySetting [isAddFriendReplyOpen=" + isAddFriendReplyOpen
+ ", isAutoreplyOpen=" + isAutoreplyOpen + ", addFriendReply="
+ addFriendReply + ", defaultReply=" + defaultReply
+ ", keywordReplyList=" + keywordReplyList + "]";
}
/**
* 关键字规则
*
* @className Rule
* @author jy
* @date 2015年4月15日
* @since JDK 1.7
* @see
*/
public static class Rule implements Serializable {
private static final long serialVersionUID = -7299903545861946025L;
/**
* 规则名称
*/
@JSONField(name = "rule_name")
private String ruleName;
/**
* 创建时间
*/
@JSONField(name = "create_time")
private Date createTime;
/**
* 回复模式
*
* @see com.foxinmy.weixin4j.mp.type.AutoreplyMode
*/
@JSONField(name = "reply_mode")
private AutoreplyMode replyMode;
/**
* 匹配的关键词列表
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting.Entry
*/
@JSONField(name = "keyword_list_info")
private List<Entry> keywordList;
/**
* 回复的信息列表
*
* @see com.foxinmy.weixin4j.mp.model.AutoReplySetting.Entry
*/
@JSONField(name = "reply_list_info")
private List<Entry> replyList;
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public AutoreplyMode getReplyMode() {
return replyMode;
}
public void setReplyMode(AutoreplyMode replyMode) {
this.replyMode = replyMode;
}
public List<Entry> getKeywordList() {
return keywordList;
}
public void setKeywordList(List<Entry> keywordList) {
this.keywordList = keywordList;
}
public List<Entry> getReplyList() {
return replyList;
}
public void setReplyList(List<Entry> replyList) {
this.replyList = replyList;
}
@Override
public String toString() {
return "Rule [ruleName=" + ruleName + ", createTime="
+ createTime + ", replyMode=" + replyMode
+ ", keywordList=" + keywordList + ", replyList="
+ replyList + "]";
}
}
/**
* 数据项
*
* @className Entry
* @author jy
* @date 2015年4月15日
* @since JDK 1.7
* @see
*/
public static class Entry implements Serializable {
private static final long serialVersionUID = -187922224547974025L;
/**
* 自动回复的类型关注后自动回复和消息自动回复的类型仅支持文本text图片img语音voice视频video
* 关键词自动回复则还多了图文消息(news)
*/
private String type;
/**
* 对于文本类型content是文本内容对于图片语音视频类型content是mediaID,news是article
*
* @see com.foxinmy.weixin4j.msg.model.MpArticle
*/
private Serializable content;
/**
* 匹配模式(仅但关键字列表)
*
* @see com.foxinmy.weixin4j.mp.type.AutomatchMode
*/
@JSONField(name = "match_mode")
private AutomatchMode matchMode;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Serializable getContent() {
return content;
}
public void setContent(Serializable content) {
this.content = content;
}
public AutomatchMode getMatchMode() {
return matchMode;
}
public void setMatchMode(AutomatchMode matchMode) {
this.matchMode = matchMode;
}
@Override
public String toString() {
return "Entry [type=" + type + ", content=" + content
+ ", matchMode=" + matchMode + "]";
}
}
}

View File

@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import java.util.List;
import com.foxinmy.weixin4j.model.Button;
/**
* 自定义菜单配置
*
* @className MenuSetting
* @author jy
* @date 2015年4月14日
* @since JDK 1.7
* @see
*/
public class MenuSetting implements Serializable {
private static final long serialVersionUID = 2461505572495855830L;
/**
* 菜单是否开启
*/
private boolean isMenuOpen;
/**
* 菜单列表
*/
private List<Button> buttons;
public MenuSetting(boolean isMenuOpen, List<Button> buttons) {
this.isMenuOpen = isMenuOpen;
this.buttons = buttons;
}
public boolean isMenuOpen() {
return isMenuOpen;
}
public List<Button> getButtons() {
return buttons;
}
@Override
public String toString() {
return "MenuSetting [isMenuOpen=" + isMenuOpen + ", buttons=" + buttons
+ "]";
}
}

View File

@ -16,87 +16,104 @@ import com.foxinmy.weixin4j.mp.type.QRType;
* @date 2014年4月8日 * @date 2014年4月8日
* @since JDK 1.7 * @since JDK 1.7
* *
* @see com.foxinmy.weixin4j.mp.type.QRType
*/ */
public class QRParameter implements Serializable { public class QRParameter implements Serializable {
private static final long serialVersionUID = 6611187606558274253L; private static final long serialVersionUID = 6611187606558274253L;
/** /**
* 该二维码有效时间以秒为单位 最大不超过1800 * 临时二维码的有效时间以秒为单位 最大不超过1800
*/ */
private int expireSeconds; private int expireSeconds;
/** /**
* 二维码类型QR_SCENE为临时,QR_LIMIT_SCENE为永久 * 二维码类型
*
* @see com.foxinmy.weixin4j.mp.type.QRType
*/ */
private QRType qrType; private QRType qrType;
/** /**
* 场景值ID临时二维码时为32位非0整型永久二维码时最大值为100000目前参数只支持1--100000 * 场景值I 根据qrType参数而定
*/ */
private int sceneId; private String sceneValue;
private QRParameter() {
}
public int getExpireSeconds() { public int getExpireSeconds() {
return expireSeconds; return expireSeconds;
} }
public void setExpireSeconds(int expireSeconds) {
this.expireSeconds = expireSeconds;
}
public QRType getQrType() { public QRType getQrType() {
return qrType; return qrType;
} }
public void setQrType(QRType qrType) { public String getSceneValue() {
this.qrType = qrType; return sceneValue;
} }
public int getSceneId() { private String content;
return sceneId;
public String getContent() {
return content;
} }
public void setSceneId(int sceneId) { /**
this.sceneId = sceneId; * 创建临时二维码
*
* @param expireSeconds
* 有效时间
* @param sceneValue
* 二维码的场景值
* @return 二维码参数
*/
public static QRParameter createTemporary(int expireSeconds, int sceneValue) {
QRParameter qr = new QRParameter();
qr.qrType = QRType.QR_SCENE;
qr.expireSeconds = expireSeconds;
qr.sceneValue = Integer.toString(sceneValue);
qr.content = String
.format("{\"expire_seconds\": %s, \"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_id\": %s}}}",
expireSeconds, QRType.QR_SCENE.name(), sceneValue);
return qr;
} }
public QRParameter(int sceneId, int expireSeconds) { /**
this(sceneId, expireSeconds, QRType.TEMPORARY); * 创建永久二维码(场景值为int)
if (expireSeconds <= 0) { *
this.qrType = QRType.PERMANENCE; * @param sceneValue
} * 场景值
* @return 二维码参数
*/
public static QRParameter createPermanenceInt(int sceneValue) {
QRParameter qr = new QRParameter();
qr.qrType = QRType.QR_LIMIT_SCENE;
qr.sceneValue = Integer.toString(sceneValue);
qr.content = String
.format("{\"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_id\": %s}}}",
QRType.QR_LIMIT_SCENE.name(), sceneValue);
return qr;
} }
public QRParameter(int sceneId, int expireSeconds, QRType qrType) { /**
this.expireSeconds = expireSeconds; * 创建永久二维码(场景值为string)
this.qrType = qrType; *
this.sceneId = sceneId; * @param sceneValue
} * 场景值
* @return 二维码参数
public String toJson() { */
/* public static QRParameter createPermanenceStr(String sceneValue) {
* XStream xstream = new XStream(new JsonHierarchicalStreamDriver() { QRParameter qr = new QRParameter();
* public HierarchicalStreamWriter createWriter(Writer writer) { return qr.qrType = QRType.QR_LIMIT_STR_SCENE;
* new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE); } }); qr.sceneValue = sceneValue;
* xstream.setMode(XStream.NO_REFERENCES); qr.content = String
* xstream.autodetectAnnotations(true); if (this.qrType == .format("{\"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_str\": \"%s\"}}}",
* QRType.QR_LIMIT_SCENE) { xstream.omitField(QRParameter.class, QRType.QR_LIMIT_STR_SCENE, sceneValue);
* "expire_seconds"); } return xstream.toXML(this); return qr;
*/
StringBuilder jsonBuilder = new StringBuilder("{");
jsonBuilder.append("\"action_name\":\"").append(qrType.getName())
.append("\"");
if (this.qrType == QRType.TEMPORARY) {
jsonBuilder.append(",\"expire_seconds\":").append(expireSeconds);
}
jsonBuilder.append(",\"action_info\":").append(
String.format("{\"scene\": {\"scene_id\": %d}}", sceneId));
jsonBuilder.append("}");
return jsonBuilder.toString();
} }
@Override @Override
public String toString() { public String toString() {
return "QRParameter [expireSeconds=" + expireSeconds + ", qrType=" return "QRParameter [expireSeconds=" + expireSeconds + ", qrType="
+ qrType + ", sceneId=" + sceneId + "]"; + qrType + ", sceneValue=" + sceneValue + "]";
} }
} }

View File

@ -292,7 +292,7 @@ public class PayUtil {
* 商户信息 * 商户信息
* @param productId * @param productId
* 与订单ID等价 * 与订单ID等价
* @return * @return 支付链接
*/ */
public static String createNativePayRequestURLV2( public static String createNativePayRequestURLV2(
WeixinMpAccount weixinAccount, String productId) { WeixinMpAccount weixinAccount, String productId) {
@ -316,7 +316,7 @@ public class PayUtil {
* 支付配置信息 * 支付配置信息
* @param productId * @param productId
* 与订单ID等价 * 与订单ID等价
* @return * @return 支付链接
* @see <a href="http://pay.weixin.qq.com/wiki/doc/api/native.php">扫码支付</a> * @see <a href="http://pay.weixin.qq.com/wiki/doc/api/native.php">扫码支付</a>
*/ */
public static String createNativePayRequestURLV3( public static String createNativePayRequestURLV3(

View File

@ -27,9 +27,12 @@ public class WeixinJSTicketCreator implements TokenCreator {
private final HttpRequest request; private final HttpRequest request;
/** /**
* <font color="red">公众平台的access_token</font> * jssdk
* *
* @param appid
* appid
* @param weixinTokenHolder * @param weixinTokenHolder
* <font color="red">公众平台的access_token</font>
*/ */
public WeixinJSTicketCreator(String appid, TokenHolder weixinTokenHolder) { public WeixinJSTicketCreator(String appid, TokenHolder weixinTokenHolder) {
this.appid = appid; this.appid = appid;

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.mp.type;
/**
* 自动匹配模式
*
* @className AutomatchMode
* @author jy
* @date 2015年4月15日
* @since JDK 1.7
* @see
*/
public enum AutomatchMode {
/**
* 代表消息中含有该关键词即可
*/
contain,
/**
* 表示消息内容必须和关键词严格相同
*/
equal
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.mp.type;
/**
* 自动回复模式
*
* @className AutoreplyMode
* @author jy
* @date 2015年4月15日
* @since JDK 1.7
* @see
*/
public enum AutoreplyMode {
/**
* 代表全部回复
*/
reply_all,
/**
* 代表随机回复其中一条
*/
random_one
}

View File

@ -13,18 +13,13 @@ public enum QRType {
/** /**
* 临时二维码 * 临时二维码
*/ */
TEMPORARY("QR_SCENE"), QR_SCENE,
/** /**
* 永久二维码 * 永久二维码(场景值为数字范围在1-100000之间)
*/ */
PERMANENCE("QR_LIMIT_SCENE"); QR_LIMIT_SCENE,
private String name; /**
* 永久二维码(场景值为字符串长度在1-64之间)
QRType(String name) { */
this.name = name; QR_LIMIT_STR_SCENE;
}
public String getName() {
return name;
}
} }

View File

@ -5,8 +5,8 @@ account={"id":"wx4ab8f8de58159a57","secret":"1d4eb0f4bf556aaed539f30ed05ca795",\
"encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u65f6\u9700\u8981\u586b\u5165",\ "encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u65f6\u9700\u8981\u586b\u5165",\
"mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"version":2,\ "version":2,\
"partnerId":"\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "partnerId":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"partnerKey":"\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "partnerKey":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"} "paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84 # \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84

View File

@ -22,4 +22,14 @@ public class HelpTest extends TokenTest {
List<String> ipList = helperApi.getcallbackip(); List<String> ipList = helperApi.getcallbackip();
Assert.assertFalse(ipList.isEmpty()); Assert.assertFalse(ipList.isEmpty());
} }
@Test
public void getMenuSetting() throws WeixinException {
System.err.println(helperApi.getMenuSetting());
}
@Test
public void getAutoReplySetting() throws WeixinException {
System.err.println(helperApi.getAutoReplySetting());
}
} }

View File

@ -75,9 +75,7 @@ public class MediaTest extends TokenTest {
@Test @Test
public void uploadMaterialArticle() throws WeixinException { public void uploadMaterialArticle() throws WeixinException {
List<MpArticle> articles = new ArrayList<MpArticle>(); List<MpArticle> articles = new ArrayList<MpArticle>();
articles.add(new MpArticle( articles.add(new MpArticle("8790403529", "title", "content"));
"8790403529",
"title", "content"));
String mediaId = mediaApi.uploadMaterialArticle(articles); String mediaId = mediaApi.uploadMaterialArticle(articles);
// 17385064953 // 17385064953
Assert.assertNotNull(mediaId); Assert.assertNotNull(mediaId);
@ -86,43 +84,34 @@ public class MediaTest extends TokenTest {
@Test @Test
public void download2() throws WeixinException, IOException { public void download2() throws WeixinException, IOException {
File file = mediaApi File file = mediaApi.downloadMedia("8790403529", MediaType.image, true);
.downloadMedia(
"8790403529",
MediaType.image, true);
Assert.assertTrue(file.exists()); Assert.assertTrue(file.exists());
} }
@Test @Test
public void downloadArticle() throws WeixinException { public void downloadArticle() throws WeixinException {
List<MpArticle> articles = mediaApi List<MpArticle> articles = mediaApi.downloadArticle("17385064953");
.downloadArticle("17385064953");
Assert.assertTrue(articles != null && !articles.isEmpty()); Assert.assertTrue(articles != null && !articles.isEmpty());
System.err.println(articles); System.err.println(articles);
} }
@Test @Test
public void deleteMaterialMedia() throws WeixinException { public void deleteMaterialMedia() throws WeixinException {
JsonResult result = mediaApi JsonResult result = mediaApi.deleteMaterialMedia("17385064953");
.deleteMaterialMedia("17385064953");
System.err.println(result); System.err.println(result);
} }
@Test @Test
public void updateMaterialArticle() throws WeixinException { public void updateMaterialArticle() throws WeixinException {
MpArticle mpArticle = new MpArticle( MpArticle mpArticle = new MpArticle("8790403529", "title", "content");
"8790403529",
"title", "content");
mpArticle.setAuthor("author_update"); mpArticle.setAuthor("author_update");
mpArticle.setDigest("digest_update"); mpArticle.setDigest("digest_update");
mpArticle.setShowCoverPic(false); mpArticle.setShowCoverPic(false);
mpArticle.setUrl("http://www.baidu.com"); mpArticle.setSourceUrl("http://www.baidu.com");
List<MpArticle> articles = new ArrayList<MpArticle>(); List<MpArticle> articles = new ArrayList<MpArticle>();
articles.add(mpArticle); articles.add(mpArticle);
JsonResult result = mediaApi JsonResult result = mediaApi.updateMaterialArticle("17385064953", 0,
.updateMaterialArticle( articles);
"17385064953",
0, articles);
System.err.println(result); System.err.println(result);
// 17385065153 // 17385065153
} }
@ -135,7 +124,8 @@ public class MediaTest extends TokenTest {
@Test @Test
public void listMaterialMedia() throws WeixinException { public void listMaterialMedia() throws WeixinException {
MediaRecord mediaRecord = mediaApi.listMaterialMedia(MediaType.news, 0, 20); MediaRecord mediaRecord = mediaApi.listMaterialMedia(MediaType.news, 0,
20);
System.err.println(mediaRecord); System.err.println(mediaRecord);
} }
} }

View File

@ -35,22 +35,18 @@ public class MenuTest extends TokenTest {
public void create() throws WeixinException { public void create() throws WeixinException {
btnList = new ArrayList<Button>(); btnList = new ArrayList<Button>();
Button b1 = new Button("我要订餐", "ORDERING", ButtonType.click); Button b1 = new Button("会员中心", "", ButtonType.click);
b1.pushSub(new Button("我的信息", "U:INFO", ButtonType.click));
b1.pushSub(new Button("修改信息", "U:UP:INFO", ButtonType.click));
btnList.add(b1); btnList.add(b1);
Button b2 = new Button("", "", ButtonType.click); Button b2 = new Button("最新兼职", "PART:NEWEST", ButtonType.click);
b2.pushSub(new Button("个人中心", "MINE", ButtonType.click));
b2.pushSub(new Button("会员中心", "MEMRBER", ButtonType.click));
b2.pushSub(new Button("我的积分", "SCORE", ButtonType.click));
b2.pushSub(new Button("我的优惠券", "COUPON", ButtonType.click));
b2.pushSub(new Button("我的订单", "MYORDER", ButtonType.click));
btnList.add(b2); btnList.add(b2);
Button b3 = new Button("商家功能", "", ButtonType.click); Button b3 = new Button("功能", "", ButtonType.click);
b3.pushSub(new Button("商家主页", "SHOPLIST", ButtonType.click)); b3.pushSub(new Button("附近兼职", "PART:NEAR", ButtonType.click));
b3.pushSub(new Button("大转盘", "WHEEL", ButtonType.click)); b3.pushSub(new Button("搜索兼职", "PART:SO", ButtonType.click));
b3.pushSub(new Button("店铺区域", "SHOPAREA", ButtonType.click)); b3.pushSub(new Button("公交查询", "BUS:SO", ButtonType.click));
b3.pushSub(new Button("店铺口味", "SHOPTASTE", ButtonType.click));
btnList.add(b3); btnList.add(b3);
JsonResult result = menuApi.createMenu(btnList); JsonResult result = menuApi.createMenu(btnList);

View File

@ -29,15 +29,19 @@ public class QRTest extends TokenTest {
@Test @Test
public void temp_qr() throws WeixinException, IOException { public void temp_qr() throws WeixinException, IOException {
QRParameter qr = new QRParameter(1200, 1200); File file = qrApi.getQR(QRParameter.createTemporary(1200, 1200));
File file = qrApi.getQR(qr);
Assert.assertTrue(file.exists()); Assert.assertTrue(file.exists());
} }
@Test @Test
public void forever_qr() throws WeixinException, IOException { public void forever_qr_int() throws WeixinException, IOException {
QRParameter qr = new QRParameter(1200, 0); File file = qrApi.getQR(QRParameter.createPermanenceInt(1200));
File file = qrApi.getQR(qr); Assert.assertTrue(file.exists());
}
@Test
public void forever_qr_str() throws WeixinException, IOException {
File file = qrApi.getQR(QRParameter.createPermanenceStr("1200中文"));
Assert.assertTrue(file.exists()); Assert.assertTrue(file.exists());
} }
} }

View File

@ -30,7 +30,7 @@ weixin4j-mp-server
"token":"开放者的token","openId":"公众号的openid 非必须",\\ "token":"开放者的token","openId":"公众号的openid 非必须",\\
"encodingAesKey":"公众号设置了加密方式且为「安全模式」时需要填入",\\ "encodingAesKey":"公众号设置了加密方式且为「安全模式」时需要填入",\\
"mchId":"V3.x版本下的微信商户号",\\ "mchId":"V3.x版本下的微信商户号",\\
"partnerId":"财付通的商户号","partnerKey":"财付通商户权限密钥Key",\\ "partnerId":"V2版本下的财付通的商户号","partnerKey":"V2版本下的财付通商户权限密钥Key",\\
"version":"针对微信支付的版本号(目前可能为2,3),如果不填则按照mchId非空与否来做判断",\\ "version":"针对微信支付的版本号(目前可能为2,3),如果不填则按照mchId非空与否来做判断",\\
"paySignKey":"微信支付中调用API的密钥"} "paySignKey":"微信支付中调用API的密钥"}

View File

@ -4,8 +4,8 @@ account={"id":"appid","secret":"appsecret",\
"encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u65f6\u9700\u8981\u586b\u5165",\ "encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u65f6\u9700\u8981\u586b\u5165",\
"mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"version":\u6570\u5b57\u7c7b\u578b(2\u6216\u80053):\u5fae\u4fe1\u652f\u4ed8\u7684\u7248\u672c,\u5927\u6982\u57282014-09-14\u4e4b\u524d\u7533\u8bf7\u5e76\u4e14\u901a\u8fc7\u7684\u516c\u4f17\u53f7\u4e3aV2,\u5728\u8fd9\u4e4b\u540e\u5219\u4e3aV3 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165,\ "version":\u6570\u5b57\u7c7b\u578b(2\u6216\u80053):\u5fae\u4fe1\u652f\u4ed8\u7684\u7248\u672c,\u5927\u6982\u57282014-09-14\u4e4b\u524d\u7533\u8bf7\u5e76\u4e14\u901a\u8fc7\u7684\u516c\u4f17\u53f7\u4e3aV2,\u5728\u8fd9\u4e4b\u540e\u5219\u4e3aV3 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165,\
"partnerId":"\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "partnerId":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"partnerKey":"\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "partnerKey":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"} "paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84 # \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84

View File

@ -4,12 +4,14 @@ import java.util.List;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.NameFilter;
import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult; import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response; import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.ButtonType;
/** /**
* 菜单相关API * 菜单相关API
@ -48,7 +50,20 @@ public class MenuApi extends QyApi {
obj.put("button", btnList); obj.put("button", btnList);
Response response = request Response response = request
.post(String.format(menu_create_uri, token.getAccessToken(), .post(String.format(menu_create_uri, token.getAccessToken(),
agentid), obj.toJSONString()); agentid), JSON.toJSONString(obj, new NameFilter() {
@Override
public String process(Object object, String name,
Object value) {
if (object instanceof Button && name.equals("content")) {
if (((Button) object).getFormatType() == ButtonType.view) {
return "url";
} else {
return "key";
}
}
return name;
}
}));
return response.getAsJsonResult(); return response.getAsJsonResult();
} }
@ -70,8 +85,18 @@ public class MenuApi extends QyApi {
Response response = request.get(String.format(menu_get_uri, Response response = request.get(String.format(menu_get_uri,
token.getAccessToken(), agentid)); token.getAccessToken(), agentid));
String text = response.getAsJson().getJSONObject("menu") String text = JSON.toJSONString(
.getString("button"); 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); return JSON.parseArray(text, Button.class);
} }