在開發之前我們要先知道用戶發送的信息是先傳送到微信服務器,微信服務器在以xml的格式發送給進行公衆號
開發流程圖
代碼實現
控制層代碼
/**
* 微信核心控制器(驗證服務號是否合法,以及消息轉發都需要通過此controller)
*/
package com.b505.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.b505.tool.DataProcess;
import com.b505.trycatch.TryCatchWeixinCoreService;
import com.b505.weixin.util.WeixinSignUtil;
import com.b505.weixin.util.WeixinVerification;
/**
* <p>Company: B505信息技術研究所 </p>
* @Description: 微信核心controller,接入微信服務層
* @Create Date: 2017年10月9日下午7:31:29
* @Version: V1.00
* @Author: 來日可期
*/
@RestController
@RequestMapping(value = "/wechat")
public class WeixinCoreController {
private static Logger logger = LoggerFactory.getLogger(WeixinCoreController.class);
@Autowired
private WeixinSignUtil weixinSignUtil;
@Autowired
private DataProcess dataProcess;
@Autowired
private WeixinCoreService weixinCoreService;
@RequestMapping(value="/access", method=RequestMethod.POST)
public String getWeiXinMessage(HttpServletRequest request, HttpServletResponse response)throws Exception{
logger.info("----------------開始處理微信發過來的消息------------------");
// 微信服務器POST消息時用的是UTF-8編碼,在接收時也要用同樣的編碼,否則中文會亂碼;
request.setCharacterEncoding("UTF-8");
// 在響應消息(回覆消息給用戶)時,也將編碼方式設置爲UTF-8,原理同上;
response.setCharacterEncoding("UTF-8");
String respXml = weixinCoreService.weixinMessageHandelCoreService(request, response);
if (dataProcess.dataIsNull(respXml)){
logger.error("-------------處理微信消息失敗-----------------------");
return null;
}else {
logger.info("----------返回微信消息處理結果-----------------------:"+respXml);
return respXml;
}
}
}
service層代碼
/**
* 微信消息處理核心service實現類
*/
package com.b505.service.impl;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.b505.dao.UserDao;
import com.b505.service.WeixinCoreService;
import com.b505.tool.DataProcess;
import com.b505.weixin.message.resp.TextMessage;
import com.b505.weixin.pojo.WeixinMessageInfo;
import com.b505.weixin.util.WeixinMessageModelUtil;
import com.b505.weixin.util.WeixinMessageUtil;
/**
* <p>Company: B505信息技術研究所 </p>
* @Description: 微信消息處理核心service實現類
* @Create Date: 2017年10月10日下午3:33:16
* @Version: V1.00
* @Author: 來日可期
*/
@Service("weixinCoreService")
public class WeixinCoreServiceImpl implements WeixinCoreService {
private static Logger logger = LoggerFactory.getLogger(WeixinCoreServiceImpl.class);
@Autowired
private WeixinMessageUtil weixinMessageUtil;
@Autowired
private WeixinMessageInfo weixinMessageInfo;
@Autowired
private WeixinMessageModelUtil weixinMessageModelUtil;
@Autowired
private UserDao userDao;
@Autowired
private DataProcess dataProcess;
@Override
public String weixinMessageHandelCoreService(HttpServletRequest request,
HttpServletResponse response) {
logger.info("------------微信消息開始處理-------------");
// 返回給微信服務器的消息,默認爲null
String respMessage = null;
try {
// 默認返回的文本消息內容
String respContent = null;
// xml分析
// 調用消息工具類MessageUtil解析微信發來的xml格式的消息,解析的結果放在HashMap裏;
Map<String, String> map = weixinMessageUtil.parseXml(request);
// 發送方賬號
String fromUserName = map.get("FromUserName");
weixinMessageInfo.setFromUserName(fromUserName);
System.out.println("fromUserName--->"+fromUserName);
// 接受方賬號(公衆號)
String toUserName = map.get("ToUserName");
weixinMessageInfo.setToUserName(toUserName);
System.out.println("toUserName----->"+toUserName);
// 消息類型
String msgType = map.get("MsgType");
weixinMessageInfo.setMessageType(msgType);
logger.info("fromUserName is:" +fromUserName+" toUserName is:" +toUserName+" msgType is:" +msgType);
// 默認回覆文本消息
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType(weixinMessageUtil.RESP_MESSAGE_TYPE_TEXT);
textMessage.setFuncFlag(0);
// 分析用戶發送的消息類型,並作出相應的處理
// 文本消息
if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_TEXT)){
respContent = "親,這是文本消息!";
textMessage.setContent(respContent);
respMessage = weixinMessageUtil.textMessageToXml(textMessage);
}
// 圖片消息
else if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
respContent = "您發送的是圖片消息!";
textMessage.setContent(respContent);
respMessage = weixinMessageUtil.textMessageToXml(textMessage);
}
// 語音消息
else if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
respContent = "您發送的是語音消息!";
textMessage.setContent(respContent);
respMessage = weixinMessageUtil.textMessageToXml(textMessage);
}
// 視頻消息
else if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_VIDEO)) {
respContent = "您發送的是視頻消息!";
textMessage.setContent(respContent);
respMessage = weixinMessageUtil.textMessageToXml(textMessage);
}
// 地理位置消息
else if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
respContent = "您發送的是地理位置消息!";
textMessage.setContent(respContent);
respMessage = weixinMessageUtil.textMessageToXml(textMessage);
}
// 鏈接消息
else if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_LINK)) {
respContent = "您發送的是鏈接消息!";
textMessage.setContent(respContent);
respMessage = weixinMessageUtil.textMessageToXml(textMessage);
}
// 事件推送(當用戶主動點擊菜單,或者掃面二維碼等事件)
else if (msgType.equals(weixinMessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
// 事件類型
String eventType =map.get("Event");
System.out.println("eventType------>"+eventType);
// 關注
if (eventType.equals(weixinMessageUtil.EVENT_TYPE_SUBSCRIBE)){
respMessage = weixinMessageModelUtil.followResponseMessageModel(weixinMessageInfo);
}
// 取消關注
else if (eventType.equals(weixinMessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
weixinMessageModelUtil.cancelAttention(fromUserName);
}
// 掃描帶參數二維碼
else if (eventType.equals(weixinMessageUtil.EVENT_TYPE_SCAN)) {
System.out.println("掃描帶參數二維碼");
}
// 上報地理位置
else if (eventType.equals(weixinMessageUtil.EVENT_TYPE_LOCATION)) {
System.out.println("上報地理位置");
}
// 自定義菜單(點擊菜單拉取消息)
else if (eventType.equals(weixinMessageUtil.EVENT_TYPE_CLICK)) {
// 事件KEY值,與創建自定義菜單時指定的KEY值對應
String eventKey=map.get("EventKey");
System.out.println("eventKey------->"+eventKey);
}
// 自定義菜單((自定義菜單URl視圖))
else if (eventType.equals(weixinMessageUtil.EVENT_TYPE_VIEW)) {
System.out.println("處理自定義菜單URI視圖");
}
}
} catch (Exception e) {
e.printStackTrace();
logger.error("系統出錯");
System.err.println("系統出錯");
respMessage = null;
}
finally{
if (null == respMessage) {
respMessage = weixinMessageModelUtil.systemErrorResponseMessageModel(weixinMessageInfo);
}
}
return respMessage;
}
}
工具類
/**
* 封裝微信消息類型,有一個解析微信發過的xml消息的工具
*/
package com.b505.weixin.util;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Component;
import com.b505.Message.model.Article;
import com.b505.weixin.message.resp.ImageMessage;
import com.b505.weixin.message.resp.MusicMessage;
import com.b505.weixin.message.resp.NewsMessage;
import com.b505.weixin.message.resp.TextMessage;
import com.b505.weixin.message.resp.VideoMessage;
import com.b505.weixin.message.resp.VoiceMessage;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
/**
* <p>Company: B505信息技術研究所 </p>
* @Description: 封裝微信消息類型,有一個解析xml格式的工具
* @Create Date: 2017年10月11日上午11:28:48
* @Version: V1.00
* @Author: 來日可期
*/
@Component
public class WeixinMessageUtil {
/**
* 請求消息類型:文本
*/
public final String REQ_MESSAGE_TYPE_TEXT = "text";
/**
* 請求消息類型:圖片
*/
public final String REQ_MESSAGE_TYPE_IMAGE="image";
/**
* 請求消息類型:語音
*/
public final String REQ_MESSAGE_TYPE_VOICE="voice";
/**
* 請求消息類型:視頻
*/
public final String REQ_MESSAGE_TYPE_VIDEO="video";
/**
* 請求消息類型:鏈接
*/
public final String REQ_MESSAGE_TYPE_LINK = "link";
/**
* 請求消息類型:地理位置
*/
public final String REQ_MESSAGE_TYPE_LOCATION="location";
/**
* 請求消息類型:小視頻
*/
public final String REQ_MESSAGE_TYPE_SHORTVIDEO="shortvideo";
/**
*請求消息類型:事件推送
*/
public final String REQ_MESSAGE_TYPE_EVENT = "event";
/**
* 返回消息類型:文本
*/
public final String RESP_MESSAGE_TYPE_TEXT = "text";
/**
* 消息返回類型:圖片
*/
public final String RESP_MESSAGE_TYPE_IMAGE="image";
/**
* 消息返回類型:語音
*/
public final String RESP_MESSAGE_TYPE_VOICE = "voice";
/**
* 消息返回類型:音樂
*/
public final String RESP_MESSAGE_TYPE_MUSIC = "music";
/**
* 消息返回類型:圖文
*/
public final String RESP_MESSAGE_TYPE_NEWS = "news";
/**
* 消息返回類型:視頻
*/
public final String RESP_MESSAGE_TYPE_VIDEO="video";
/**
* 事件類型:訂閱
*/
public final String EVENT_TYPE_SUBSCRIBE = "subscribe";
/**
* 事件類型:取消訂閱
*/
public final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
/**
* 事件類型:scan(關注用戶掃描帶參二維碼)
*/
public final String EVENT_TYPE_SCAN = "scan";
/**
* 事件類型:location(上報地理位置)
*/
public final String EVENT_TYPE_LOCATION = "location";
/**
* 事件類型:CLICK(點擊菜單拉取消息)
*/
public final String EVENT_TYPE_CLICK ="CLICK";
/**
* 事件類型:VIEW(自定義菜單URl視圖)
*/
public final String EVENT_TYPE_VIEW ="VIEW";
/**
* 事件類型:TEMPLATESENDJOBFINISH(模板消息送達情況提醒)
*/
public final String EVENT_TYPE_TEMPLATESENDJOBFINISH="TEMPLATESENDJOBFINISH";
/**
* @Description: 解析微信服務器發過來的xml格式的消息將其轉換爲map
* @Parameters: WeixinMessageUtil
* @Return: Map<String, String>
* @Create Date: 2017年10月11日上午11:41:23
* @Version: V1.00
* @author:來日可期
*/
public Map<String, String> parseXml(HttpServletRequest request)throws Exception{
// 將解析結果存儲在HashMap中
Map<String, String>map =new HashMap<String, String>();
// 從request中得到輸入流
InputStream inputStream=request.getInputStream();
// 讀取輸入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到XML的根元素
Element root = document.getRootElement();
// 得到根元素的所有子節點
@SuppressWarnings("unchecked")
List<Element> elementList = root.elements();
// 判斷又沒有子元素列表
if (elementList.size()==0){
map.put(root.getName(), root.getText());
}else {
for (Element e : elementList)
map.put(e.getName(), e.getText());
}
// 釋放資源
inputStream.close();
inputStream = null;
System.out.println("---------xml轉換爲map-----:"+map);
return map;
}
/**
* @Description: 文本消息對象轉換成xml
* @param textMessage
* @date 2016-12-01
* @return xml
*/
public String textMessageToXml(TextMessage textMessage) {
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
/**
* @Description: 圖文消息對象轉換成xml
* @param newsMessage
* @date 2016-12-01
* @return xml
*/
public String newsMessageToXml(NewsMessage newsMessage) {
xstream.alias("xml", newsMessage.getClass());
xstream.alias("item", new Article().getClass());
return xstream.toXML(newsMessage);
}
/**
* @Description: 圖片消息對象轉換成xml
* @param imageMessage
* @date 2016-12-01
* @return xml
*/
public String imageMessageToXml(ImageMessage imageMessage) {
xstream.alias("xml", imageMessage.getClass());
return xstream.toXML(imageMessage);
}
/**
* @Description: 語音消息對象轉換成xml
* @param voiceMessage
* @date 2016-12-01
* @return xml
*/
public String voiceMessageToXml(VoiceMessage voiceMessage) {
xstream.alias("xml", voiceMessage.getClass());
return xstream.toXML(voiceMessage);
}
/**
* @Description: 視頻消息對象轉換成xml
* @param videoMessage
* @date 2016-12-01
* @return xml
*/
public String videoMessageToXml(VideoMessage videoMessage) {
xstream.alias("xml", videoMessage.getClass());
return xstream.toXML(videoMessage);
}
/**
* @Description: 音樂消息對象轉換成xml
* @param MusicMessage
* @date 2016-12-01
* @return xml
*/
public String musicMessageToXml(MusicMessage musicMessage) {
xstream.alias("xml", musicMessage.getClass());
return xstream.toXML(musicMessage);
}
/**
* 對象到xml的處理
* 擴展xstream,使其支持CDATA塊
*/
private XStream xstream = new XStream(new XppDriver() {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 對所有xml節點的轉換都增加CDATA標記
boolean cdata = true;
@Override
@SuppressWarnings("rawtypes")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
}
封裝的WeixinMessageInfo類
/**
* 將發送方,接收方,微信用戶名等封裝成類
*/
package com.b505.weixin.pojo;
import java.io.Serializable;
import org.springframework.stereotype.Component;
/**
* <p>Company: B505信息技術研究所 </p>
* @Description: 將發送方,接收方,微信用戶名封裝
* @Create Date: 2017年10月23日上午11:45:44
* @Version: V1.00
* @Author: 來日可期
*/
@Component
public class WeixinMessageInfo implements Serializable{
private static final long serialVersionUID = 1L;
private String fromUserName; // 發送發微信賬號
private String toUserName; // 接收方微信賬號
private String weixinUserName; // 微信用戶名
private String messageType; // 消息類型
/**
* @return the fromUserName
*/
public String getFromUserName() {
return fromUserName;
}
/**
* @param fromUserName the fromUserName to set
*/
public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}
/**
* @return the toUserName
*/
public String getToUserName() {
return toUserName;
}
/**
* @param toUserName the toUserName to set
*/
public void setToUserName(String toUserName) {
this.toUserName = toUserName;
}
/**
* @return the weixinUserName
*/
public String getWeixinUserName() {
return weixinUserName;
}
/**
* @param weixinUserName the weixinUserName to set
*/
public void setWeixinUserName(String weixinUserName) {
this.weixinUserName = weixinUserName;
}
/**
* @return the messageType
*/
public String getMessageType() {
return messageType;
}
/**
* @param messageType the messageType to set
*/
public void setMessageType(String messageType) {
this.messageType = messageType;
}
@Override
public String toString() {
return "WeixinMessageInfo [fromUserName=" + fromUserName
+ ", toUserName=" + toUserName + ", weixinUserName="
+ weixinUserName + ", messageType=" + messageType + "]";
}
}
文中涉及到的圖文消息等回覆的消息將在下節中介紹