Merge pull request #144 from sutra/custommsg

Implement WXA custom message related APIs.
This commit is contained in:
jinyu 2018-05-29 15:19:55 +08:00 committed by GitHub
commit 2c244fb92b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 432 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,6 @@
/**
* Models for {@link com.foxinmy.weixin4j.wxa.api.CustomMessageApi}.
*
* @since 1.8
*/
package com.foxinmy.weixin4j.wxa.model.custommessage;

View File

@ -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=%

View File

@ -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\"}"));
}
}

View File

@ -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());
}
}