소스 검색

#1050 客服消息支持发送菜单消息

Binary Wang 6 년 전
부모
커밋
084ffcf8bb

+ 7 - 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java

@@ -85,14 +85,19 @@ public class WxConsts {
     public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";
 
     /**
-     * 小程序卡片(要求小程序与公众号已关联)
+     * 小程序卡片(要求小程序与公众号已关联).
      */
     public static final String MINIPROGRAMPAGE = "miniprogrampage";
 
     /**
-     * 任务卡片消息
+     * 任务卡片消息.
      */
     public static final String TASKCARD = "taskcard";
+
+    /**
+     * 菜单消息.
+     */
+    public static final String MSGMENU = "msgmenu";
   }
 
   /**

+ 26 - 11
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java

@@ -10,7 +10,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * 客服消息
+ * 客服消息.
  *
  * @author chanjarster
  */
@@ -34,64 +34,79 @@ public class WxMpKefuMessage implements Serializable {
   private String miniProgramPagePath;
   private List<WxArticle> articles = new ArrayList<>();
 
+  private String headContent;
+  private String tailContent;
   /**
-   * 获得文本消息builder
+   * 菜单消息里的菜单内容.
+   * 请使用逗号分割的形式将id和content连起来放在数组的里面
+   */
+  private String[] msgMenuList;
+
+  /**
+   * 获得文本消息builder.
    */
   public static TextBuilder TEXT() {
     return new TextBuilder();
   }
 
   /**
-   * 获得图片消息builder
+   * 获得图片消息builder.
    */
   public static ImageBuilder IMAGE() {
     return new ImageBuilder();
   }
 
   /**
-   * 获得语音消息builder
+   * 获得语音消息builder.
    */
   public static VoiceBuilder VOICE() {
     return new VoiceBuilder();
   }
 
   /**
-   * 获得视频消息builder
+   * 获得视频消息builder.
    */
   public static VideoBuilder VIDEO() {
     return new VideoBuilder();
   }
 
   /**
-   * 获得音乐消息builder
+   * 获得音乐消息builder.
    */
   public static MusicBuilder MUSIC() {
     return new MusicBuilder();
   }
 
   /**
-   * 获得图文消息(点击跳转到外链)builder
+   * 获得图文消息(点击跳转到外链)builder.
    */
   public static NewsBuilder NEWS() {
     return new NewsBuilder();
   }
 
   /**
-   * 获得图文消息(点击跳转到图文消息页面)builder
+   * 获得图文消息(点击跳转到图文消息页面)builder.
    */
   public static MpNewsBuilder MPNEWS() {
     return new MpNewsBuilder();
   }
 
   /**
-   * 获得卡券消息builder
+   * 获得卡券消息builder.
    */
   public static WxCardBuilder WXCARD() {
     return new WxCardBuilder();
   }
 
   /**
-   * 小程序卡片
+   * 获得菜单消息builder.
+   */
+  public static WxMsgMenuBuilder MSGMENU() {
+    return new WxMsgMenuBuilder();
+  }
+
+  /**
+   * 小程序卡片.
    */
   public static MiniProgramPageBuilder MINIPROGRAMPAGE() {
     return new MiniProgramPageBuilder();
@@ -110,8 +125,8 @@ public class WxMpKefuMessage implements Serializable {
    * {@link WxConsts.KefuMsgType#WXCARD}
    * {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE}
    * {@link WxConsts.KefuMsgType#TASKCARD}
+   * {@link WxConsts.KefuMsgType#MSGMENU}
    * </pre>
-   *
    */
   public void setMsgType(String msgType) {
     this.msgType = msgType;

+ 7 - 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java

@@ -634,11 +634,17 @@ public class WxMpXmlMessage implements Serializable {
   private String regionCode;
 
   /**
-   * 审核未通过的原因
+   * 审核未通过的原因.
    */
   @XStreamAlias("ReasonMsg")
   private String reasonMsg;
 
+  /**
+   * 给用户发菜单消息类型的客服消息后,用户所点击的菜单ID.
+   */
+  @XStreamAlias("bizmsgmenuid")
+  private String bizMsgMenuId;
+
   public static WxMpXmlMessage fromXml(String xml) {
     //修改微信变态的消息内容格式,方便解析
     xml = xml.replace("</PicList><PicList>", "");

+ 46 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxMsgMenuBuilder.java

@@ -0,0 +1,46 @@
+package me.chanjar.weixin.mp.builder.kefu;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
+
+/**
+ * 卡券消息builder
+ * <pre>
+ * 用法: WxMpKefuMessage m = WxMpKefuMessage.WXCARD().cardId(...).toUser(...).build();
+ * </pre>
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+public final class WxMsgMenuBuilder extends BaseBuilder<WxMsgMenuBuilder> {
+  private String headContent;
+  private String tailContent;
+  private String[] msgMenuList;
+
+  public WxMsgMenuBuilder() {
+    this.msgType = WxConsts.KefuMsgType.MSGMENU;
+  }
+
+  @Override
+  public WxMpKefuMessage build() {
+    WxMpKefuMessage m = super.build();
+    m.setHeadContent(this.headContent);
+    m.setMsgMenuList(this.msgMenuList);
+    m.setTailContent(this.tailContent);
+    return m;
+  }
+
+  public WxMsgMenuBuilder headContent(String headContent) {
+    this.headContent = headContent;
+    return this;
+  }
+
+  public WxMsgMenuBuilder tailContent(String tailContent) {
+    this.tailContent = tailContent;
+    return this;
+  }
+
+  public WxMsgMenuBuilder msgMenuList(String... msgMenuList) {
+    this.msgMenuList = msgMenuList;
+    return this;
+  }
+}

+ 83 - 71
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java

@@ -1,7 +1,6 @@
 package me.chanjar.weixin.mp.util.json;
 
 import com.google.gson.*;
-import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.api.WxConsts.KefuMsgType;
 import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
 import org.apache.commons.lang3.StringUtils;
@@ -16,77 +15,90 @@ public class WxMpKefuMessageGsonAdapter implements JsonSerializer<WxMpKefuMessag
     messageJson.addProperty("touser", message.getToUser());
     messageJson.addProperty("msgtype", message.getMsgType());
 
-    if (WxConsts.KefuMsgType.TEXT.equals(message.getMsgType())) {
-      JsonObject text = new JsonObject();
-      text.addProperty("content", message.getContent());
-      messageJson.add("text", text);
-    }
-
-    if (WxConsts.KefuMsgType.IMAGE.equals(message.getMsgType())) {
-      JsonObject image = new JsonObject();
-      image.addProperty("media_id", message.getMediaId());
-      messageJson.add("image", image);
-    }
-
-    if (WxConsts.KefuMsgType.VOICE.equals(message.getMsgType())) {
-      JsonObject voice = new JsonObject();
-      voice.addProperty("media_id", message.getMediaId());
-      messageJson.add("voice", voice);
-    }
-
-    if (WxConsts.KefuMsgType.VIDEO.equals(message.getMsgType())) {
-      JsonObject video = new JsonObject();
-      video.addProperty("media_id", message.getMediaId());
-      video.addProperty("thumb_media_id", message.getThumbMediaId());
-      video.addProperty("title", message.getTitle());
-      video.addProperty("description", message.getDescription());
-      messageJson.add("video", video);
-    }
-
-    if (WxConsts.KefuMsgType.MUSIC.equals(message.getMsgType())) {
-      JsonObject music = new JsonObject();
-      music.addProperty("title", message.getTitle());
-      music.addProperty("description", message.getDescription());
-      music.addProperty("thumb_media_id", message.getThumbMediaId());
-      music.addProperty("musicurl", message.getMusicUrl());
-      music.addProperty("hqmusicurl", message.getHqMusicUrl());
-      messageJson.add("music", music);
-    }
-
-    if (WxConsts.KefuMsgType.NEWS.equals(message.getMsgType())) {
-      JsonObject newsJsonObject = new JsonObject();
-      JsonArray articleJsonArray = new JsonArray();
-      for (WxMpKefuMessage.WxArticle article : message.getArticles()) {
-        JsonObject articleJson = new JsonObject();
-        articleJson.addProperty("title", article.getTitle());
-        articleJson.addProperty("description", article.getDescription());
-        articleJson.addProperty("url", article.getUrl());
-        articleJson.addProperty("picurl", article.getPicUrl());
-        articleJsonArray.add(articleJson);
+    switch (message.getMsgType()) {
+      case KefuMsgType.TEXT:
+        JsonObject text = new JsonObject();
+        text.addProperty("content", message.getContent());
+        messageJson.add("text", text);
+        break;
+      case KefuMsgType.IMAGE:
+        JsonObject image = new JsonObject();
+        image.addProperty("media_id", message.getMediaId());
+        messageJson.add("image", image);
+        break;
+      case KefuMsgType.VOICE:
+        JsonObject voice = new JsonObject();
+        voice.addProperty("media_id", message.getMediaId());
+        messageJson.add("voice", voice);
+        break;
+      case KefuMsgType.VIDEO:
+        JsonObject video = new JsonObject();
+        video.addProperty("media_id", message.getMediaId());
+        video.addProperty("thumb_media_id", message.getThumbMediaId());
+        video.addProperty("title", message.getTitle());
+        video.addProperty("description", message.getDescription());
+        messageJson.add("video", video);
+        break;
+      case KefuMsgType.MUSIC:
+        JsonObject music = new JsonObject();
+        music.addProperty("title", message.getTitle());
+        music.addProperty("description", message.getDescription());
+        music.addProperty("thumb_media_id", message.getThumbMediaId());
+        music.addProperty("musicurl", message.getMusicUrl());
+        music.addProperty("hqmusicurl", message.getHqMusicUrl());
+        messageJson.add("music", music);
+        break;
+      case KefuMsgType.NEWS:
+        JsonObject newsJsonObject = new JsonObject();
+        JsonArray articleJsonArray = new JsonArray();
+        for (WxMpKefuMessage.WxArticle article : message.getArticles()) {
+          JsonObject articleJson = new JsonObject();
+          articleJson.addProperty("title", article.getTitle());
+          articleJson.addProperty("description", article.getDescription());
+          articleJson.addProperty("url", article.getUrl());
+          articleJson.addProperty("picurl", article.getPicUrl());
+          articleJsonArray.add(articleJson);
+        }
+        newsJsonObject.add("articles", articleJsonArray);
+        messageJson.add("news", newsJsonObject);
+        break;
+      case KefuMsgType.MPNEWS:
+        JsonObject json = new JsonObject();
+        json.addProperty("media_id", message.getMpNewsMediaId());
+        messageJson.add("mpnews", json);
+        break;
+      case KefuMsgType.WXCARD:
+        JsonObject wxcard = new JsonObject();
+        wxcard.addProperty("card_id", message.getCardId());
+        messageJson.add("wxcard", wxcard);
+        break;
+      case KefuMsgType.MINIPROGRAMPAGE:
+        JsonObject miniProgramPage = new JsonObject();
+        miniProgramPage.addProperty("title", message.getTitle());
+        miniProgramPage.addProperty("appid", message.getMiniProgramAppId());
+        miniProgramPage.addProperty("pagepath", message.getMiniProgramPagePath());
+        miniProgramPage.addProperty("thumb_media_id", message.getThumbMediaId());
+        messageJson.add("miniprogrampage", miniProgramPage);
+        break;
+      case KefuMsgType.MSGMENU: {
+        JsonObject msgMenu = new JsonObject();
+        JsonArray array = new JsonArray();
+        for (String s : message.getMsgMenuList()) {
+          JsonObject innerJson = new JsonObject();
+          final String[] split = s.split(",");
+          innerJson.addProperty("id", split[0]);
+          innerJson.addProperty("content", split[1]);
+          array.add(innerJson);
+        }
+        msgMenu.addProperty("head_content", message.getHeadContent());
+        msgMenu.add("list", array);
+        msgMenu.addProperty("tail_content", message.getTailContent());
+        messageJson.add("msgmenu", msgMenu);
+        break;
+      }
+      default: {
+        throw new RuntimeException("非法消息类型,暂不支持");
       }
-      newsJsonObject.add("articles", articleJsonArray);
-      messageJson.add("news", newsJsonObject);
-    }
-
-    if (WxConsts.KefuMsgType.MPNEWS.equals(message.getMsgType())) {
-      JsonObject json = new JsonObject();
-      json.addProperty("media_id", message.getMpNewsMediaId());
-      messageJson.add("mpnews", json);
-    }
-
-    if (WxConsts.KefuMsgType.WXCARD.equals(message.getMsgType())) {
-      JsonObject wxcard = new JsonObject();
-      wxcard.addProperty("card_id", message.getCardId());
-      messageJson.add("wxcard", wxcard);
-    }
-
-    if (KefuMsgType.MINIPROGRAMPAGE.equals(message.getMsgType())) {
-      JsonObject miniProgramPage = new JsonObject();
-      miniProgramPage.addProperty("title", message.getTitle());
-      miniProgramPage.addProperty("appid", message.getMiniProgramAppId());
-      miniProgramPage.addProperty("pagepath", message.getMiniProgramPagePath());
-      miniProgramPage.addProperty("thumb_media_id", message.getThumbMediaId());
-      messageJson.add("miniprogrampage", miniProgramPage);
     }
 
     if (StringUtils.isNotBlank(message.getKfAccount())) {

+ 14 - 1
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java

@@ -5,6 +5,8 @@ import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage.WxArticle;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 @Test
 public class WxMpKefuMessageTest {
 
@@ -141,7 +143,6 @@ public class WxMpKefuMessageTest {
   }
 
   public void testMiniProgramPageBuild() {
-
     WxMpKefuMessage reply = WxMpKefuMessage.MINIPROGRAMPAGE()
       .toUser("OPENID")
       .title("title")
@@ -154,4 +155,16 @@ public class WxMpKefuMessageTest {
       "{\"touser\":\"OPENID\",\"msgtype\":\"miniprogrampage\",\"miniprogrampage\":{\"title\":\"title\",\"appid\":\"appid\",\"pagepath\":\"pagepath\",\"thumb_media_id\":\"thumb_media_id\"}}");
   }
 
+  public void testMsgMenuBuild() {
+    WxMpKefuMessage reply = WxMpKefuMessage.MSGMENU()
+      .toUser("OPENID")
+      .msgMenuList("101,满意", "102,不满意")
+      .headContent("您对本次服务是否满意呢?")
+      .tailContent("欢迎再次光临")
+      .build();
+
+    assertThat(reply.toJson())
+      .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"msgmenu\",\"msgmenu\":{\"head_content\":\"您对本次服务是否满意呢?\",\"list\":[{\"id\":\"101\",\"content\":\"满意\"},{\"id\":\"102\",\"content\":\"不满意\"}],\"tail_content\":\"欢迎再次光临\"}}");
+  }
+
 }