一、前言
hello小夥伴們,大家好,本篇的主題是使用Java開發微信公衆號之被動回覆用戶消息-回覆圖文消息
,那麼對於不太瞭解微信公衆號被動回覆用戶消息(文本消息、圖片消息)
的小夥伴們,可以先看一下前面我寫過的文章: Java開發微信公衆號之被動回覆用戶消息-回覆文本消息 , 廢話不多說,下面開始進入主題。
微信開發文檔: 微信官方文檔-被動回覆用戶消息
二、版本說明
- spring boot.version: v2.1.7.RELEASE
- java.version: 1.8
- weixin-java-mp.version: 3.5.0
三、被動回覆消息
當用戶發送消息給公衆號時(或某些特定的用戶操作引發的事件推送時),會產生一個POST
請求,開發者可以在響應包(Get
)中返回特定XML結構,來對該消息進行響應(現支持回覆文本
、圖片
、圖文
、語音
、視頻
、音樂
)。嚴格來說,發送被動響應消息其實並不是一種接口,而是對微信服務器發過來消息的一次回覆。
四、消息排重
微信服務器在將用戶的消息發給公衆號的開發者服務器URL
地址(開發者中心處配置)後,微信服務器在五秒內收不到響應會斷掉連接,並且重新發起請求,總共重試三次,如果在Debug
調試中,發現用戶無法收到響應的消息,可以檢查是否消息處理超時,一般debug
調試的情況下,超過5秒就會收不到響應。關於重試的消息排重,每一次消息都應該保持其冪等性,在推送的xml
數據結構中,有msgid
的消息推薦使用msgid
排重。如果是事件類型消息推薦使用FromUserName + CreateTime
進行排重。
五、如何保證五秒內處理並回復
假如服務器無法保證在五秒內處理並回復,必須做出下述回覆,這樣微信服務器纔不會對此作任何處理,並且不會發起重試(這種情況下,可以使用客服消息接口進行異步回覆),否則,將出現嚴重的錯誤提示。詳見下面說明:
- 1、直接回復
success
(推薦方式) - 2、直接回復空串(指字節長度爲0的空字符串,而不是
XML
結構體中content
字段的內容爲空)
一旦遇到服務器五秒內沒有做處理並且響應回覆或者開發者回覆了異常數據,比如JSON
數據等;微信都會在公衆號會話中,向用戶下發系統提示“該公衆號暫時無法提供服務,請稍後再試”
;
六、如何回覆圖文消息
回覆圖片(不支持gif動圖)多媒體消息時需要預先通過素材管理接口上傳臨時素材到微信服務器;也可以通過上傳素材管理接口中的永久素材到微信微服務器,只不過臨時素材在微信服務器上存儲的有效期只有3天,而永久素材永遠有效,由於微信永久素材的數量有上限,如果你的項目環境對對媒體素材文件需求量很大,謹慎使用永久素材上傳,使用臨時素材即可;不過也沒關係,微信開發者文檔也提供了刪除永久素材和刪除臨時素材的接口,根據場景合理使用即可。
通過素材管理接口上傳完臨時素材或者永久素材後,接口會返回一個關鍵字段叫做mediaId
,這個mediaId
返回給我們之後需要我們自己存儲記錄起來,等到需要使用多媒體文件素材的時候,都是通過傳遞mediaId
到接口參數中,微信服務器會根據該mediaId
去查找對應的素材文件,然後做對應的回覆處理,比如回覆圖片信息、回覆圖文信息等等。
七、預先上傳圖片素材
請注意: 前面已經強調過,回覆圖片(不支持gif動圖)
等多媒體消息時需要預先通過素材管理接口上傳臨時素材到微信服務器,可以使用素材管理中的臨時素材,也可以使用永久素材。
所以我們在實現被動回覆消息-回覆圖文消息的功能之前,需要先上傳圖片臨時素材或者圖片永久素材到微信平臺,接下來我們先觀察下圖文消息所需要的xml格式
和參數說明。
1. 回覆圖文消息xml格式
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title>
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles>
</xml>
2. 回覆圖文消息參數說明
參數 | 是否必須 | 說明 |
---|---|---|
ToUserName | 是 | 接收方帳號(收到的OpenID) |
FromUserName | 是 | 開發者微信號 |
CreateTime | 是 | 消息創建時間 (整型) |
MsgType | 是 | 消息類型,圖文爲news |
ArticleCount | 是 | 圖文消息個數;當用戶發送文本、圖片、語音、視頻、圖文、地理位置這六種消息時,開發者只能回覆1條圖文消息;其餘場景最多可回覆8條圖文消息 |
Articles | 是 | 圖文消息信息,注意,如果圖文數超過限制,則將只發限制內的條數 |
Title | 是 | 圖文消息標題 |
Description | 是 | 圖文消息描述 |
PicUrl | 是 | 圖片鏈接,支持JPG、PNG格式,較好的效果爲大圖360200,小圖200200 |
Url | 是 | 點擊圖文消息跳轉鏈接 |
3. 上傳永久圖片素材
/**
* @desc:
* @author: cao_wencao
* @date: 2020-05-21 13:47
*/
@Slf4j
@RestController
@RequestMapping("/wx/material")
public class WxMaterialController {
private static final SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
@Autowired
private WeChatMaterialUtils materialUtils;
/**
* 上傳單個圖片文件
*/
@PostMapping("/uploadImg")
public ApiResult uploadImg(@RequestParam("imgFile") MultipartFile imgFile) throws IOException {
log.info("multipartFile = " + imgFile);
log.info("ContentType = " + imgFile.getContentType());
log.info("OriginalFilename = " + imgFile.getOriginalFilename());
log.info("Name = " + imgFile.getName());
log.info("Size = " + imgFile.getSize());
String fileExt = imgFile.getOriginalFilename().substring(imgFile.getOriginalFilename().lastIndexOf(".") + 1);
log.info("fileExt = " + fileExt);
WxMpMaterialUploadResult wxMpMaterialUploadResult
= materialUtils.uploadFilesToWeChat("image", fileExt ,imgFile.getName(), imgFile.getInputStream());
if(null == wxMpMaterialUploadResult){
return ApiResult.error("上傳圖片失敗");
}
log.info("wxMpMaterialUploadResult = : " + JSON.toJSONString(wxMpMaterialUploadResult));
return ApiResult.succee(wxMpMaterialUploadResult,"上傳圖片成功");
}
}
4. PostMan測試圖片上傳
4.1 傳參如下
- headers
“key”:“Content-Type”, “value”:“multipart/form-data”
- body
- 響應結果
{
"code": 200,
"message": "上傳圖片成功",
"data": {
"mediaId": "1C72rnlYrj7ZqBiRGdKCoUUudPCjA5qMkJZeODvTN9U",
"url": "http://mmbiz.qpic.cn/mmbiz_jpg/DwMlrmia5oVQrbNsb6GJ64xlUtfTXspTcyuV1m6ykgiaGJQYR374WWfGGrgibJOibYc1df7Wyicw4u9f5CV3u7oAzow/0?wx_fmt=jpeg"
}
}
- 訪問url
八、封裝圖文消息Handler
- TextMsgHandler.java
package com.thinkingcao.weixin.handler;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @desc: 文本類型消息處理-TEXT
* @link: XmlMsgType.TEXT
* @author: cao_wencao
* @date: 2020-05-20 15:15
*/
@Component
public class TextMsgHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
//判斷傳遞過來的消息,類型是否爲TEXT
if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) {
//TODO: 如果需要做微信消息日誌存儲,可以在這裏進行日誌存儲到數據庫,這裏省略不寫。
}
// 獲取微信用戶基本信息
WxMpUser userWxInfo = wxMpService.getUserService().userInfo(wxMessage.getFromUser(), "zh_CN");
if (null == userWxInfo){
return null;
}
String content = wxMessage.getContent();
if ("文本".equals(content)){
//下面兩種響應方式都可以
//return new TextBuilder().build("您的一互動,泛起了我內心的漣漪。",wxMessage,wxMpService);
return WxMpXmlOutMessage
.TEXT()
.content("您的一互動,就激起了我內心的無限可能")
.fromUser(wxMessage.getToUser())
.toUser(wxMessage.getFromUser())
.build();
}
if ("圖片".equals(content)){
return WxMpXmlOutMessage
.IMAGE()
.mediaId("1C72rnlYrj7ZqBiRGdKCoS54AXQwSo4iULd9qRhOC-U")
.fromUser(wxMessage.getToUser())
.toUser(wxMessage.getFromUser())
.build();
}
if ("圖文".equals(content)){
List<WxMpXmlOutNewsMessage.Item> articles = new ArrayList<>();
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item();
item.setDescription("使用Java語言進行開發微信公衆號發送圖文消息");
item.setPicUrl("http://mmbiz.qpic.cn/mmbiz_jpg/DwMlrmia5oVQrbNsb6GJ64xlUtfTXspTcgAU6Jvbt3uL72LqN3ToB4ibiaWkTZT7SD0IUD56uGiaFRXpI8vBYtYrAQ/0?wx_fmt=jpeg");
item.setTitle("SpringBoot開發微信公衆號");
item.setUrl("https://blog.csdn.net/thinkingcao/category_9277860.html");
articles.add(item);
return WxMpXmlOutMessage
.NEWS()
.addArticle(item)
.articles(articles)
.fromUser(wxMessage.getToUser())
.toUser(wxMessage.getFromUser())
.build();
}
return null;
}
}
九、測試被動回覆圖文消息
在公衆號對話框內輸入: 圖文 關鍵字後,後臺會通過路由匹配該消息類型MsgType = text ,然後找到具體的文本消息處理器,也就是TextMsgHandler,然後進一步匹配關鍵字‘圖文’,然後組裝回復圖文消息字段,將圖文消息回覆給用戶互動者,那麼這裏簡單插一句,在實際項目開發中,構建被動消息回覆肯定是要實現動態的內容,那麼所有字段產生的數據肯定都是要通過數據庫表記錄的,在構建被動消息回覆時,從數據庫取即可,這樣一來就實現了動態被動回覆用戶消息。
- 後臺請求響應
2020-06-10 23:51:23.334 DEBUG 4812 --- [nio-8080-exec-3] m.c.w.mp.api.impl.BaseWxMpServiceImpl :
【請求地址】: https://api.weixin.qq.com/cgi-bin/user/info?access_token=34_Gsg49pv1E8sqzyc7DeHUXSVtUQZSxxTXkXp3h_S_MCOXYwIuqqH41B4a6eF3HpokFucpJVjJIPpPvKwQYCXFMWdAJc45Yd8BmEk-PPHWDg_iJ5INbTuWn9m3gQdHQV-zcbtFN2V8-DGDZo6WSHXfAAAWLB
【請求參數】:openid=oGjQdw2EyT7CBNfN84Te6IpmflCM&lang=zh_CN
【響應數據】:{"subscribe":1,"openid":"oGjQdw2EyT7CBNfN84Te6IpmflCM","nickname":"曹","sex":1,"language":"zh_CN","city":"墨爾本","province":"維多利亞","country":"澳大利亞","headimgurl":"http:\/\/thirdwx.qlogo.cn\/mmopen\/1ZMUBCDTp8ZAsxH99cX3icFXXDSstNaIR1FDpibnmfNPEn1J7Hf9yLXicSHJiciaEgtwgTXRicib9X2mua4bpeEg2sWNics6rXnIKKq7\/132","subscribe_time":1589956861,"remark":"","groupid":0,"tagid_list":[],"subscribe_scene":"ADD_SCENE_QR_CODE","qr_scene":0,"qr_scene_str":""}
2020-06-10 23:51:23.335 DEBUG 4812 --- [nio-8080-exec-3] m.c.weixin.mp.api.WxMpMessageRouter : End session access: async=false, sessionId=oGjQdw2EyT7CBNfN84Te6IpmflCM
2020-06-10 23:51:23.335 DEBUG 4812 --- [pool-1-thread-4] m.c.weixin.mp.api.WxMpMessageRouter : End session access: async=true, sessionId=oGjQdw2EyT7CBNfN84Te6IpmflCM
2020-06-10 23:51:23.336 DEBUG 4812 --- [nio-8080-exec-3] c.t.w.controller.WxPortalController :
組裝回覆信息:<xml>
<ToUserName><![CDATA[oGjQdw2EyT7CBNfN84Te6IpmflCM]]></ToUserName>
<FromUserName><![CDATA[gh_833ac613acf7]]></FromUserName>
<CreateTime>1591804283</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<Articles>
<item>
<Title><![CDATA[SpringBoot開發微信公衆號]]></Title>
<Description><![CDATA[使用Java語言進行開發微信公衆號發送圖文消息]]></Description>
<PicUrl><![CDATA[http://mmbiz.qpic.cn/mmbiz_jpg/DwMlrmia5oVQrbNsb6GJ64xlUtfTXspTcyuV1m6ykgiaGJQYR374WWfGGrgibJOibYc1df7Wyicw4u9f5CV3u7oAzow/0?wx_fmt=jpeg]]></PicUrl>
<Url><![CDATA[https://blog.csdn.net/thinkingcao/category_9277860.html]]></Url>
</item>
</Articles>
<ArticleCount>1</ArticleCount>
</xml>
- 對話框互動
十、源碼
源碼: https://github.com/Thinkingcao/SpringBootLearning/tree/master/springboot-wechat