Merge pull request #141 from sutra/notice
Implement template management and template message sending for WXA.
This commit is contained in:
commit
0566d8e290
@ -8,7 +8,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
/**
|
||||
* 对微信小程序用户加密数据的解密.
|
||||
* 对微信小程序用户加密数据的解密。
|
||||
*
|
||||
* @since 1.8
|
||||
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api/signature.html#wxchecksessionobject">加密数据解密算法</a>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 获取二维码.
|
||||
* 获取二维码。
|
||||
*
|
||||
* <p>
|
||||
* 通过后台接口可以获取小程序任意页面的二维码,扫描该二维码可以直接进入小程序对应的页面。
|
||||
@ -20,12 +19,10 @@ import com.foxinmy.weixin4j.token.TokenManager;
|
||||
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api/qrcode.html">获取二维码</a>
|
||||
* @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();
|
||||
}
|
||||
|
||||
@ -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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#%E6%A8%A1%E7%89%88%E6%B6%88%E6%81%AF%E7%AE%A1%E7%90%86">模版消息管理</a>
|
||||
* @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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#1%E8%8E%B7%E5%8F%96%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%A8%A1%E6%9D%BF%E5%BA%93%E6%A0%87%E9%A2%98%E5%88%97%E8%A1%A8">获取小程序模板库标题列表</a>
|
||||
*/
|
||||
public Pagedata<Template> 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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#2%E8%8E%B7%E5%8F%96%E6%A8%A1%E6%9D%BF%E5%BA%93%E6%9F%90%E4%B8%AA%E6%A8%A1%E6%9D%BF%E6%A0%87%E9%A2%98%E4%B8%8B%E5%85%B3%E9%94%AE%E8%AF%8D%E5%BA%93">获取模板库某个模板标题下关键词库</a>
|
||||
*/
|
||||
public Template getTemplateInLibrary(String id) throws WeixinException {
|
||||
final Map<String, String> params = new HashMap<String, String>(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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#3%E7%BB%84%E5%90%88%E6%A8%A1%E6%9D%BF%E5%B9%B6%E6%B7%BB%E5%8A%A0%E8%87%B3%E5%B8%90%E5%8F%B7%E4%B8%8B%E7%9A%84%E4%B8%AA%E4%BA%BA%E6%A8%A1%E6%9D%BF%E5%BA%93">组合模板并添加至帐号下的个人模板库</a>
|
||||
*/
|
||||
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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#4%E8%8E%B7%E5%8F%96%E5%B8%90%E5%8F%B7%E4%B8%8B%E5%B7%B2%E5%AD%98%E5%9C%A8%E7%9A%84%E6%A8%A1%E6%9D%BF%E5%88%97%E8%A1%A8">获取帐号下已存在的模板列表</a>
|
||||
*/
|
||||
public List<Template> 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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#5%E5%88%A0%E9%99%A4%E5%B8%90%E5%8F%B7%E4%B8%8B%E7%9A%84%E6%9F%90%E4%B8%AA%E6%A8%A1%E6%9D%BF">删除帐号下的某个模板</a>
|
||||
*/
|
||||
public void deleteTemplate(String id) throws WeixinException {
|
||||
final Map<String, String> params = new HashMap<String, String>(1);
|
||||
params.put("template_id", id);
|
||||
final WxaApiResult r = this.post(
|
||||
"wxopen_template_del",
|
||||
params,
|
||||
WxaApiResult.TYPE_REFERENCE
|
||||
);
|
||||
r.checkErrCode();
|
||||
}
|
||||
|
||||
private Map<String, Integer> toPageableRequestParams(final Pageable pageable) {
|
||||
final Map<String, Integer> params = new HashMap<String, Integer>(2);
|
||||
params.put("offset", pageable.getOffset());
|
||||
params.put("count", pageable.getPageSize());
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<TemplateListResult> TYPE_REFERENCE
|
||||
= new TypeReference<TemplateListResult>() {
|
||||
};
|
||||
|
||||
private List<Template> list;
|
||||
private Long totalCount;
|
||||
|
||||
public List<Template> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<Template> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public Long getTotalCount() {
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
public void setTotalCount(Long totalCount) {
|
||||
this.totalCount = totalCount;
|
||||
}
|
||||
|
||||
public Pagedata<Template> toPage(Pageable pageable) throws WeixinException {
|
||||
this.checkErrCode();
|
||||
|
||||
final Pagedata<Template> page = new Pagedata<Template>(
|
||||
pageable,
|
||||
this.getTotalCount().intValue(),
|
||||
this.getList()
|
||||
);
|
||||
return page;
|
||||
}
|
||||
|
||||
public List<Template> toList() throws WeixinException {
|
||||
this.checkErrCode();
|
||||
|
||||
return this.getList();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#%E5%8F%91%E9%80%81%E6%A8%A1%E7%89%88%E6%B6%88%E6%81%AF">发送模版消息</a>
|
||||
* @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 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/notice.html#%E5%8F%91%E9%80%81%E6%A8%A1%E7%89%88%E6%B6%88%E6%81%AF">发送模版消息</a>
|
||||
*/
|
||||
public void sendTemplateMessage(
|
||||
final String toUser,
|
||||
final String templateId,
|
||||
final String page,
|
||||
final String formId,
|
||||
final Map<String, String> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String, TemplateMessageData> data;
|
||||
private String emphasisKeyword;
|
||||
|
||||
public TemplateMessageParameter() {
|
||||
}
|
||||
|
||||
public TemplateMessageParameter(
|
||||
String toUser,
|
||||
String templateId,
|
||||
String page,
|
||||
String formId,
|
||||
Map<String, String> data,
|
||||
String emphasisKeyword
|
||||
) {
|
||||
this.toUser = toUser;
|
||||
this.templateId = templateId;
|
||||
this.page = page;
|
||||
this.formId = formId;
|
||||
if (data != null) {
|
||||
this.data = new HashMap<String, TemplateMessageData>(data.size());
|
||||
for (Map.Entry<String, String> 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<String, TemplateMessageData> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Map<String, TemplateMessageData> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<TemplateResult> TYPE_REFERENCE
|
||||
= new TypeReference<TemplateResult>() {
|
||||
};
|
||||
|
||||
private String id;
|
||||
private String title;
|
||||
private List<Keyword> 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<Keyword> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
@JSONField(name = "keyword_list")
|
||||
public void setKeywords(List<Keyword> keywords) {
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
public Template toTemplate() throws WeixinException {
|
||||
this.checkErrCode();
|
||||
|
||||
return new Template(id, title, keywords);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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> T post(String key, Object params, TypeReference<T> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<WxaApiResult> TYPE_REFERENCE
|
||||
= new TypeReference<WxaApiResult>() {
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
/**
|
||||
* APIs.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
package com.foxinmy.weixin4j.wxa.api;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回会话密钥
|
||||
* <p>注意:会话密钥 session_key 是对用户数据进行
|
||||
* <a href="https://developers.weixin.qq.com/miniprogram/dev/api/signature.html#wxchecksessionobject">加密签名</a>
|
||||
* 的密钥。
|
||||
* 为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,
|
||||
* 也不应该对外提供这个密钥。</p>
|
||||
*
|
||||
* @return 会话密钥
|
||||
* @see WXBizDataCrypt
|
||||
*/
|
||||
public String getSessionKey() {
|
||||
return sessionKey;
|
||||
}
|
||||
@ -29,6 +44,15 @@ public class Session implements Serializable {
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回UnionID
|
||||
* <p>
|
||||
* UnionID 只在满足一定条件的情况下返回。
|
||||
* 具体参看 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/unionID.html">UnionID机制说明</a>。
|
||||
* </p>
|
||||
*
|
||||
* @return UnionID
|
||||
*/
|
||||
public String getUnionId() {
|
||||
return unionId;
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
/**
|
||||
* Models.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
package com.foxinmy.weixin4j.wxa.model;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Keyword> keywords;
|
||||
private String content;
|
||||
private String example;
|
||||
|
||||
public Template() {
|
||||
}
|
||||
|
||||
public Template(String id, String title, List<Keyword> 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<Keyword> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
public void setKeywords(List<Keyword> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Models for {@link com.foxinmy.weixin4j.wxa.api.TemplateApi}.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
package com.foxinmy.weixin4j.wxa.model.template;
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String, TemplateMessageData> data = new LinkedHashMap<String, TemplateMessageData>();
|
||||
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\":"));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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": "咖啡"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user