diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 3e08462017..c1e6d39910 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -120,6 +120,37 @@ public static class KefuMsgType { * 小程序通知消息. */ 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"; } /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java index 244e7fed07..a2f6e6c5c6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java +++ b/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.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; @@ -70,6 +71,134 @@ public class WxCpMessage implements Serializable { private String taskId; private List 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 vertical_contents; + + /** + * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + */ + private List horizontal_contents; + + /** + * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + */ + private List 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 buttons; + + /** + * 投票选择型卡片需要指定 + * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + */ + private String checkbox_question_key; + + /** + * 选择题模式,单选:0,多选:1,不填默认0 + */ + private Integer checkbox_mode; + + /** + * 选项list,选项个数不超过 20 个,最少1个 + */ + private List options; + + /** + * 提交按钮样式 + * 按钮文案,建议不超过10个字,不填默认为提交 + */ + private String submit_button_text; + /** + * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 + */ + private String submit_button_key; + /** + * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 + */ + private List selects; + /** * 获得文本消息builder. */ @@ -140,6 +269,13 @@ public static TaskCardBuilder TASKCARD() { return new TaskCardBuilder(); } + /** + * 获得任务卡片消息builder. + */ + public static TemplateCardBuilder TEMPLATECARD() { + return new TemplateCardBuilder(); + } + /** * 获得小程序通知消息builder. */ @@ -160,6 +296,7 @@ public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() { * {@link KefuMsgType#MARKDOWN} * {@link KefuMsgType#TASKCARD} * {@link KefuMsgType#MINIPROGRAM_NOTICE} + * {@link KefuMsgType#TEMPLATE_CARD} * * * @param msgType 消息类型 @@ -328,6 +465,150 @@ private void handleMsgType(JsonObject messageJson) { 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 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 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 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 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 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 } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java new file mode 100644 index 0000000000..7a29491ab1 --- /dev/null +++ b/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; +/** + *
+ * 模板卡片消息Builder
+ * 用法: WxCustomMessage m = WxCustomMessage.TEMPLATECARD().title(...)....toUser(...).build();
+ * 
+ * + * @author yzts + * @date 2019-05-16 + */ +public class TemplateCardBuilder extends BaseBuilder{ + /** + * 模板卡片类型,文本通知型卡片填写 “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 vertical_contents; + + /** + * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + */ + private List horizontal_contents; + + /** + * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + */ + private List 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 buttons; + + /** + * 投票选择型卡片需要指定 + * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + */ + private String checkbox_question_key; + + /** + * 选择题模式,单选:0,多选:1,不填默认0 + */ + private Integer checkbox_mode; + + /** + * 选项list,选项个数不超过 20 个,最少1个 + */ + private List options; + + /** + * 提交按钮样式 + * 按钮文案,建议不超过10个字,不填默认为提交 + */ + private String submit_button_text; + /** + * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 + */ + private String submit_button_key; + + /** + * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 + */ + private List 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 vertical_contents) { + this.vertical_contents = vertical_contents; + return this; + } + + public TemplateCardBuilder horizontal_contents(List horizontal_contents) { + this.horizontal_contents = horizontal_contents; + return this; + } + + public TemplateCardBuilder jumps(List 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 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 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 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; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java new file mode 100644 index 0000000000..2f6b5b1a97 --- /dev/null +++ b/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; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java new file mode 100644 index 0000000000..4f1160e933 --- /dev/null +++ b/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; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java new file mode 100644 index 0000000000..145a6c4426 --- /dev/null +++ b/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 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 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; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java new file mode 100644 index 0000000000..4ac9e005e4 --- /dev/null +++ b/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; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java new file mode 100644 index 0000000000..6d297e9c0b --- /dev/null +++ b/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; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java new file mode 100644 index 0000000000..7d364ff106 --- /dev/null +++ b/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; + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java index 3f7859116e..a1cfee801b 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java @@ -1,9 +1,12 @@ package me.chanjar.weixin.cp.bean.message; +import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.cp.bean.article.MpnewsArticle; import me.chanjar.weixin.cp.bean.article.NewArticle; import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; +import me.chanjar.weixin.cp.bean.templatecard.*; +import org.testng.Assert; import org.testng.annotations.Test; import java.util.Arrays; @@ -141,4 +144,284 @@ public void testTaskCardBuilder() { .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"taskcard\",\"taskcard\":{\"title\":\"任务卡片\",\"description\":\"有一条待处理任务\",\"url\":\"http://www.qq.com\",\"task_id\":\"task_123\",\"btn\":[{\"key\":\"yes\",\"name\":\"批准\",\"replace_name\":\"已批准\",\"color\":\"blue\",\"is_bold\":true},{\"key\":\"yes\",\"name\":\"拒绝\",\"replace_name\":\"已拒绝\",\"color\":\"red\",\"is_bold\":false}]}}"); } + /** + * 测试模板卡片消息 + * 文本通知型 + */ + public void TestTemplateCardBuilder_text_notice() { + + HorizontalContent hContent1 = HorizontalContent.builder() + .keyname("邀请人") + .value("张三") + .build(); + HorizontalContent hContent2 = HorizontalContent.builder() + .type(1) + .keyname("企业微信官网") + .value("点击访问") + .url("https://work.weixin.qq.com") + .build(); + HorizontalContent hContent3 = HorizontalContent.builder() + .type(2) + .keyname("企业微信下载") + .value("企业微信.apk") + .media_id("文件的media_id") + .build(); + + TemplateCardJump jump1 = TemplateCardJump.builder() + .type(1) + .title("企业微信官网") + .url("https://work.weixin.qq.com") + .build(); + TemplateCardJump jump2 = TemplateCardJump.builder() + .type(2) + .title("跳转小程序") + .appid("小程序的appid") + .pagepath("/index.html") + .build(); + + WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID") + .agentId(1000002) + .card_type(WxConsts.TemplateCardType.TEXT_NOTICE) + .source_icon_url("图片的url") + .source_desc("企业微信") + .main_title_title("欢迎使用企业微信") + .main_title_desc("您的好友正在邀请您加入企业微信") + .emphasis_content_title("100") + .emphasis_content_desc("核心数据") + .sub_title_text("下载企业微信还能抢红包!") + .horizontal_contents(Arrays.asList(hContent1,hContent2,hContent3)) + .jumps(Arrays.asList(jump1,jump2)) + .card_action_type(2) + .card_action_appid("小程序的appid") + .card_action_url("https://work.weixin.qq.com") + .card_action_pagepath("/index.html") + .build(); + reply.setEnableIdTrans(false); + reply.setEnableDuplicateCheck(false); + reply.setDuplicateCheckInterval(1800); +// System.out.println(reply.toJson()); + assertThat(reply.toJson()) + .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"text_notice\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"emphasis_content\":{\"title\":\"100\",\"desc\":\"核心数据\"},\"sub_title_text\":\"下载企业微信还能抢红包!\",\"horizontal_content_list\":[{\"keyname\":\"邀请人\",\"value\":\"张三\"},{\"type\":1,\"keyname\":\"企业微信官网\",\"value\":\"点击访问\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"keyname\":\"企业微信下载\",\"value\":\"企业微信.apk\",\"media_id\":\"文件的media_id\"}],\"jump_list\":[{\"type\":1,\"title\":\"企业微信官网\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"title\":\"跳转小程序\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}],\"card_action\":{\"type\":2,\"url\":\"https://work.weixin.qq.com\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}}}"); + + } + + /** + * 测试模板卡片消息 + * 图文展示型 + */ + public void TestTemplateCardBuilder_news_notice() { + + VerticalContent vContent1 = VerticalContent.builder() + .title("惊喜红包等你来拿") + .desc("下载企业微信还能抢红包!") + .build(); + VerticalContent vContent2 = VerticalContent.builder() + .title("二级垂直内容") + .desc("二级垂直内容!") + .build(); + + HorizontalContent hContent1 = HorizontalContent.builder() + .keyname("邀请人") + .value("张三") + .build(); + HorizontalContent hContent2 = HorizontalContent.builder() + .type(1) + .keyname("企业微信官网") + .value("点击访问") + .url("https://work.weixin.qq.com") + .build(); + HorizontalContent hContent3 = HorizontalContent.builder() + .type(2) + .keyname("企业微信下载") + .value("企业微信.apk") + .media_id("文件的media_id") + .build(); + + TemplateCardJump jump1 = TemplateCardJump.builder() + .type(1) + .title("企业微信官网") + .url("https://work.weixin.qq.com") + .build(); + TemplateCardJump jump2 = TemplateCardJump.builder() + .type(2) + .title("跳转小程序") + .appid("小程序的appid") + .pagepath("/index.html") + .build(); + + WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID") + .agentId(1000002) + .card_type(WxConsts.TemplateCardType.NEWS_NOTICE) + .source_icon_url("图片的url") + .source_desc("企业微信") + .main_title_title("欢迎使用企业微信") + .main_title_desc("您的好友正在邀请您加入企业微信") + .vertical_contents(Arrays.asList(vContent1,vContent2)) + .horizontal_contents(Arrays.asList(hContent1,hContent2,hContent3)) + .jumps(Arrays.asList(jump1,jump2)) + .card_action_type(2) + .card_action_appid("小程序的appid") + .card_action_url("https://work.weixin.qq.com") + .card_action_pagepath("/index.html") + .build(); + reply.setEnableIdTrans(false); + reply.setEnableDuplicateCheck(false); + reply.setDuplicateCheckInterval(1800); + System.out.println(reply.toJson()); + assertThat(reply.toJson()) + .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"news_notice\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"vertical_content_list\":[{\"title\":\"惊喜红包等你来拿\",\"desc\":\"下载企业微信还能抢红包!\"},{\"title\":\"二级垂直内容\",\"desc\":\"二级垂直内容!\"}],\"horizontal_content_list\":[{\"keyname\":\"邀请人\",\"value\":\"张三\"},{\"type\":1,\"keyname\":\"企业微信官网\",\"value\":\"点击访问\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"keyname\":\"企业微信下载\",\"value\":\"企业微信.apk\",\"media_id\":\"文件的media_id\"}],\"jump_list\":[{\"type\":1,\"title\":\"企业微信官网\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"title\":\"跳转小程序\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}],\"card_action\":{\"type\":2,\"url\":\"https://work.weixin.qq.com\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}}}"); + } + + /** + * 测试模板卡片消息 + * 按钮交互型 + */ + public void TestTemplateCardBuilder_button_interaction() { + + TemplateCardButton tButton1 = TemplateCardButton.builder() + .text("按钮1") + .style(1) + .key("button_key_1") + .build(); + TemplateCardButton tButton2 = TemplateCardButton.builder() + .text("按钮2") + .style(2) + .key("button_key_2") + .build(); + + HorizontalContent hContent1 = HorizontalContent.builder() + .keyname("邀请人") + .value("张三") + .build(); + HorizontalContent hContent2 = HorizontalContent.builder() + .type(1) + .keyname("企业微信官网") + .value("点击访问") + .url("https://work.weixin.qq.com") + .build(); + HorizontalContent hContent3 = HorizontalContent.builder() + .type(2) + .keyname("企业微信下载") + .value("企业微信.apk") + .media_id("文件的media_id") + .build(); + + WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID") + .agentId(1000002) + .card_type(WxConsts.TemplateCardType.BUTTON_INTERACTION) + .source_icon_url("图片的url") + .source_desc("企业微信") + .main_title_title("欢迎使用企业微信") + .main_title_desc("您的好友正在邀请您加入企业微信") + .sub_title_text("下载企业微信还能抢红包!") + .horizontal_contents(Arrays.asList(hContent1,hContent2,hContent3)) + .card_action_type(2) + .card_action_appid("小程序的appid") + .card_action_url("https://work.weixin.qq.com") + .card_action_pagepath("/index.html") + .task_id("task_id") + .buttons(Arrays.asList(tButton1,tButton2)) + .build(); + reply.setEnableIdTrans(false); + reply.setEnableDuplicateCheck(false); + reply.setDuplicateCheckInterval(1800); + System.out.println(reply.toJson()); + assertThat(reply.toJson()) + .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"button_interaction\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"sub_title_text\":\"下载企业微信还能抢红包!\",\"task_id\":\"task_id\",\"horizontal_content_list\":[{\"keyname\":\"邀请人\",\"value\":\"张三\"},{\"type\":1,\"keyname\":\"企业微信官网\",\"value\":\"点击访问\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"keyname\":\"企业微信下载\",\"value\":\"企业微信.apk\",\"media_id\":\"文件的media_id\"}],\"card_action\":{\"type\":2,\"url\":\"https://work.weixin.qq.com\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"},\"button_list\":[{\"text\":\"按钮1\",\"style\":1,\"key\":\"button_key_1\"},{\"text\":\"按钮2\",\"style\":2,\"key\":\"button_key_2\"}]}}"); + } + + /** + * 测试模板卡片消息 + * 投票选择型 + */ + public void TestTemplateCardBuilder_vote_interaction() { + CheckboxOption option1 = CheckboxOption.builder() + .id("option_id1") + .text("选择题选项1") + .is_checked(true) + .build(); + CheckboxOption option2 = CheckboxOption.builder() + .id("option_id2") + .text("选择题选项2") + .is_checked(false) + .build(); + + WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID") + .agentId(1000002) + .card_type(WxConsts.TemplateCardType.VOTE_INTERACTION) + .source_icon_url("图片的url") + .source_desc("企业微信") + .main_title_title("欢迎使用企业微信") + .main_title_desc("您的好友正在邀请您加入企业微信") + .task_id("task_id") + .checkbox_question_key("question_key1") + .checkbox_mode(1) + .options(Arrays.asList(option1,option2)) + .submit_button_key("key") + .submit_button_text("提交") + .build(); + reply.setEnableIdTrans(false); + reply.setEnableDuplicateCheck(false); + reply.setDuplicateCheckInterval(1800); + System.out.println(reply.toJson()); + assertThat(reply.toJson()) + .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"vote_interaction\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"task_id\":\"task_id\",\"checkbox\":{\"question_key\":\"question_key1\",\"mode\":1,\"option_list\":[{\"id\":\"option_id1\",\"text\":\"选择题选项1\",\"is_checked\":true},{\"id\":\"option_id2\",\"text\":\"选择题选项2\",\"is_checked\":false}]},\"submit_button\":{\"text\":\"提交\",\"key\":\"key\"}}}"); + } + + /** + * 测试模板卡片消息 + * 投票选择型 + */ + public void TestTemplateCardBuilder_multiple_interaction() { + CheckboxOption option1 = CheckboxOption.builder() + .id("selection_id1") + .text("选择器选项1") + .build(); + CheckboxOption option2 = CheckboxOption.builder() + .id("selection_id2") + .text("选择题选项2") + .build(); + CheckboxOption option3 = CheckboxOption.builder() + .id("selection_id3") + .text("选择器选项3") + .build(); + CheckboxOption option4 = CheckboxOption.builder() + .id("selection_id4") + .text("选择题选项4") + .build(); + + MultipleSelect mSelect1 = MultipleSelect.builder() + .question_key("question_key1") + .title("选择器标签1") + .selected_id("selection_id1") + .options(Arrays.asList(option1,option2)) + .build(); + MultipleSelect mSelect2 = MultipleSelect.builder() + .question_key("question_key2") + .title("选择器标签2") + .selected_id("selection_id3") + .options(Arrays.asList(option3,option4)) + .build(); + + + WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID") + .agentId(1000002) + .card_type(WxConsts.TemplateCardType.MULTIPLE_INTERACTION) + .source_icon_url("图片的url") + .source_desc("企业微信") + .main_title_title("欢迎使用企业微信") + .main_title_desc("您的好友正在邀请您加入企业微信") + .task_id("task_id") + .selects(Arrays.asList(mSelect1,mSelect2)) + .submit_button_key("key") + .submit_button_text("提交") + .build(); + reply.setEnableIdTrans(false); + reply.setEnableDuplicateCheck(false); + reply.setDuplicateCheckInterval(1800); + System.out.println(reply.toJson()); + assertThat(reply.toJson()) + .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"multiple_interaction\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"task_id\":\"task_id\",\"submit_button\":{\"text\":\"提交\",\"key\":\"key\"},\"select_list\":[{\"question_key\":\"question_key1\",\"title\":\"选择器标签1\",\"selected_id\":\"selection_id1\",\"option_list\":[{\"id\":\"selection_id1\",\"text\":\"选择器选项1\"},{\"id\":\"selection_id2\",\"text\":\"选择题选项2\"}]},{\"question_key\":\"question_key2\",\"title\":\"选择器标签2\",\"selected_id\":\"selection_id3\",\"option_list\":[{\"id\":\"selection_id3\",\"text\":\"选择器选项3\"},{\"id\":\"selection_id4\",\"text\":\"选择题选项4\"}]}]}}"); + } + }