Java微信公眾平臺(tái)之消息管理
Java微信公眾平臺(tái)開(kāi)發(fā)之消息管理,一定要先看下官方文檔
微信消息管理分為接收普通消息、接收事件推送、發(fā)送消息(被動(dòng)回復(fù))、客服消息、群發(fā)消息、模板消息這幾部分
一、接收普通消息
當(dāng)普通微信用戶(hù)向公眾賬號(hào)發(fā)消息時(shí),微信服務(wù)器將POST消息的XML數(shù)據(jù)包到開(kāi)發(fā)者填寫(xiě)的URL上。
關(guān)于MsgId,官方給出解釋?zhuān)喈?dāng)于每個(gè)消息ID,關(guān)于重試的消息排重,推薦使用msgid排重。微信服務(wù)器在五秒內(nèi)收不到響應(yīng)會(huì)斷掉連接,并且重新發(fā)起請(qǐng)求,總共重試三次。
比如文本消息的Xml示例
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a test]]></Content> <MsgId>1234567890123456</MsgId> </xml>
其他的消息去官方文檔查看,簡(jiǎn)單封裝如下
消息抽象基類(lèi)AbstractMsg.java
package com.phil.wechat.msg.model.req; import java.io.Serializable; /** * 基礎(chǔ)消息類(lèi) * * @author phil * */ public abstract class AbstractMsg implements Serializable { private static final long serialVersionUID = -6244277633057415731L; private String ToUserName; // 開(kāi)發(fā)者微信號(hào) private String FromUserName; // 發(fā)送方帳號(hào)(一個(gè)OpenID) private String MsgType = SetMsgType(); // 消息類(lèi)型 例如 /text/image private long CreateTime; // 消息創(chuàng)建時(shí)間 (整型) private long MsgId; // 消息id,64位整型 /** * 消息類(lèi)型 * * @return */ public abstract String SetMsgType(); }
文本消息TextMsg.java
package com.phil.wechat.msg.model.req; /** * 文本消息 * @author phil * @date 2017年6月30日 * */ public class TextMsg extends AbstractMsg { private static final long serialVersionUID = -1764016801417503409L; private String Content; // 文本消息 @Override public String SetMsgType() { return "text"; } }
其他的依樣畫(huà)葫蘆......
二、被動(dòng)回復(fù)用戶(hù)消息
微信服務(wù)器在將用戶(hù)的消息發(fā)給公眾號(hào)的開(kāi)發(fā)者服務(wù)器地址(開(kāi)發(fā)者中心處配置)后,微信服務(wù)器在五秒內(nèi)收不到響應(yīng)會(huì)斷掉連接,并且重新發(fā)起請(qǐng)求,總共重試三次,如果在調(diào)試中,發(fā)現(xiàn)用戶(hù)無(wú)法收到響應(yīng)的消息,可以檢查是否消息處理超時(shí)。假如服務(wù)器無(wú)法保證在五秒內(nèi)處理并回復(fù),可以直接回復(fù)空串,微信服務(wù)器不會(huì)對(duì)此作任何處理,并且不會(huì)發(fā)起重試。
如果出現(xiàn)“該公眾號(hào)暫時(shí)無(wú)法提供服務(wù),請(qǐng)稍后再試”,原因有兩個(gè)
- 開(kāi)發(fā)者在5秒內(nèi)未回復(fù)任何內(nèi)容
- 開(kāi)發(fā)者回復(fù)了異常數(shù)據(jù)
比如回復(fù)的文本消息Xml示例
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好]]></Content> </xml>
簡(jiǎn)單封裝下
回復(fù)消息抽象基類(lèi)RespAbstractMsg.java
package com.phil.wechat.msg.model.resp; import java.io.Serializable; /** * 消息基類(lèi)(公眾帳號(hào) -> 普通用戶(hù)) * * @author phil * */ public abstract class RespAbstractMsg{ // 接收方帳號(hào)(收到的OpenID) private String ToUserName; // 開(kāi)發(fā)者微信號(hào) private String FromUserName; // 消息創(chuàng)建時(shí)間 (整型) private long CreateTime; // 消息類(lèi)型(text/music/news) private String MsgType = setMsgType(); // 消息類(lèi)型 public abstract String setMsgType(); }
回復(fù)文本消息RespTextMsg.java
package com.phil.wechat.msg.model.resp; /** * 回復(fù)圖片消息 * * @author phil * @data 2017年3月26日 * */ public class RespImageMsg extends RespAbstractMsg { private Image Image; @Override public String setMsgType() { return "image"; } /** * * @author phil * @date 2017年7月19日 * */ public class Image { // 通過(guò)素材管理中的接口上傳多媒體文件,得到的id。 private String MediaId; public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } } }
其他消息類(lèi)型依樣畫(huà)葫蘆......
三、消息的處理
掌握xml解析
package com.phil.wechat.msg.controller; import java.io.IOException; import java.io.InputStream; import java.util.Map; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.dom4j.DocumentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.phil.modules.config.WechatConfig; import com.phil.modules.util.MsgUtil; import com.phil.modules.util.SignatureUtil; import com.phil.modules.util.XmlUtil; import com.phil.wechat.base.controller.BaseController; import com.phil.wechat.base.result.WechatResult; import com.phil.wechat.msg.model.req.BasicMsg; import com.phil.wechat.msg.model.resp.RespAbstractMsg; import com.phil.wechat.msg.model.resp.RespNewsMsg; import com.phil.wechat.msg.service.WechatMsgService; /** * @author phil * @date 2017年9月19日 * */ @Controller @RequestMapping("/wechat") public class WechatMsgController extends BaseController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private WechatMsgService wechatMsgService; /** * 校驗(yàn)信息是否是從微信服務(wù)器發(fā)出,處理消息 * @param out * @throws IOException */ @RequestMapping(value = "/handler", method = { RequestMethod.GET, RequestMethod.POST }) public void processPost() throws Exception { this.getRequest().setCharacterEncoding("UTF-8"); this.getResponse().setCharacterEncoding("UTF-8"); boolean ispost = Objects.equals("POST", this.getRequest().getMethod().toUpperCase()); if (ispost) { logger.debug("接入成功,正在處理邏輯"); String respXml = defaultMsgDisPose(this.getRequest().getInputStream());//processRequest(request, response); if (StringUtils.isNotBlank(respXml)) { this.getResponse().getWriter().write(respXml); } } else { String signature = this.getRequest().getParameter("signature"); // 時(shí)間戳 String timestamp = this.getRequest().getParameter("timestamp"); // 隨機(jī)數(shù) String nonce = this.getRequest().getParameter("nonce"); // 通過(guò)檢驗(yàn)signature對(duì)請(qǐng)求進(jìn)行校驗(yàn),若校驗(yàn)成功則原樣返回echostr,表示接入成功,否則接入失敗 if (SignatureUtil.checkSignature(signature, timestamp, nonce)) { // 隨機(jī)字符串 String echostr = this.getRequest().getParameter("echostr"); logger.debug("接入成功,echostr {}", echostr); this.getResponse().getWriter().write(echostr); } } } /** * 默認(rèn)處理方法 * @param input * @return * @throws Exception * @throws DocumentException */ private String defaultMsgDisPose(InputStream inputStream) throws Exception { String result = null; if (inputStream != null) { Map<String, String> params = XmlUtil.parseStreamToMap(inputStream); if (params != null && params.size() > 0) { BasicMsg msgInfo = new BasicMsg(); String createTime = params.get("CreateTime"); String msgId = params.get("MsgId"); msgInfo.setCreateTime((createTime != null && !"".equals(createTime)) ? Integer.parseInt(createTime) : 0); msgInfo.setFromUserName(params.get("FromUserName")); msgInfo.setMsgId((msgId != null && !"".equals(msgId)) ? Long.parseLong(msgId) : 0); msgInfo.setToUserName(params.get("ToUserName")); WechatResult resultObj = coreHandler(msgInfo, params); if(resultObj == null){ // return null; } boolean success = resultObj.isSuccess(); //如果 為true,則表示返回xml文件, 直接轉(zhuǎn)換即可,否則按類(lèi)型 if (success) { result = resultObj.getObject().toString(); } else { int type = resultObj.getType(); // 這里規(guī)定 1 圖文消息 否則直接轉(zhuǎn)換 if (type == WechatResult.NEWSMSG) { RespNewsMsg newsMsg = (RespNewsMsg) resultObj.getObject(); result = MsgUtil.newsMsgToXml(newsMsg); } else { RespAbstractMsg basicMsg = (RespAbstractMsg) resultObj.getObject(); result = MsgUtil.msgToXml(basicMsg); } } } else { result = "msg is wrong"; } } return result; } /** * 核心處理 * * @param msg * 消息基類(lèi) * @param params * xml 解析出來(lái)的 數(shù)據(jù) * @return */ private WechatResult coreHandler(BasicMsg msg, Map<String, String> params) { WechatResult result = null; String msgType = params.get("MsgType"); if (StringUtils.isEmpty(msgType)) { switch (msgType) { case WechatConfig.REQ_MESSAGE_TYPE_TEXT: // 文本消息 result = wechatMsgService.textMsg(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_IMAGE: // 圖片消息 result = wechatMsgService.imageMsg(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_LINK: // 鏈接消息 result = wechatMsgService.linkMsg(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_LOCATION: // 地理位置 result = wechatMsgService.locationMsg(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_VOICE: // 音頻消息 result = wechatMsgService.voiceMsg(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_SHORTVIDEO: // 短視頻消息 result = wechatMsgService.shortvideo(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_VIDEO: // 視頻消息 result = wechatMsgService.videoMsg(msg, params); break; case WechatConfig.REQ_MESSAGE_TYPE_EVENT: // 事件消息 String eventType = params.get("Event"); // if (eventType != null && !"".equals(eventType)) { switch (eventType) { case WechatConfig.EVENT_TYPE_SUBSCRIBE: result = wechatMsgService.subscribe(msg, params); break; case WechatConfig.EVENT_TYPE_UNSUBSCRIBE: result = wechatMsgService.unsubscribe(msg, params); break; case WechatConfig.EVENT_TYPE_SCAN: result = wechatMsgService.scan(msg, params); break; case WechatConfig.EVENT_TYPE_LOCATION: result = wechatMsgService.eventLocation(msg, params); break; case WechatConfig.EVENT_TYPE_CLICK: result = wechatMsgService.eventClick(msg, params); break; case WechatConfig.EVENT_TYPE_VIEW: result = wechatMsgService.eventView(msg, params); break; case WechatConfig.KF_CREATE_SESSION: result = wechatMsgService.kfCreateSession(msg, params); break; case WechatConfig.KF_CLOSE_SESSION: result = wechatMsgService.kfCloseSession(msg, params); break; case WechatConfig.KF_SWITCH_SESSION: result = wechatMsgService.kfSwitchSession(msg, params); break; default: wechatMsgService.eventDefaultReply(msg, params); break; } } break; default: wechatMsgService.defaultMsg(msg, params); } } return result; } }
只是提供個(gè)思路,如若參考代碼請(qǐng)移步
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Spring Boot應(yīng)用的啟動(dòng)和停止(start啟動(dòng))
這篇文章主要介紹了詳解Spring Boot應(yīng)用的啟動(dòng)和停止(start啟動(dòng)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯
最近項(xiàng)目要求部署到其他公司的服務(wù)器上,但是又不想將源碼泄露出去,要求對(duì)正式環(huán)境的啟動(dòng)包進(jìn)行安全性處理,防止客戶(hù)直接通過(guò)反編譯工具將代碼反編譯出來(lái),本文介紹了如何對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯,需要的朋友可以參考下2023-10-10淺談MyBatis3 DynamicSql風(fēng)格語(yǔ)法使用指南
這篇文章主要介紹了淺談MyBatis3 DynamicSql風(fēng)格語(yǔ)法使用指南,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03LIS 最長(zhǎng)遞增子序列 Java的簡(jiǎn)單實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇LIS 最長(zhǎng)遞增子序列 Java的簡(jiǎn)單實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09Redis使用RedisTemplate模板類(lèi)的常用操作方式
這篇文章主要介紹了Redis使用RedisTemplate模板類(lèi)的常用操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java inputstream和outputstream使用詳解
這篇文章主要介紹了Java inputstream和outputstream使用詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08java搜索無(wú)向圖中兩點(diǎn)之間所有路徑的算法
這篇文章主要介紹了java搜索無(wú)向圖中兩點(diǎn)之間所有路徑的算法2019-01-01JAVA中Collections工具類(lèi)sort()排序方法
這篇文章主要介紹了JAVA中Collections工具類(lèi)sort()排序方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2016-11-11springboot實(shí)現(xiàn)異步調(diào)用@Async的示例
這篇文章主要介紹了springboot實(shí)現(xiàn)異步調(diào)用@Async的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12