Merge pull request #144 from sutra/custommsg
Implement WXA custom message related APIs.
This commit is contained in:
commit
2c244fb92b
@ -7,6 +7,7 @@ import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.mp.token.WeixinTokenCreator;
|
||||
import com.foxinmy.weixin4j.token.TokenCreator;
|
||||
import com.foxinmy.weixin4j.token.TokenManager;
|
||||
import com.foxinmy.weixin4j.wxa.api.CustomMessageApi;
|
||||
import com.foxinmy.weixin4j.wxa.api.LoginApi;
|
||||
import com.foxinmy.weixin4j.wxa.api.QrCodeApi;
|
||||
import com.foxinmy.weixin4j.wxa.api.TemplateApi;
|
||||
@ -23,6 +24,7 @@ public class WXAFacade {
|
||||
private final QrCodeApi qrCodeApi;
|
||||
private final TemplateApi templateApi;
|
||||
private final TemplateMessageApi templateMessageApi;
|
||||
private final CustomMessageApi customMessageApi;
|
||||
|
||||
/**
|
||||
* Constructs {@link WXAFacade} using {@link FileCacheStorager}.
|
||||
@ -81,6 +83,7 @@ public class WXAFacade {
|
||||
this.qrCodeApi = new QrCodeApi(tokenManager);
|
||||
this.templateApi = new TemplateApi(tokenManager);
|
||||
this.templateMessageApi = new TemplateMessageApi(tokenManager);
|
||||
this.customMessageApi = new CustomMessageApi(tokenManager);
|
||||
}
|
||||
|
||||
public LoginApi getLoginApi() {
|
||||
@ -99,4 +102,8 @@ public class WXAFacade {
|
||||
return templateMessageApi;
|
||||
}
|
||||
|
||||
public CustomMessageApi getCustomMessageApi() {
|
||||
return customMessageApi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package com.foxinmy.weixin4j.wxa.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
import com.foxinmy.weixin4j.wxa.model.custommessage.Command;
|
||||
import com.foxinmy.weixin4j.wxa.model.custommessage.CustomMessage;
|
||||
|
||||
/**
|
||||
* Adapters for {@link CustomMessageApi}.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
final class CustomMessageAdapters {
|
||||
|
||||
private CustomMessageAdapters() {
|
||||
}
|
||||
|
||||
public static Map<String, Object> toMap(CustomMessage customMessage) {
|
||||
final NotifyTuple tuple = customMessage.getTuple();
|
||||
final String msgType = tuple.getMessageType();
|
||||
|
||||
final Map<String, Object> params = new HashMap<String, Object>(3);
|
||||
params.put("touser", customMessage.getToUser());
|
||||
params.put("msgtype", msgType);
|
||||
params.put(msgType, tuple);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
public static Map<String, String> toMap(String toUser, Command command) {
|
||||
final Map<String, String> params = new HashMap<String, String>(2);
|
||||
params.put("touser", toUser);
|
||||
params.put("command", command.toString());
|
||||
return params;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.foxinmy.weixin4j.wxa.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.token.TokenManager;
|
||||
import com.foxinmy.weixin4j.wxa.model.custommessage.Command;
|
||||
import com.foxinmy.weixin4j.wxa.model.custommessage.CustomMessage;
|
||||
|
||||
/**
|
||||
* 客服消息。
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public class CustomMessageApi extends TokenManagerApi {
|
||||
|
||||
public CustomMessageApi(TokenManager tokenManager) {
|
||||
super(tokenManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送客服消息。
|
||||
*
|
||||
* <p>
|
||||
* 当用户和小程序客服产生特定动作的交互时(具体动作列表请见下方说明),
|
||||
* 微信将会把消息数据推送给开发者,开发者可以在一段时间内(目前修改为48小时)调用客服接口,
|
||||
* 通过POST一个JSON数据包来发送消息给普通用户。
|
||||
* 此接口主要用于客服等有人工消息处理环节的功能,方便开发者为用户提供更加优质的服务。
|
||||
* </p>
|
||||
*
|
||||
* @param customMessage the {@link CustomMessage}.
|
||||
* @throws WeixinException indicates getting access token failed, or sending custom message failed.
|
||||
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api/custommsg/conversation.html">发送客服消息</a>
|
||||
*/
|
||||
public void sendCustomMessage(final CustomMessage customMessage)
|
||||
throws WeixinException {
|
||||
final Map<String, Object> params = CustomMessageAdapters.toMap(customMessage);
|
||||
final WxaApiResult r = this.post("message_custom_send",
|
||||
params, WxaApiResult.TYPE_REFERENCE);
|
||||
r.checkErrCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 客服输入状态。
|
||||
*
|
||||
* <p>开发者可通过调用“客服输入状态接口”,返回客服当前输入状态给用户。</p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>此接口需要客服消息接口权限。</li>
|
||||
* <li>如果不满足发送客服消息的触发条件,则无法下发输入状态。</li>
|
||||
* <li>下发输入状态,需要客服之前30秒内跟用户有过消息交互。</li>
|
||||
* <li>在输入状态中(持续15s),不可重复下发输入态。</li>
|
||||
* <li>在输入状态中,如果向用户下发消息,会同时取消输入状态。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param toUser 普通用户(openid)
|
||||
* @param command "Typing":对用户下发“正在输入"状态;"CancelTyping":取消对用户的”正在输入"状态
|
||||
* @throws WeixinException indicates getting access token failed, or sending typing status failed.
|
||||
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api/custommsg/typing.html">客服输入状态</a>
|
||||
*/
|
||||
public void typingCustomMessage(String toUser, Command command)
|
||||
throws WeixinException {
|
||||
final Map<String, String> params = CustomMessageAdapters.toMap(toUser, command);
|
||||
final WxaApiResult r = this.post("message_custom_typing",
|
||||
params, WxaApiResult.TYPE_REFERENCE);
|
||||
r.checkErrCode();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import com.foxinmy.weixin4j.wxa.api.CustomMessageApi;
|
||||
|
||||
/**
|
||||
* 客服输入状态命令。
|
||||
*
|
||||
* @see CustomMessageApi#typingCustomMessage(String, Command)
|
||||
* @since 1.8
|
||||
*/
|
||||
public enum Command {
|
||||
|
||||
/**
|
||||
* 对用户下发“正在输入"状态。
|
||||
*/
|
||||
TYPING("Typing"),
|
||||
|
||||
/**
|
||||
* 取消对用户的”正在输入"状态。
|
||||
*/
|
||||
CANCEL_TYPING("CancelTyping");
|
||||
|
||||
private final String value;
|
||||
|
||||
Command(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
|
||||
/**
|
||||
* 客服消息。
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public class CustomMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2018052901L;
|
||||
|
||||
private String toUser;
|
||||
|
||||
private NotifyTuple tuple;
|
||||
|
||||
public String getToUser() {
|
||||
return toUser;
|
||||
}
|
||||
|
||||
public void setToUser(String toUser) {
|
||||
this.toUser = toUser;
|
||||
}
|
||||
|
||||
public NotifyTuple getTuple() {
|
||||
return tuple;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link NotifyTuple}.
|
||||
*
|
||||
* @param tuple the tuple.
|
||||
* @see Text
|
||||
* @see Image
|
||||
* @see Link
|
||||
* @see MiniProgramPage
|
||||
*/
|
||||
public void setTuple(NotifyTuple tuple) {
|
||||
this.tuple = tuple;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* 图片消息。
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public class Image extends com.foxinmy.weixin4j.tuple.Image {
|
||||
|
||||
private static final long serialVersionUID = 2018052901L;
|
||||
|
||||
public Image(String mediaId) {
|
||||
super(mediaId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JSONField(serialize = false)
|
||||
public String getMessageType() {
|
||||
return super.getMessageType();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
|
||||
/**
|
||||
* 图文链接。
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public class Link implements NotifyTuple {
|
||||
|
||||
private static final long serialVersionUID = 2018052901L;
|
||||
|
||||
private String title;
|
||||
private String description;
|
||||
private String url;
|
||||
private String thumbUrl;
|
||||
|
||||
public Link() {
|
||||
}
|
||||
|
||||
public Link(
|
||||
String title,
|
||||
String description,
|
||||
String url,
|
||||
String thumbUrl
|
||||
) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.url = url;
|
||||
this.thumbUrl = thumbUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JSONField(serialize = false)
|
||||
public String getMessageType() {
|
||||
return "link";
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@JSONField(name = "thumb_url")
|
||||
public String getThumbUrl() {
|
||||
return thumbUrl;
|
||||
}
|
||||
|
||||
public void setThumbUrl(String thumbUrl) {
|
||||
this.thumbUrl = thumbUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
|
||||
/**
|
||||
* 小程序卡片。
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public class MiniProgramPage implements NotifyTuple {
|
||||
|
||||
private static final long serialVersionUID = 2018052901L;
|
||||
|
||||
private String title;
|
||||
private String pagePath;
|
||||
private String thumbMediaId;
|
||||
|
||||
public MiniProgramPage() {
|
||||
}
|
||||
|
||||
public MiniProgramPage(
|
||||
String title,
|
||||
String pagePath,
|
||||
String thumbMediaId
|
||||
) {
|
||||
this.title = title;
|
||||
this.pagePath = pagePath;
|
||||
this.thumbMediaId = thumbMediaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JSONField(serialize = false)
|
||||
public String getMessageType() {
|
||||
return "miniprogrampage";
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@JSONField(name = "pagepath")
|
||||
public String getPagePath() {
|
||||
return pagePath;
|
||||
}
|
||||
|
||||
public void setPagePath(String pagePath) {
|
||||
this.pagePath = pagePath;
|
||||
}
|
||||
|
||||
@JSONField(name = "thumb_media_id")
|
||||
public String getThumbMediaId() {
|
||||
return thumbMediaId;
|
||||
}
|
||||
|
||||
public void setThumbMediaId(String thumbMediaId) {
|
||||
this.thumbMediaId = thumbMediaId;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* 文本消息。
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public class Text extends com.foxinmy.weixin4j.tuple.Text {
|
||||
|
||||
private static final long serialVersionUID = 2018052901L;
|
||||
|
||||
public Text(String content) {
|
||||
super(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JSONField(serialize = false)
|
||||
public String getMessageType() {
|
||||
return super.getMessageType();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Models for {@link com.foxinmy.weixin4j.wxa.api.CustomMessageApi}.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
@ -36,3 +36,10 @@ 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
|
||||
|
||||
|
||||
# \u53d1\u9001\u5ba2\u670d\u6d88\u606f
|
||||
message_custom_send={api_cgi_url}/message/custom/send?access_token=%
|
||||
|
||||
# \u5ba2\u670d\u8f93\u5165\u72b6\u6001
|
||||
message_custom_typing={api_cgi_url}/message/custom/typing?access_token=%
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package com.foxinmy.weixin4j.wxa.api;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.foxinmy.weixin4j.wxa.model.custommessage.CustomMessage;
|
||||
import com.foxinmy.weixin4j.wxa.model.custommessage.Text;
|
||||
|
||||
public class CustomMessageAdaptersTest {
|
||||
|
||||
@Test
|
||||
public void testToMap() {
|
||||
CustomMessage customMessage = new CustomMessage();
|
||||
customMessage.setToUser("OPENID");
|
||||
customMessage.setTuple(new Text("Hello World"));
|
||||
|
||||
String json = JSON.toJSONString(CustomMessageAdapters.toMap(customMessage));
|
||||
assertTrue(json.contains("\"touser\":\"OPENID\""));
|
||||
assertTrue(json.contains("\"msgtype\":\"text\""));
|
||||
assertTrue(json.contains("\"text\":{\"content\":\"Hello World\"}"));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.foxinmy.weixin4j.wxa.model.custommessage;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CommandTest {
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
assertEquals("Typing", Command.TYPING.toString());
|
||||
assertEquals("CancelTyping", Command.CANCEL_TYPING.toString());
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user