| @@ -23,14 +23,15 @@ public class WxConsts { | |||
| /////////////////////// | |||
| // 主动发送消息(即客服消息)的消息类型 | |||
| /////////////////////// | |||
| public static final String CUSTOM_MSG_TEXT = "text"; | |||
| public static final String CUSTOM_MSG_IMAGE = "image"; | |||
| public static final String CUSTOM_MSG_VOICE = "voice"; | |||
| public static final String CUSTOM_MSG_VIDEO = "video"; | |||
| public static final String CUSTOM_MSG_MUSIC = "music"; | |||
| public static final String CUSTOM_MSG_NEWS = "news"; | |||
| public static final String CUSTOM_MSG_FILE = "file"; | |||
| public static final String CUSTOM_MSG_WXCARD = "wxcard"; | |||
| public static final String CUSTOM_MSG_TEXT = "text";//文本消息 | |||
| public static final String CUSTOM_MSG_IMAGE = "image";//图片消息 | |||
| public static final String CUSTOM_MSG_VOICE = "voice";//语音消息 | |||
| public static final String CUSTOM_MSG_VIDEO = "video";//视频消息 | |||
| public static final String CUSTOM_MSG_MUSIC = "music";//音乐消息 | |||
| public static final String CUSTOM_MSG_NEWS = "news";//图文消息(点击跳转到外链) | |||
| public static final String CUSTOM_MSG_MPNEWS = "mpnews";//图文消息(点击跳转到图文消息页面) | |||
| public static final String CUSTOM_MSG_FILE = "file";//发送文件(CP专用) | |||
| public static final String CUSTOM_MSG_WXCARD = "wxcard";//卡券消息 | |||
| public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | |||
| public static final String CUSTOM_MSG_SAFE_NO = "0"; | |||
| public static final String CUSTOM_MSG_SAFE_YES = "1"; | |||
| @@ -193,7 +194,7 @@ public class WxConsts { | |||
| * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 | |||
| */ | |||
| public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; | |||
| /** | |||
| * 网页应用登录授权作用域 snsapi_login | |||
| */ | |||
| @@ -1,17 +1,16 @@ | |||
| package me.chanjar.weixin.mp.bean; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import java.io.Serializable; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| public class WxMpMaterialNews implements Serializable { | |||
| /** | |||
| * | |||
| */ | |||
| private static final long serialVersionUID = -3283203652013494976L; | |||
| private List<WxMpMaterialNewsArticle> articles = new ArrayList<>(); | |||
| public List<WxMpMaterialNewsArticle> getArticles() { | |||
| @@ -81,7 +80,6 @@ public class WxMpMaterialNews implements Serializable { | |||
| /** | |||
| * 点击图文消息跳转链接 | |||
| * @return | |||
| */ | |||
| private String url; | |||
| @@ -159,14 +157,12 @@ public class WxMpMaterialNews implements Serializable { | |||
| @Override | |||
| public String toString() { | |||
| return "WxMpMassNewsArticle [" + "thumbMediaId=" + this.thumbMediaId + "thumbUrl=" + this.thumbUrl + ", author=" + this.author + ", title=" + this.title + | |||
| ", contentSourceUrl=" + this.contentSourceUrl + ", content=" + this.content + ", digest=" + this.digest + | |||
| ", showCoverPic=" + this.showCoverPic +", url=" + this.url + "]"; | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||
| } | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return "WxMpMaterialNews [" + "articles=" + this.articles + "]"; | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| package me.chanjar.weixin.mp.bean.custombuilder; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | |||
| /** | |||
| * 图文消息builder | |||
| * <pre> | |||
| * 用法: | |||
| * WxMpKefuMessage m = WxMpKefuMessage.NEWS().mediaId("xxxxx").toUser(...).build(); | |||
| * </pre> | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public final class MpNewsBuilder extends BaseBuilder<MpNewsBuilder> { | |||
| private String mediaId; | |||
| public MpNewsBuilder() { | |||
| this.msgType = WxConsts.CUSTOM_MSG_MPNEWS; | |||
| } | |||
| public MpNewsBuilder mediaId(String mediaId) { | |||
| this.mediaId = mediaId; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMpKefuMessage build() { | |||
| WxMpKefuMessage m = super.build(); | |||
| m.setMpNewsMediaId(this.mediaId); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -1,16 +1,23 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.*; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import java.io.Serializable; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.ImageBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.MpNewsBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.MusicBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.NewsBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.TextBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.VideoBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.VoiceBuilder; | |||
| import me.chanjar.weixin.mp.bean.custombuilder.WxCardBuilder; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| /** | |||
| * 客服消息 | |||
| * @author chanjarster | |||
| * | |||
| * @author chanjarster | |||
| */ | |||
| public class WxMpKefuMessage implements Serializable { | |||
| private static final long serialVersionUID = -9196732086954365246L; | |||
| @@ -26,18 +33,85 @@ public class WxMpKefuMessage implements Serializable { | |||
| private String hqMusicUrl; | |||
| private String kfAccount; | |||
| private String cardId; | |||
| private String mpNewsMediaId; | |||
| private List<WxArticle> articles = new ArrayList<>(); | |||
| /** | |||
| * 获得文本消息builder | |||
| */ | |||
| public static TextBuilder TEXT() { | |||
| return new TextBuilder(); | |||
| } | |||
| /** | |||
| * 获得图片消息builder | |||
| */ | |||
| public static ImageBuilder IMAGE() { | |||
| return new ImageBuilder(); | |||
| } | |||
| /** | |||
| * 获得语音消息builder | |||
| */ | |||
| public static VoiceBuilder VOICE() { | |||
| return new VoiceBuilder(); | |||
| } | |||
| /** | |||
| * 获得视频消息builder | |||
| */ | |||
| public static VideoBuilder VIDEO() { | |||
| return new VideoBuilder(); | |||
| } | |||
| /** | |||
| * 获得音乐消息builder | |||
| */ | |||
| public static MusicBuilder MUSIC() { | |||
| return new MusicBuilder(); | |||
| } | |||
| /** | |||
| * 获得图文消息(点击跳转到外链)builder | |||
| */ | |||
| public static NewsBuilder NEWS() { | |||
| return new NewsBuilder(); | |||
| } | |||
| /** | |||
| * 获得图文消息(点击跳转到图文消息页面)builder | |||
| */ | |||
| public static MpNewsBuilder MPNEWS() { | |||
| return new MpNewsBuilder(); | |||
| } | |||
| /** | |||
| * 获得卡券消息builder | |||
| */ | |||
| public static WxCardBuilder WXCARD() { | |||
| return new WxCardBuilder(); | |||
| } | |||
| public String getToUser() { | |||
| return this.toUser; | |||
| } | |||
| public void setToUser(String toUser) { | |||
| this.toUser = toUser; | |||
| } | |||
| public String getMsgType() { | |||
| return this.msgType; | |||
| } | |||
| public String getMpNewsMediaId() { | |||
| return this.mpNewsMediaId; | |||
| } | |||
| public void setMpNewsMediaId(String mpNewsMediaId) { | |||
| this.mpNewsMediaId = mpNewsMediaId; | |||
| } | |||
| /** | |||
| * <pre> | |||
| * 请使用 | |||
| @@ -47,52 +121,68 @@ public class WxMpKefuMessage implements Serializable { | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MUSIC} | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO} | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS} | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MPNEWS} | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_WXCARD} | |||
| * </pre> | |||
| * | |||
| * @param msgType | |||
| */ | |||
| public void setMsgType(String msgType) { | |||
| this.msgType = msgType; | |||
| } | |||
| public String getContent() { | |||
| return this.content; | |||
| } | |||
| public void setContent(String content) { | |||
| this.content = content; | |||
| } | |||
| public String getMediaId() { | |||
| return this.mediaId; | |||
| } | |||
| public void setMediaId(String mediaId) { | |||
| this.mediaId = mediaId; | |||
| } | |||
| public String getThumbMediaId() { | |||
| return this.thumbMediaId; | |||
| } | |||
| public void setThumbMediaId(String thumbMediaId) { | |||
| this.thumbMediaId = thumbMediaId; | |||
| } | |||
| public String getTitle() { | |||
| return this.title; | |||
| } | |||
| public void setTitle(String title) { | |||
| this.title = title; | |||
| } | |||
| public String getDescription() { | |||
| return this.description; | |||
| } | |||
| public void setDescription(String description) { | |||
| this.description = description; | |||
| } | |||
| public String getMusicUrl() { | |||
| return this.musicUrl; | |||
| } | |||
| public void setMusicUrl(String musicUrl) { | |||
| this.musicUrl = musicUrl; | |||
| } | |||
| public String getHqMusicUrl() { | |||
| return this.hqMusicUrl; | |||
| } | |||
| public void setHqMusicUrl(String hqMusicUrl) { | |||
| this.hqMusicUrl = hqMusicUrl; | |||
| } | |||
| @@ -108,6 +198,7 @@ public class WxMpKefuMessage implements Serializable { | |||
| public List<WxArticle> getArticles() { | |||
| return this.articles; | |||
| } | |||
| public void setArticles(List<WxArticle> articles) { | |||
| this.articles = articles; | |||
| } | |||
| @@ -116,8 +207,15 @@ public class WxMpKefuMessage implements Serializable { | |||
| return WxMpGsonBuilder.INSTANCE.create().toJson(this); | |||
| } | |||
| public static class WxArticle { | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| public static class WxArticle { | |||
| private String title; | |||
| private String description; | |||
| private String url; | |||
| @@ -126,85 +224,34 @@ public class WxMpKefuMessage implements Serializable { | |||
| public String getTitle() { | |||
| return this.title; | |||
| } | |||
| public void setTitle(String title) { | |||
| this.title = title; | |||
| } | |||
| public String getDescription() { | |||
| return this.description; | |||
| } | |||
| public void setDescription(String description) { | |||
| this.description = description; | |||
| } | |||
| public String getUrl() { | |||
| return this.url; | |||
| } | |||
| public void setUrl(String url) { | |||
| this.url = url; | |||
| } | |||
| public String getPicUrl() { | |||
| return this.picUrl; | |||
| } | |||
| public void setPicUrl(String picUrl) { | |||
| this.picUrl = picUrl; | |||
| } | |||
| } | |||
| /** | |||
| * 获得文本消息builder | |||
| */ | |||
| public static TextBuilder TEXT() { | |||
| return new TextBuilder(); | |||
| } | |||
| /** | |||
| * 获得图片消息builder | |||
| */ | |||
| public static ImageBuilder IMAGE() { | |||
| return new ImageBuilder(); | |||
| } | |||
| /** | |||
| * 获得语音消息builder | |||
| */ | |||
| public static VoiceBuilder VOICE() { | |||
| return new VoiceBuilder(); | |||
| } | |||
| /** | |||
| * 获得视频消息builder | |||
| */ | |||
| public static VideoBuilder VIDEO() { | |||
| return new VideoBuilder(); | |||
| } | |||
| /** | |||
| * 获得音乐消息builder | |||
| */ | |||
| public static MusicBuilder MUSIC() { | |||
| return new MusicBuilder(); | |||
| } | |||
| /** | |||
| * 获得图文消息builder | |||
| */ | |||
| public static NewsBuilder NEWS() { | |||
| return new NewsBuilder(); | |||
| } | |||
| /** | |||
| * 获得卡券消息builder | |||
| */ | |||
| public static WxCardBuilder WXCARD() { | |||
| return new WxCardBuilder(); | |||
| } | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| } | |||
| @@ -75,6 +75,12 @@ public class WxMpKefuMessageGsonAdapter implements JsonSerializer<WxMpKefuMessag | |||
| messageJson.add("news", newsJsonObject); | |||
| } | |||
| if (WxConsts.CUSTOM_MSG_MPNEWS.equals(message.getMsgType())) { | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("media_id", message.getMpNewsMediaId()); | |||
| messageJson.add("mpnews", json); | |||
| } | |||
| if (WxConsts.CUSTOM_MSG_WXCARD.equals(message.getMsgType())) { | |||
| JsonObject wxcard = new JsonObject(); | |||
| wxcard.addProperty("card_id", message.getCardId()); | |||
| @@ -1,29 +1,21 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import java.io.File; | |||
| import java.util.Date; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.api.ApiTestModule; | |||
| import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage; | |||
| import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.*; | |||
| import org.joda.time.DateTime; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.DataProvider; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.api.ApiTestModule; | |||
| import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfInfo; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; | |||
| import java.io.File; | |||
| import java.util.Date; | |||
| /** | |||
| * 测试客服相关接口 | |||
| @@ -37,7 +29,18 @@ public class WxMpKefuServiceImplTest { | |||
| @Inject | |||
| protected WxMpServiceImpl wxService; | |||
| public void testSendCustomMessage() throws WxErrorException { | |||
| public void testSendKefuMpNewsMessage() throws WxErrorException { | |||
| WxXmlMpInMemoryConfigStorage configStorage = (WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpKefuMessage message = new WxMpKefuMessage(); | |||
| message.setMsgType(WxConsts.CUSTOM_MSG_MPNEWS); | |||
| message.setToUser(configStorage.getOpenid()); | |||
| message.setMpNewsMediaId("52R6dL2FxDpM9N1rCY3sYBqHwq-L7K_lz1sPI71idMg"); | |||
| this.wxService.getKefuService().sendKefuMessage(message); | |||
| } | |||
| public void testSendKefuMessage() throws WxErrorException { | |||
| WxXmlMpInMemoryConfigStorage configStorage = (WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpKefuMessage message = new WxMpKefuMessage(); | |||
| @@ -49,7 +52,7 @@ public class WxMpKefuServiceImplTest { | |||
| this.wxService.getKefuService().sendKefuMessage(message); | |||
| } | |||
| public void testSendCustomMessageWithKfAccount() throws WxErrorException { | |||
| public void testSendKefuMessageWithKfAccount() throws WxErrorException { | |||
| WxXmlMpInMemoryConfigStorage configStorage = (WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpKefuMessage message = new WxMpKefuMessage(); | |||
| @@ -98,41 +98,40 @@ public class WxMpMaterialServiceImplTest { | |||
| @Test(dependsOnMethods = {"testUploadMaterial"}) | |||
| public void testAddNews() throws WxErrorException { | |||
| // 单图文消息 | |||
| WxMpMaterialNews wxMpMaterialNewsSingle = new WxMpMaterialNews(); | |||
| WxMpMaterialNews.WxMpMaterialNewsArticle mpMaterialNewsArticleSingle = new WxMpMaterialNews.WxMpMaterialNewsArticle(); | |||
| mpMaterialNewsArticleSingle.setAuthor("author"); | |||
| mpMaterialNewsArticleSingle.setThumbMediaId(this.thumbMediaId); | |||
| mpMaterialNewsArticleSingle.setTitle("single title"); | |||
| mpMaterialNewsArticleSingle.setContent("single content"); | |||
| mpMaterialNewsArticleSingle.setContentSourceUrl("content url"); | |||
| mpMaterialNewsArticleSingle.setShowCoverPic(true); | |||
| mpMaterialNewsArticleSingle.setDigest("single news"); | |||
| wxMpMaterialNewsSingle.addArticle(mpMaterialNewsArticleSingle); | |||
| WxMpMaterialNews.WxMpMaterialNewsArticle article = new WxMpMaterialNews.WxMpMaterialNewsArticle(); | |||
| article.setAuthor("author"); | |||
| article.setThumbMediaId(this.thumbMediaId); | |||
| article.setTitle("single title"); | |||
| article.setContent("single content"); | |||
| article.setContentSourceUrl("content url"); | |||
| article.setShowCoverPic(true); | |||
| article.setDigest("single news"); | |||
| wxMpMaterialNewsSingle.addArticle(article); | |||
| // 多图文消息 | |||
| WxMpMaterialNews wxMpMaterialNewsMultiple = new WxMpMaterialNews(); | |||
| WxMpMaterialNews.WxMpMaterialNewsArticle wxMpMaterialNewsArticleMultiple1 = new WxMpMaterialNews.WxMpMaterialNewsArticle(); | |||
| wxMpMaterialNewsArticleMultiple1.setAuthor("author1"); | |||
| wxMpMaterialNewsArticleMultiple1.setThumbMediaId(this.thumbMediaId); | |||
| wxMpMaterialNewsArticleMultiple1.setTitle("multi title1"); | |||
| wxMpMaterialNewsArticleMultiple1.setContent("content 1"); | |||
| wxMpMaterialNewsArticleMultiple1.setContentSourceUrl("content url"); | |||
| wxMpMaterialNewsArticleMultiple1.setShowCoverPic(true); | |||
| wxMpMaterialNewsArticleMultiple1.setDigest(""); | |||
| WxMpMaterialNews.WxMpMaterialNewsArticle wxMpMaterialNewsArticleMultiple2 = new WxMpMaterialNews.WxMpMaterialNewsArticle(); | |||
| wxMpMaterialNewsArticleMultiple2.setAuthor("author2"); | |||
| wxMpMaterialNewsArticleMultiple2.setThumbMediaId(this.thumbMediaId); | |||
| wxMpMaterialNewsArticleMultiple2.setTitle("multi title2"); | |||
| wxMpMaterialNewsArticleMultiple2.setContent("content 2"); | |||
| wxMpMaterialNewsArticleMultiple2.setContentSourceUrl("content url"); | |||
| wxMpMaterialNewsArticleMultiple2.setShowCoverPic(true); | |||
| wxMpMaterialNewsArticleMultiple2.setDigest(""); | |||
| wxMpMaterialNewsMultiple.addArticle(wxMpMaterialNewsArticleMultiple1); | |||
| wxMpMaterialNewsMultiple.addArticle(wxMpMaterialNewsArticleMultiple2); | |||
| WxMpMaterialNews.WxMpMaterialNewsArticle article1 = new WxMpMaterialNews.WxMpMaterialNewsArticle(); | |||
| article1.setAuthor("author1"); | |||
| article1.setThumbMediaId(this.thumbMediaId); | |||
| article1.setTitle("multi title1"); | |||
| article1.setContent("content 1"); | |||
| article1.setContentSourceUrl("content url"); | |||
| article1.setShowCoverPic(true); | |||
| article1.setDigest(""); | |||
| WxMpMaterialNews.WxMpMaterialNewsArticle article2 = new WxMpMaterialNews.WxMpMaterialNewsArticle(); | |||
| article2.setAuthor("author2"); | |||
| article2.setThumbMediaId(this.thumbMediaId); | |||
| article2.setTitle("multi title2"); | |||
| article2.setContent("content 2"); | |||
| article2.setContentSourceUrl("content url"); | |||
| article2.setShowCoverPic(true); | |||
| article2.setDigest(""); | |||
| wxMpMaterialNewsMultiple.addArticle(article1); | |||
| wxMpMaterialNewsMultiple.addArticle(article2); | |||
| WxMpMaterialUploadResult resSingle = this.wxService.getMaterialService().materialNewsUpload(wxMpMaterialNewsSingle); | |||
| this.singleNewsMediaId = resSingle.getMediaId(); | |||
| @@ -268,6 +267,7 @@ public class WxMpMaterialServiceImplTest { | |||
| // 以下为media接口的测试 | |||
| private List<String> mediaIdsToDownload = new ArrayList<>(); | |||
| @Test(dataProvider="mediaFiles") | |||
| public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { | |||
| try(InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)){ | |||