1.准备

1.准备服务

与微信对接的url要具备以下条件:

(1)在公网上能够访问

(2)端口只支持80端口

  在这里如果是公网能够访问的服务最好,也可以通过花生壳或者其他外网映射工具进行映射,比如ngrok。

2.数据交互原理

  开发模式与编辑模式是互斥的,打开开发模式的时候,编辑模式的自动回复与自定义菜单失效;打开编辑模式的自动回复或者自定义菜单的时候开发模式会失效。

 

 

开发模式的数据交互原理如下:

 

  我们需要开发的任务就是维信公众号服务器,包括业务逻辑、身份验证等操作。

2.接入后台

  参考公众号开发文档:   开发->开发者工具-》开发者文档,里面有类似于对接钉钉的文档,有接入指南以及其他接口文档。

  https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html

1.  第一步:填写服务器配置

  到公众号后台: https://mp.weixin.qq.com/

 

 

2 第二步:验证消息的确来自微信服务器(在自己的微信服务器进行验证)

  开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

 

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:

1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

SpringMVC接收代码如下:

package cn.qlq.controller.weixin;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import cn.qlq.controller.UserController;

import cn.qlq.utils.weixin.WeixinCheckUtils;

@Controller

@RequestMapping("weixin")

public class WeixinController {

private static final Logger logger = LoggerFactory.getLogger(UserController.class);

@ResponseBody

@RequestMapping("index")

public String index(@RequestParam(required = false) String signature,

@RequestParam(required = false) String timestamp, @RequestParam(required = false) String nonce,

@RequestParam(required = false) String echostr) {

logger.debug("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr);

if (StringUtils.isNoneBlank(signature, timestamp, nonce)

&& WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) {

return echostr;

}

return "error";

}

}

 

验证工具如下:

package cn.qlq.utils.weixin;

import java.security.MessageDigest;

import java.util.Arrays;

public class WeixinCheckUtils {

// token,与微信公众号后台的一致

private static final String token = "devqiaolq";

public static boolean checkSignature(String signature, String timestamp, String nonce) {

String[] arr = new String[] { token, timestamp, nonce };

// 排序

Arrays.sort(arr);

// 生成字符串

StringBuffer content = new StringBuffer();

for (int i = 0; i < arr.length; i++) {

content.append(arr[i]);

}

// sha1加密

String temp = getSha1(content.toString());

return temp.equals(signature);

}

public static String getSha1(String str) {

if (str == null || str.length() == 0) {

return null;

}

char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

try {

MessageDigest mdTemp = MessageDigest.getInstance("SHA1");

mdTemp.update(str.getBytes("UTF-8"));

byte[] md = mdTemp.digest();

int j = md.length;

char buf[] = new char[j * 2];

int k = 0;

for (int i = 0; i < j; i++) {

byte byte0 = md[i];

buf[k++] = hexDigits[byte0 >>> 4 & 0xf];

buf[k++] = hexDigits[byte0 & 0xf];

}

return new String(buf);

} catch (Exception e) {

return null;

}

}

}

 

  注意:如果有登录过滤器,记得在过滤器中放行微信请求.

 

3. 第三步:依据接口文档实现业务逻辑

1. 首先需要启用开发者模式:(启用开发者模式之后自己的自定义菜单就不会生效)

2. 接收与响应文字消息

  当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

文本消息的xml数据格式如下:

1348831860

1234567890123456

 

参数解释:

参数描述

ToUserName

开发者微信号

FromUserName

发送方帐号(一个OpenID)

CreateTime

消息创建时间 (整型)

MsgType

消息类型,文本为text

Content

文本消息内容

MsgId

消息id,64位整型

 

(1)建立后台对应的TextMessage实体类:

package cn.qlq.bean.weixin;

public class TextMessage {

/**

* 开发者微信号

*/

private String ToUserName;

/**

* 发送方帐号(一个OpenID)

*/

private String FromUserName;

/**

* 消息创建时间 (整型)

*/

private long CreateTime;

/**

* text

*/

private String MsgType;

/**

* 文本消息内容

*/

private String Content;

/**

* 消息id,64位整型

*/

private String MsgId;

@Override

public String toString() {

return "TextMessage{" + "ToUserName='" + ToUserName + '\'' + ", FromUserName='" + FromUserName + '\''

+ ", CreateTime=" + CreateTime + ", MsgType='" + MsgType + '\'' + ", Content='" + Content + '\''

+ ", MsgId='" + MsgId + '\'' + '}';

}

public String getToUserName() {

return ToUserName;

}

public void setToUserName(String toUserName) {

ToUserName = toUserName;

}

public String getFromUserName() {

return FromUserName;

}

public void setFromUserName(String fromUserName) {

FromUserName = fromUserName;

}

public long getCreateTime() {

return CreateTime;

}

public void setCreateTime(long createTime) {

CreateTime = createTime;

}

public String getMsgType() {

return MsgType;

}

public void setMsgType(String msgType) {

MsgType = msgType;

}

public String getContent() {

return Content;

}

public void setContent(String content) {

Content = content;

}

public String getMsgId() {

return MsgId;

}

public void setMsgId(String msgId) {

MsgId = msgId;

}

}

 

(2)编写工具类实现xml(接收的是xml格式的数据)转map和TextMessage对象转换成xml(响应数据格式为xml)

pom加入:

dom4j

dom4j

1.6.1

com.thoughtworks.xstream

xstream

1.4.10

 

工具类:

package cn.qlq.utils.weixin;

import java.io.IOException;

import java.io.InputStream;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;

import org.dom4j.DocumentException;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

import com.thoughtworks.xstream.XStream;

import cn.qlq.bean.weixin.TextMessage;

public class MessageUtils {

/**

* xml数据转map

*

* @param request

* @return

* @throws IOException

* @throws DocumentException

*/

public static Map xmlToMap(HttpServletRequest request) throws IOException, DocumentException {

Map map = new HashMap<>();

SAXReader reader = new SAXReader();

InputStream inputStream = request.getInputStream();

Document document = reader.read(inputStream);

Element root = document.getRootElement();

List list = root.elements();

for (Element element : list) {

map.put(element.getName(), element.getText());

}

inputStream.close();

return map;

}

/**

* 将文本消息对象转换成xml

*

* @param textMessage

* @return

*/

public static String textMessageToXml(TextMessage textMessage) {

XStream xStream = new XStream();

// 将xml的根元素替换成xml

xStream.alias("xml", textMessage.getClass());

return xStream.toXML(textMessage);

}

}

 

(3)重写Controller接收消息和响应消息

package cn.qlq.controller.weixin;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Date;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import org.dom4j.DocumentException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import cn.qlq.bean.weixin.TextMessage;

import cn.qlq.controller.UserController;

import cn.qlq.utils.weixin.MessageUtils;

import cn.qlq.utils.weixin.WeixinCheckUtils;

@Controller

@RequestMapping("weixin")

public class WeixinController {

private static final Logger logger = LoggerFactory.getLogger(UserController.class);

@RequestMapping(value = "index", method = { RequestMethod.GET, RequestMethod.POST })

public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {

// 将请求、响应的编码均设置为UTF-8(防止中文乱码)

request.setCharacterEncoding("UTF-8"); // 微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;

response.setCharacterEncoding("UTF-8"); // 在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;

String method = request.getMethod().toLowerCase();

logger.info("method: {}", method);

// 验证是否是微信请求

if ("get".equals(method)) {

doGet(request, response);

return;

}

// POST请求接收消息,且给客户响应消息

doPost(request, response);

}

/**

* Post请求用于接收消息且处理消息之后回传消息

*

* @param request

* @param response

* @throws IOException

*/

private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

PrintWriter out = response.getWriter();

try {

Map map = MessageUtils.xmlToMap(request);

String fromUserName = map.get("FromUserName");

String toUserName = map.get("ToUserName");

String msgType = map.get("MsgType");

String content = map.get("Content");

logger.info("map: {}", map);

if (StringUtils.isNotBlank(content)) {

System.out.println("接收的的消息为:" + content + ",你可以根据关键字进行搜索或者做其他");

}

String message = null;

if ("text".equals(msgType)) {

TextMessage textMessage = new TextMessage();

// 回传消息,所以讲fromuser和toUser交换

textMessage.setFromUserName(toUserName);

textMessage.setToUserName(fromUserName);

textMessage.setMsgType(msgType);

textMessage.setCreateTime(new Date().getTime());

textMessage.setContent("您发送的消息为: " + content);

logger.info("textMessage: {}", textMessage);

message = MessageUtils.textMessageToXml(textMessage);

}

out.print(message);// 把消息发送到客户端

} catch (DocumentException e) {

logger.error("dispose post request error", e);

} finally {

out.close();

}

}

/**

* Get请求用于微信配置验证

*

* @param request

* @param response

* @throws IOException

*/

private void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {

String signature = request.getParameter("signature");// 微信加密签名

String timestamp = request.getParameter("timestamp");// 时间戳

String nonce = request.getParameter("nonce");// 随机数

String echostr = request.getParameter("echostr");// 随机字符串

logger.info("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr);

if (StringUtils.isNoneBlank(signature, timestamp, nonce)

&& WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) {

response.getWriter().write(echostr);

}

}

}

 

测试:

 

Java服务器日志如下:

2019-10-23 23:34:15.625 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : method: post2019-10-23 23:34:15.633 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : map: {MsgId=22503405793257008, FromUserName=o_qAo0u6Snhoc7Z45RfSxYatMWpo, CreateTime=1571844638, Content=怎么了, ToUserName=gh_fc4bd5c2fda8, MsgType=text}接收的的消息为:怎么了,你可以根据关键字进行搜索或者做其他2019-10-23 23:34:15.635 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : textMessage: TextMessage{ToUserName='o_qAo0u6Snhoc7Z45RfSxYatMWpo', FromUserName='gh_fc4bd5c2fda8', CreateTime=1571844855635, MsgType='text', Content='您发送的消息为: 怎么了', MsgId='null'}2019-10-23 23:34:20.288 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : method: post2019-10-23 23:34:20.295 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : map: {MsgId=22503406289173072, FromUserName=o_qAo0u6Snhoc7Z45RfSxYatMWpo, CreateTime=1571844642, Content=什么意思, ToUserName=gh_fc4bd5c2fda8, MsgType=text}接收的的消息为:什么意思,你可以根据关键字进行搜索或者做其他2019-10-23 23:34:20.296 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : textMessage: TextMessage{ToUserName='o_qAo0u6Snhoc7Z45RfSxYatMWpo', FromUserName='gh_fc4bd5c2fda8', CreateTime=1571844860296, MsgType='text', Content='您发送的消息为: 什么意思', MsgId='null'}

 

4. 接收其他类型的消息以及返回文本消息、图文消息

  公众号可以接受的消息大概有文本消息(在上面已经接收)、图片(image)消息、语音(voice,在后台接口开启语音识别可以自动识别文字)消息、视频(video)消息、短视频(shortvideo)消息、地理位置(location)消息、链接(link)消息、事件(event,事件类型:subscribe(订阅)、unsubscribe(取消订阅))消息。

  公众号可以回复的消息有: 文本(text)消息、图片消息(image)、语音消息(voice)、视频消息(video)、音乐消息(music)、图文(news)消息。

 

注意:这里需要注意接收的消息和发送的消息数据数据格式不一样。如下是回复的图片消息和图文消息的数据格式:

回传图片消息数据格式:

12345678

 

回传图文消息数据格式:

12345678

1

<![CDATA[title1]]>

 

自己封装的controller接收数据和消息处理的工具类如下:

package cn.qlq.controller.weixin;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import org.dom4j.DocumentException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import cn.qlq.bean.weixin.response.AbstractResponseMessage;

import cn.qlq.controller.UserController;

import cn.qlq.utils.weixin.MessageHandler;

import cn.qlq.utils.weixin.MessageUtils;

import cn.qlq.utils.weixin.WeixinCheckUtils;

@Controller

@RequestMapping("weixin")

public class WeixinController {

private static final Logger logger = LoggerFactory.getLogger(UserController.class);

@RequestMapping(value = "index", method = { RequestMethod.GET, RequestMethod.POST })

public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {

// 将请求、响应的编码均设置为UTF-8(防止中文乱码)

request.setCharacterEncoding("UTF-8"); // 微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;

response.setCharacterEncoding("UTF-8"); // 在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;

String method = request.getMethod().toLowerCase();

logger.info("method: {}", method);

// 验证是否是微信请求

if ("get".equals(method)) {

doGet(request, response);

return;

}

// POST请求接收消息,且给客户响应消息

doPost(request, response);

}

/**

* Post请求用于接收消息且处理消息之后回传消息

*

* @param request

* @param response

* @throws IOException

*/

private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

PrintWriter out = response.getWriter();

try {

// 消息转map

Map map = MessageUtils.xmlToMap(request);

logger.info("接收到的消息map: {}", map);

// 调用工具类处理完之后显示回传消息

AbstractResponseMessage responseMessage = MessageHandler.handlMessage(map);

logger.info("回传的消息responseMessage: {}", responseMessage);

if (responseMessage == null) {

return;

}

String messageToXml = MessageUtils.messageToXml(responseMessage);

logger.info("回传的消息responseMessage messageToXml: {}", messageToXml);

out.print(messageToXml);// 把消息发送到客户端

} catch (DocumentException e) {

logger.error("dispose post request error", e);

} finally {

out.close();

}

}

/**

* Get请求用于微信配置验证

*

* @param request

* @param response

* @throws IOException

*/

private void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {

String signature = request.getParameter("signature");// 微信加密签名

String timestamp = request.getParameter("timestamp");// 时间戳

String nonce = request.getParameter("nonce");// 随机数

String echostr = request.getParameter("echostr");// 随机字符串

logger.info("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr);

if (StringUtils.isNoneBlank(signature, timestamp, nonce)

&& WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) {

response.getWriter().write(echostr);

}

}

}

 

package cn.qlq.utils.weixin;

import java.util.Date;

import java.util.Map;

import org.apache.commons.collections.MapUtils;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import cn.qlq.bean.weixin.EventMessage;

import cn.qlq.bean.weixin.ImageMessage;

import cn.qlq.bean.weixin.LinkMessage;

import cn.qlq.bean.weixin.LocationMessage;

import cn.qlq.bean.weixin.TextMessage;

import cn.qlq.bean.weixin.VideoMessage;

import cn.qlq.bean.weixin.VoiceMessage;

import cn.qlq.bean.weixin.response.AbstractResponseMessage;

import cn.qlq.bean.weixin.response.ImageResponseMessage;

import cn.qlq.bean.weixin.response.NewsResponseMessage;

import cn.qlq.bean.weixin.response.NewsResponseMessageArticle;

import cn.qlq.bean.weixin.response.NewsResponseMessageArticleItem;

import cn.qlq.bean.weixin.response.TextResponseMessage;

import cn.qlq.utils.BeanUtils;

public class MessageHandler {

public static final String MESSAGE_TEXT = "text";

public static final String MESSAGE_IMAGE = "image";

public static final String MESSAGE_VOICE = "voice";

public static final String MESSAGE_VIDEO = "video";

public static final String MESSAGE_LINK = "link";

public static final String MESSAGE_LOCATION = "location";

public static final String MESSAGE_EVENT = "event";// 事件

public static final String MESSAGE_SUBSCRIBE = "subscribe";// 关注

public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";// 取消关注

public static final String MESSAGE_CLICK = "CLICK";

public static final String MESSAGE_VIEW = "VIEW";

private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class);

public static AbstractResponseMessage handlMessage(Map messageMap) {

if (MapUtils.isEmpty(messageMap)) {

logger.error("message is empty");

return null;

}

String msgType = (String) messageMap.get("MsgType");

if (MESSAGE_TEXT.equals(msgType)) {

return handleTextMessage(messageMap);

} else if (MESSAGE_IMAGE.equals(msgType)) {

return handleImageMessage(messageMap);

} else if (MESSAGE_LOCATION.equals(msgType)) {

return handleLocationMessage(messageMap);

} else if (MESSAGE_EVENT.equals(msgType)) {

return handleEventMessage(messageMap);

} else if (MESSAGE_LINK.equals(msgType)) {

return handleLinkMessage(messageMap);

} else if (MESSAGE_VOICE.equals(msgType)) {

return handleVoiceMessage(messageMap);

} else if (MESSAGE_VIDEO.equals(msgType)) {

return handleVideoMessage(messageMap);

}

return null;

}

private static AbstractResponseMessage handleVideoMessage(Map messageMap) {

VideoMessage message = BeanUtils.map2Bean(messageMap, VideoMessage.class, true);

String thumbMediaId = message.getThumbMediaId();

// 可以用图片路径做其他操作

if (StringUtils.isNotBlank(thumbMediaId)) {

System.out.println("您接收到视频消息, thumbMediaId为: " + thumbMediaId);

}

String responseMsg = "您发送了一条视频消息,thumbMediaId为: " + thumbMediaId;

return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg);

}

private static AbstractResponseMessage handleVoiceMessage(Map messageMap) {

VoiceMessage message = BeanUtils.map2Bean(messageMap, VoiceMessage.class, true);

String recognition = message.getRecognition();

String format = message.getFormat();

// 可以用图片路径做其他操作

if (StringUtils.isNotBlank(recognition)) {

System.out.println("您接收到语音消息, 格式为: " + format + ", 转换后的文字为: " + recognition);

}

String responseMsg = "您发送了一条语音消息,格式为: " + format + ", 转换后的文字为: " + recognition;

return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg);

}

/**

* 处理链接消息(回复一条图文消息)

*

* @param message

* @return

*/

private static AbstractResponseMessage handleLinkMessage(Map messageMap) {

LinkMessage message = BeanUtils.map2Bean(messageMap, LinkMessage.class, true);

String desc = message.getDescription();

String title = message.getTitle();

String url = message.getUrl();

// 可以用图片路径做其他操作

if (StringUtils.isNotBlank(url)) {

System.out.println("您接收到链接消息, title: " + title + ", desc: " + desc + ", url: " + url);

}

// 回复一条图文消息

NewsResponseMessage news = new NewsResponseMessage();

news.setCreateTime(System.currentTimeMillis());

news.setFromUserName(message.getToUserName());

news.setToUserName(message.getFromUserName());

news.setArticleCount("1");

news.setMsgType("news");

NewsResponseMessageArticle article = new NewsResponseMessageArticle();

news.setArticles(article);

// 创建多条图文消息

for (int i = 0; i < 1; i++) {

NewsResponseMessageArticleItem item = new NewsResponseMessageArticleItem();

item.setTitle("18年写的面试心得");

item.setPicUrl("https://images.cnblogs.com/cnblogs_com/qlqwjy/1031659/o_9.bmp");

item.setUrl("https://www.cnblogs.com/qlqwjy/p/9194434.html");

item.setDescription("18年毕设心血来潮写的毕设心得,1年后再看有点东西。");

article.addNewsResponseMessageArticleItem(item);

}

return news;

}

/**

* 处理事件消息(订阅和取消订阅)

*

* @param messageMap

* @return

*/

private static AbstractResponseMessage handleEventMessage(Map messageMap) {

EventMessage message = BeanUtils.map2Bean(messageMap, EventMessage.class, true);

String event = message.getEvent();

if (StringUtils.isNotBlank(event)) {

System.out.println("您接收到事件消息, 事件类型为: " + event);

}

if (MESSAGE_SUBSCRIBE.equals(event)) {

// 关注的时候

System.out.println("这里可以向数据库插入数据");

String responseMsg = MessageUtils.subscribeWelcomeText();

return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg);

} else {

// 取消关注(不用回传消息.需要将用户产生的数据删除)

System.out.println("这时需要从数据删除 " + message.getFromUserName() + " 用户产生的相关数据");

return null;

}

}

private static AbstractResponseMessage handleLocationMessage(Map messageMap) {

LocationMessage message = BeanUtils.map2Bean(messageMap, LocationMessage.class, true);

String label = message.getLabel();

if (StringUtils.isNotBlank(label)) {

System.out.println("您接收到位置消息, 地理位置信息为: " + message);

}

String responseMsg = "您发送了一条位置消息, 您的地理位置信息为:" + label;

return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg);

}

/**

* 处理图片消息(回复一条图片消息)

*

* @param message

* @return

*/

private static AbstractResponseMessage handleImageMessage(Map message) {

ImageMessage imageMessage = BeanUtils.map2Bean(message, ImageMessage.class, true);

String url = imageMessage.getPicUrl();

// 可以用图片路径做其他操作

if (StringUtils.isNotBlank(url)) {

System.out.println("您接收到的图片消息url为: " + url);

}

// 回传一条图片消息

ImageResponseMessage responseMessage = new ImageResponseMessage();

responseMessage.setCreateTime(System.currentTimeMillis());

responseMessage.setFromUserName(imageMessage.getToUserName());

responseMessage.setToUserName(imageMessage.getFromUserName());

responseMessage.setMediaId(imageMessage.getMediaId());

responseMessage.setMsgType(MESSAGE_IMAGE);

return responseMessage;

}

/**

* 处理文本消息

*

* @param message

* @return

*/

private static AbstractResponseMessage handleTextMessage(Map message) {

TextMessage textMessage = BeanUtils.map2Bean(message, TextMessage.class, true);

String content = textMessage.getContent();

// 可以根据文本消息去查库或者进行其他操作

if (StringUtils.isNotBlank(content)) {

System.out.println("您接收到的文本消息内容为: " + content);

}

// 设置回传的消息内容

TextResponseMessage responseMessage = new TextResponseMessage();

responseMessage.setContent("服务器已接收到您的消息,内容为: " + content);

responseMessage.setCreateTime(new Date().getTime());

responseMessage.setFromUserName(textMessage.getToUserName());

responseMessage.setToUserName(textMessage.getFromUserName());

responseMessage.setMsgType(MESSAGE_TEXT);

return responseMessage;

}

}

 

package cn.qlq.utils.weixin;

import java.io.IOException;

import java.io.InputStream;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;

import org.dom4j.DocumentException;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

import com.thoughtworks.xstream.XStream;

import cn.qlq.bean.weixin.response.AbstractResponseMessage;

import cn.qlq.bean.weixin.response.TextResponseMessage;

public class MessageUtils {

/**

* xml数据转map

*

* @param request

* @return

* @throws IOException

* @throws DocumentException

*/

public static Map xmlToMap(HttpServletRequest request) throws IOException, DocumentException {

Map map = new HashMap<>();

SAXReader reader = new SAXReader();

InputStream inputStream = request.getInputStream();

Document document = reader.read(inputStream);

Element root = document.getRootElement();

List list = root.elements();

for (Element element : list) {

map.put(element.getName(), element.getText());

}

inputStream.close();

return map;

}

/**

* 将文本消息对象转换成xml

*

* @param message

* @return

*/

public static String messageToXml(AbstractResponseMessage message) {

XStream xStream = new XStream();

// 将xml的根元素替换成xml

xStream.alias("xml", message.getClass());

xStream.alias("item", cn.qlq.bean.weixin.response.NewsResponseMessageArticleItem.class);

xStream.addImplicitArray(cn.qlq.bean.weixin.response.NewsResponseMessageArticle.class, "items");

return xStream.toXML(message);

}

/**

* 将文本消息对象转换成xml

*

* @param message

* @return

*/

public static String messageToXml2(AbstractResponseMessage message) {

XStream xStream = new XStream();

// 将xml的根元素替换成xml

xStream.alias("xml", message.getClass());

return xStream.toXML(message);

}

/**

* 订阅后的欢迎信息

*

* @return

*/

public static String subscribeWelcomeText() {

StringBuffer sb = new StringBuffer();

sb.append("欢迎您的关注,这里是乔治个人平台:\n\n");

sb.append("1.推荐一些优秀的文章\n");// \n代表换行

sb.append("2.记录一些美好时刻\n\n");

return sb.toString();

}

/**

* 生成文本消息

*

* @param fromUserName

* @param toUserName

* @param content

* @return

*/

public static TextResponseMessage initTextMessage(String fromUserName, String toUserName, String content) {

TextResponseMessage textResponseMessage = new TextResponseMessage();

textResponseMessage.setFromUserName(fromUserName);

textResponseMessage.setToUserName(toUserName);

textResponseMessage.setMsgType(MessageHandler.MESSAGE_TEXT);

textResponseMessage.setCreateTime(System.currentTimeMillis());

textResponseMessage.setContent(content);

return textResponseMessage;

}

}

 结果如下:

 

5. 获取用户的精度纬度信息 

1.首先在接口设置开启经纬度事件(开启之后在用户进入公众号之后会主动拉取用户的位置信息)

 

2. 处理上报地理位置事件

  用户同意上报地理位置后,每次进入公众号会话时,都会在进入时上报地理位置,或在进入会话后每5秒上报一次地理位置,公众号可以在公众平台网站中修改以上设置。上报地理位置时,微信会将上报地理位置事件推送到开发者填写的URL。 

推送的XML数据包格式如下:

123456789

23.137466

113.352425

119.385040

 

java处理:

封装工具类:

package cn.qlq.bean.weixin;

/**

* 上报地理位置事件

*

* @author Administrator

*

*/

public class LocationEventMessage extends EventMessage {

/**

* 地理位置纬度

*/

private String Latitude;

/**

* 地理位置经度

*/

private String Longitude;

/**

* 地理位置精度

*/

private String Precision;

public String getLatitude() {

return Latitude;

}

public void setLatitude(String latitude) {

Latitude = latitude;

}

public String getLongitude() {

return Longitude;

}

public void setLongitude(String longitude) {

Longitude = longitude;

}

public String getPrecision() {

return Precision;

}

public void setPrecision(String precision) {

Precision = precision;

}

@Override

public String toString() {

return "LocationEventMessage [Latitude=" + Latitude + ", Longitude=" + Longitude + ", Precision=" + Precision

+ ", ToUserName=" + ToUserName + ", FromUserName=" + FromUserName + ", CreateTime=" + CreateTime

+ ", MsgType=" + MsgType + ", Event=" + Event + "]";

}

}

 

修改处理事件消息的代码:(CLICK和VIEW是用于处理自定义菜单事件)

/**

* 处理事件消息(订阅和取消订阅)

*

* @param messageMap

* @return

*/

private static AbstractResponseMessage handleEventMessage(Map messageMap) {

EventMessage message = BeanUtils.map2Bean(messageMap, EventMessage.class, true);

String event = message.getEvent();

if (StringUtils.isNotBlank(event)) {

System.out.println("您接收到事件消息, 事件类型为: " + event);

}

// 关注的时候

if (MESSAGE_SUBSCRIBE.equals(event)) {

System.out.println("这里可以向数据库插入数据");

String responseMsg = MessageUtils.subscribeWelcomeText();

return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg);

}

// 取消关注(不用回传消息.需要将用户产生的数据删除)

if (MESSAGE_SUBSCRIBE.equals(event)) {

System.out.println("这时需要从数据删除 " + message.getFromUserName() + " 用户产生的相关数据");

return null;

}

// 点击自定义的点击菜单事件

if (MESSAGE_EVENT_CLICK.equals(event)) {

ClickViewEventMessage map2Bean = BeanUtils.map2Bean(messageMap, ClickViewEventMessage.class, true);

String eventKey = map2Bean.getEventKey();

String content = "您点击的按钮的key为: " + eventKey;

return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content);

}

// VIEW菜单的事件

if (MESSAGE_EVENT_VIEW.equals(event)) {

ClickViewEventMessage map2Bean = BeanUtils.map2Bean(messageMap, ClickViewEventMessage.class, true);

String eventKey = map2Bean.getEventKey();

String content = "您点击的按钮跳转的URL为: " + eventKey;

return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content);

}

// LOCATION菜单的事件

if (MESSAGE_EVENT_LOCATION.equals(event)) {

LocationEventMessage map2Bean = BeanUtils.map2Bean(messageMap, LocationEventMessage.class, true);

String latitude = map2Bean.getLatitude();

String longitude = map2Bean.getLongitude();

String precision = map2Bean.getPrecision();

String content = "您的经度:" + latitude + ", 您的维度:" + longitude + ", 您的精度: " + precision;

return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content);

}

return null;

}

测试结果:(需要开启GPS才可以触发该事件消息发送)

 

总结:

(1)回传xml数据格式的数据时,可以用字符串进行拼接,不一定用XStream进行转换

(2)微信回传的消息支持换行,换行符为\n

(3)图文消息是最常见的消息,回传消息的时候注意回传的xml数据格式

 

完整的代码接收消息与回传消息代码参考:https://github.com/qiao-zhi/springboot-ssm.git

 

查看原文