diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java
index 6c2ffe7d..361461a9 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java
@@ -8,7 +8,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
- * 对微信小程序用户加密数据的解密.
+ * 对微信小程序用户加密数据的解密。
*
* @since 1.8
* @see 加密数据解密算法
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WeixinProxy.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WeixinProxy.java
index e21ded07..d86f10e0 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WeixinProxy.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WeixinProxy.java
@@ -9,14 +9,20 @@ import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.token.TokenManager;
import com.foxinmy.weixin4j.wxa.api.LoginApi;
import com.foxinmy.weixin4j.wxa.api.QrCodeApi;
+import com.foxinmy.weixin4j.wxa.api.TemplateApi;
+import com.foxinmy.weixin4j.wxa.api.TemplateMessageApi;
/**
+ * The facade of WXA APIs.
+ *
* @since 1.8
*/
public class WeixinProxy {
private final LoginApi loginApi;
private final QrCodeApi qrCodeApi;
+ private final TemplateApi templateApi;
+ private final TemplateMessageApi templateMessageApi;
public WeixinProxy(
WeixinAccount weixinAccount
@@ -62,6 +68,8 @@ public class WeixinProxy {
this.loginApi = new LoginApi(weixinAccount);
this.qrCodeApi = new QrCodeApi(tokenManager);
+ this.templateApi = new TemplateApi(tokenManager);
+ this.templateMessageApi = new TemplateMessageApi(tokenManager);
}
public LoginApi getLoginApi() {
@@ -72,4 +80,12 @@ public class WeixinProxy {
return qrCodeApi;
}
+ public TemplateApi getTemplateApi() {
+ return templateApi;
+ }
+
+ public TemplateMessageApi getTemplateMessageApi() {
+ return templateMessageApi;
+ }
+
}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/AddTemplateParameter.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/AddTemplateParameter.java
new file mode 100644
index 00000000..c4bf016b
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/AddTemplateParameter.java
@@ -0,0 +1,39 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.io.Serializable;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+class AddTemplateParameter implements Serializable {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ private String id;
+ private int[] keywordIds;
+
+ public AddTemplateParameter() {
+ }
+
+ public AddTemplateParameter(String id, int[] keywordIds) {
+ this.id = id;
+ this.keywordIds = keywordIds;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @JSONField(name = "keyword_id_list")
+ public int[] getKeywordIds() {
+ return keywordIds;
+ }
+
+ public void setKeywordIds(int[] keywordIds) {
+ this.keywordIds = keywordIds;
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/LoginApi.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/LoginApi.java
index 1a664931..ddb5500a 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/LoginApi.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/LoginApi.java
@@ -7,6 +7,8 @@ import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.wxa.model.Session;
/**
+ * 登录凭证校验。
+ *
* @since 1.8
*/
public class LoginApi extends WxaApi {
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/QrCodeApi.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/QrCodeApi.java
index 4f0890a4..85540a1a 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/QrCodeApi.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/QrCodeApi.java
@@ -1,7 +1,6 @@
package com.foxinmy.weixin4j.wxa.api;
import java.awt.Color;
-import java.io.IOException;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.exception.WeixinException;
@@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.token.TokenManager;
/**
- * 获取二维码.
+ * 获取二维码。
*
*
* 通过后台接口可以获取小程序任意页面的二维码,扫描该二维码可以直接进入小程序对应的页面。
@@ -20,12 +19,10 @@ import com.foxinmy.weixin4j.token.TokenManager;
* @see 获取二维码
* @since 1.8
*/
-public class QrCodeApi extends WxaApi {
-
- private final TokenManager tokenManager;
+public class QrCodeApi extends TokenManagerApi {
public QrCodeApi(TokenManager tokenManager) {
- this.tokenManager = tokenManager;
+ super(tokenManager);
}
/**
@@ -54,8 +51,7 @@ public class QrCodeApi extends WxaApi {
Color lineColor,
Boolean hyaline
) throws WeixinException {
- final String accessToken = tokenManager.getAccessToken();
- final String getWxaCodeUri = this.getRequestUri("wxa_getwxacode", accessToken);
+ final String getWxaCodeUri = this.getAccessTokenRequestUri("wxa_getwxacode");
final WxaCodeParameter param = new WxaCodeParameter(path, width, autoColor, lineColor, hyaline);
return this.postAsImageBytes(getWxaCodeUri, param);
}
@@ -105,8 +101,7 @@ public class QrCodeApi extends WxaApi {
Color lineColor,
Boolean hyaline
) throws WeixinException {
- final String accessToken = tokenManager.getAccessToken();
- final String getWxaCodeUnlimitUri = this.getRequestUri("wxa_getwxacodeunlimit", accessToken);
+ final String getWxaCodeUnlimitUri = this.getAccessTokenRequestUri("wxa_getwxacodeunlimit");
final WxaCodeUnlimitParameter param = new WxaCodeUnlimitParameter(scene, page, width, autoColor, lineColor, hyaline);
return this.postAsImageBytes(getWxaCodeUnlimitUri, param);
}
@@ -131,8 +126,7 @@ public class QrCodeApi extends WxaApi {
String path,
Integer width
) throws WeixinException {
- final String accessToken = tokenManager.getAccessToken();
- final String createWxaQrCode = this.getRequestUri("wxaapp_createwxaqrcode", accessToken);
+ final String createWxaQrCode = this.getAccessTokenRequestUri("wxaapp_createwxaqrcode");
final WxaQrCodeParameter param = new WxaQrCodeParameter(path, width);
return this.postAsImageBytes(createWxaQrCode, param);
}
@@ -144,20 +138,10 @@ public class QrCodeApi extends WxaApi {
}
private byte[] toImageBytes(WeixinResponse response) throws WeixinException {
- try {
- return readImageBytes(response);
- } catch (IOException e) {
- throw new WeixinException(e);
- } catch (WxaCodeError e) {
- throw new WeixinException(Integer.toString(e.getErrcode()), e.getErrmsg());
- }
- }
-
- private byte[] readImageBytes(WeixinResponse response) throws IOException, WxaCodeError {
final String contentType = response.getHeaders().getContentType();
if (contentType != null && contentType.equals(ContentType.APPLICATION_JSON.getMimeType().getType())) {
- WxaCodeError wxaCodeError = JSON.parseObject(response.getContent(), WxaCodeError.class);
- throw wxaCodeError;
+ final WxaApiResult r = response.getAsObject(WxaApiResult.TYPE_REFERENCE);
+ throw r.toWeixinException();
} else {
return response.getContent();
}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateApi.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateApi.java
new file mode 100644
index 00000000..e1bd0fac
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateApi.java
@@ -0,0 +1,122 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.model.paging.Pageable;
+import com.foxinmy.weixin4j.model.paging.Pagedata;
+import com.foxinmy.weixin4j.token.TokenManager;
+import com.foxinmy.weixin4j.wxa.model.template.Template;
+
+/**
+ * 模版消息管理。
+ *
+ * @see 模版消息管理
+ * @since 1.8
+ */
+public class TemplateApi extends TokenManagerApi {
+
+ public TemplateApi(TokenManager tokenManager) {
+ super(tokenManager);
+ }
+
+ /**
+ * 获取小程序模板库标题列表
+ *
+ * @param pageable the pagination information.
+ * @return templates in library.
+ * @throws WeixinException indicates getting access token failed or getting templates failed.
+ * @see 获取小程序模板库标题列表
+ */
+ public Pagedata getTemplatesInLibrary(final Pageable pageable) throws WeixinException {
+ final TemplateListResult r = post(
+ "wxopen_template_library_list",
+ toPageableRequestParams(pageable),
+ TemplateListResult.TYPE_REFERENCE
+ );
+ return r.toPage(pageable);
+ }
+
+ /**
+ * 获取模板库某个模板标题下关键词库
+ *
+ * @param id 模板标题id,可通过接口获取,也可登录小程序后台查看获取
+ * @return the template in library with specified ID.
+ * @throws WeixinException indicates getting access token failed or getting template failed.
+ * @see 获取模板库某个模板标题下关键词库
+ */
+ public Template getTemplateInLibrary(String id) throws WeixinException {
+ final Map params = new HashMap(1);
+ params.put("id", id);
+ final TemplateResult r = this.post(
+ "wxopen_template_library_get",
+ params,
+ TemplateResult.TYPE_REFERENCE
+ );
+ return r.toTemplate();
+ }
+
+ /**
+ * 组合模板并添加至帐号下的个人模板库
+ *
+ * @param id 模板标题id,可通过接口获取,也可登录小程序后台查看获取
+ * @param keywordIds 开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如[3,5,4]或[4,5,3]),最多支持10个关键词组合
+ * @return the added template ID.
+ * @throws WeixinException indicates getting access token failed or adding template failed.
+ * @see 组合模板并添加至帐号下的个人模板库
+ */
+ public String addTemplate(String id, int[] keywordIds) throws WeixinException {
+ final AddTemplateParameter params = new AddTemplateParameter(id, keywordIds);
+ final TemplateResult r = this.post(
+ "wxopen_template_add",
+ params,
+ TemplateResult.TYPE_REFERENCE
+ );
+ return r.toTemplate().getId();
+ }
+
+ /**
+ * 获取帐号下已存在的模板列表
+ *
+ * @param pageable the pagination information.
+ * @return the templates.
+ * @throws WeixinException indicates getting access token failed or getting template failed.
+ * @see 获取帐号下已存在的模板列表
+ */
+ public List getTemplates(final Pageable pageable) throws WeixinException {
+ final TemplateListResult r = post(
+ "wxopen_template_list",
+ toPageableRequestParams(pageable),
+ TemplateListResult.TYPE_REFERENCE
+ );
+ return r.toList();
+ }
+
+ /**
+ * 删除帐号下的某个模板
+ *
+ * @param id 要删除的模板id
+ * @throws WeixinException indicates getting access token failed or deleting template failed.
+ * @see 删除帐号下的某个模板
+ */
+ public void deleteTemplate(String id) throws WeixinException {
+ final Map params = new HashMap(1);
+ params.put("template_id", id);
+ final WxaApiResult r = this.post(
+ "wxopen_template_del",
+ params,
+ WxaApiResult.TYPE_REFERENCE
+ );
+ r.checkErrCode();
+ }
+
+ private Map toPageableRequestParams(final Pageable pageable) {
+ final Map params = new HashMap(2);
+ params.put("offset", pageable.getOffset());
+ params.put("count", pageable.getPageSize());
+ return params;
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateListResult.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateListResult.java
new file mode 100644
index 00000000..e2d0f4c7
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateListResult.java
@@ -0,0 +1,55 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.util.List;
+
+import com.alibaba.fastjson.TypeReference;
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.model.paging.Pageable;
+import com.foxinmy.weixin4j.model.paging.Pagedata;
+import com.foxinmy.weixin4j.wxa.model.template.Template;
+
+class TemplateListResult extends WxaApiResult {
+
+ private static final long serialVersionUID = 2018052602L;
+
+ public static final TypeReference TYPE_REFERENCE
+ = new TypeReference() {
+ };
+
+ private List list;
+ private Long totalCount;
+
+ public List getList() {
+ return list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
+
+ public Long getTotalCount() {
+ return totalCount;
+ }
+
+ public void setTotalCount(Long totalCount) {
+ this.totalCount = totalCount;
+ }
+
+ public Pagedata toPage(Pageable pageable) throws WeixinException {
+ this.checkErrCode();
+
+ final Pagedata page = new Pagedata(
+ pageable,
+ this.getTotalCount().intValue(),
+ this.getList()
+ );
+ return page;
+ }
+
+ public List toList() throws WeixinException {
+ this.checkErrCode();
+
+ return this.getList();
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageApi.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageApi.java
new file mode 100644
index 00000000..3424e529
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageApi.java
@@ -0,0 +1,51 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.util.Map;
+
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.token.TokenManager;
+
+/**
+ * 发送模版消息。
+ *
+ * @see 发送模版消息
+ * @since 1.8
+ */
+public class TemplateMessageApi extends TokenManagerApi {
+
+ public TemplateMessageApi(TokenManager tokenManager) {
+ super(tokenManager);
+ }
+
+ /**
+ * 发送模板消息
+ *
+ * @param toUser 接收者(用户)的 openid。
+ * @param templateId 所需下发的模板消息的 id。
+ * @param page 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
+ * @param formId 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id。
+ * @param data 模板内容,不填则下发空模板。
+ * @param emphasisKeyword 模板需要放大的关键词,不填则默认无放大。
+ * @throws WeixinException indicates getting access token failed, or sending template message failed.
+ * @see 发送模版消息
+ */
+ public void sendTemplateMessage(
+ final String toUser,
+ final String templateId,
+ final String page,
+ final String formId,
+ final Map data,
+ final String emphasisKeyword
+ ) throws WeixinException {
+ final TemplateMessageParameter message = new TemplateMessageParameter(
+ toUser, templateId, page, formId, data, emphasisKeyword
+ );
+ final WxaApiResult r = this.post(
+ "wxopen_template_message_send",
+ message,
+ WxaApiResult.TYPE_REFERENCE
+ );
+ r.checkErrCode();
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageParameter.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageParameter.java
new file mode 100644
index 00000000..243795cb
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageParameter.java
@@ -0,0 +1,116 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+class TemplateMessageParameter implements Serializable {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ private String toUser;
+ private String templateId;
+ private String page;
+ private String formId;
+ private Map data;
+ private String emphasisKeyword;
+
+ public TemplateMessageParameter() {
+ }
+
+ public TemplateMessageParameter(
+ String toUser,
+ String templateId,
+ String page,
+ String formId,
+ Map data,
+ String emphasisKeyword
+ ) {
+ this.toUser = toUser;
+ this.templateId = templateId;
+ this.page = page;
+ this.formId = formId;
+ if (data != null) {
+ this.data = new HashMap(data.size());
+ for (Map.Entry entry : data.entrySet()) {
+ this.data.put(entry.getKey(), new TemplateMessageData(entry.getValue()));
+ }
+ }
+ this.emphasisKeyword = emphasisKeyword;
+ }
+
+ @JSONField(name = "touser")
+ public String getToUser() {
+ return toUser;
+ }
+
+ public void setToUser(String toUser) {
+ this.toUser = toUser;
+ }
+
+ @JSONField(name = "template_id")
+ public String getTemplateId() {
+ return templateId;
+ }
+
+ public void setTemplateId(String templateId) {
+ this.templateId = templateId;
+ }
+
+ public String getPage() {
+ return page;
+ }
+
+ public void setPage(String page) {
+ this.page = page;
+ }
+
+ @JSONField(name = "form_id")
+ public String getFormId() {
+ return formId;
+ }
+
+ public void setFormId(String formId) {
+ this.formId = formId;
+ }
+
+ public Map getData() {
+ return data;
+ }
+
+ public void setData(Map data) {
+ this.data = data;
+ }
+
+ @JSONField(name = "emphasis_keyword")
+ public String getEmphasisKeyword() {
+ return emphasisKeyword;
+ }
+
+ public void setEmphasisKeyword(String emphasisKeyword) {
+ this.emphasisKeyword = emphasisKeyword;
+ }
+
+ public static class TemplateMessageData implements Serializable {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ private String value;
+
+ public TemplateMessageData(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateResult.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateResult.java
new file mode 100644
index 00000000..1cdc39a1
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TemplateResult.java
@@ -0,0 +1,54 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.util.List;
+
+import com.alibaba.fastjson.TypeReference;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.wxa.model.template.Keyword;
+import com.foxinmy.weixin4j.wxa.model.template.Template;
+
+class TemplateResult extends WxaApiResult {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ public static final TypeReference TYPE_REFERENCE
+ = new TypeReference() {
+ };
+
+ private String id;
+ private String title;
+ private List keywords;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public List getKeywords() {
+ return keywords;
+ }
+
+ @JSONField(name = "keyword_list")
+ public void setKeywords(List keywords) {
+ this.keywords = keywords;
+ }
+
+ public Template toTemplate() throws WeixinException {
+ this.checkErrCode();
+
+ return new Template(id, title, keywords);
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TokenManagerApi.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TokenManagerApi.java
new file mode 100644
index 00000000..90a4365b
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/TokenManagerApi.java
@@ -0,0 +1,30 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.foxinmy.weixin4j.exception.WeixinException;
+import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
+import com.foxinmy.weixin4j.token.TokenManager;
+
+abstract class TokenManagerApi extends WxaApi {
+
+ private final TokenManager tokenManager;
+
+ public TokenManagerApi(final TokenManager tokenManager) {
+ this.tokenManager = tokenManager;
+ }
+
+ T post(String key, Object params, TypeReference typeReference) throws WeixinException {
+ final String uri = this.getAccessTokenRequestUri(key);
+ final String body = JSON.toJSONString(params);
+ final WeixinResponse response = this.weixinExecutor.post(uri, body);
+ return response.getAsObject(typeReference);
+ }
+
+ String getAccessTokenRequestUri(String key) throws WeixinException {
+ final String accessToken = tokenManager.getAccessToken();
+ final String uri = this.getRequestUri(key, accessToken);
+ return uri;
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApi.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApi.java
index f4dc2b62..2c6c89c3 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApi.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApi.java
@@ -7,7 +7,7 @@ import com.foxinmy.weixin4j.api.BaseApi;
/**
* @since 1.8
*/
-public abstract class WxaApi extends BaseApi {
+abstract class WxaApi extends BaseApi {
private static final ResourceBundle WEIXIN_BUNDLE;
@@ -15,12 +15,13 @@ public abstract class WxaApi extends BaseApi {
WEIXIN_BUNDLE = ResourceBundle
.getBundle("com/foxinmy/weixin4j/wxa/api/weixin");
}
+
@Override
protected ResourceBundle weixinBundle() {
return WEIXIN_BUNDLE;
}
- protected String getRequestUri(String key, Object... args) {
+ String getRequestUri(String key, Object... args) {
return String.format(getRequestUri(key), args);
}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApiResult.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApiResult.java
new file mode 100644
index 00000000..79aeac30
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaApiResult.java
@@ -0,0 +1,48 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import java.io.Serializable;
+
+import com.alibaba.fastjson.TypeReference;
+import com.foxinmy.weixin4j.exception.WeixinException;
+
+class WxaApiResult implements Serializable {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ public static final TypeReference TYPE_REFERENCE
+ = new TypeReference() {
+ };
+
+ private int errCode;
+ private String errMsg;
+
+ public WxaApiResult() {
+ }
+
+ public int getErrCode() {
+ return errCode;
+ }
+
+ public void setErrCode(int errCode) {
+ this.errCode = errCode;
+ }
+
+ public String getErrMsg() {
+ return errMsg;
+ }
+
+ public void setErrMsg(String errMsg) {
+ this.errMsg = errMsg;
+ }
+
+ public WeixinException toWeixinException() {
+ return new WeixinException(Integer.toString(errCode), errMsg);
+ }
+
+ public void checkErrCode() throws WeixinException {
+ if (getErrCode() != 0) {
+ throw this.toWeixinException();
+ }
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaCodeError.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaCodeError.java
deleted file mode 100644
index ff31c025..00000000
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/WxaCodeError.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.foxinmy.weixin4j.wxa.api;
-
-class WxaCodeError extends Exception {
-
- private static final long serialVersionUID = 2018052201L;
-
- private int errcode;
- private String errmsg;
-
- public WxaCodeError() {
- }
-
- public WxaCodeError(int errcode, String errmsg) {
- this.errcode = errcode;
- this.errmsg = errmsg;
- }
-
- public int getErrcode() {
- return errcode;
- }
-
- public void setErrcode(int errcode) {
- this.errcode = errcode;
- }
-
- public String getErrmsg() {
- return errmsg;
- }
-
- public void setErrmsg(String errmsg) {
- this.errmsg = errmsg;
- }
-
- @Override
- public String getMessage() {
- return errmsg;
- }
-
-}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/package-info.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/package-info.java
index 4033640a..552586c2 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/package-info.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/package-info.java
@@ -1,4 +1,6 @@
/**
+ * APIs.
+ *
* @since 1.8
*/
package com.foxinmy.weixin4j.wxa.api;
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/Session.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/Session.java
index 679c7918..d35e76db 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/Session.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/Session.java
@@ -2,7 +2,11 @@ package com.foxinmy.weixin4j.wxa.model;
import java.io.Serializable;
+import com.foxinmy.weixin4j.wxa.WXBizDataCrypt;
+
/**
+ * 登录会话。
+ *
* @since 1.8
*/
public class Session implements Serializable {
@@ -21,6 +25,17 @@ public class Session implements Serializable {
this.openId = openId;
}
+ /**
+ * 返回会话密钥
+ * 注意:会话密钥 session_key 是对用户数据进行
+ * 加密签名
+ * 的密钥。
+ * 为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,
+ * 也不应该对外提供这个密钥。
+ *
+ * @return 会话密钥
+ * @see WXBizDataCrypt
+ */
public String getSessionKey() {
return sessionKey;
}
@@ -29,6 +44,15 @@ public class Session implements Serializable {
this.sessionKey = sessionKey;
}
+ /**
+ * 返回UnionID
+ *
+ * UnionID 只在满足一定条件的情况下返回。
+ * 具体参看 UnionID机制说明。
+ *
+ *
+ * @return UnionID
+ */
public String getUnionId() {
return unionId;
}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/package-info.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/package-info.java
index e12e627b..f34f12bf 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/package-info.java
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/package-info.java
@@ -1,4 +1,6 @@
/**
+ * Models.
+ *
* @since 1.8
*/
package com.foxinmy.weixin4j.wxa.model;
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/Keyword.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/Keyword.java
new file mode 100644
index 00000000..b4ed513a
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/Keyword.java
@@ -0,0 +1,45 @@
+package com.foxinmy.weixin4j.wxa.model.template;
+
+import java.io.Serializable;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 模板消息的模板里的关键词。
+ *
+ * @since 1.8
+ */
+public class Keyword implements Serializable {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ private int id;
+ private String name;
+ private String example;
+
+ public int getId() {
+ return id;
+ }
+
+ @JSONField(name = "keyword_id")
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getExample() {
+ return example;
+ }
+
+ public void setExample(String example) {
+ this.example = example;
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/Template.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/Template.java
new file mode 100644
index 00000000..f38f53ec
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/Template.java
@@ -0,0 +1,73 @@
+package com.foxinmy.weixin4j.wxa.model.template;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 模板消息的模板。
+ *
+ * @since 1.8
+ */
+public class Template implements Serializable {
+
+ private static final long serialVersionUID = 2018052601L;
+
+ private String id;
+ private String title;
+ private List keywords;
+ private String content;
+ private String example;
+
+ public Template() {
+ }
+
+ public Template(String id, String title, List keywords) {
+ this.id = id;
+ this.title = title;
+ this.keywords = keywords;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @JSONField(name = "id", alternateNames = { "template_id" })
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public List getKeywords() {
+ return keywords;
+ }
+
+ public void setKeywords(List keywords) {
+ this.keywords = keywords;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getExample() {
+ return example;
+ }
+
+ public void setExample(String example) {
+ this.example = example;
+ }
+
+}
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/package-info.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/package-info.java
new file mode 100644
index 00000000..ff95918c
--- /dev/null
+++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/model/template/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Models for {@link com.foxinmy.weixin4j.wxa.api.TemplateApi}.
+ *
+ * @since 1.8
+ */
+package com.foxinmy.weixin4j.wxa.model.template;
diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/weixin.properties b/weixin4j-wxa/src/main/resources/com/foxinmy/weixin4j/wxa/api/weixin.properties
similarity index 52%
rename from weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/weixin.properties
rename to weixin4j-wxa/src/main/resources/com/foxinmy/weixin4j/wxa/api/weixin.properties
index 72a3725c..a5842379 100644
--- a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/api/weixin.properties
+++ b/weixin4j-wxa/src/main/resources/com/foxinmy/weixin4j/wxa/api/weixin.properties
@@ -3,10 +3,12 @@
# ----------------------------------------------------------------------------
api_base_url=https://api.weixin.qq.com
+api_cgi_url={api_base_url}/cgi-bin
# \u767b\u5f55\u51ed\u8bc1\u6821\u9a8c
sns_jscode2session={api_base_url}/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=%s
+
# \u83b7\u53d6\u5c0f\u7a0b\u5e8f\u7801 \u63a5\u53e3A: \u9002\u7528\u4e8e\u9700\u8981\u7684\u7801\u6570\u91cf\u8f83\u5c11\u7684\u4e1a\u52a1\u573a\u666f
wxa_getwxacode={api_base_url}/wxa/getwxacode?access_token=%s
@@ -15,3 +17,22 @@ wxa_getwxacodeunlimit={api_base_url}/wxa/getwxacodeunlimit?access_token=%s
# \u83b7\u53d6\u5c0f\u7a0b\u5e8f\u4e8c\u7ef4\u7801 \u63a5\u53e3C\uff1a\u9002\u7528\u4e8e\u9700\u8981\u7684\u7801\u6570\u91cf\u8f83\u5c11\u7684\u4e1a\u52a1\u573a\u666f
wxaapp_createwxaqrcode={api_base_url}/cgi-bin/wxaapp/createwxaqrcode?access_token=%s
+
+
+# \u83b7\u53d6\u5c0f\u7a0b\u5e8f\u6a21\u677f\u5e93\u6807\u9898\u5217\u8868
+wxopen_template_library_list={api_cgi_url}/wxopen/template/library/list?access_token=%s
+
+# \u83b7\u53d6\u6a21\u677f\u5e93\u67d0\u4e2a\u6a21\u677f\u6807\u9898\u4e0b\u5173\u952e\u8bcd\u5e93
+wxopen_template_library_get={api_cgi_url}/wxopen/template/library/get?access_token=%s
+
+# \u7ec4\u5408\u6a21\u677f\u5e76\u6dfb\u52a0\u81f3\u5e10\u53f7\u4e0b\u7684\u4e2a\u4eba\u6a21\u677f\u5e93
+wxopen_template_add={api_cgi_url}/wxopen/template/add?access_token=%s
+
+# \u83b7\u53d6\u5e10\u53f7\u4e0b\u5df2\u5b58\u5728\u7684\u6a21\u677f\u5217\u8868
+wxopen_template_list={api_cgi_url}/wxopen/template/list?access_token=%s
+
+# \u5220\u9664\u5e10\u53f7\u4e0b\u7684\u67d0\u4e2a\u6a21\u677f
+wxopen_template_del={api_cgi_url}/wxopen/template/del?access_token=%s
+
+# \u53d1\u9001\u6a21\u7248\u6d88\u606f
+wxopen_template_message_send={api_cgi_url}/message/wxopen/template/send?access_token=%s
diff --git a/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/AddTemplateParameterTest.java b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/AddTemplateParameterTest.java
new file mode 100644
index 00000000..dd8d4e3a
--- /dev/null
+++ b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/AddTemplateParameterTest.java
@@ -0,0 +1,18 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.fastjson.JSON;
+
+public class AddTemplateParameterTest {
+
+ @Test
+ public void test() {
+ AddTemplateParameter param = new AddTemplateParameter("AT0002", new int[] { 3, 4, 5 });
+ String json = JSON.toJSONString(param);
+ assertEquals("{\"id\":\"AT0002\",\"keyword_id_list\":[3,4,5]}", json);
+ }
+
+}
diff --git a/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateListResultTest.java b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateListResultTest.java
new file mode 100644
index 00000000..fa90a364
--- /dev/null
+++ b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateListResultTest.java
@@ -0,0 +1,52 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+import com.alibaba.fastjson.JSON;
+import com.foxinmy.weixin4j.util.IOUtil;
+
+public class TemplateListResultTest {
+
+ @Test
+ public void testTemplatesInLibrary() throws IOException {
+ TemplateListResult r = readTemplateList("TemplateListInLibrary.json");
+ assertEquals(0, r.getErrCode());
+ assertEquals("ok", r.getErrMsg());
+ assertEquals(599, r.getTotalCount().longValue());
+
+ assertEquals(5, r.getList().size());
+
+ assertEquals("AT0002", r.getList().get(0).getId());
+ assertEquals("购买成功通知", r.getList().get(0).getTitle());
+
+ assertEquals("AT0003", r.getList().get(1).getId());
+ assertEquals("购买失败通知", r.getList().get(1).getTitle());
+ }
+
+ @Test
+ public void testMyTemplates() throws IOException {
+ TemplateListResult r = readTemplateList("TemplateList.json");
+ assertEquals(0, r.getErrCode());
+ assertEquals("ok", r.getErrMsg());
+ assertEquals("wDYzYZVxobJivW9oMpSCpuvACOfJXQIoKUm0PY397Tc", r.getList().get(0).getId());
+ assertEquals("购买成功通知", r.getList().get(0).getTitle());
+ assertEquals("购买地点{{keyword1.DATA}}\n购买时间{{keyword2.DATA}}\n物品名称{{keyword3.DATA}}\n", r.getList().get(0).getContent());
+ assertEquals("购买地点:TIT造舰厂\n购买时间:2016年6月6日\n物品名称:咖啡\n", r.getList().get(0).getExample());
+ }
+
+ private TemplateListResult readTemplateList(String resourceName) throws IOException {
+ InputStream inputStream = getClass().getResourceAsStream(resourceName);
+ try {
+ TemplateListResult tl = JSON.parseObject(inputStream, TemplateListResult.class);
+ return tl;
+ } finally {
+ IOUtil.close(inputStream);
+ }
+ }
+
+}
diff --git a/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageParameterTest.java b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageParameterTest.java
new file mode 100644
index 00000000..2ee86e6e
--- /dev/null
+++ b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateMessageParameterTest.java
@@ -0,0 +1,39 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.alibaba.fastjson.JSON;
+import com.foxinmy.weixin4j.wxa.api.TemplateMessageParameter.TemplateMessageData;
+
+public class TemplateMessageParameterTest {
+
+ @Test
+ public void test() {
+ TemplateMessageParameter param = new TemplateMessageParameter();
+ param.setToUser("OPENID");
+ param.setTemplateId("TEMPLATE_ID");
+ param.setPage("index");
+ param.setFormId("FORMID");
+ Map data = new LinkedHashMap();
+ data.put("keyword1", new TemplateMessageData("339208499"));
+ data.put("keyword2", new TemplateMessageData("2015年01月05日 12:30"));
+ data.put("keyword3", new TemplateMessageData("粤海喜来登酒店"));
+ data.put("keyword4", new TemplateMessageData("广州市天河区天河路208号"));
+ param.setData(data);
+ param.setEmphasisKeyword("keyword1.DATA");
+ String json = JSON.toJSONString(param);
+ System.out.println(json);
+ assertTrue(json.contains("\"touser\":"));
+ assertTrue(json.contains("\"template_id\":"));
+ assertTrue(json.contains("\"page\":"));
+ assertTrue(json.contains("\"form_id\":"));
+ assertTrue(json.contains("\"data\":{\"keyword1\":{\"value\":\"339208499\"},\"keyword2\":{\"value\":\"2015年01月05日 12:30\"},\"keyword3\":{\"value\":\"粤海喜来登酒店\"},\"keyword4\":{\"value\":\"广州市天河区天河路208号\"}}"));
+ assertTrue(json.contains("\"emphasis_keyword\":"));
+ }
+
+}
diff --git a/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateResultTest.java b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateResultTest.java
new file mode 100644
index 00000000..6185b152
--- /dev/null
+++ b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/api/TemplateResultTest.java
@@ -0,0 +1,38 @@
+package com.foxinmy.weixin4j.wxa.api;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+import com.alibaba.fastjson.JSON;
+import com.foxinmy.weixin4j.util.IOUtil;
+
+public class TemplateResultTest {
+
+ @Test
+ public void test() throws IOException {
+ TemplateResult t = readTemplate();
+ assertEquals(0, t.getErrCode());
+ assertEquals("ok", t.getErrMsg());
+ assertEquals("AT0002", t.getId());
+ assertEquals("购买成功通知", t.getTitle());
+ assertEquals(3, t.getKeywords().size());
+ assertEquals(3, t.getKeywords().get(0).getId());
+ assertEquals("购买地点", t.getKeywords().get(0).getName());
+ assertEquals("TIT造舰厂", t.getKeywords().get(0).getExample());
+ }
+
+ private TemplateResult readTemplate() throws IOException {
+ InputStream inputStream = getClass().getResourceAsStream("Template.json");
+ try {
+ TemplateResult t = JSON.parseObject(inputStream, TemplateResult.class);
+ return t;
+ } finally {
+ IOUtil.close(inputStream);
+ }
+ }
+
+}
diff --git a/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/Template.json b/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/Template.json
new file mode 100644
index 00000000..a648607b
--- /dev/null
+++ b/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/Template.json
@@ -0,0 +1,23 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "id": "AT0002",
+ "title": "购买成功通知",
+ "keyword_list": [
+ {
+ "keyword_id": 3,
+ "name": "购买地点",
+ "example": "TIT造舰厂"
+ },
+ {
+ "keyword_id": 4,
+ "name": "购买时间",
+ "example": "2016年6月6日"
+ },
+ {
+ "keyword_id": 5,
+ "name": "物品名称",
+ "example": "咖啡"
+ }
+ ]
+}
diff --git a/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/TemplateList.json b/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/TemplateList.json
new file mode 100644
index 00000000..0cc80d66
--- /dev/null
+++ b/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/TemplateList.json
@@ -0,0 +1,12 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "list": [
+ {
+ "template_id": "wDYzYZVxobJivW9oMpSCpuvACOfJXQIoKUm0PY397Tc",
+ "title": "购买成功通知",
+ "content": "购买地点{{keyword1.DATA}}\n购买时间{{keyword2.DATA}}\n物品名称{{keyword3.DATA}}\n",
+ "example": "购买地点:TIT造舰厂\n购买时间:2016年6月6日\n物品名称:咖啡\n"
+ }
+ ]
+}
diff --git a/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/TemplateListInLibrary.json b/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/TemplateListInLibrary.json
new file mode 100644
index 00000000..4dd1069e
--- /dev/null
+++ b/weixin4j-wxa/src/test/resources/com/foxinmy/weixin4j/wxa/api/TemplateListInLibrary.json
@@ -0,0 +1,27 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "list": [
+ {
+ "id": "AT0002",
+ "title": "购买成功通知"
+ },
+ {
+ "id": "AT0003",
+ "title": "购买失败通知"
+ },
+ {
+ "id": "AT0004",
+ "title": "交易提醒"
+ },
+ {
+ "id": "AT0005",
+ "title": "付款成功通知"
+ },
+ {
+ "id": "AT0006",
+ "title": "付款失败通知"
+ }
+ ],
+ "total_count": 599
+}