Skip to content

添加一次性订阅消息接口。 #436

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
1. 微信支付Demo:[码云](http://gitee.com/binary/weixin-java-pay-demo)、[GitHub](http://github.com/binarywang/weixin-java-pay-demo)
1. 企业号/企业微信Demo:[码云](http://gitee.com/binary/weixin-java-cp-demo)、[GitHub](http://github.com/binarywang/weixin-java-cp-demo)
1. 微信小程序Demo:[码云](http://gitee.com/binary/weixin-java-miniapp-demo)、[GitHub](http://github.com/binarywang/weixin-java-miniapp-demo)
1. 开放平台Demo:[码云](http://gitee.com/binary/weixin-java-open-demo)、[GitHub](http://github.com/binarywang/weixin-java-open-demo)
1. 开放平台Demo:[码云](http://gitee.com/binary/weixin-java-open-demo)、[GitHub](http://github.com/Wechat-Group/weixin-java-open-demo)
1. 公众号Demo:
- 使用Spring MVC实现的公众号Demo:[码云](http://gitee.com/binary/weixin-java-mp-demo)、[GitHub](http://github.com/binarywang/weixin-java-mp-demo)
- 使用Spring Boot实现的公众号Demo:[码云](http://gitee.com/binary/weixin-java-mp-demo-springboot)、[GitHub](http://github.com/binarywang/weixin-java-mp-demo-springboot)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public interface WxMpConfigStorage {

String getAesKey();

String getTemplateId();

long getExpiresTime();

String getOauth2redirectUri();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
protected volatile String appId;
protected volatile String secret;
protected volatile String token;
protected volatile String templateId;
protected volatile String accessToken;
protected volatile String aesKey;
protected volatile long expiresTime;
Expand Down Expand Up @@ -173,6 +174,15 @@ public void setToken(String token) {
this.token = token;
}

@Override
public String getTemplateId() {
return this.templateId;
}

public void setTemplateId(String templateId) {
this.templateId = templateId;
}

@Override
public long getExpiresTime() {
return this.expiresTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,13 @@ public interface WxMpService {
*/
WxMpTemplateMsgService getTemplateMsgService();

/**
* 返回一次性订阅消息相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMpSubscribeMsgService
*/
WxMpSubscribeMsgService getSubscribeMsgService();

/**
* 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package me.chanjar.weixin.mp.api;

import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage;

/**
* <pre>
* 一次性订阅消息接口
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB
* </pre>
*
* @author Mklaus
* @date 2018-01-22 上午11:07
*/
public interface WxMpSubscribeMsgService {

/**
* <pre>
* 构造用户订阅一条模板消息授权的url连接
* 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB
* </pre>
*
* @param redirectURI 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode
* @param scene 重定向后会带上scene参数,开发者可以填0-10000的整形值,用来标识订阅场景值
* @param reserved 用于保持请求和回调的状态,授权请后原样带回给第三方 (最多128字节,要求做urlencode)
* @return url
*/
String subscribeMsgAuthorizationUrl(String redirectURI, int scene, String reserved);

/**
* <pre>
* 发送一次性订阅消息
* 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB
* </pre>
*
* @return 消息Id
*/
boolean sendSubscribeMessage(WxMpSubscribeMessage message) throws WxErrorException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public abstract class WxMpServiceAbstractImpl<H, P> implements WxMpService, Requ
private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this);
private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this);
private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this);
private WxMpSubscribeMsgService subscribeMsgService = new WxMpSubscribeMsgServiceImpl(this);
private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this);
private WxMpShakeService shakeService = new WxMpShakeServiceImpl(this);
private WxMpMemberCardService memberCardService = new WxMpMemberCardServiceImpl(this);
Expand Down Expand Up @@ -375,6 +376,11 @@ public WxMpTemplateMsgService getTemplateMsgService() {
return this.templateMsgService;
}

@Override
public WxMpSubscribeMsgService getSubscribeMsgService() {
return this.subscribeMsgService;
}

@Override
public WxMpDeviceService getDeviceService() {
return this.deviceService;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package me.chanjar.weixin.mp.api.impl;

import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.http.URIUtil;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpSubscribeMsgService;
import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage;

/**
* @author Mklaus
* @date 2018-01-22 上午11:19
*/
public class WxMpSubscribeMsgServiceImpl implements WxMpSubscribeMsgService {
private static final String SUBSCRIBE_MESSAGE_AUTHORIZE_URL =
"https://mp.weixin.qq.com/mp/subscribemsg?action=get_confirm&appid=%s&scene=%d&template_id=%s&redirect_url=%s&reserved=%s#wechat_redirect";
private static final String SEND_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/subscribe";


private WxMpService wxMpService;

public WxMpSubscribeMsgServiceImpl(WxMpService wxMpService) {
this.wxMpService = wxMpService;
}

@Override
public String subscribeMsgAuthorizationUrl(String redirectURI, int scene, String reserved) {
WxMpConfigStorage storage = this.wxMpService.getWxMpConfigStorage();
return String.format(SUBSCRIBE_MESSAGE_AUTHORIZE_URL,
storage.getAppId(), scene, storage.getTemplateId(), URIUtil.encodeURIComponent(redirectURI), reserved);
}

@Override
public boolean sendSubscribeMessage(WxMpSubscribeMessage message) throws WxErrorException {
if (message.getTemplateId() == null) {
message.setTemplateId(this.wxMpService.getWxMpConfigStorage().getTemplateId());
}

String responseContent = this.wxMpService.post(SEND_MESSAGE_URL, message.toJson());
return responseContent != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package me.chanjar.weixin.mp.bean.subscribe;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;

/**
* @author Mklaus
* @date 2018-01-22 下午12:18
*/
@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class WxMpSubscribeMessage {

/**
* 接收者openid.
*/
private String toUser;

/**
* 模板ID.
*/
private String templateId;

/**
* 模板跳转链接.
* <pre>
* url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。
* 开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
* </pre>
*/
private String url;

/**
* 跳小程序所需数据,不需跳小程序可不用传该数据.
*
* @see #url
*/
private WxMpTemplateMessage.MiniProgram miniProgram;

/**
* 订阅场景值
*/
private String scene;

/**
* 消息标题 (15字以内)
*/
private String title;

/**
* 消息内容文本 (200字以内)
*/
private String contentValue;

/**
* 消息内容文本颜色
*/
private String contentColor;


public String toJson() {
return WxMpGsonBuilder.INSTANCE.create().toJson(this);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult;
import me.chanjar.weixin.mp.bean.material.*;
import me.chanjar.weixin.mp.bean.result.*;
import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;

Expand All @@ -30,6 +31,7 @@ public class WxMpGsonBuilder {
INSTANCE.registerTypeAdapter(WxMpMassUploadResult.class, new WxMpMassUploadResultAdapter());
INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter());
INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter());
INSTANCE.registerTypeAdapter(WxMpSubscribeMessage.class, new WxMpSubscribeMessageGsonAdapter());
INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter());
INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter());
INSTANCE.registerTypeAdapter(WxDataCubeUserSummary.class, new WxMpUserSummaryGsonAdapter());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package me.chanjar.weixin.mp.util.json;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage;

import java.lang.reflect.Type;

/**
* @author Mklaus
* @date 2018-01-22 下午12:31
*/
public class WxMpSubscribeMessageGsonAdapter implements JsonSerializer<WxMpSubscribeMessage> {

@Override
public JsonElement serialize(WxMpSubscribeMessage message, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject messageJson = new JsonObject();
messageJson.addProperty("touser", message.getToUser());
messageJson.addProperty("template_id", message.getTemplateId());

if (message.getUrl() != null) {
messageJson.addProperty("url", message.getUrl());
}

if (message.getMiniProgram() != null) {
JsonObject miniProgramJson = new JsonObject();
miniProgramJson.addProperty("appid", message.getMiniProgram().getAppid());
miniProgramJson.addProperty("pagepath", message.getMiniProgram().getPagePath());
messageJson.add("miniprogram", miniProgramJson);
}

messageJson.addProperty("scene", message.getScene());
messageJson.addProperty("title", message.getTitle());

JsonObject data = new JsonObject();
messageJson.add("data", data);

JsonObject content = new JsonObject();
data.add("content", content);

if (message.getContentValue() != null) {
content.addProperty("value", message.getContentValue());
}

if (message.getContentColor() != null) {
content.addProperty("color", message.getContentColor());
}

return messageJson;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package me.chanjar.weixin.mp.api.impl;

import com.google.inject.Inject;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.test.ApiTestModule;
import me.chanjar.weixin.mp.api.test.TestConfigStorage;
import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

/**
* @author Mklaus
* @date 2018-01-22 下午2:02
*/
@Guice(modules = ApiTestModule.class)
public class WxMpSubscribeMsgServiceImplTest {

@Inject
protected WxMpService wxService;

@Test
public void testSendSubscribeMessage() throws WxErrorException {
TestConfigStorage configStorage = (TestConfigStorage) this.wxService
.getWxMpConfigStorage();

WxMpSubscribeMessage message = WxMpSubscribeMessage.builder()
.title("weixin test")
.toUser(configStorage.getOpenid())
.scene("1000")
.contentColor("#FF0000")
.contentValue("Send subscribe message test")
.build();

try {
boolean send = this.wxService.getSubscribeMsgService().sendSubscribeMessage(message);
Assert.assertTrue(send);
} catch (WxErrorException e) {
// 当用户没有授权,获取之前的授权已使用。微信会返回错误代码 {"errcode":43101,"errmsg":"user refuse to accept the msg hint: [xxxxxxxxxxx]"}
if (e.getError().getErrorCode() != 43101) {
throw e;
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package me.chanjar.weixin.mp.bean.subscribe;

import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.testng.annotations.Test;
import static org.testng.AssertJUnit.assertEquals;

/**
* @author Mklaus
* @date 2018-01-22 下午1:41
*/
public class WxMpSubscribeMessageTest {
@Test
public void testToJson() {
String actual = "{" +
"\"touser\":\"OPENID\"," +
"\"template_id\":\"TEMPLATE_ID\"," +
"\"url\":\"URL\"," +
"\"miniprogram\":{" +
"\"appid\":\"xiaochengxuappid12345\"," +
"\"pagepath\":\"index?foo=bar\"" +
"}," +
"\"scene\":\"SCENE\"," +
"\"title\":\"TITLE\"," +
"\"data\":{" +
"\"content\":{" +
"\"value\":\"VALUE\"," +
"\"color\":\"COLOR\"" +
"}" +
"}" +
"}";

WxMpSubscribeMessage message = WxMpSubscribeMessage.builder()
.toUser("OPENID")
.templateId("TEMPLATE_ID")
.url("URL")
.miniProgram(new WxMpTemplateMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar"))
.scene("SCENE")
.title("TITLE")
.contentValue("VALUE")
.contentColor("COLOR")
.build();

assertEquals(message.toJson(), actual);

}
}
Loading