diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDraftService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDraftService.java new file mode 100644 index 000000000..3e38410d5 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDraftService.java @@ -0,0 +1,127 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.draft.WxMpAddDraft; +import me.chanjar.weixin.mp.bean.draft.WxMpDraftInfo; +import me.chanjar.weixin.mp.bean.draft.WxMpDraftList; +import me.chanjar.weixin.mp.bean.draft.WxMpUpdateDraft; + +/** + * 微信 草稿箱 接口. + * + * @author dragon + * @date 2021-10-22 + */ +public interface WxMpDraftService { + + /** + * 新建草稿 - 只有默认必填参数 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Add_draft.html + *+ * + * @param title 标题 + * @param content 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS,涉及图片url必须来源 "上传图文消息内的图片获取URL"接口获取。外部图片url将被过滤。 + * @param thumbMediaId 图文消息的封面图片素材id(必须是永久MediaID) + * @throws WxErrorException . + */ + String addDraft(String title, String content, String thumbMediaId) throws WxErrorException; + + /** + * 新建草稿 - 完整参数 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Add_draft.html + *+ * + * @param addDraft 新建草稿信息 + * @throws WxErrorException . + */ + String addDraft(WxMpAddDraft addDraft) throws WxErrorException; + + /** + * 修改草稿 - 完整参数 + * 正常情况下调用成功时,errcode将为0。错误时微信会返回错误码等信息,请根据错误码查询错误信息 + *
+ * 请求地址: POST https://api.weixin.qq.com/cgi-bin/draft/update?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Update_draft.html + *+ * + * @param updateDraftInfo 修改草稿信息 + * @throws WxErrorException . + */ + Boolean updateDraft(WxMpUpdateDraft updateDraftInfo) throws WxErrorException; + + /** + * 获取草稿信息 + * + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/get?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Get_draft.html + *+ * + * @param mediaId 要获取的草稿的media_id + * @return 草稿信息 + * @throws WxErrorException . + */ + WxMpDraftInfo getDraft(String mediaId) throws WxErrorException; + + /** + * 删除草稿 + * 正常情况下调用成功时,errcode将为0。错误时微信会返回错误码等信息,请根据错误码查询错误信息。 + * 多次删除同一篇草稿,也返回 0. + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/delete?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Delete_draft.html + *+ * + * @param mediaId 要删除的草稿的media_id + * @throws WxErrorException . + */ + Boolean delDraft(String mediaId) throws WxErrorException; + + /** + * 获取草稿列表 + * + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/batchget?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Get_draft_list.html + *+ * + * @param offset 分页页数,从0开始 从全部素材的该偏移位置开始返回,0表示从第一个素材返回 + * @param count 每页数量 返回素材的数量,取值在1到20之间 + * @param noContent 1 表示不返回 content 字段,0 表示正常返回,默认为 0 + * @return 草稿信息列表 + * @throws WxErrorException . + */ + WxMpDraftList listDraft(int offset, int count, int noContent) throws WxErrorException; + + /** + * 获取草稿列表 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/batchget?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Get_draft_list.html + *+ * + * @param offset 分页页数,从0开始 从全部素材的该偏移位置开始返回,0表示从第一个素材返回 + * @param count 每页数量 返回素材的数量,取值在1到20之间 + * @return + * @throws WxErrorException + */ + WxMpDraftList listDraft(int offset, int count) throws WxErrorException; + + /** + * 获取草稿数量 + * 开发者可以根据本接口来获取草稿的总数。此接口只统计数量,不返回草稿的具体内容。 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/count?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Count_drafts.html + *+ * + * @return 草稿的总数 + * @throws WxErrorException . + */ + Long countDraft() throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpFreePublishService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpFreePublishService.java new file mode 100644 index 000000000..c69594279 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpFreePublishService.java @@ -0,0 +1,113 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishInfo; +import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishList; +import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishStatus; + +/** + * 微信 发布能力 接口. + * + * @author dragon + * @date 2021-10-23 + */ +public interface WxMpFreePublishService { + + /** + * 发布接口 - 只有默认必填参数 + * 开发者需要先将图文素材以草稿的形式保存(见“草稿箱/新建草稿”,如需从已保存的草稿中选择,见“草稿箱/获取草稿列表”),选择要发布的草稿 media_id 进行发布 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Publish.html + *+ * + * @param mediaId 要发布的草稿的media_id + * @throws WxErrorException . + */ + String submit(String mediaId) throws WxErrorException; + + /** + * 发布状态轮询接口 + * 开发者可以尝试通过下面的发布状态轮询接口获知发布情况。 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/freepublish/get?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Get_status.html + *+ * + * @param publishId 发布任务id + * @throws WxErrorException . + */ + WxMpFreePublishStatus getPushStatus(String publishId) throws WxErrorException; + + /** + * 删除发布 + * 发布成功之后,随时可以通过该接口删除。此操作不可逆,请谨慎操作。 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/freepublish/delete?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Delete_posts.html + *+ * + * @param articleId 成功发布时返回的 article_id + * @param index 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章 + * @throws WxErrorException . + */ + Boolean deletePush(String articleId, Integer index) throws WxErrorException; + + /** + * 删除发布 - 此条发布的所有内容,不指定文章编号 + * 发布成功之后,随时可以通过该接口删除。此操作不可逆,请谨慎操作。 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/freepublish/delete?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Delete_posts.html + *+ * + * @param articleId 成功发布时返回的 article_id + * @throws WxErrorException . + */ + Boolean deletePushAllArticle(String articleId) throws WxErrorException; + + /** + * 通过 article_id 获取已发布文章 + * 开发者可以通过 article_id 获取已发布的图文信息。 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/freepublish/getarticle?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Get_article_from_id.html + *+ * + * @param articleId 要获取的草稿的article_id + * @return 已发布文章信息 + * @throws WxErrorException . + */ + WxMpFreePublishInfo getArticleFromId(String articleId) throws WxErrorException; + + /** + * 获取成功发布列表 - 支持选择是否返回:图文消息的具体内容 + * + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/batchget?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Get_publication_records.html + *+ * + * @param offset 分页页数,从0开始 从全部素材的该偏移位置开始返回,0表示从第一个素材返回 + * @param count 每页数量 返回素材的数量,取值在1到20之间 + * @param noContent 1 表示不返回 content 字段,0 表示正常返回,默认为 0 + * @return 草稿信息列表 + * @throws WxErrorException . + */ + WxMpFreePublishList getPublicationRecords(int offset, int count, int noContent) throws WxErrorException; + + /** + * 获取成功发布列表 - 默认返回 图文消息的具体内容 + *
+ * 请求地址:POST https://api.weixin.qq.com/cgi-bin/draft/batchget?access_token=ACCESS_TOKEN + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Publish/Get_publication_records.html + *+ * + * @param offset 分页页数,从0开始 从全部素材的该偏移位置开始返回,0表示从第一个素材返回 + * @param count 每页数量 返回素材的数量,取值在1到20之间 + * @return + * @throws WxErrorException + */ + WxMpFreePublishList getPublicationRecords(int offset, int count) throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index 6b6e30b7f..fbe9e2d43 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -567,6 +567,20 @@ public interface WxMpService extends WxService { */ WxMpReimburseInvoiceService getReimburseInvoiceService(); + /** + * 返回草稿箱相关接口 + * + * @return WxMpDraftService + */ + WxMpDraftService getDraftService(); + + /** + * 返回发布能力接口 + * + * @return WxMpFreePublishService + */ + WxMpFreePublishService getFreePublishService(); + /** * . * @@ -818,4 +832,18 @@ public interface WxMpService extends WxService { * @param merchantInvoiceService the merchant invoice service */ void setMerchantInvoiceService(WxMpMerchantInvoiceService merchantInvoiceService); + + /** + * Sets draft service. + * + * @param draftService the draft service + */ + void setDraftService(WxMpDraftService draftService); + + /** + * Sets free publish service. + * + * @param freePublishService the free publish service + */ + void setFreePublishService(WxMpFreePublishService freePublishService); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java index 2b8d9bfd3..d11499bd4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java @@ -25,7 +25,11 @@ import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.common.util.DataUtils; import me.chanjar.weixin.common.util.RandomUtils; import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.http.*; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.URIUtil; import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.api.*; @@ -42,7 +46,16 @@ import java.io.IOException; import java.util.Map; import java.util.concurrent.locks.Lock; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.*; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.CLEAR_QUOTA_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.FETCH_SHORTEN_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GEN_SHORTEN_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_CALLBACK_IP_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_CURRENT_AUTOREPLY_INFO_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_TICKET_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.NETCHECK_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.QRCONNECT_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.SEMANTIC_SEMPROXY_SEARCH_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.SHORTURL_API_URL; /** * 基础实现类. @@ -146,6 +159,14 @@ public abstract class BaseWxMpServiceImpl
@@ -1374,6 +1377,90 @@ public interface WxMpApiUrl {
;
+ private final String prefix;
+ private final String path;
+
+ }
+
+ /**
+ * 草稿箱 能力:
+ * 新建草稿
+ * 获取草稿
+ * 删除草稿
+ * 修改草稿
+ * 获取草稿总数
+ * 获取草稿列表
+ * MP端开关(仅内测期间使用)- 上线后废弃,没实现,可以自己去公众号后台开启草稿箱
+ */
+ @AllArgsConstructor
+ @Getter
+ enum Draft implements WxMpApiUrl {
+
+ /**
+ * 新建草稿
+ */
+ ADD_DRAFT(API_DEFAULT_HOST_URL, "/cgi-bin/draft/add"),
+ /**
+ * 修改草稿
+ */
+ UPDATE_DRAFT(API_DEFAULT_HOST_URL, "/cgi-bin/draft/update"),
+ /**
+ * 获取草稿
+ */
+ GET_DRAFT(API_DEFAULT_HOST_URL, "/cgi-bin/draft/get"),
+ /**
+ * 删除草稿
+ */
+ DEL_DRAFT(API_DEFAULT_HOST_URL, "/cgi-bin/draft/delete"),
+ /**
+ * 获取草稿列表
+ */
+ LIST_DRAFT(API_DEFAULT_HOST_URL, "/cgi-bin/draft/batchget"),
+ /**
+ * 获取草稿总数
+ */
+ COUNT_DRAFT(API_DEFAULT_HOST_URL, "/cgi-bin/draft/count");
+
+ private final String prefix;
+ private final String path;
+
+ }
+
+ /**
+ * 发布能力:
+ * 发布接口
+ * 发布状态轮询接口
+ * 事件推送发布结果 -- 是回调,没实现
+ * 删除发布
+ * 通过 article_id 获取已发布文章
+ * 获取成功发布列表
+ */
+ @AllArgsConstructor
+ @Getter
+ enum FreePublish implements WxMpApiUrl {
+
+ /**
+ * 发布接口
+ */
+ SUBMIT(API_DEFAULT_HOST_URL, "/cgi-bin/freepublish/submit"),
+ /**
+ * 通过 article_id 获取已发布文章
+ */
+ GET_ARTICLE(API_DEFAULT_HOST_URL, "/cgi-bin/freepublish/getarticle"),
+ /**
+ * 发布状态轮询接口
+ */
+ GET_PUSH_STATUS(API_DEFAULT_HOST_URL, "/cgi-bin/freepublish/get"),
+ /**
+ * 删除发布
+ */
+ DEL_PUSH(API_DEFAULT_HOST_URL, "/cgi-bin/freepublish/delete"),
+ /**
+ * 获取成功发布列表
+ */
+ BATCH_GET(API_DEFAULT_HOST_URL, "/cgi-bin/freepublish/batchget")
+ ;
+
private final String prefix;
private final String path;
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java
new file mode 100644
index 000000000..193580a9f
--- /dev/null
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java
@@ -0,0 +1,127 @@
+package me.chanjar.weixin.mp.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.test.ApiTestModule;
+import me.chanjar.weixin.mp.bean.draft.WxMpAddDraft;
+import me.chanjar.weixin.mp.bean.draft.WxMpDraftArticles;
+import me.chanjar.weixin.mp.bean.draft.WxMpDraftInfo;
+import me.chanjar.weixin.mp.bean.draft.WxMpDraftList;
+import me.chanjar.weixin.mp.bean.draft.WxMpUpdateDraft;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * 草稿箱单元测试.
+ *
+ * @author dragon
+ * @date 2021-10-22
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxMpDraftServiceImplTest {
+
+ /**
+ * 1.先上传一个永久图片素材:me.chanjar.weixin.mp.api.impl.WxMpMaterialServiceImplTest.testUploadMaterial
+ * 2.后续图文需要设置一个永久素材id
+ */
+ final String thumbMediaId = "zUUtT8ZYeXzZ4slFbtnAkh7Yd-f45DbFoF9ERzVC6s4";
+
+ /**
+ * 新增草稿后返回的id,后续查询、修改、删除,获取等需要使用
+ */
+ final String mediaId = "zUUtT8ZYeXzZ4slFbtnAkpgGKyqnTsjtUvMdVBRWJVk";
+
+ @Inject
+ protected WxMpService wxService;
+
+ @Test
+ public void testAddDraft() throws WxErrorException {
+ // {"mediaId":"zUUtT8ZYeXzZ4slFbtnAkh7Yd-f45DbFoF9ERzVC6s4","url":"http://mmbiz.qpic.cn/mmbiz_jpg/fLtyChQRfH84IyicNUbGt3l3IlHxJRibSFz7Tky0ibmzKykzVbo9tZGYhXQGJ2npFtDPbvPhKYxBz6JxkYIibTmUicQ/0?wx_fmt=jpeg"}
+ this.wxService.getDraftService().addDraft("标题", "图文消息的具体内容", thumbMediaId);
+ // 【响应数据】:{"media_id":"zUUtT8ZYeXzZ4slFbtnAks-nZeGiPQmwvhShTh72CqM","item":[]}
+ }
+
+ @Test
+ public void testAddGuide_another() throws WxErrorException {
+ List draftArticleList = new ArrayList<>();
+ WxMpDraftArticles draftArticle = WxMpDraftArticles.builder()
+ .title("新建草稿-对象形式")
+ .author("dragon")
+ .digest("图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空")
+ .content("图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS")
+ .contentSourceUrl("https://github.com/Wechat-Group/WxJava")
+ .thumbMediaId(thumbMediaId)
+ // 显示封面、打开评论、所有人可评论
+ .showCoverPic(1).needOpenComment(1).onlyFansCanComment(0)
+ .build();
+ draftArticleList.add(draftArticle);
+
+ WxMpAddDraft addDraft = WxMpAddDraft.builder().articles(draftArticleList).build();
+ String mediaId = this.wxService.getDraftService().addDraft(addDraft);
+ // 【响应数据】:{"media_id":"zUUtT8ZYeXzZ4slFbtnAkpgGKyqnTsjtUvMdVBRWJVk","item":[]}
+ assertThat(mediaId).isNotNull();
+ }
+
+ @Test
+ public void testGetDraft() throws WxErrorException {
+ final WxMpDraftInfo draftInfo = this.wxService.getDraftService().getDraft(mediaId);
+ assertThat(draftInfo).isNotNull();
+ // 【响应数据】:{"news_item":[{"title":"标题","author":"","digest":"图文消息的具体内容","content":"图文消息的具体内容","content_source_url":"","thumb_media_id":"zUUtT8ZYeXzZ4slFbtnAkh7Yd-f45DbFoF9ERzVC6s4","show_cover_pic":1,"url":"http:\/\/mp.weixin.qq.com\/s?__biz=Mzk0OTI5MzM1OQ==&mid=100000006&idx=1&sn=89903965ae5ebd6014903c7c5ca34daa&chksm=435bd946742c5050d18da32289904db5ede8bbd157d181438231a1762b85030419b3c0ed4c00#rd","thumb_url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/fLtyChQRfH84IyicNUbGt3l3IlHxJRibSFz7Tky0ibmzKykzVbo9tZGYhXQGJ2npFtDPbvPhKYxBz6JxkYIibTmUicQ\/0?wx_fmt=jpeg","need_open_comment":0,"only_fans_can_comment":0}],"create_time":1634886802,"update_time":1634886802}
+ }
+
+ @Test
+ public void testUpdateDraft() throws WxErrorException {
+ WxMpDraftArticles draftArticles = WxMpDraftArticles.builder()
+ .title("新标题").content("新图文消息的具体内容").thumbMediaId(thumbMediaId).build();
+ WxMpUpdateDraft updateDraft = WxMpUpdateDraft.builder()
+ .mediaId(mediaId)
+ .index(0)
+ .articles(draftArticles)
+ .build();
+ Boolean updateDraftResult = this.wxService.getDraftService().updateDraft(updateDraft);
+ // assertThat(updateDraftResult).isTrue();
+ assertThat(updateDraftResult).isTrue();
+ }
+
+ @Test
+ public void testDelDraft() throws WxErrorException {
+ Boolean delDraftResult = this.wxService.getDraftService().delDraft(mediaId);
+ // 【响应数据】:{"errcode":0,"errmsg":"ok"}
+ assertThat(delDraftResult).isTrue();
+ }
+
+ @Test
+ public void testListDraft() throws WxErrorException {
+ WxMpDraftList draftList = this.wxService.getDraftService().listDraft(0, 10);
+ /*
+ 【响应数据】:{"item":[{"media_id":"zUUtT8ZYeXzZ4slFbtnAks-nZeGiPQmwvhShTh72CqM",
+ "content":{
+ "news_item":
+ [
+ {"title":"标题","author":"","digest":"图文消息的具体内容","content":"图文消息的具体内容",
+ "content_source_url":"","thumb_media_id":"zUUtT8ZYeXzZ4slFbtnAkh7Yd-f45DbFoF9ERzVC6s4",
+ "show_cover_pic":1,"url":"http:\/\/mp.weixin.qq.com\/s?__biz=Mzk0OTI5MzM1?wx_fmt=jpeg",
+ "need_open_comment":0,"only_fans_can_comment":0}],
+ "create_time":1634886802,"update_time":1634886802},"update_time":1634886802}
+ ]
+ ,"total_count":1,"item_count":1}
+
+ */
+ assertThat(draftList).isNotNull();
+ }
+
+ @Test
+ public void testCountDraft() throws WxErrorException {
+ Long countDraft = this.wxService.getDraftService().countDraft();
+ // 【响应数据】:{"total_count":1}
+ assertThat(countDraft).isNotNull();
+ }
+
+}
+
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpFreePublishServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpFreePublishServiceImplTest.java
new file mode 100644
index 000000000..ff5cd0e5d
--- /dev/null
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpFreePublishServiceImplTest.java
@@ -0,0 +1,109 @@
+package me.chanjar.weixin.mp.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.test.ApiTestModule;
+import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishInfo;
+import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishList;
+import me.chanjar.weixin.mp.bean.freepublish.WxMpFreePublishStatus;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * 发布能力-单元测试.
+ *
+ * @author dragon
+ * @date 2021-10-23
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxMpFreePublishServiceImplTest {
+
+ /**
+ * 新增草稿后返回的id,发布需要使用
+ */
+ final String mediaId = "HKVdzjkDfooMqBqJtvSs2EEeRAJaM33gJgkii_JDOHg";
+
+ /**
+ * 发布后的id,后续查询等需要使用
+ */
+ final String publishId = "2650177669";
+
+ /**
+ * 图文的 article_id,后续查询图文详情、删除发布内容 需要使用
+ * 要根据 publishId 来获取 article_id
+ *
+ * @see this.testGetPushStatus
+ */
+ final String articleId = "zjMKVd1g66BkEkpetwml4ElbDdniE8JeI2Ec324Sjqg";
+
+ @Inject
+ protected WxMpService wxService;
+
+ @Test
+ public void testSubmit() throws WxErrorException {
+ String submit = this.wxService.getFreePublishService().submit(mediaId);
+ assertThat(submit).isNotBlank();
+ // 【响应数据】:{"errcode":0,"errmsg":"ok","publish_id":2650177668}
+ }
+
+ @Test
+ public void testGetPushStatus() throws WxErrorException {
+ WxMpFreePublishStatus pushStatus = this.wxService.getFreePublishService().getPushStatus(publishId);
+ assertThat(pushStatus).isNotNull();
+ // 【响应数据】:{"publish_id":2650177668,"publish_status":0,"article_id":"zjMKVd1g66BkEkpetwml4J-4gNf4I1nsh-B-r_inemw",
+ // "article_detail":{"count":1,"item":
+ // [{"idx":1,"article_url":
+ // "https://mp.weixin.qq.com/s?__biz=MzAwMTE2MzA1xxxxxxxxxx"
+ // }]},"fail_idx":[]}
+ // article_url -> 已发布内容可被自定义菜单、自动回复、话题引用,也可用于公开传播
+ }
+
+ @Test
+ public void testGetArticleFromId() throws WxErrorException {
+ WxMpFreePublishInfo articleFromId = this.wxService.getFreePublishService().getArticleFromId(articleId);
+ assertThat(articleFromId).isNotNull();
+ /* 【响应数据】:{"news_item":[{"title":"欢迎你加入啊~ 这是我的第一条文字消息草稿","author":"","digest":"","content":"欢迎你加入啊~ 这是我的第一条文字消息草稿",
+ "content_source_url":"","thumb_media_id":"","show_cover_pic":0,"url":"http:\/\/mp.weixin.qq
+ .com\/s?__biz=MzAwMTE2MzA1Mg==&mid=2650177668","thumb_url":"","need_open_comment":1,"only_fans_can_comment":1,"is_deleted":false}],
+ "create_time":1634961670,"update_time":1634961672}
+ */
+ }
+
+ @Test
+ public void testDelPush() throws WxErrorException {
+ Boolean deletePush = this.wxService.getFreePublishService().deletePush(articleId, 0);
+ // 【响应数据】:{"errcode":0,"errmsg":"ok"}
+ assertThat(deletePush).isTrue();
+ }
+
+ @Test
+ public void testDeletePushAllArticle() throws WxErrorException {
+ Boolean deletePush = this.wxService.getFreePublishService().deletePushAllArticle(articleId);
+ // 【响应数据】:{"errcode":0,"errmsg":"ok"}
+ assertThat(deletePush).isTrue();
+ }
+
+ @Test
+ public void testGetPublicationRecords() throws WxErrorException {
+ WxMpFreePublishList publicationRecords = this.wxService.getFreePublishService().getPublicationRecords(0, 10, 0);
+ /*
+ 【响应数据】:
+ {"item":[{"article_id":"zjMKVd1g66BkEkpetwml4BOSzatuEYNY3TFhCc0kSIE","content":{"news_item":[
+ {"title":"新建草稿-对象形式","author":"dragon","digest":"图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空",
+ "content":"图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS",
+ "content_source_url":"https:\/\/github.com\/Wechat-Group\/WxJava","thumb_media_id":"HKVdzjkDfooMqBqJtvSs2Ajz2v6L_vtGhyyr_mqKcPU",
+ "show_cover_pic":1,"url":"http:\/\/mp.weixin.qq.com\/s?__biz=MzAwMTE2MzA1Mg==&mid=26501776710e5adb91#rd",
+ "thumb_url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/0QSAUfroWrUmxHthQ\/0?wx_fmt=jpeg",
+ "need_open_comment":1,"only_fans_can_comment":0,"is_deleted":false}],
+ "create_time":1634976306,"update_time":1634976318}}
+ ]
+ ,"total_count":1,"item_count":1}
+ */
+ assertThat(publicationRecords).isNotNull();
+ }
+
+}
+