Browse Source

:new: #2937【企业微信】发送机器人消息支持模板卡片消息,客服消息支持以客服纬度查询

ChengKeJ 2 years ago
parent
commit
3acf55a0d2

+ 10 - 0
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;
+
 }

+ 4 - 0
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;
+
   /**
    * 发送消息
    * 当微信客户处于“新接入待处理”或“由智能助手接待”状态下,可调用该接口给用户发送消息。

+ 5 - 0
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());
+  }
+
 }

+ 25 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java

@@ -150,6 +150,31 @@ public class WxCpKfServiceImpl implements WxCpKfService {
   }
 
   @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);
 

+ 324 - 0
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<ActionMenuItem> 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<VerticalContent> verticalContents;
+
+  /**
+   * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+   */
+  private List<HorizontalContent> horizontalContents;
+
+  /**
+   * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
+   */
+  private List<TemplateCardJump> 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<TemplateCardButton> buttons;
+
+  /**
+   * 投票选择型卡片需要指定
+   * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节
+   */
+  private String checkboxQuestionKey;
+
+  /**
+   * 选择题模式,单选:0,多选:1,不填默认0
+   */
+  private Integer checkboxMode;
+
+  /**
+   * 选项list,选项个数不超过 20 个,最少1个
+   */
+  private List<CheckboxOption> options;
+
+  /**
+   * 提交按钮样式
+   * 按钮文案,建议不超过10个字,不填默认为提交
+   */
+  private String submitButtonText;
+  /**
+   * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节
+   */
+  private String submitButtonKey;
+  /**
+   * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器
+   */
+  private List<MultipleSelect> 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<ActionMenuItem> 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<VerticalContent> 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<HorizontalContent> 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<TemplateCardJump> 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<TemplateCardButton> 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<MultipleSelect> 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:
 
     }

+ 5 - 0
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";
+
   }
 
   /**