From 3acf55a0d2acb7cdad1a15ef06005f0171e09a32 Mon Sep 17 00:00:00 2001 From: ChengKeJ Date: Tue, 21 Feb 2023 17:14:20 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20#2937=E3=80=90=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E5=8F=91=E9=80=81=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=E6=B6=88=E6=81=AF=E6=94=AF=E6=8C=81=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E6=B6=88=E6=81=AF=EF=BC=8C=E5=AE=A2=E6=9C=8D?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=94=AF=E6=8C=81=E4=BB=A5=E5=AE=A2=E6=9C=8D?= =?UTF-8?q?=E7=BA=AC=E5=BA=A6=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/cp/api/WxCpGroupRobotService.java | 10 + .../chanjar/weixin/cp/api/WxCpKfService.java | 4 + .../api/impl/WxCpGroupRobotServiceImpl.java | 5 + .../weixin/cp/api/impl/WxCpKfServiceImpl.java | 25 ++ .../bean/message/WxCpGroupRobotMessage.java | 324 ++++++++++++++++++ .../weixin/cp/constant/WxCpConsts.java | 5 + 6 files changed, 373 insertions(+) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java index 769cab6f7..bc6e13054 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java @@ -2,6 +2,7 @@ package me.chanjar.weixin.cp.api; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.bean.message.WxCpGroupRobotMessage; import java.util.List; @@ -96,4 +97,13 @@ public interface WxCpGroupRobotService { * @throws WxErrorException 异常 */ void sendFile(String webhookUrl, String mediaId) throws WxErrorException; + + /** + * 发送模板卡片消息 + * @param webhookUrl + * @param wxCpGroupRobotMessage + * @throws WxErrorException + */ + void sendTemplateCardMessage(String webhookUrl, WxCpGroupRobotMessage wxCpGroupRobotMessage) throws WxErrorException; + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java index 6f47ab908..48a51a16b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java @@ -135,9 +135,13 @@ public interface WxCpKfService { * @return 微信消息 wx cp kf msg list resp * @throws WxErrorException 异常 */ + @Deprecated WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat) throws WxErrorException; + WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat,String open_kfid) + throws WxErrorException; + /** * 发送消息 * 当微信客户处于“新接入待处理”或“由智能助手接待”状态下,可调用该接口给用户发送消息。 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java index a5749cbc1..30e447499 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java @@ -92,4 +92,9 @@ public class WxCpGroupRobotServiceImpl implements WxCpGroupRobotService { .setMediaId(mediaId).toJson()); } + @Override + public void sendTemplateCardMessage(String webhookUrl, WxCpGroupRobotMessage wxCpGroupRobotMessage) throws WxErrorException { + this.cpService.postWithoutToken(webhookUrl, wxCpGroupRobotMessage.toJson()); + } + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java index 247b1d090..754aaa064 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java @@ -149,6 +149,31 @@ public class WxCpKfServiceImpl implements WxCpKfService { return WxCpKfMsgListResp.fromJson(responseContent); } + @Override + public WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat, String openKfId) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SYNC_MSG); + + JsonObject json = new JsonObject(); + if (cursor!=null) { + json.addProperty("cursor", cursor); + } + if (token!=null) { + json.addProperty("token", token); + } + if (limit!=null) { + json.addProperty("limit", limit); + } + if (voiceFormat!=null) { + json.addProperty("voice_format", voiceFormat); + } + if (openKfId != null) { + json.addProperty("open_kfid", openKfId); + } + + String responseContent = cpService.post(url, json); + return WxCpKfMsgListResp.fromJson(responseContent); + } + @Override public WxCpKfMsgSendResp sendMsg(WxCpKfMsgSendRequest request) throws WxErrorException { String url = cpService.getWxCpConfigStorage().getApiUrl(SEND_MSG); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java index a0e9be359..6c889b6ce 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java @@ -7,11 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.bean.templatecard.*; +import org.apache.commons.lang3.StringUtils; import java.io.Serializable; import java.util.List; import static me.chanjar.weixin.cp.constant.WxCpConsts.GroupRobotMsgType.*; +import static me.chanjar.weixin.cp.constant.WxCpConsts.GroupRobotMsgType.TEMPLATE_CARD; /** * 微信群机器人消息 @@ -61,6 +64,154 @@ public class WxCpGroupRobotMessage implements Serializable { */ private String mediaId; + private Integer agentId; + + // 模板型卡片特有属性 + /** + * 模板卡片类型,文本通知型卡片填写 “text_notice”, + * 图文展示型卡片此处填写 “news_notice”, + * 按钮交互型卡片填写”button_interaction”, + * 投票选择型卡片填写”vote_interaction”, + * 多项选择型卡片填写 “multiple_interaction” + */ + private String cardType; + + /** + * 卡片来源样式信息,不需要来源样式可不填写 + * 来源图片的url + */ + private String sourceIconUrl; + /** + * 卡片来源样式信息,不需要来源样式可不填写 + * 来源图片的描述,建议不超过20个字 + */ + private String sourceDesc; + + /** + * 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 + */ + private Integer sourceDescColor; + + /** + * 更多操作界面的描述 + */ + private String actionMenuDesc; + + /** + * 操作列表,列表长度取值范围为 [1, 3] + */ + private List actionMenuActionList; + + /** + * 一级标题,建议不超过36个字 + */ + private String mainTitleTitle; + /** + * 标题辅助信息,建议不超过44个字 + */ + private String mainTitleDesc; + + /** + * 图文展示型的卡片必须有图片字段。 + * 图片的url. + */ + private String cardImageUrl; + + /** + * 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3 + */ + private Float cardImageAspectRatio; + /** + * 关键数据样式 + * 关键数据样式的数据内容,建议不超过14个字 + */ + private String emphasisContentTitle; + /** + * 关键数据样式的数据描述内容,建议不超过22个字 + */ + private String emphasisContentDesc; + + /** + * 二级普通文本,建议不超过160个字 + */ + private String subTitleText; + + /** + * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 + */ + private List verticalContents; + + /** + * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + */ + private List horizontalContents; + + /** + * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + */ + private List jumps; + + /** + * 整体卡片的点击跳转事件,text_notice必填本字段 + * 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] + */ + private Integer cardActionType; + /** + * 跳转事件的url,card_action.type是1时必填 + */ + private String cardActionUrl; + + /** + * 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填 + */ + private String cardActionAppid; + + /** + * 跳转事件的小程序的pagepath,card_action.type是2时选填 + */ + private String cardActionPagepath; + + /** + * 按钮交互型卡片需指定。 + * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + */ + private List buttons; + + /** + * 投票选择型卡片需要指定 + * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + */ + private String checkboxQuestionKey; + + /** + * 选择题模式,单选:0,多选:1,不填默认0 + */ + private Integer checkboxMode; + + /** + * 选项list,选项个数不超过 20 个,最少1个 + */ + private List options; + + /** + * 提交按钮样式 + * 按钮文案,建议不超过10个字,不填默认为提交 + */ + private String submitButtonText; + /** + * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 + */ + private String submitButtonKey; + /** + * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 + */ + private List selects; + + /** + * 引用文献样式 + */ + private QuoteArea quoteArea; + /** * To json string. * @@ -69,6 +220,9 @@ public class WxCpGroupRobotMessage implements Serializable { public String toJson() { JsonObject messageJson = new JsonObject(); messageJson.addProperty("msgtype", this.getMsgType()); + if (this.getAgentId() != null) { + messageJson.addProperty("agentid", this.getAgentId()); + } switch (this.getMsgType()) { case TEXT: { @@ -127,6 +281,176 @@ public class WxCpGroupRobotMessage implements Serializable { messageJson.add("file", file); break; } + case TEMPLATE_CARD: { + JsonObject template = new JsonObject(); + template.addProperty("card_type", this.getCardType()); + + if (StringUtils.isNotBlank(this.getSourceIconUrl()) || StringUtils.isNotBlank(this.getSourceDesc())) { + JsonObject source = new JsonObject(); + if (StringUtils.isNotBlank(this.getSourceIconUrl())) { + source.addProperty("icon_url", this.getSourceIconUrl()); + } + if (StringUtils.isNotBlank(this.getSourceDesc())) { + source.addProperty("desc", this.getSourceDesc()); + } + source.addProperty("desc_color", this.getSourceDescColor()); + template.add("source", source); + } + + if (StringUtils.isNotBlank(this.getActionMenuDesc())) { + JsonObject action_menu = new JsonObject(); + action_menu.addProperty("desc", this.getActionMenuDesc()); + JsonArray actionList = new JsonArray(); + List actionMenuItemList = this.getActionMenuActionList(); + for (ActionMenuItem actionItemI : actionMenuItemList) { + actionList.add(actionItemI.toJson()); + } + action_menu.add("action_list", actionList); + template.add("action_menu", action_menu); + } + + if (StringUtils.isNotBlank(this.getMainTitleTitle()) || StringUtils.isNotBlank(this.getMainTitleDesc())) { + JsonObject mainTitle = new JsonObject(); + if (StringUtils.isNotBlank(this.getMainTitleTitle())) { + mainTitle.addProperty("title", this.getMainTitleTitle()); + } + if (StringUtils.isNotBlank(this.getMainTitleDesc())) { + mainTitle.addProperty("desc", this.getMainTitleDesc()); + } + template.add("main_title", mainTitle); + } + + if (StringUtils.isNotBlank(this.getCardImageUrl()) || this.getCardImageAspectRatio() != null) { + JsonObject cardImage = new JsonObject(); + if (StringUtils.isNotBlank(this.getCardImageUrl())) { + cardImage.addProperty("url", this.getCardImageUrl()); + } + if (null != this.getCardImageAspectRatio()) { + cardImage.addProperty("aspect_ratio", this.getCardImageAspectRatio()); + } + template.add("card_image", cardImage); + } + + if (StringUtils.isNotBlank(this.getEmphasisContentTitle()) || StringUtils.isNotBlank(this.getEmphasisContentDesc())) { + JsonObject emphasisContent = new JsonObject(); + if (StringUtils.isNotBlank(this.getEmphasisContentTitle())) { + emphasisContent.addProperty("title", this.getEmphasisContentTitle()); + } + if (StringUtils.isNotBlank(this.getEmphasisContentDesc())) { + emphasisContent.addProperty("desc", this.getEmphasisContentDesc()); + } + template.add("emphasis_content", emphasisContent); + } + + + if (StringUtils.isNotBlank(this.getSubTitleText())) { + template.addProperty("sub_title_text", this.getSubTitleText()); + } + + List verticalContents = this.getVerticalContents(); + if (null != verticalContents && !verticalContents.isEmpty()) { + JsonArray vContentJsonArray = new JsonArray(); + for (VerticalContent vContent : this.getVerticalContents()) { + JsonObject tempObject = vContent.toJson(); + vContentJsonArray.add(tempObject); + } + template.add("vertical_content_list", vContentJsonArray); + } + + List horizontalContents = this.getHorizontalContents(); + if (null != horizontalContents && !horizontalContents.isEmpty()) { + JsonArray hContentJsonArray = new JsonArray(); + for (HorizontalContent hContent : this.getHorizontalContents()) { + JsonObject tempObject = hContent.toJson(); + hContentJsonArray.add(tempObject); + } + template.add("horizontal_content_list", hContentJsonArray); + } + + List jumps = this.getJumps(); + if (null != jumps && !jumps.isEmpty()) { + JsonArray jumpJsonArray = new JsonArray(); + for (TemplateCardJump jump : this.getJumps()) { + JsonObject tempObject = jump.toJson(); + jumpJsonArray.add(tempObject); + } + template.add("jump_list", jumpJsonArray); + } + + if (null != this.getCardActionType()) { + JsonObject cardAction = new JsonObject(); + cardAction.addProperty("type", this.getCardActionType()); + if (StringUtils.isNotBlank(this.getCardActionUrl())) { + cardAction.addProperty("url", this.getCardActionUrl()); + } + if (StringUtils.isNotBlank(this.getCardActionAppid())) { + cardAction.addProperty("appid", this.getCardActionAppid()); + } + if (StringUtils.isNotBlank(this.getCardActionPagepath())) { + cardAction.addProperty("pagepath", this.getCardActionPagepath()); + } + template.add("card_action", cardAction); + } + + List buttons = this.getButtons(); + if (null != buttons && !buttons.isEmpty()) { + 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.getCheckboxQuestionKey())) { + JsonObject checkBox = new JsonObject(); + checkBox.addProperty("question_key", this.getCheckboxQuestionKey()); + if (null != this.getCheckboxMode()) { + checkBox.addProperty("mode", this.getCheckboxMode()); + } + 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.getSubmitButtonText()) || StringUtils.isNotBlank(this.getSubmitButtonKey())) { + JsonObject submit_button = new JsonObject(); + if (StringUtils.isNotBlank(this.getSubmitButtonText())) { + submit_button.addProperty("text", this.getSubmitButtonText()); + } + if (StringUtils.isNotBlank(this.getSubmitButtonKey())) { + submit_button.addProperty("key", this.getSubmitButtonKey()); + } + template.add("submit_button", submit_button); + } + + // select_list + List selects = this.getSelects(); + if (null != selects && !selects.isEmpty()) { + JsonArray selectJsonArray = new JsonArray(); + for (MultipleSelect select : this.getSelects()) { + JsonObject tempObject = select.toJson(); + selectJsonArray.add(tempObject); + } + template.add("select_list", selectJsonArray); + } + + QuoteArea quoteArea = this.getQuoteArea(); + if (null != quoteArea) { + JsonObject quoteAreaJson = quoteArea.toJson(); + template.add("quote_area", quoteAreaJson); + } + + messageJson.add("template_card", template); + break; + } default: } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java index b3feb7e4f..a097f6414 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java @@ -481,6 +481,11 @@ public class WxCpConsts { */ public static final String FILE = "file"; + /** + * 模版类型消息. + */ + public static final String TEMPLATE_CARD = "template_card"; + } /**