package com.github.sd4324530.fastweixin.servlet;
import com.github.sd4324530.fastweixin.handle.EventHandle;
import com.github.sd4324530.fastweixin.handle.MessageHandle;
import com.github.sd4324530.fastweixin.message.BaseMsg;
import com.github.sd4324530.fastweixin.message.TextMsg;
import com.github.sd4324530.fastweixin.message.aes.AesException;
import com.github.sd4324530.fastweixin.message.aes.WXBizMsgCrypt;
import com.github.sd4324530.fastweixin.message.req.*;
import com.github.sd4324530.fastweixin.util.MessageUtil;
import com.github.sd4324530.fastweixin.util.SignUtil;
import com.github.sd4324530.fastweixin.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import static com.github.sd4324530.fastweixin.util.BeanUtil.isNull;
import static com.github.sd4324530.fastweixin.util.BeanUtil.nonNull;
import static com.github.sd4324530.fastweixin.util.CollectionUtil.isEmpty;
import static com.github.sd4324530.fastweixin.util.CollectionUtil.isNotEmpty;
import static com.github.sd4324530.fastweixin.util.StrUtil.isNotBlank;
/**
* 将微信处理通用部分再抽象一层,使用其他框架框架的同学可以自行继承此类集成微信
*
* @author peiyu
* @since 1.1
*/
public abstract class WeixinSupport {
private static final Logger LOG = LoggerFactory.getLogger(WeixinSupport.class);
//充当锁
private static final Object LOCK = new Object();
// protected String fromUserName, toUserName;
/**
* 微信消息处理器列表
*/
private static List<MessageHandle> messageHandles;
/**
* 微信事件处理器列表
*/
private static List<EventHandle> eventHandles;
/**
* 子类重写,加入自定义的微信消息处理器,细化消息的处理
*
* @return 微信消息处理器列表
*/
protected List<MessageHandle> initMessageHandles() {
return null;
}
/**
* 子类重写,加入自定义的微信事件处理器,细化消息的处理
*
* @return 微信事件处理器列表
*/
protected List<EventHandle> initEventHandles() {
return null;
}
/**
* 子类提供token用于绑定微信公众平台
*
* @return token值
*/
protected abstract String getToken();
/**
* 公众号APPID,使用消息加密模式时用户自行设置
*
* @return 微信公众平台提供的appid
*/
protected String getAppId() {
return null;
}
/**
* 加密的密钥,使用消息加密模式时用户自行设置
*
* @return 用户自定义的密钥
*/
protected String getAESKey() {
return null;
}
/**
* 绑定服务器的方法
* @param request 请求
* @param response 响应
*/
public void bindServer(HttpServletRequest request, HttpServletResponse response) {
if (isLegal(request)) {
try {
PrintWriter pw = response.getWriter();
pw.write(request.getParameter("echostr"));
pw.flush();
pw.close();
} catch (Exception e) {
LOG.error("绑定服务器异常", e);
}
}
}
/**
* 处理微信服务器发来的请求方法
*
* @param request http请求对象
* @return 处理消息的结果,已经是接口要求的xml报文了
*/
public String processRequest(HttpServletRequest request) {
Map<String, Object> reqMap = MessageUtil.parseXml(request, getToken(), getAppId(), getAESKey());
String fromUserName = (String) reqMap.get("FromUserName");
String toUserName = (String) reqMap.get("ToUserName");
String msgType = (String) reqMap.get("MsgType");
LOG.debug("收到消息,消息类型:{}", msgType);
BaseMsg msg = null;
if (msgType.equals(ReqType.EVENT)) {
String eventType = (String) reqMap.get("Event");
String ticket = (String) reqMap.get("Ticket");
QrCodeEvent qrCodeEvent = null;
if (isNotBlank(ticket)) {
String eventKey = (String) reqMap.get("EventKey");
LOG.debug("eventKey:{}", eventKey);
LOG.debug("ticket:{}", ticket);
qrCodeEvent = new QrCodeEvent(eventKey, ticket);
buildBasicEvent(reqMap, qrCodeEvent);
if (eventType.equals(EventType.SCAN)) {
msg = handleQrCodeEvent(qrCodeEvent);
if (isNull(msg)) {
msg = processEventHandle(qrCodeEvent);
}
}
}
if (eventType.equals(EventType.SUBSCRIBE)) {
BaseEvent event = new BaseEvent();
if (qrCodeEvent != null) {
event = qrCodeEvent;
} else {
buildBasicEvent(reqMap, event);
}
msg = handleSubscribe(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (eventType.equals(EventType.UNSUBSCRIBE)) {
BaseEvent event = new BaseEvent();
buildBasicEvent(reqMap, event);
msg = handleUnsubscribe(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (eventType.equals(EventType.CLICK)) {
String eventKey = (String) reqMap.get("EventKey");
LOG.debug("eventKey:{}", eventKey);
MenuEvent event = new MenuEvent(eventKey);
buildBasicEvent(reqMap, event);
msg = handleMenuClickEvent(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (eventType.equals(EventType.VIEW)) {
String eventKey = (String) reqMap.get("EventKey");
LOG.debug("eventKey:{}", eventKey);
MenuEvent event = new MenuEvent(eventKey);
buildBasicEvent(reqMap, event);
msg = handleMenuViewEvent(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (eventType.equals(EventType.LOCATION)) {
double latitude = Double.parseDouble((String) reqMap.get("Latitude"));
double longitude = Double.parseDouble((String) reqMap.get("Longitude"));
double precision = Double.parseDouble((String) reqMap.get("Precision"));
LocationEvent event = new LocationEvent(latitude, longitude,
precision);
buildBasicEvent(reqMap, event);
msg = handleLocationEvent(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (EventType.SCANCODEPUSH.equals(eventType) || EventType.SCANCODEWAITMSG.equals(eventType)) {
String eventKey = (String) reqMap.get("EventKey");
Map<String, Object> scanCodeInfo = (Map<String, Object>)reqMap.get("ScanCodeInfo");
String scanType = (String) scanCodeInfo.get("ScanType");
String scanResult = (String) scanCodeInfo.get("ScanResult");
ScanCodeEvent event = new ScanCodeEvent(eventKey, scanType, scanResult);
buildBasicEvent(reqMap, event);
msg = handleScanCodeEvent(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (EventType.PICPHOTOORALBUM.equals(eventType) || EventType.PICSYSPHOTO.equals(eventType) || EventType.PICWEIXIN.equals(eventType)) {
String eventKey = (String) reqMap.get("EventKey");
Map<String, Object> sendPicsInfo = (Map<String, Object>)reqMap.get("SendPicsInfo");
int count = Integer.parseInt((String) sendPicsInfo.get("Count"));
List<Map> picList = (List) sendPicsInfo.get("PicList");
SendPicsInfoEvent event = new SendPicsInfoEvent(eventKey, count, picList);
buildBasicEvent(reqMap, event);
msg = handlePSendPicsInfoEvent(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
} else if (EventType.TEMPLATESENDJOBFINISH.equals(eventType)) {
String msgId = (String) reqMap.get("MsgID");
String status = (String) reqMap.get("Status");
TemplateMsgEvent event = new TemplateMsgEvent(msgId,status);
buildBasicEvent(reqMap, event);
msg = handleTemplateMsgEvent(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
}else if(EventType.MASSSENDJOBFINISH.equals(eventType)){
String msgId=(String)reqMap.get("MsgID");
String status=(String)reqMap.get("Status");
Integer TotalCount=Integer.valueOf(String.valueOf(reqMap.get("TotalCount")));
Integer filterCount=Integer.valueOf(String.valueOf(reqMap.get("FilterCount")));
Integer sentCount=Integer.valueOf(String.valueOf(reqMap.get("SentCount")));
Integer errorCount=Integer.valueOf(String.valueOf(reqMap.get("ErrorCount")));
SendMessageEvent event=new SendMessageEvent(msgId,status,TotalCount,filterCount,sentCount,errorCount);
buildBasicEvent(reqMap, event);
msg=callBackAllMessage(event);
if (isNull(msg)) {
msg = processEventHandle(event);
}
}
} else {
if (msgType.equals(ReqType.TEXT)) {
String content = (String) reqMap.get("Content");
LOG.debug("文本消息内容:{}", content);
TextReqMsg textReqMsg = new TextReqMsg(content);
buildBasicReqMsg(reqMap, textReqMsg);
msg = handleTextMsg(textReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(textReqMsg);
}
} else if (msgType.equals(ReqType.IMAGE)) {
String picUrl = (String) reqMap.get("PicUrl");
String mediaId = (String) reqMap.get("MediaId");
ImageReqMsg imageReqMsg = new ImageReqMsg(picUrl, mediaId);
buildBasicReqMsg(reqMap, imageReqMsg);
msg = handleImageMsg(imageReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(imageReqMsg);
}
} else if (msgType.equals(ReqType.VOICE)) {
String format = (String) reqMap.get("Format");
String mediaId = (String) reqMap.get("MediaId");
String recognition = (String) reqMap.get("Recognition");
VoiceReqMsg voiceReqMsg = new VoiceReqMsg(mediaId, format,
recognition);
buildBasicReqMsg(reqMap, voiceReqMsg);
msg = handleVoiceMsg(voiceReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(voiceReqMsg);
}
} else if (msgType.equals(ReqType.VIDEO)) {
String thumbMediaId = (String) reqMap.get("ThumbMediaId");
String mediaId = (String) reqMap.get("MediaId");
VideoReqMsg videoReqMsg = new VideoReqMsg(mediaId, thumbMediaId);
buildBasicReqMsg(reqMap, videoReqMsg);
msg = handleVideoMsg(videoReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(videoReqMsg);
}
} else if (msgType.equals(ReqType.SHORT_VIDEO)) {
String thumbMediaId = (String) reqMap.get("ThumbMediaId");
String mediaId = (String) reqMap.get("MediaId");
VideoReqMsg videoReqMsg = new VideoReqMsg(mediaId, thumbMediaId);
buildBasicReqMsg(reqMap, videoReqMsg);
msg = hadnleShortVideoMsg(videoReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(videoReqMsg);
}
} else if (msgType.equals(ReqType.LOCATION)) {
double locationX = Double.parseDouble((String) reqMap.get("Location_X"));
double locationY = Double.parseDouble((String) reqMap.get("Location_Y"));
int scale = Integer.parseInt((String) reqMap.get("Scale"));
String label = (String) reqMap.get("Label");
LocationReqMsg locationReqMsg = new LocationReqMsg(locationX,
locationY, scale, label);
buildBasicReqMsg(reqMap, locationReqMsg);
msg = handleLocationMsg(locationReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(locationReqMsg);
}
} else if (msgType.equals(ReqType.LINK)) {
String title = (String) reqMap.get("Title");
String description = (String) reqMap.get("Description");
String url = (String) reqMap.get("Url");
LOG.debug("链接消息地址:{}", url);
LinkReqMsg linkReqMsg = new LinkReqMsg(title, description, url);
buildBasicReqMsg(reqMap, linkReqMsg);
msg = handleLinkMsg(linkReqMsg);
if (isNull(msg)) {
msg = processMessageHandle(linkReqMsg);
}
}
}
String result = "";
if (nonNull(msg)) {
msg.setFromUserName(toUserName);
msg.setToUserName(fromUserName);
result = msg.toXml();
if (StrUtil.isNotBlank(getAESKey())) {
try {
WXBizMsgCrypt pc = new WXBizMsgCrypt(getToken(), getAESKey(), getAppId());
result = pc.encryptMsg(result, request.getParameter("timestamp"), request.getParameter("nonce"));
LOG.debug("加密后密文:{}", result);
} catch (AesException e) {
LOG.error("加密异常", e);
}
}
}
return result;
}
private BaseMsg processMessageHandle(BaseReqMsg msg) {
if (isEmpty(messageHandles)) {
synchronized (LOCK) {
messageHandles = this.initMessageHandles();
}
}
if (isNotEmpty(messageHandles)) {
for (MessageHandle messageHandle : messageHandles) {
BaseMsg resultMsg = null;
boolean result;
try {
result = messageHandle.beforeHandle(msg);
} catch (Exception e) {
result = false;
}
if (result) {
resultMsg = messageHandle.handle(msg);
}
if (nonNull(resultMsg)) {
return resultMsg;
}
}
}
return null;
}
private BaseMsg processEventHandle(BaseEvent event) {
if (isEmpty(eventHandles)) {
synchronized (LOCK) {
eventHandles = this.initEventHandles();
}
}
if (isNotEmpty(eventHandles)) {
for (EventHandle eventHandle : eventHandles) {
BaseMsg resultMsg = null;
boolean result;
try {
result = eventHandle.beforeHandle(event);
} catch (Exception e) {
result = false;
}
if (result) {
resultMsg = eventHandle.handle(event);
}
if (nonNull(resultMsg)) {
return resultMsg;
}
}
}
return null;
}
/**
* 处理文本消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg handleTextMsg(TextReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理图片消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg handleImageMsg(ImageReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理语音消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg handleVoiceMsg(VoiceReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理视频消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg handleVideoMsg(VideoReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理小视频消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg hadnleShortVideoMsg(VideoReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理地理位置消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg handleLocationMsg(LocationReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理链接消息,有需要时子类重写
*
* @param msg 请求消息对象
* @return 响应消息对象
*/
protected BaseMsg handleLinkMsg(LinkReqMsg msg) {
return handleDefaultMsg(msg);
}
/**
* 处理扫描二维码事件,有需要时子类重写
*
* @param event 扫描二维码事件对象
* @return 响应消息对象
*/
protected BaseMsg handleQrCodeEvent(QrCodeEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理地理位置事件,有需要时子类重写
*
* @param event 地理位置事件对象
* @return 响应消息对象
*/
protected BaseMsg handleLocationEvent(LocationEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理菜单点击事件,有需要时子类重写
*
* @param event 菜单点击事件对象
* @return 响应消息对象
*/
protected BaseMsg handleMenuClickEvent(MenuEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理菜单跳转事件,有需要时子类重写
*
* @param event 菜单跳转事件对象
* @return 响应消息对象
*/
protected BaseMsg handleMenuViewEvent(MenuEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理菜单扫描推事件,有需要时子类重写
*
* @param event 菜单扫描推事件对象
* @return 响应的消息对象
*/
protected BaseMsg handleScanCodeEvent(ScanCodeEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理菜单弹出相册事件,有需要时子类重写
*
* @param event 菜单弹出相册事件
* @return 响应的消息对象
*/
protected BaseMsg handlePSendPicsInfoEvent(SendPicsInfoEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理模版消息发送事件,有需要时子类重写
*
* @param event 菜单弹出相册事件
* @return 响应的消息对象
*/
protected BaseMsg handleTemplateMsgEvent(TemplateMsgEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理添加关注事件,有需要时子类重写
*
* @param event 添加关注事件对象
* @return 响应消息对象
*/
protected BaseMsg handleSubscribe(BaseEvent event) {
return new TextMsg("感谢您的关注!");
}
/**
* 接收群发消息的回调方法
*
* @param event 群发回调方法
* @return 响应消息对象
*/
protected BaseMsg callBackAllMessage(SendMessageEvent event){return handleDefaultEvent(event);}
/**
* 处理取消关注事件,有需要时子类重写
*
* @param event 取消关注事件对象
* @return 响应消息对象
*/
protected BaseMsg handleUnsubscribe(BaseEvent event) {
return null;
}
protected BaseMsg handleDefaultMsg(BaseReqMsg msg) {
return null;
}
protected BaseMsg handleDefaultEvent(BaseEvent event) {
return null;
}
private void buildBasicReqMsg(Map<String, Object> reqMap, BaseReqMsg reqMsg) {
addBasicReqParams(reqMap, reqMsg);
reqMsg.setMsgId((String) reqMap.get("MsgId"));
}
private void buildBasicEvent(Map<String, Object> reqMap, BaseEvent event) {
addBasicReqParams(reqMap, event);
event.setEvent((String) reqMap.get("Event"));
}
private void addBasicReqParams(Map<String, Object> reqMap, BaseReq req) {
req.setMsgType((String) reqMap.get("MsgType"));
req.setFromUserName((String) reqMap.get("FromUserName"));
req.setToUserName((String) reqMap.get("ToUserName"));
req.setCreateTime(Long.parseLong((String) reqMap.get("CreateTime")));
}
protected boolean isLegal(HttpServletRequest request) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
return SignUtil.checkSignature(getToken(), signature, timestamp, nonce);
}
}