一、验证公众号配置的服务器信息.
须知:处理微信服务器发来的消息之前必须先通过公众号配置的服务器验证
获取AccessToken,里面的HttpClientUtil类可以从我csdn资源中找
/**
* 获取AccessToken
*
* @return
*/
public String getAccessToken() {
log.info("\n获取accessToken~");
String appid = null;
String secret = null;
appid = wxmpProperties.getAppId();
secret = wxmpProperties.getAppSecret();
//查询token是否存在
String key = "tjcloud" + appid + "_AccessToken";
String accessToken = (String) redisUtil.get(key);
// 存在直接返回,不存在重新获取AccessToken
if (!StringUtils.isBlank(accessToken)) {
log.info("\n从缓存中取到的AccessToken为:{}", accessToken);
return accessToken;
}
// 获取AccessToken的url
String grantType = "client_credential";
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grantType + "&appid=" + appid + "&secret=" + secret;
// 获取到AccessToken
accessToken = HttpClientUtil.get(url);
log.info("\n重新获取到的AccessToken为:{}", accessToken);
Map<String, Object> map = null;
try {
map = objectMapper.readValue(accessToken, Map.class);
} catch (IOException e) {
log.error("公众号异常通知-获取AccessToken-转化异常", e);
}
if (map.get("errcode") != null || map.get("errmsg") != null) {
String errcode = String.valueOf(map.get("errcode"));
String errmsg = String.valueOf(map.get("errmsg"));
if (!errcode.equals("0")) {
log.error("获取token失败:code=" + errcode + "msg=" + errmsg);
return "";
}
}
accessToken = String.valueOf(map.get("access_token"));
// 把AccessToken存入缓存中,并设置过期时间,因为access_token的过期时间是两小时,我们缓存的时间一定要小于两小时,
redisUtil.set(key, accessToken, 5000);
return accessToken;
}
controller控制层
@Autowired
private TdCMpLoginService mpLoginService;
/**
* 处理微信服务器发来的消息
*/
@PostMapping("/serverCheck")
public String doPost(HttpServletRequest request, HttpServletResponse response) {
// 调用核心服务类接收处理请求
return mpLoginService.processRequest(request);
}
service业务层
TdCMpLoginService.java
String processRequest(HttpServletRequest request);
TdCMpLoginServiceImpl.java
从代码中可以看出微信服务器可以通过post方式访问我们的接口,把消息类型和事件传过来,我们可以根据不同的类型和事件来做出相应的操作,我这里只对订阅和取消订阅的操作进行了存储。
/**
* @Description: 处理微信服务器发来的消息
* @Param: [request]
* @return: java.lang.String
* @Author: XQD
* @Date:2021/9/17 10:19
*/
@Transactional
@Override
public String processRequest(HttpServletRequest request) {
// xml格式的消息数据
String respXml = null;
// 默认返回的文本消息内容
String respContent;
try {
// 调用parseXml方法解析请求消息
Map<String, String> requestMap = WeChatUtil.parseXml(request);
// 消息类型
String msgType = requestMap.get(WeChatContant.MsgType);
log.info("\n消息类型:{}", msgType);
String mes = null;
// 文本消息
if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_TEXT)) {
respContent = "您发送的是文本消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 图片消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_IMAGE)) {
respContent = "您发送的是图片消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 语音消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_VOICE)) {
respContent = "您发送的是语音消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 视频消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_VIDEO)) {
respContent = "您发送的是视频消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 地理位置消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_LOCATION)) {
respContent = "您发送的是地理位置消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 链接消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_LINK)) {
respContent = "您发送的是链接消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 事件推送
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_EVENT)) {
// 事件类型
String eventType = requestMap.get(WeChatContant.Event);
log.info("\n事件类型为:{}", eventType);
// 关注
if (eventType.equals(WeChatContant.EVENT_TYPE_SUBSCRIBE)) {
log.info("\n搜索关注公众号!");
subscribeMPUserInfo(null, requestMap.get(WeChatContant.FromUserName));
respContent = "谢谢您的关注!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 取消关注
else if (eventType.equals(WeChatContant.EVENT_TYPE_UNSUBSCRIBE)) {
unsubscribeMPUserInfo(requestMap.get(WeChatContant.FromUserName));
// TODO 取消订阅后用户不会再收到公众账号发送的消息,因此不需要回复
}
// 扫描带参数二维码
else if (eventType.equals(WeChatContant.EVENT_TYPE_SCAN)) {
// TODO 处理扫描带参数二维码事件
}
// 上报地理位置
else if (eventType.equals(WeChatContant.EVENT_TYPE_LOCATION)) {
// TODO 处理上报地理位置事件
}
// 自定义菜单
else if (eventType.equals(WeChatContant.EVENT_TYPE_CLICK)) {
// TODO 处理菜单点击事件
}
}
mes = mes == null ? "不知道你在干嘛" : mes;
if (respXml == null) {
respXml = WeChatUtil.sendTextMsg(requestMap, mes);
}
log.info("\n返回的数据:" + respXml);
return respXml;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* @Description: 订阅的公众号用户信息存入数据库
* @Param: [openId]
* @return: void
* @Author: XQD
* @Date:2021/1/4 17:45
*/
public void subscribeMPUserInfo(String projectId, String openId) {
String accessToken = getAccessToken();
if (StringUtils.isBlank(accessToken)){
log.info("\n accessToken为空");
}else {
String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
String mpUserInfo = HttpClientUtil.get(url);
Map<String, Object> map = null;
try {
map = objectMapper.readValue(mpUserInfo, Map.class);
} catch (IOException e) {
log.error("公众号异常通知-获取用户信息转化异常", e);
}
QueryWrapper<TdCMpLogin> mpStaffLoginQueryWrapper = new QueryWrapper<>();
mpStaffLoginQueryWrapper.eq("open_id", openId)
.eq("status", 0);
TdCMpLogin mpLogin = mpLoginMapper.selectOne(mpStaffLoginQueryWrapper);
if (mpLogin == null) {
mpLogin = new TdCMpLogin();
mpLogin.setId(GeneratorIDUtil.generatorId())
.setOpenId(openId)
.setNickName((String) map.get("nickname"))
.setHeadImageUrl((String) map.get("headimgurl"))
.setSex((Integer) map.get("sex"))
.setSubscribe((Integer) map.get("subscribe"))
.setUnionId((String) map.get("unionid"))
.setCountry((String) map.get("country"))
.setProvince((String) map.get("province"))
.setCity((String) map.get("city"));
mpLoginMapper.insert(mpLogin);
log.info("\n公众号用户信息添加成功 openId = {}", openId);
} else {
mpLogin.setSubscribe(1);
mpLoginMapper.updateById(mpLogin);
log.info("\n公众号用户信息修改成功 openId = {}", openId);
}
}
}
/**
* @Description: 取消订阅用户
* @Param: [projectId, openId]
* @return: void
* @Author: XQD
* @Date:2021/1/4 17:45
*/
public void unsubscribeMPUserInfo(String openId) {
UpdateWrapper<TdCMpLogin> mpLoginUpdateWrapper = new UpdateWrapper<>();
mpLoginUpdateWrapper.set("subscribe", 0)
.eq("open_id", openId);
if (update(mpLoginUpdateWrapper)) {
log.info("\n取消关注的用户操作成功 openId = {}", openId);
} else {
log.info("\n取消关注的用户操作失败 openId = {}", openId);
}
}
常量类:WeChatContant.java
/**
* @ClassName: WeChatContant
* @Description: TODO
* @Authror: XQD
* @Date: 2021/1/4 15:37
*/
public class WeChatContant {
// Token
public static final String TOKEN = "zch";
// 文本
public static final Object REQ_MESSAGE_TYPE_TEXT = "text";
// 图片
public static final Object REQ_MESSAGE_TYPE_IMAGE = "image";
// 语音
public static final Object REQ_MESSAGE_TYPE_VOICE = "voice";
// 视频
public static final Object REQ_MESSAGE_TYPE_VIDEO = "video";
// 地理位置
public static final Object REQ_MESSAGE_TYPE_LOCATION = "location";
// 链接
public static final Object REQ_MESSAGE_TYPE_LINK = "link";
// 消息类型
public static final Object REQ_MESSAGE_TYPE_EVENT = "event";
// 事件类型:订阅,扫码订阅
public static final Object EVENT_TYPE_SUBSCRIBE = "subscribe";
// 事件类型:取消订阅
public static final Object EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
// 事件类型:已关注后扫码推送类型
public static final Object EVENT_TYPE_SCAN = "SCAN";
// 事件类型:上报地理位置事件
public static final Object EVENT_TYPE_LOCATION = "LOCATION";
// 事件类型:自定义菜单事件
public static final Object EVENT_TYPE_CLICK = "CLICK";
// 开发者微信号
public static final String ToUserName = "ToUserName";
// 发送方账号(一个OpenID)
public static final String FromUserName = "FromUserName";
// 消息类型
public static final String MsgType = "MsgType";
// 内容
public static final String Content = "Content";
// 事件类型
public static final String Event = "Event";
// 事件KEY值
public static final String EventKey = "EventKey";
// 二维码的ticket
public static final String Ticket = "Ticket";
公众号用户的表结构
也可以根据返回的数据自行获取需要用到的数据
官网地址:https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId