浏览代码

:new: #2324【企业微信】添加对新的模版卡片消息的支持

nickname263 3 年之前
父节点
当前提交
cecdace1c5

+ 31 - 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java

@@ -120,6 +120,37 @@ public class WxConsts {
      * 小程序通知消息.
      */
     public static final String MINIPROGRAM_NOTICE = "miniprogram_notice";
+
+    /**
+     * 模板卡片消息.
+     */
+    public static final String TEMPLATE_CARD = "template_card";
+  }
+
+  /**
+   * 企业微信模板卡片消息的卡片类型
+   */
+  public static class TemplateCardType {
+    /**
+     * 文本通知型卡片
+     */
+    public static final String TEXT_NOTICE = "text_notice";
+    /**
+     * 图文展示型卡片
+     */
+    public static final String NEWS_NOTICE = "news_notice";
+    /**
+     * 按钮交互型卡片
+     */
+    public static final String BUTTON_INTERACTION = "button_interaction";
+    /**
+     * 投票选择型卡片
+     */
+    public static final String VOTE_INTERACTION = "vote_interaction";
+    /**
+     * 多项选择型卡片
+     */
+    public static final String MULTIPLE_INTERACTION = "multiple_interaction";
   }
 
   /**

+ 281 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java

@@ -8,6 +8,7 @@ import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
 import me.chanjar.weixin.cp.bean.article.NewArticle;
 import me.chanjar.weixin.cp.bean.messagebuilder.*;
 import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
+import me.chanjar.weixin.cp.bean.templatecard.*;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.Serializable;
@@ -71,6 +72,134 @@ public class WxCpMessage implements Serializable {
   private List<TaskCardButton> taskButtons = new ArrayList<>();
 
   /**
+   * 模板型卡片特有属性
+   */
+  /**
+   * 模板卡片类型,文本通知型卡片填写 “text_notice”,
+   * 图文展示型卡片此处填写 “news_notice”,
+   * 按钮交互型卡片填写”button_interaction”,
+   * 投票选择型卡片填写”vote_interaction”,
+   * 多项选择型卡片填写 “multiple_interaction”
+   */
+  private String card_type;
+
+  /**
+   * 卡片来源样式信息,不需要来源样式可不填写
+   * 来源图片的url
+   */
+  private String source_icon_url;
+  /**
+   * 卡片来源样式信息,不需要来源样式可不填写
+   * 来源图片的描述,建议不超过20个字
+   */
+  private String source_desc;
+
+  /**
+   * 一级标题,建议不超过36个字
+   */
+  private String main_title_title;
+  /**
+   * 标题辅助信息,建议不超过44个字
+   */
+  private String main_title_desc;
+
+  /**
+   * 图文展示型的卡片必须有图片字段。
+   * 图片的url.
+   */
+  private String card_image_url;
+
+  /**
+   * 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3
+   */
+  private Float card_image_aspect_ratio;
+  /**
+   * 关键数据样式
+   * 关键数据样式的数据内容,建议不超过14个字
+   */
+  private String emphasis_content_title;
+  /**
+   * 关键数据样式的数据描述内容,建议不超过22个字
+   */
+  private String emphasis_content_desc;
+
+  /**
+   * 二级普通文本,建议不超过160个字
+   */
+  private String sub_title_text;
+
+  /**
+   * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4
+   */
+  private List<VerticalContent> vertical_contents;
+
+  /**
+   * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+   */
+  private List<HorizontalContent> horizontal_contents;
+
+  /**
+   * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
+   */
+  private List<TemplateCardJump> jumps;
+
+  /**
+   * 整体卡片的点击跳转事件,text_notice必填本字段
+   * 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2]
+   */
+  private Integer  card_action_type;
+  /**
+   * 跳转事件的url,card_action.type是1时必填
+   */
+  private String card_action_url;
+
+  /**
+   * 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填
+   */
+  private String card_action_appid;
+
+  /**
+   * 跳转事件的小程序的pagepath,card_action.type是2时选填
+   */
+  private String card_action_pagepath;
+
+  /**
+   * 按钮交互型卡片需指定。
+   * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+   */
+  private List<TemplateCardButton> buttons;
+
+  /**
+   * 投票选择型卡片需要指定
+   * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节
+   */
+  private String checkbox_question_key;
+
+  /**
+   * 选择题模式,单选:0,多选:1,不填默认0
+   */
+  private Integer checkbox_mode;
+
+  /**
+   * 选项list,选项个数不超过 20 个,最少1个
+   */
+  private List<CheckboxOption> options;
+
+  /**
+   * 提交按钮样式
+   * 按钮文案,建议不超过10个字,不填默认为提交
+   */
+  private String submit_button_text;
+  /**
+   * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节
+   */
+  private String submit_button_key;
+  /**
+   * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器
+   */
+  private List<MultipleSelect> selects;
+
+  /**
    * 获得文本消息builder.
    */
   public static TextBuilder TEXT() {
@@ -141,6 +270,13 @@ public class WxCpMessage implements Serializable {
   }
 
   /**
+   * 获得任务卡片消息builder.
+   */
+  public static TemplateCardBuilder TEMPLATECARD() {
+    return new TemplateCardBuilder();
+  }
+
+  /**
    * 获得小程序通知消息builder.
    */
   public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() {
@@ -160,6 +296,7 @@ public class WxCpMessage implements Serializable {
    * {@link KefuMsgType#MARKDOWN}
    * {@link KefuMsgType#TASKCARD}
    * {@link KefuMsgType#MINIPROGRAM_NOTICE}
+   * {@link KefuMsgType#TEMPLATE_CARD}
    * </pre>
    *
    * @param msgType 消息类型
@@ -328,6 +465,150 @@ public class WxCpMessage implements Serializable {
         messageJson.add("miniprogram_notice", notice);
         break;
       }
+      case TEMPLATE_CARD: {
+        JsonObject template = new JsonObject();
+        template.addProperty("card_type", this.getCard_type());
+
+        if (StringUtils.isNotBlank(this.getSource_icon_url()) || StringUtils.isNotBlank(this.getSource_desc())) {
+          JsonObject source = new JsonObject();
+          if (StringUtils.isNotBlank(this.getSource_icon_url())) {
+            source.addProperty("icon_url", this.getSource_icon_url());
+          }
+          if (StringUtils.isNotBlank(this.getSource_desc())) {
+            source.addProperty("desc", this.getSource_desc());
+          }
+          template.add("source", source);
+        }
+
+        if (StringUtils.isNotBlank(this.getMain_title_title()) || StringUtils.isNotBlank(this.getMain_title_desc())) {
+          JsonObject main_title = new JsonObject();
+          if (StringUtils.isNotBlank(this.getMain_title_title())) {
+            main_title.addProperty("title", this.getMain_title_title());
+          }
+          if (StringUtils.isNotBlank(this.getMain_title_desc())) {
+            main_title.addProperty("desc", this.getMain_title_desc());
+          }
+          template.add("main_title", main_title);
+        }
+
+        if (StringUtils.isNotBlank(this.getEmphasis_content_title()) || StringUtils.isNotBlank(this.getEmphasis_content_desc())) {
+          JsonObject emphasis_content = new JsonObject();
+          if (StringUtils.isNotBlank(this.getEmphasis_content_title())) {
+            emphasis_content.addProperty("title", this.getEmphasis_content_title());
+          }
+          if (StringUtils.isNotBlank(this.getEmphasis_content_desc())) {
+            emphasis_content.addProperty("desc", this.getEmphasis_content_desc());
+          }
+          template.add("emphasis_content", emphasis_content);
+        }
+
+
+        if (StringUtils.isNotBlank(this.getSub_title_text())) {
+          template.addProperty("sub_title_text", this.getSub_title_text());
+        }
+
+        if (StringUtils.isNotBlank(this.getTaskId())) {
+          template.addProperty("task_id", this.getTaskId());
+        }
+
+        List<VerticalContent> verticalContents = this.getVertical_contents();
+        if(null != verticalContents && verticalContents.size() > 0) {
+          JsonArray vContentJsonArray = new JsonArray();
+          for (VerticalContent vContent : this.getVertical_contents()) {
+            JsonObject tempObject = vContent.toJson();
+            vContentJsonArray.add(tempObject);
+          }
+          template.add("vertical_content_list", vContentJsonArray);
+        }
+
+        List<HorizontalContent> horizontalContents = this.getHorizontal_contents();
+        if(null != horizontalContents && horizontalContents.size() > 0) {
+          JsonArray hContentJsonArray = new JsonArray();
+          for (HorizontalContent hContent : this.getHorizontal_contents()) {
+            JsonObject tempObject = hContent.toJson();
+            hContentJsonArray.add(tempObject);
+          }
+          template.add("horizontal_content_list", hContentJsonArray);
+        }
+
+        List<TemplateCardJump> jumps = this.getJumps();
+        if(null != jumps && jumps.size() > 0) {
+          JsonArray jumpJsonArray = new JsonArray();
+          for (TemplateCardJump jump : this.getJumps()) {
+            JsonObject tempObject = jump.toJson();
+            jumpJsonArray.add(tempObject);
+          }
+          template.add("jump_list", jumpJsonArray);
+        }
+
+        if (null != this.getCard_action_type()) {
+          JsonObject cardAction = new JsonObject();
+          cardAction.addProperty("type", this.getCard_action_type());
+          if (StringUtils.isNotBlank(this.getCard_action_url())) {
+            cardAction.addProperty("url", this.getCard_action_url());
+          }
+          if (StringUtils.isNotBlank(this.getCard_action_appid())) {
+            cardAction.addProperty("appid", this.getCard_action_appid());
+          }
+          if (StringUtils.isNotBlank(this.getCard_action_pagepath())) {
+            cardAction.addProperty("pagepath", this.getCard_action_pagepath());
+          }
+          template.add("card_action", cardAction);
+        }
+
+        List<TemplateCardButton> buttons = this.getButtons();
+        if(null != buttons && buttons.size() > 0) {
+          JsonArray btnJsonArray = new JsonArray();
+          for (TemplateCardButton btn : this.getButtons()) {
+            JsonObject tempObject = btn.toJson();
+            btnJsonArray.add(tempObject);
+          }
+          template.add("button_list", btnJsonArray);
+        }
+
+        // checkbox
+        if (StringUtils.isNotBlank(this.getCheckbox_question_key())) {
+          JsonObject checkBox = new JsonObject();
+          checkBox.addProperty("question_key", this.getCheckbox_question_key());
+          if (null != this.getCheckbox_mode()) {
+            checkBox.addProperty("mode", this.getCheckbox_mode());
+          }
+          JsonArray optionArray = new JsonArray();
+          for (CheckboxOption option : this.getOptions()) {
+            JsonObject tempObject = option.toJson();
+            optionArray.add(tempObject);
+          }
+          checkBox.add("option_list", optionArray);
+
+          template.add("checkbox", checkBox);
+        }
+
+        // submit_button
+        if (StringUtils.isNotBlank(this.getSubmit_button_text()) || StringUtils.isNotBlank(this.getSubmit_button_key())) {
+          JsonObject submit_button = new JsonObject();
+          if (StringUtils.isNotBlank(this.getSubmit_button_text())) {
+            submit_button.addProperty("text", this.getSubmit_button_text());
+          }
+          if (StringUtils.isNotBlank(this.getSubmit_button_key())) {
+            submit_button.addProperty("key", this.getSubmit_button_key());
+          }
+          template.add("submit_button", submit_button);
+        }
+
+        // select_list
+        List<MultipleSelect> selects = this.getSelects();
+        if(null != selects && selects.size() > 0) {
+          JsonArray selectJsonArray = new JsonArray();
+          for (MultipleSelect select : this.getSelects()) {
+            JsonObject tempObject = select.toJson();
+            selectJsonArray.add(tempObject);
+          }
+          template.add("select_list", selectJsonArray);
+        }
+
+        messageJson.add("template_card", template);
+        break;
+      }
       default: {
         // do nothing
       }

+ 300 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java

@@ -0,0 +1,300 @@
+package me.chanjar.weixin.cp.bean.messagebuilder;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.cp.bean.message.WxCpMessage;
+import me.chanjar.weixin.cp.bean.templatecard.*;
+
+import java.util.List;
+/**
+ * <pre>
+ * 模板卡片消息Builder
+ * 用法: WxCustomMessage m = WxCustomMessage.TEMPLATECARD().title(...)....toUser(...).build();
+ * </pre>
+ *
+ * @author yzts</a>
+ * @date 2019-05-16
+ */
+public class TemplateCardBuilder  extends BaseBuilder<TemplateCardBuilder>{
+  /**
+   * 模板卡片类型,文本通知型卡片填写 “text_notice”,
+   * 图文展示型卡片此处填写 “news_notice”,
+   * 按钮交互型卡片填写”button_interaction”,
+   * 投票选择型卡片填写”vote_interaction”,
+   * 多项选择型卡片填写 “multiple_interaction”
+   */
+  private String card_type;
+
+  /**
+   * 卡片来源样式信息,不需要来源样式可不填写
+   * 来源图片的url
+   */
+  private String source_icon_url;
+  /**
+   * 卡片来源样式信息,不需要来源样式可不填写
+   * 来源图片的描述,建议不超过20个字
+   */
+  private String source_desc;
+
+  /**
+   * 一级标题,建议不超过36个字
+   */
+  private String main_title_title;
+  /**
+   * 标题辅助信息,建议不超过44个字
+   */
+  private String main_title_desc;
+
+  /**
+   * 图文展示型的卡片必须有图片字段。
+   * 图片的url.
+   */
+  private String card_image_url;
+
+  /**
+   * 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3
+   */
+  private Float card_image_aspect_ratio;
+  /**
+   * 关键数据样式
+   * 关键数据样式的数据内容,建议不超过14个字
+   */
+  private String emphasis_content_title;
+  /**
+   * 关键数据样式的数据描述内容,建议不超过22个字
+   */
+  private String emphasis_content_desc;
+
+  /**
+   * 二级普通文本,建议不超过160个字
+   */
+  private String sub_title_text;
+
+  /**
+   * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4
+   */
+  private List<VerticalContent> vertical_contents;
+
+  /**
+   * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+   */
+  private List<HorizontalContent> horizontal_contents;
+
+  /**
+   * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
+   */
+  private List<TemplateCardJump> jumps;
+
+  /**
+   * 整体卡片的点击跳转事件,text_notice必填本字段
+   * 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2]
+   */
+  private Integer  card_action_type;
+  /**
+   * 跳转事件的url,card_action.type是1时必填
+   */
+  private String card_action_url;
+
+  /**
+   * 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填
+   */
+  private String card_action_appid;
+
+  /**
+   * 跳转事件的小程序的pagepath,card_action.type是2时选填
+   */
+  private String card_action_pagepath;
+
+  /**
+   * 任务id,同一个应用任务id不能重复,只能由数字、字母和“_-@”组成,最长128字节
+   */
+  private String task_id;
+
+  /**
+   * 按钮交互型卡片需指定。
+   * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+   */
+  private List<TemplateCardButton> buttons;
+
+  /**
+   * 投票选择型卡片需要指定
+   * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节
+   */
+  private String checkbox_question_key;
+
+  /**
+   * 选择题模式,单选:0,多选:1,不填默认0
+   */
+  private Integer checkbox_mode;
+
+  /**
+   * 选项list,选项个数不超过 20 个,最少1个
+   */
+  private List<CheckboxOption> options;
+
+  /**
+   * 提交按钮样式
+   * 按钮文案,建议不超过10个字,不填默认为提交
+   */
+  private String submit_button_text;
+  /**
+   * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节
+   */
+  private String submit_button_key;
+
+  /**
+   * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器
+   */
+  private List<MultipleSelect> selects;
+
+
+  public TemplateCardBuilder() {
+    this.msgType = WxConsts.KefuMsgType.TEMPLATE_CARD;
+  }
+
+  public TemplateCardBuilder card_type(String card_type) {
+    this.card_type = card_type;
+    return this;
+  }
+
+  public TemplateCardBuilder source_icon_url(String source_icon_url) {
+    this.source_icon_url = source_icon_url;
+    return this;
+  }
+
+  public TemplateCardBuilder source_desc(String source_desc) {
+    this.source_desc = source_desc;
+    return this;
+  }
+
+  public TemplateCardBuilder main_title_title(String main_title_title) {
+    this.main_title_title = main_title_title;
+    return this;
+  }
+
+  public TemplateCardBuilder main_title_desc(String mainTitleDesc) {
+    this.main_title_desc = mainTitleDesc;
+    return this;
+  }
+
+  public TemplateCardBuilder emphasis_content_title(String emphasis_content_title) {
+    this.emphasis_content_title = emphasis_content_title;
+    return this;
+  }
+
+  public TemplateCardBuilder emphasis_content_desc(String emphasis_content_desc) {
+    this.emphasis_content_desc = emphasis_content_desc;
+    return this;
+  }
+
+  public TemplateCardBuilder sub_title_text(String sub_title_text) {
+    this.sub_title_text = sub_title_text;
+    return this;
+  }
+
+  public TemplateCardBuilder  vertical_contents(List<VerticalContent> vertical_contents) {
+    this.vertical_contents = vertical_contents;
+    return this;
+  }
+
+  public TemplateCardBuilder horizontal_contents(List<HorizontalContent> horizontal_contents) {
+    this.horizontal_contents = horizontal_contents;
+    return this;
+  }
+
+  public TemplateCardBuilder jumps(List<TemplateCardJump> jumps) {
+    this.jumps = jumps;
+    return this;
+  }
+
+  public TemplateCardBuilder card_action_type(Integer card_action_type) {
+    this.card_action_type = card_action_type;
+    return this;
+  }
+
+  public TemplateCardBuilder card_action_url(String card_action_url) {
+    this.card_action_url = card_action_url;
+    return this;
+  }
+
+  public TemplateCardBuilder card_action_appid(String card_action_appid) {
+    this.card_action_appid = card_action_appid;
+    return this;
+  }
+
+  public TemplateCardBuilder card_action_pagepath(String card_action_pagepath) {
+    this.card_action_pagepath = card_action_pagepath;
+    return this;
+  }
+
+  public TemplateCardBuilder task_id(String taskId) {
+    this.task_id = taskId;
+    return this;
+  }
+
+  public TemplateCardBuilder buttons(List<TemplateCardButton> buttons) {
+    this.buttons = buttons;
+    return this;
+  }
+
+  public TemplateCardBuilder checkbox_question_key(String checkbox_question_key) {
+    this.checkbox_question_key = checkbox_question_key;
+    return this;
+  }
+
+  public TemplateCardBuilder checkbox_mode(Integer checkbox_mode) {
+    this.checkbox_mode = checkbox_mode;
+    return this;
+  }
+
+  public TemplateCardBuilder options(List<CheckboxOption> options) {
+    this.options = options;
+    return this;
+  }
+
+  public TemplateCardBuilder submit_button_text(String submit_button_text) {
+    this.submit_button_text = submit_button_text;
+    return this;
+  }
+
+  public TemplateCardBuilder submit_button_key(String submit_button_key) {
+    this.submit_button_key = submit_button_key;
+    return this;
+  }
+
+  public TemplateCardBuilder selects(List<MultipleSelect> selects) {
+    this.selects = selects;
+    return this;
+  }
+
+  @Override
+  public WxCpMessage build() {
+    WxCpMessage m = super.build();
+    m.setSafe(null);
+    m.setCard_type(this.card_type);
+    m.setSource_icon_url(this.source_icon_url);
+    m.setSource_desc(this.source_desc);
+    m.setMain_title_title(this.main_title_title);
+    m.setMain_title_desc(this.main_title_desc);
+    m.setCard_image_url(this.card_image_url);
+    m.setCard_image_aspect_ratio(this.card_image_aspect_ratio);
+    m.setEmphasis_content_title(this.emphasis_content_title);
+    m.setEmphasis_content_desc(this.emphasis_content_desc);
+    m.setSub_title_text(this.sub_title_text);
+    m.setVertical_contents(this.vertical_contents);
+    m.setHorizontal_contents(this.horizontal_contents);
+    m.setJumps(this.jumps);
+    m.setCard_action_type(this.card_action_type);
+    m.setCard_action_appid(this.card_action_appid);
+    m.setCard_action_pagepath(this.card_action_pagepath);
+    m.setCard_action_url(this.card_action_url);
+    m.setTaskId(this.task_id);
+    m.setButtons(this.buttons);
+    m.setCheckbox_mode(this.checkbox_mode);
+    m.setCheckbox_question_key(this.checkbox_question_key);
+    m.setOptions(this.options);
+    m.setSubmit_button_text(this.submit_button_text);
+    m.setSubmit_button_key(this.submit_button_key);
+    m.setSelects(this.selects);
+    return m;
+  }
+}

+ 49 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java

@@ -0,0 +1,49 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+ * @author yzts
+ * @date 2021/9/22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CheckboxOption  implements Serializable {
+  private static final long serialVersionUID = 5405702239190050250L;
+
+  /**
+   * 选项id,用户提交选项后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复
+   * 必填
+   */
+  private String id;
+  /**
+   * 选项文案描述,建议不超过17个字.
+   * 必填
+   */
+  private String text;
+  /**
+   * 该选项是否要默认选中
+   * 必填
+   */
+  private Boolean is_checked;
+
+  public JsonObject toJson() {
+    JsonObject optionJson = new JsonObject();
+    optionJson.addProperty("id", this.getId());
+    optionJson.addProperty("text", this.getText());
+    if(null != this.getIs_checked()) {
+      optionJson.addProperty("is_checked", this.getIs_checked());
+    }
+    return optionJson;
+  }
+}

+ 66 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java

@@ -0,0 +1,66 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+ * @author yzts
+ * @date 2021/9/22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class HorizontalContent implements Serializable {
+
+  private static final long serialVersionUID = -2209656515382964372L;
+
+  /**
+   * 链接类型,0或不填代表不是链接,1 代表跳转url,2 代表下载附件
+   */
+  private Integer type;
+  /**
+   * 二级标题,建议不超过5个字
+   */
+  private String keyname;
+  /**
+   * 二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过30个字
+   */
+  private String value;
+  /**
+   * 链接跳转的url,horizontal_content_list.type是1时必填
+   */
+  private String url;
+  /**
+   * 附件的media_id,horizontal_content_list.type是2时必填
+   */
+  private String media_id;
+
+  public JsonObject toJson() {
+    JsonObject hContentJson = new JsonObject();
+
+    if(null !=  this.getType()){
+      hContentJson.addProperty("type", this.getType());
+    }
+    hContentJson.addProperty("keyname", this.getKeyname());
+
+    if (StringUtils.isNotBlank(this.getValue())) {
+      hContentJson.addProperty("value", this.getValue());
+    }
+    if (StringUtils.isNotBlank(this.getUrl())) {
+      hContentJson.addProperty("url", this.getUrl());
+    }
+    if (StringUtils.isNotBlank(this.getMedia_id())) {
+      hContentJson.addProperty("media_id", this.getMedia_id());
+    }
+    return hContentJson;
+  }
+
+}

+ 68 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java

@@ -0,0 +1,68 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器
+ * @author yzts
+ * @date 2021/9/22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MultipleSelect implements Serializable {
+  private static final long serialVersionUID = 3446092543065698516L;
+
+  /**
+   * 下拉式的选择器题目的key,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节,不可重复
+   */
+  private String question_key;
+  /**
+   * 下拉式的选择器上面的title
+   */
+  private String title;
+  /**
+   * 默认选定的id,不填或错填默认第一个
+   */
+  private String selected_id;
+
+  /**
+   * 选项列表,下拉选项不超过 10 个,最少1个
+   */
+  private List<CheckboxOption> options;
+
+  public JsonObject toJson() {
+    JsonObject selectJson = new JsonObject();
+
+    selectJson.addProperty("question_key", this.getQuestion_key());
+
+    if (StringUtils.isNotBlank(this.getTitle())) {
+      selectJson.addProperty("title", this.getTitle());
+    }
+    if (StringUtils.isNotBlank(this.getSelected_id())) {
+      selectJson.addProperty("selected_id", this.getSelected_id());
+    }
+// select_list
+    List<CheckboxOption> options = this.getOptions();
+    if(null != options && options.size() > 0) {
+      JsonArray optionJsonArray = new JsonArray();
+      for (CheckboxOption option : this.getOptions()) {
+        JsonObject tempObject = option.toJson();
+        optionJsonArray.add(tempObject);
+      }
+      selectJson.add("option_list", optionJsonArray);
+    }
+
+    return selectJson;
+  }
+}

+ 49 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java

@@ -0,0 +1,49 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+ * @author yzts
+ * @date 2021/9/22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TemplateCardButton  implements Serializable {
+  private static final long serialVersionUID = -4826551822490837002L;
+
+  /**
+   * 按钮文案,建议不超过10个字
+   */
+  private String text;
+  /**
+   * 按钮样式,目前可填1~4,不填或错填默认1
+   */
+  private Integer style;
+  /**
+   * 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复
+   */
+  private String key;
+
+  public JsonObject toJson() {
+    JsonObject btnObject = new JsonObject();
+
+
+    btnObject.addProperty("text", this.getText());
+
+    if (null != this.getStyle()) {
+      btnObject.addProperty("style", this.getStyle());
+    }
+    btnObject.addProperty("key", this.getKey());
+    return btnObject;
+  }
+}

+ 65 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java

@@ -0,0 +1,65 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
+ * @author yzts
+ * @date 2021/9/22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TemplateCardJump implements Serializable {
+  private static final long serialVersionUID = 4440089247405968074L;
+
+  /**
+   * 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序
+   */
+  private Integer type;
+  /**
+   * 跳转链接样式的文案内容,建议不超过18个字
+   */
+  private String title;
+  /**
+   * 跳转链接的url,jump_list.type是1时必填
+   */
+  private String url;
+  /**
+   * 跳转链接的小程序的appid,必须是与当前应用关联的小程序,jump_list.type是2时必填
+   */
+  private String appid;
+  /**
+   * 跳转链接的小程序的pagepath,jump_list.type是2时选填
+   */
+  private String pagepath;
+
+  public JsonObject toJson() {
+    JsonObject hContentJson = new JsonObject();
+
+    if(null !=  this.getType()){
+      hContentJson.addProperty("type", this.getType());
+    }
+    hContentJson.addProperty("title", this.getTitle());
+
+    if (StringUtils.isNotBlank(this.getUrl())) {
+      hContentJson.addProperty("url", this.getUrl());
+    }
+    if (StringUtils.isNotBlank(this.getAppid())) {
+      hContentJson.addProperty("appid", this.getAppid());
+    }
+    if (StringUtils.isNotBlank(this.getPagepath())) {
+      hContentJson.addProperty("pagepath", this.getPagepath());
+    }
+    return hContentJson;
+  }
+
+}

+ 44 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java

@@ -0,0 +1,44 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonObject;
+import kotlin.text.UStringsKt;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4
+ * @author yzts
+ * @date 2021/9/22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VerticalContent implements Serializable {
+  private static final long serialVersionUID = -1383852553854573558L;
+
+  /**
+   * 卡片二级标题,建议不超过38个字.必填字段
+   */
+  private String title;
+  /**
+   * 二级普通文本,建议不超过160个字
+   */
+  private String desc;
+
+  public JsonObject toJson() {
+    JsonObject vContentJson = new JsonObject();
+
+    vContentJson.addProperty("title", this.getTitle());
+
+    if (StringUtils.isNotBlank(this.getDesc())) {
+      vContentJson.addProperty("desc", this.getDesc());
+    }
+    return vContentJson;
+  }
+}

文件差异内容过多而无法显示
+ 283 - 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java