1.需求:关注取关发送用户消息
微信操作事件回调
回调地址需要在微信公众号配置
(1)controller
/**
* 微信操作事件回调
* @param request
* @return
*/
@RequestMapping(value = "/check", method = {RequestMethod.GET,RequestMethod.POST})
public void check(HttpServletRequest request,HttpServletResponse resp) throws Exception {
// xml请求解析
Map<String, String> requestMap = MessageUtil.xmlToMap(request);
log.info("xml请求解析返回参数,{}", requestMap);
String messageRequest = WechatService.newMessageRequest(requestMap);
if (messageRequest!= null){
resp.getWriter().write(messageRequest);
}
}
(2)微信回调操作
package com.ty.message.wxMsg;
import com.ty.common.utils.CryptoUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Map;
@Slf4j
@Service
public class WechatService {
public static String newMessageRequest(Map<String, String> requestMap) {
String respMessage = null;
try {
// 发送方帐号(open_id)
String fromUserName = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
// 消息内容
String content = requestMap.get("Content");
log.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType );
// 文本消息
if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
//这里根据关键字执行相应的逻辑
//自动回复
if (content.equals(contentName)){
String contentText = menuText(unionId);
respMessage = initText(toUserName, fromUserName, contentText);
}
}
// 事件推送
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
String eventType = requestMap.get("Event");// 事件类型
// 订阅
if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
//文本消息
String contentText = menuText();
respMessage = initText(toUserName, fromUserName, contentText);
}
// 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
// 取消订阅
else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
log.error("用户已取关");
}
}
}catch (Exception e){
e.printStackTrace();
}
return respMessage;
}
/**
* 发送用户信息
* @param toUserName
* @param fromUserName
* @param content
* @return
*/
public static String initText(String toUserName, String fromUserName, String content){
WechatOutTextMsg text = new WechatOutTextMsg();
text.setFromUserName(toUserName);
text.setToUserName(fromUserName);
text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
text.setCreateTime(new Date().getTime());
text.setContent(content);
return MessageUtil.toXml(text);
}
/**
* 发送内容
* @return
*/
public static String menuText(){
StringBuffer sb = new StringBuffer();
sb.append("您好!欢迎关注~");
return sb.toString();
}
}
(3)工具类
package com.ty.message.wxMsg;
import com.thoughtworks.xstream.XStream;
import com.ty.common.utils.http.HttpHelper;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.json.XML;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
@Slf4j
public class MessageUtil {
/**
* 返回消息类型:文本
*/
public static final String RESP_MESSAGE_TYPE_TEXT = "text";
/**
* 返回消息类型:音乐
*/
public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
/**
* 返回消息类型:图文
*/
public static final String RESP_MESSAGE_TYPE_NEWS = "news";
/**
* 请求消息类型:文本
*/
public static final String REQ_MESSAGE_TYPE_TEXT = "text";
/**
* 请求消息类型:图片
*/
public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
/**
* 请求消息类型:链接
*/
public static final String REQ_MESSAGE_TYPE_LINK = "link";
/**
* 请求消息类型:地理位置
*/
public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
/**
* 请求消息类型:音频
*/
public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
/**
* 请求消息类型:推送
*/
public static final String REQ_MESSAGE_TYPE_EVENT = "event";
/**
* 事件类型:subscribe(订阅)
*/
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
/**
* 事件类型:unsubscribe(取消订阅)
*/
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
/**
* 事件类型:CLICK(自定义菜单点击事件)
*/
public static final String EVENT_TYPE_CLICK = "CLICK";
private static final XStream xStream = XStreamFactory.getXStream();
/*
*
* 将xml转换成map集合
*
* */
public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException{
String wxRestStr = HttpHelper.getBodyString(request);
log.info("微信回调报文:{}",wxRestStr);
org.json.JSONObject jsonObject = XML.toJSONObject(wxRestStr);
Map<String, Object> wxMap = jsonObject.toMap();
Map<String, String> map = (Map<String, String>) wxMap.get("xml");
return map;
}
/**
* 对象转xml
* @param obj 对象
* @return
*/
public static String toXml(Object obj) {
xStream.processAnnotations(obj.getClass());
return xStream.toXML(obj);
}
}
(4)转xml实体类
package com.ty.message.wxMsg;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
import java.io.Serializable;
@Data
@XStreamAlias("xml")
public class WechatOutTextMsg implements Serializable {
private static final long serialVersionUID = -1883441132514476532L;
/**
* 接收方账号(收到的OpenID)
*/
@XStreamAlias("ToUserName")
@XStreamCDATA
private String toUserName;
/**
* 开发者微信号
*/
@XStreamAlias("FromUserName")
@XStreamCDATA
private String fromUserName;
/**
* 消息创建时间(整形)
*/
@XStreamAlias("CreateTime")
private Long createTime;
/**
* 消息类型
*/
@XStreamAlias("MsgType")
@XStreamCDATA
private String msgType;
/**
* 回复的消息内容
*/
@XStreamAlias("Content")
@XStreamCDATA
private String content;
}
(5)XStreamCDATA
package com.ty.message.wxMsg;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @program: ty
* @description:
* @author: HQY
* @create: 2021-06-09 13:05
**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface XStreamCDATA {
}
(6)实体转xml[data]
package com.ty.message.wxMsg;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDomDriver;
import java.io.Writer;
import java.lang.reflect.Field;
public class XStreamFactory {
public static final String CDATA_PREFIX = "<![CDATA[";
public static final String CDATA_SUFFIX = "]]>";
public static XStream getXStream() {
final NameCoder nameCoder = new NoNameCoder();
XStream xStream = new XStream(new XppDomDriver(nameCoder) {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out, nameCoder) {
boolean cdataFlag = false;
Class<?> targetClass = null;
@Override
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
if (targetClass == null) {
targetClass = clazz;
}
cdataFlag = isCDATA(targetClass, name);
}
@Override
public void writeText(QuickWriter writer, String text) {
if (cdataFlag) {
writer.write(XStreamFactory.CDATA_PREFIX);
writer.write(text);
writer.write(XStreamFactory.CDATA_SUFFIX);
} else {
writer.write(text);
}
}
};
}
});
return xStream;
}
private static boolean isCDATA(Class<?> clazz, String fieldAlias) {
//检查类本身
boolean cdataFlag = isExistCDATA(clazz, fieldAlias);
if (cdataFlag) {
return cdataFlag;
}
//继续检查父类
Class<?> superClazz = clazz.getSuperclass();
while (!superClazz.equals(Object.class)) {
cdataFlag = isExistCDATA(superClazz, fieldAlias);
if (cdataFlag) {
return cdataFlag;
}
superClazz = superClazz.getClass().getSuperclass();
}
return false;
}
/**
* 检查是否有 @XStreamCDATA 注解
* @param clazz clazz
* @param fieldAlias fieldAlias
* @return
*/
private static boolean isExistCDATA(Class<?> clazz, String fieldAlias) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(XStreamCDATA.class) != null) {
XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
if (xStreamAlias != null && fieldAlias.equals(xStreamAlias.value())) {
return true;
} else {
if (fieldAlias.equals(field.getName())) {
return true;
}
}
}
}
return false;
}
}