@@ -85,14 +85,19 @@ public class WxConsts { | |||||
public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | ||||
/** | /** | ||||
* 小程序卡片(要求小程序与公众号已关联) | |||||
* 小程序卡片(要求小程序与公众号已关联). | |||||
*/ | */ | ||||
public static final String MINIPROGRAMPAGE = "miniprogrampage"; | public static final String MINIPROGRAMPAGE = "miniprogrampage"; | ||||
/** | /** | ||||
* 任务卡片消息 | |||||
* 任务卡片消息. | |||||
*/ | */ | ||||
public static final String TASKCARD = "taskcard"; | public static final String TASKCARD = "taskcard"; | ||||
/** | |||||
* 菜单消息. | |||||
*/ | |||||
public static final String MSGMENU = "msgmenu"; | |||||
} | } | ||||
/** | /** | ||||
@@ -10,7 +10,7 @@ import java.util.ArrayList; | |||||
import java.util.List; | import java.util.List; | ||||
/** | /** | ||||
* 客服消息 | |||||
* 客服消息. | |||||
* | * | ||||
* @author chanjarster | * @author chanjarster | ||||
*/ | */ | ||||
@@ -34,64 +34,79 @@ public class WxMpKefuMessage implements Serializable { | |||||
private String miniProgramPagePath; | private String miniProgramPagePath; | ||||
private List<WxArticle> articles = new ArrayList<>(); | private List<WxArticle> articles = new ArrayList<>(); | ||||
private String headContent; | |||||
private String tailContent; | |||||
/** | /** | ||||
* 获得文本消息builder | |||||
* 菜单消息里的菜单内容. | |||||
* 请使用逗号分割的形式将id和content连起来放在数组的里面 | |||||
*/ | |||||
private String[] msgMenuList; | |||||
/** | |||||
* 获得文本消息builder. | |||||
*/ | */ | ||||
public static TextBuilder TEXT() { | public static TextBuilder TEXT() { | ||||
return new TextBuilder(); | return new TextBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得图片消息builder | |||||
* 获得图片消息builder. | |||||
*/ | */ | ||||
public static ImageBuilder IMAGE() { | public static ImageBuilder IMAGE() { | ||||
return new ImageBuilder(); | return new ImageBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得语音消息builder | |||||
* 获得语音消息builder. | |||||
*/ | */ | ||||
public static VoiceBuilder VOICE() { | public static VoiceBuilder VOICE() { | ||||
return new VoiceBuilder(); | return new VoiceBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得视频消息builder | |||||
* 获得视频消息builder. | |||||
*/ | */ | ||||
public static VideoBuilder VIDEO() { | public static VideoBuilder VIDEO() { | ||||
return new VideoBuilder(); | return new VideoBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得音乐消息builder | |||||
* 获得音乐消息builder. | |||||
*/ | */ | ||||
public static MusicBuilder MUSIC() { | public static MusicBuilder MUSIC() { | ||||
return new MusicBuilder(); | return new MusicBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得图文消息(点击跳转到外链)builder | |||||
* 获得图文消息(点击跳转到外链)builder. | |||||
*/ | */ | ||||
public static NewsBuilder NEWS() { | public static NewsBuilder NEWS() { | ||||
return new NewsBuilder(); | return new NewsBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得图文消息(点击跳转到图文消息页面)builder | |||||
* 获得图文消息(点击跳转到图文消息页面)builder. | |||||
*/ | */ | ||||
public static MpNewsBuilder MPNEWS() { | public static MpNewsBuilder MPNEWS() { | ||||
return new MpNewsBuilder(); | return new MpNewsBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得卡券消息builder | |||||
* 获得卡券消息builder. | |||||
*/ | */ | ||||
public static WxCardBuilder WXCARD() { | public static WxCardBuilder WXCARD() { | ||||
return new WxCardBuilder(); | return new WxCardBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 小程序卡片 | |||||
* 获得菜单消息builder. | |||||
*/ | |||||
public static WxMsgMenuBuilder MSGMENU() { | |||||
return new WxMsgMenuBuilder(); | |||||
} | |||||
/** | |||||
* 小程序卡片. | |||||
*/ | */ | ||||
public static MiniProgramPageBuilder MINIPROGRAMPAGE() { | public static MiniProgramPageBuilder MINIPROGRAMPAGE() { | ||||
return new MiniProgramPageBuilder(); | return new MiniProgramPageBuilder(); | ||||
@@ -110,8 +125,8 @@ public class WxMpKefuMessage implements Serializable { | |||||
* {@link WxConsts.KefuMsgType#WXCARD} | * {@link WxConsts.KefuMsgType#WXCARD} | ||||
* {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE} | * {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE} | ||||
* {@link WxConsts.KefuMsgType#TASKCARD} | * {@link WxConsts.KefuMsgType#TASKCARD} | ||||
* {@link WxConsts.KefuMsgType#MSGMENU} | |||||
* </pre> | * </pre> | ||||
* | |||||
*/ | */ | ||||
public void setMsgType(String msgType) { | public void setMsgType(String msgType) { | ||||
this.msgType = msgType; | this.msgType = msgType; | ||||
@@ -634,11 +634,17 @@ public class WxMpXmlMessage implements Serializable { | |||||
private String regionCode; | private String regionCode; | ||||
/** | /** | ||||
* 审核未通过的原因。 | |||||
* 审核未通过的原因. | |||||
*/ | */ | ||||
@XStreamAlias("ReasonMsg") | @XStreamAlias("ReasonMsg") | ||||
private String reasonMsg; | private String reasonMsg; | ||||
/** | |||||
* 给用户发菜单消息类型的客服消息后,用户所点击的菜单ID. | |||||
*/ | |||||
@XStreamAlias("bizmsgmenuid") | |||||
private String bizMsgMenuId; | |||||
public static WxMpXmlMessage fromXml(String xml) { | public static WxMpXmlMessage fromXml(String xml) { | ||||
//修改微信变态的消息内容格式,方便解析 | //修改微信变态的消息内容格式,方便解析 | ||||
xml = xml.replace("</PicList><PicList>", ""); | xml = xml.replace("</PicList><PicList>", ""); | ||||
@@ -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; | |||||
} | |||||
} |
@@ -1,7 +1,6 @@ | |||||
package me.chanjar.weixin.mp.util.json; | package me.chanjar.weixin.mp.util.json; | ||||
import com.google.gson.*; | import com.google.gson.*; | ||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; | import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
@@ -16,77 +15,90 @@ public class WxMpKefuMessageGsonAdapter implements JsonSerializer<WxMpKefuMessag | |||||
messageJson.addProperty("touser", message.getToUser()); | messageJson.addProperty("touser", message.getToUser()); | ||||
messageJson.addProperty("msgtype", message.getMsgType()); | 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())) { | if (StringUtils.isNotBlank(message.getKfAccount())) { | ||||
@@ -5,6 +5,8 @@ import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage.WxArticle; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
@Test | @Test | ||||
public class WxMpKefuMessageTest { | public class WxMpKefuMessageTest { | ||||
@@ -141,7 +143,6 @@ public class WxMpKefuMessageTest { | |||||
} | } | ||||
public void testMiniProgramPageBuild() { | public void testMiniProgramPageBuild() { | ||||
WxMpKefuMessage reply = WxMpKefuMessage.MINIPROGRAMPAGE() | WxMpKefuMessage reply = WxMpKefuMessage.MINIPROGRAMPAGE() | ||||
.toUser("OPENID") | .toUser("OPENID") | ||||
.title("title") | .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\"}}"); | "{\"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\":\"欢迎再次光临\"}}"); | |||||
} | |||||
} | } |