From 537a9332c0bf29d549e7ed788a3ed183f2c054c1 Mon Sep 17 00:00:00 2001 From: Boris Date: Sun, 31 Oct 2021 23:25:27 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20#2361=20=E3=80=90=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E5=A2=9E=E5=8A=A0=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E6=9C=8B=E5=8F=8B=E5=9C=88=E7=9B=B8=E5=85=B3=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cp/api/WxCpExternalContactService.java | 108 ++++++++++++++++- .../impl/WxCpExternalContactServiceImpl.java | 112 +++++++++++++++++- .../cp/bean/external/WxCpAddMomentResult.java | 27 +++++ .../cp/bean/external/WxCpAddMomentTask.java | 36 ++++++ .../bean/external/WxCpGetMomentComments.java | 40 +++++++ .../external/WxCpGetMomentCustomerList.java | 30 +++++ .../cp/bean/external/WxCpGetMomentList.java | 30 +++++ .../external/WxCpGetMomentSendResult.java | 30 +++++ .../cp/bean/external/WxCpGetMomentTask.java | 41 +++++++ .../external/WxCpGetMomentTaskResult.java | 50 ++++++++ .../WxCpUserExternalTagGroupInfo.java | 4 +- .../cp/bean/external/moment/CustomerItem.java | 14 +++ .../external/moment/ExternalContactList.java | 13 ++ .../cp/bean/external/moment/MomentInfo.java | 28 +++++ .../cp/bean/external/moment/SenderList.java | 15 +++ .../cp/bean/external/moment/VisibleRange.java | 13 ++ .../weixin/cp/bean/external/msg/Link.java | 2 + .../weixin/cp/bean/external/msg/Location.java | 16 +++ .../weixin/cp/bean/external/msg/Video.java | 2 + .../cp/bean/message/WxCpTpXmlMessage.java | 33 ++++++ .../weixin/cp/constant/WxCpApiPathConsts.java | 9 ++ .../WxCpExternalContactServiceImplTest.java | 4 +- 22 files changed, 651 insertions(+), 6 deletions(-) create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentResult.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentCustomerList.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Location.java diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java index 32a4df416..d252fb831 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java @@ -3,8 +3,16 @@ package me.chanjar.weixin.cp.api; import lombok.NonNull; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.external.WxCpAddMomentResult; +import me.chanjar.weixin.cp.bean.external.WxCpAddMomentTask; import me.chanjar.weixin.cp.bean.external.WxCpContactWayInfo; import me.chanjar.weixin.cp.bean.external.WxCpContactWayResult; +import me.chanjar.weixin.cp.bean.external.WxCpGetMomentComments; +import me.chanjar.weixin.cp.bean.external.WxCpGetMomentCustomerList; +import me.chanjar.weixin.cp.bean.external.WxCpGetMomentList; +import me.chanjar.weixin.cp.bean.external.WxCpGetMomentSendResult; +import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTask; +import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTaskResult; import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplate; import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplateAddResult; import me.chanjar.weixin.cp.bean.external.WxCpUpdateRemarkRequest; @@ -181,7 +189,7 @@ public interface WxCpExternalContactService { * @return 该企业的外部联系人ID * @throws WxErrorException . */ - String unionidToExternalUserid(String unionid) throws WxErrorException; + String unionidToExternalUserid(@NotNull String unionid,String openid) throws WxErrorException; /** * 批量获取客户详情. @@ -586,6 +594,104 @@ public interface WxCpExternalContactService { */ WxCpBaseResp markTag(String userid, String externalUserid, String[] addTag, String[] removeTag) throws WxErrorException; + /** + *
+ *   企业和第三方应用可通过该接口创建客户朋友圈的发表任务。
+ *   https://open.work.weixin.qq.com/api/doc/90000/90135/95094
+   * 
+ * @param task + * @return wx cp add moment result + * @throws WxErrorException the wx error exception + */ + WxCpAddMomentResult addMomentTask(WxCpAddMomentTask task) throws WxErrorException; + + /** + *
+   * 由于发表任务的创建是异步执行的,应用需要再调用该接口以获取创建的结果。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/95094
+   * 
+ * @param jobId 异步任务id,最大长度为64字节,由创建发表内容到客户朋友圈任务接口获取 + * @return + * @throws WxErrorException + */ + WxCpGetMomentTaskResult getMomentTaskResult(String jobId) throws WxErrorException; + + /** + *
+   * 获取客户朋友圈全部的发表记录 获取企业全部的发表列表
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/93333
+   * 
+ * @param startTime 朋友圈记录开始时间。Unix时间戳 + * @param endTime 朋友圈记录结束时间。Unix时间戳 + * @param creator 朋友圈创建人的userid + * @param filterType 朋友圈类型。0:企业发表 1:个人发表 2:所有,包括个人创建以及企业创建,默认情况下为所有类型 + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @param limit 返回的最大记录数,整型,最大值100,默认值100,超过最大值时取默认值 + * @return + * @throws WxErrorException + */ + WxCpGetMomentList getMomentList(Long startTime, Long endTime, String creator, Integer filterType, + String cursor, Integer limit) throws WxErrorException; + + /** + *
+   * 获取客户朋友圈全部的发表记录 获取客户朋友圈企业发表的列表
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/93333
+   * 
+ * @param momentId 朋友圈id,仅支持企业发表的朋友圈id + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @param limit 返回的最大记录数,整型,最大值1000,默认值500,超过最大值时取默认值 + * @return + * @throws WxErrorException + */ + WxCpGetMomentTask getMomentTask(String momentId, String cursor, Integer limit) + throws WxErrorException; + + /** + *
+   * 获取客户朋友圈全部的发表记录 获取客户朋友圈发表时选择的可见范围
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/93333
+   * 
+ * @param momentId 朋友圈id + * @param userId 企业发表成员userid,如果是企业创建的朋友圈,可以通过获取客户朋友圈企业发表的 + * 列表获取已发表成员userid,如果是个人创建的朋友圈,创建人userid就是企业发表成员userid + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @param limit 返回的最大记录数,整型,最大值1000,默认值500,超过最大值时取默认值 + * @return + * @throws WxErrorException + */ + WxCpGetMomentCustomerList getMomentCustomerList(String momentId, String userId, + String cursor, Integer limit) throws WxErrorException; + + /** + *
+   * 获取客户朋友圈全部的发表记录 获取客户朋友圈发表后的可见客户列表
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/93333
+   * 
+ * @param momentId 朋友圈id + * @param userId 企业发表成员userid,如果是企业创建的朋友圈,可以通过获取客户朋友圈企业发表的列表获取已发表成员userid, + * 如果是个人创建的朋友圈,创建人userid就是企业发表成员userid + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @param limit 返回的最大记录数,整型,最大值5000,默认值3000,超过最大值时取默认值 + * @return + * @throws WxErrorException + */ + WxCpGetMomentSendResult getMomentSendResult(String momentId, String userId, + String cursor, Integer limit) throws WxErrorException; + + /** + *
+   * 获取客户朋友圈全部的发表记录 获取客户朋友圈的互动数据
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/93333
+   * 
+ * @param momentId 朋友圈id + * @param userId 企业发表成员userid,如果是企业创建的朋友圈,可以通过获取客户朋友圈企业发表的列表获取已发表成员userid, + * 如果是个人创建的朋友圈,创建人userid就是企业发表成员userid + * @return + * @throws WxErrorException + */ + WxCpGetMomentComments getMomentComments(String momentId, String userId) + throws WxErrorException; /** *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
index 598f4ac28..72c2c93b9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
@@ -12,8 +12,16 @@ import me.chanjar.weixin.common.util.json.GsonParser;
 import me.chanjar.weixin.cp.api.WxCpExternalContactService;
 import me.chanjar.weixin.cp.api.WxCpService;
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.WxCpAddMomentResult;
+import me.chanjar.weixin.cp.bean.external.WxCpAddMomentTask;
 import me.chanjar.weixin.cp.bean.external.WxCpContactWayInfo;
 import me.chanjar.weixin.cp.bean.external.WxCpContactWayResult;
+import me.chanjar.weixin.cp.bean.external.WxCpGetMomentComments;
+import me.chanjar.weixin.cp.bean.external.WxCpGetMomentCustomerList;
+import me.chanjar.weixin.cp.bean.external.WxCpGetMomentList;
+import me.chanjar.weixin.cp.bean.external.WxCpGetMomentSendResult;
+import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTask;
+import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTaskResult;
 import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplate;
 import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplateAddResult;
 import me.chanjar.weixin.cp.bean.external.WxCpUpdateRemarkRequest;
@@ -137,9 +145,12 @@ public class WxCpExternalContactServiceImpl implements WxCpExternalContactServic
   }
 
   @Override
-  public String unionidToExternalUserid(@NotNull String unionid) throws WxErrorException {
+  public String unionidToExternalUserid(@NotNull String unionid,String openid) throws WxErrorException {
     JsonObject json = new JsonObject();
     json.addProperty("unionid", unionid);
+    if(StringUtils.isNotEmpty(openid)){
+      json.addProperty("openid",openid);
+    }
     final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UNIONID_TO_EXTERNAL_USERID);
     String responseContent = this.mainService.post(url, json.toString());
     JsonObject tmpJson = GsonParser.parse(responseContent);
@@ -449,6 +460,105 @@ public class WxCpExternalContactServiceImpl implements WxCpExternalContactServic
     return WxCpBaseResp.fromJson(result);
   }
 
+  @Override
+  public WxCpAddMomentResult addMomentTask(WxCpAddMomentTask task) throws WxErrorException {
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(ADD_MOMENT_TASK);
+    final String result = this.mainService.post(url, task.toJson());
+    return WxCpAddMomentResult.fromJson(result);
+  }
+
+  @Override
+  public WxCpGetMomentTaskResult getMomentTaskResult(String jobId) throws WxErrorException {
+    String params = "&jobid=" + jobId;
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_MOMENT_TASK_RESULT);
+    final String result = this.mainService.get(url, params);
+    return WxCpGetMomentTaskResult.fromJson(result);
+  }
+
+  @Override
+  public WxCpGetMomentList getMomentList(Long startTime, Long endTime, String creator, Integer filterType,
+    String cursor, Integer limit) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("start_time", startTime);
+    json.addProperty("end_time", endTime);
+    if (!StringUtils.isEmpty(creator)) {
+      json.addProperty("creator", creator);
+    }
+    if (filterType!=null) {
+      json.addProperty("filter_type", filterType);
+    }
+    if (!StringUtils.isEmpty(cursor)) {
+      json.addProperty("cursor", cursor);
+    }
+    if (limit!=null) {
+      json.addProperty("limit", limit);
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_MOMENT_LIST);
+    final String result = this.mainService.post(url, json.toString());
+    return WxCpGetMomentList.fromJson(result);
+  }
+
+  @Override
+  public WxCpGetMomentTask getMomentTask(String momentId, String cursor, Integer limit)
+    throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("moment_id", momentId);
+    if (!StringUtils.isEmpty(cursor)) {
+      json.addProperty("cursor", cursor);
+    }
+    if (limit!=null) {
+      json.addProperty("limit", limit);
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_MOMENT_TASK);
+    final String result = this.mainService.post(url, json.toString());
+    return WxCpGetMomentTask.fromJson(result);
+  }
+
+  @Override
+  public WxCpGetMomentCustomerList getMomentCustomerList(String momentId, String userId,
+    String cursor, Integer limit) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("moment_id", momentId);
+    json.addProperty("userid", userId);
+    if (!StringUtils.isEmpty(cursor)) {
+      json.addProperty("cursor", cursor);
+    }
+    if (limit!=null) {
+      json.addProperty("limit", limit);
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_MOMENT_CUSTOMER_LIST);
+    final String result = this.mainService.post(url, json.toString());
+    return WxCpGetMomentCustomerList.fromJson(result);
+  }
+
+  @Override
+  public WxCpGetMomentSendResult getMomentSendResult(String momentId, String userId,
+    String cursor, Integer limit) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("moment_id", momentId);
+    json.addProperty("userid", userId);
+    if (!StringUtils.isEmpty(cursor)) {
+      json.addProperty("cursor", cursor);
+    }
+    if (limit!=null) {
+      json.addProperty("limit", limit);
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_MOMENT_SEND_RESULT);
+    final String result = this.mainService.post(url, json.toString());
+    return WxCpGetMomentSendResult.fromJson(result);
+  }
+
+  @Override
+  public WxCpGetMomentComments getMomentComments(String momentId, String userId)
+    throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("moment_id", momentId);
+    json.addProperty("userid", userId);
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_MOMENT_COMMENTS);
+    final String result = this.mainService.post(url, json.toString());
+    return WxCpGetMomentComments.fromJson(result);
+  }
+
   /**
    * 
    * 企业和第三方应用可通过此接口获取企业与成员的群发记录。
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentResult.java
new file mode 100644
index 000000000..3cfd66bb3
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentResult.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 创建发表任务结果
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpAddMomentResult extends WxCpBaseResp {
+
+  @SerializedName("jobid")
+  private String jobId;
+
+  public static WxCpAddMomentResult fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpAddMomentResult.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java
new file mode 100644
index 000000000..3e952ccb9
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java
@@ -0,0 +1,36 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import java.io.Serializable;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.cp.bean.external.msg.Attachment;
+import me.chanjar.weixin.cp.bean.external.msg.Text;
+import me.chanjar.weixin.cp.bean.external.moment.VisibleRange;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 创建发表任务
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class WxCpAddMomentTask implements Serializable {
+  @SerializedName("visible_range")
+  private VisibleRange visibleRange;
+
+  private Text text;
+
+  private List attachments;
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java
new file mode 100644
index 000000000..0cf49adca
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java
@@ -0,0 +1,40 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 获取客户朋友圈的互动数据
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpGetMomentComments extends WxCpBaseResp {
+  @SerializedName("comment_list")
+  private List commentList;
+  @SerializedName("like_list")
+  private List likeList;
+
+  @Getter
+  @Setter
+  public static class CommentLikeItem {
+    @SerializedName("external_userid")
+    private String externalUserId;
+    @SerializedName("create_time")
+    private Long createTime;
+  }
+
+  public static WxCpGetMomentComments fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGetMomentComments.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentCustomerList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentCustomerList.java
new file mode 100644
index 000000000..795c9520d
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentCustomerList.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.moment.CustomerItem;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 获取客户朋友圈发表时选择的可见范围
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpGetMomentCustomerList extends WxCpBaseResp {
+  @SerializedName("next_cursor")
+  private String nextCursor;
+  @SerializedName("customer_list")
+  private List customerList;
+
+  public static WxCpGetMomentCustomerList fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGetMomentCustomerList.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java
new file mode 100644
index 000000000..6f4d07288
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.moment.MomentInfo;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 获取企业全部的发表列表
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpGetMomentList extends WxCpBaseResp {
+  @SerializedName("next_cursor")
+  private String nextCursor;
+  @SerializedName("moment_list")
+  private List momentList;
+
+  public static WxCpGetMomentList fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGetMomentList.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java
new file mode 100644
index 000000000..c7a6f4852
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.moment.CustomerItem;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 获取客户朋友圈发表后的可见客户列表
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpGetMomentSendResult extends WxCpBaseResp {
+  @SerializedName("next_cursor")
+  private String nextCursor;
+  @SerializedName("customer_list")
+  private List customerList;
+
+  public static WxCpGetMomentSendResult fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGetMomentSendResult.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java
new file mode 100644
index 000000000..041d5e802
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java
@@ -0,0 +1,41 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 获取客户朋友圈企业发表的列表
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpGetMomentTask extends WxCpBaseResp {
+  @SerializedName("next_cursor")
+  private String nextCursor;
+
+  @SerializedName("task_list")
+  private List taskList;
+
+  @Getter
+  @Setter
+  public static class MomentTaskItem {
+    @SerializedName("userid")
+    private String userId;
+    @SerializedName("publish_status")
+    private String publishStatus;
+  }
+
+  public static WxCpGetMomentTask fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGetMomentTask.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java
new file mode 100644
index 000000000..f7ca6d8f3
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.moment.ExternalContactList;
+import me.chanjar.weixin.cp.bean.external.moment.SenderList;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 企业发表内容到客户的朋友圈 获取任务创建结果
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class WxCpGetMomentTaskResult extends WxCpBaseResp {
+  private Integer status;
+  private String type;
+  private TaskResult result;
+
+  @Getter
+  @Setter
+  public static class TaskResult extends WxCpBaseResp {
+    @SerializedName("moment_id")
+    private String momentId;
+    @SerializedName("invalid_sender_list")
+    private SenderList invalidSenderList;
+    @SerializedName("invalid_external_contact_list")
+    private ExternalContactList invalidExternalContactList;
+
+    public static TaskResult fromJson(String json) {
+      return WxCpGsonBuilder.create().fromJson(json, TaskResult.class);
+    }
+
+    public String toJson() {
+      return WxCpGsonBuilder.create().toJson(this);
+    }
+  }
+
+  public static WxCpGetMomentTaskResult fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGetMomentTaskResult.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java
index 589306596..69e337b82 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java
@@ -35,7 +35,7 @@ public class WxCpUserExternalTagGroupInfo extends WxCpBaseResp {
     private Long createTime;
 
     @SerializedName("order")
-    private Integer order;
+    private Long order;
 
     @SerializedName("deleted")
     private Boolean deleted;
@@ -67,7 +67,7 @@ public class WxCpUserExternalTagGroupInfo extends WxCpBaseResp {
     private Long createTime;
 
     @SerializedName("order")
-    private Integer order;
+    private Long order;
 
     @SerializedName("deleted")
     private Boolean deleted;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java
new file mode 100644
index 000000000..08b124239
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java
@@ -0,0 +1,14 @@
+package me.chanjar.weixin.cp.bean.external.moment;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CustomerItem {
+  @SerializedName("external_userid")
+  private String externalUserId;
+  @SerializedName("userid")
+  private String userId;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java
new file mode 100644
index 000000000..4d08bf358
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java
@@ -0,0 +1,13 @@
+package me.chanjar.weixin.cp.bean.external.moment;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ExternalContactList {
+  @SerializedName("tag_list")
+  private List tagList;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java
new file mode 100644
index 000000000..2ed770e10
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java
@@ -0,0 +1,28 @@
+package me.chanjar.weixin.cp.bean.external.moment;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.external.msg.Image;
+import me.chanjar.weixin.cp.bean.external.msg.Link;
+import me.chanjar.weixin.cp.bean.external.msg.Location;
+import me.chanjar.weixin.cp.bean.external.msg.Text;
+import me.chanjar.weixin.cp.bean.external.msg.Video;
+
+@Data
+public class MomentInfo {
+  @SerializedName("moment_id")
+  private String momentId;
+  @SerializedName("creator")
+  private String creator;
+  @SerializedName("create_time")
+  private String createTime;
+  @SerializedName("create_type")
+  private Integer createType;
+  @SerializedName("visible_type")
+  private Integer visibleType;
+  private Text text;
+  private Image image;
+  private Video video;
+  private Link link;
+  private Location location;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java
new file mode 100644
index 000000000..45889684c
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java
@@ -0,0 +1,15 @@
+package me.chanjar.weixin.cp.bean.external.moment;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class SenderList {
+  @SerializedName("user_list")
+  private List userList;
+  @SerializedName("department_list")
+  private List departmentList;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java
new file mode 100644
index 000000000..251fb5e64
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java
@@ -0,0 +1,13 @@
+package me.chanjar.weixin.cp.bean.external.moment;
+
+import com.google.gson.annotations.SerializedName;
+import java.io.Serializable;
+import lombok.Data;
+
+@Data
+public class VisibleRange implements Serializable {
+  @SerializedName("sender_list")
+  private SenderList senderList;
+  @SerializedName("external_contact_list")
+  private ExternalContactList externalContactList;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java
index a949a1a0f..a33f4ad9a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java
@@ -19,4 +19,6 @@ public class Link implements Serializable {
   private String picUrl;
   private String desc;
   private String url;
+  @SerializedName("media_id")
+  private String mediaId;
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Location.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Location.java
new file mode 100644
index 000000000..944f2f487
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Location.java
@@ -0,0 +1,16 @@
+package me.chanjar.weixin.cp.bean.external.msg;
+
+import lombok.Data;
+
+/**
+ * 地理位置
+ *
+ * @author leiin
+ * @date 2021-10-29
+ */
+@Data
+public class Location {
+  private String latitude;
+  private String longitude;
+  private String name;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Video.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Video.java
index 237fb75cf..863b02812 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Video.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Video.java
@@ -16,4 +16,6 @@ public class Video implements Serializable {
   private static final long serialVersionUID = -6048642921382867138L;
   @SerializedName("media_id")
   private String mediaId;
+  @SerializedName("thumb_media_id")
+  private String thumbMediaId;
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java
index e165e7c29..fc159a9a3 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java
@@ -189,6 +189,39 @@ public class WxCpTpXmlMessage implements Serializable {
   @XStreamConverter(value = XStreamCDataConverter.class)
   protected String externalUserID;
 
+  @XStreamAlias("State")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  protected String state;
+
+  @XStreamAlias("Source")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  protected String source;
+
+  @XStreamAlias("FailReason")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  protected String failReason;
+
+  @XStreamAlias("ChatId")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  protected String chatId;
+
+  @XStreamAlias("UpdateDetail")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  protected String updateDetail;
+
+  @XStreamAlias("JoinScene")
+  protected Integer joinScene;
+
+  @XStreamAlias("QuitScene")
+  protected Integer quitScene;
+
+  @XStreamAlias("MemChangeCnt")
+  protected Integer memChangeCnt;
+
+  @XStreamAlias("TagType")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  protected String tagType;
+
   @XStreamAlias("WelcomeCode")
   @XStreamConverter(value = XStreamCDataConverter.class)
   protected String welcomeCode;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
index dfa0c6347..c09116d75 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
@@ -206,8 +206,17 @@ public interface WxCpApiPathConsts {
     String DEL_CORP_TAG = "/cgi-bin/externalcontact/del_corp_tag";
     String MARK_TAG = "/cgi-bin/externalcontact/mark_tag";
 
+    String ADD_MOMENT_TASK = "/cgi-bin/externalcontact/add_moment_task";
+    String GET_MOMENT_TASK_RESULT = "/cgi-bin/externalcontact/get_moment_task_result";
+    String GET_MOMENT_LIST = "/cgi-bin/externalcontact/get_moment_list";
+    String GET_MOMENT_TASK = "/cgi-bin/externalcontact/get_moment_task";
+    String GET_MOMENT_CUSTOMER_LIST = "/cgi-bin/externalcontact/get_moment_customer_list";
+    String GET_MOMENT_SEND_RESULT = "/cgi-bin/externalcontact/get_moment_send_result";
+    String GET_MOMENT_COMMENTS = "/cgi-bin/externalcontact/get_moment_comments";
+
     String GET_GROUP_MSG_SEND_RESULT = "/cgi-bin/externalcontact/get_groupmsg_send_result";
     String GET_GROUP_MSG_TASK = "/cgi-bin/externalcontact/get_groupmsg_task";
     String GET_GROUP_MSG_LIST_V2 = "/cgi-bin/externalcontact/get_groupmsg_list_v2";
+
   }
 }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
index cfbdcca93..60fdeb9b2 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
@@ -138,13 +138,13 @@ public class WxCpExternalContactServiceImplTest {
     List list = new ArrayList<>();
     WxCpUserExternalTagGroupInfo.Tag tag = new WxCpUserExternalTagGroupInfo.Tag();
     tag.setName("测试标签20");
-    tag.setOrder(1);
+    tag.setOrder(1L);
     list.add(tag);
 
     WxCpUserExternalTagGroupInfo tagGroupInfo = new WxCpUserExternalTagGroupInfo();
     WxCpUserExternalTagGroupInfo.TagGroup tagGroup = new WxCpUserExternalTagGroupInfo.TagGroup();
     tagGroup.setGroupName("其他");
-    tagGroup.setOrder(1);
+    tagGroup.setOrder(1L);
     tagGroup.setTag(list);
     tagGroupInfo.setTagGroup(tagGroup);