欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 微信公众平台开发(二)——事件推送与被动回复

程序员文章站 2024-01-24 09:26:46
...

完成公众号的接入之后,公众号的开发才正式开始。

在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,详细内容如下:

关注/取消关注事件
扫描带参数二维码事件
上报地理位置事件
自定义菜单事件
点击菜单拉取消息时的事件推送
点击菜单跳转链接时的事件推送

这里以关注取消事件为例

用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做帐号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。

微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。

关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。

假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

推送XML数据包示例:

<xml>
<ToUserName>< ![CDATA[toUser] ]></ToUserName>
<FromUserName>< ![CDATA[FromUser] ]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType>< ![CDATA[event] ]></MsgType>
<Event>< ![CDATA[subscribe] ]></Event>
</xml>

参数说明:

参数               描述
ToUserName       开发者微信号
FromUserName     发送方帐号(一个OpenID)
CreateTime       消息创建时间 (整型)
MsgType          消息类型,event
Event            事件类型,subscribe(订阅)、unsubscribe(取消订阅)

当用户点击关注,微信通过post请求访问URL:域名/端口/wechat/handler
经过控制器controller的判断后,将处理后的入参传给接口MessageHandleService

package cc.feefox.wechat.main;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;

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 cc.feefox.wechat.common.util.SignatureUtil;
import cc.feefox.wechat.common.util.StringUtils;
import cc.feefox.wechat.common.util.XmlUtil;
import cc.feefox.wechat.message.service.interfaces.MessageHandleService;

/**
 * 微信消息入口
 * 
 * @Package: cc.feefox.wechat.main
 * @author: cc
 * @date: 2018年8月18日 下午12:18:39
 */
@RestController
@RequestMapping("/wechat")
public class WeChatController extends BaseController {

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

    @Autowired
    private MessageHandleService messageHandleService;

    /**
     * 校验信息是否是从微信服务器发出,处理消息
     * 
     * @param request
     * @param out
     * @throws IOException
     */
    @RequestMapping(value = "/handler", method = { RequestMethod.GET, RequestMethod.POST })
    public void processPost() throws Exception {

        this.getRequest().setCharacterEncoding("UTF-8");
        this.getResponse().setCharacterEncoding("UTF-8");

        logger.info("开始校验信息是否是从微信服务器发出");
        boolean ispost = Objects.equals("POST", this.getRequest().getMethod().toUpperCase());
        if (ispost) {
            logger.debug("接入成功,正在处理逻辑");
            InputStream inputStream = this.getRequest().getInputStream();
            Map<String, String> params = XmlUtil.parseStreamToMap(inputStream);
            String respXml = messageHandleService.handleMessage(params);
            if (StringUtils.isNotNull(respXml)) {
                // 输出流
                this.getResponse().getWriter().write(respXml);
            }

        } else {
            // 签名
            String signature = this.getRequest().getParameter("signature");
            // 时间戳
            String timestamp = this.getRequest().getParameter("timestamp");
            // 随机数
            String nonce = this.getRequest().getParameter("nonce");
            // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败

            if (SignatureUtil.checkSignature(signature, timestamp, nonce)) {
                // 随机字符串
                String echostr = this.getRequest().getParameter("echostr");
                logger.debug("接入成功,echostr {}", echostr);
                this.getResponse().getWriter().write(echostr);
            }
        }
    }
}

MessageHandleService接口对参数进行封装处理,传给CodeHandleService进行分类处理

/**
  * @ClassName: MessageHandleServiceImpl 
  * @Description: TODO
  * @author: cc
  * @date: 2018年8月18日 上午11:48:28
  * 
  */
package cc.feefox.wechat.message.service.impl;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cc.feefox.wechat.common.result.WeChatResult;
import cc.feefox.wechat.common.util.MessageUtil;
import cc.feefox.wechat.message.model.BaseMessage;
import cc.feefox.wechat.message.model.NewsMessage;
import cc.feefox.wechat.message.service.interfaces.CodeHandleService;
import cc.feefox.wechat.message.service.interfaces.MessageHandleService;

/**
 * @Package: cc.feefox.wechat.message.service.impl
 * @author: cc
 * @date: 2018年8月18日 上午11:48:28
 */
@Service
public class MessageHandleServiceImpl implements MessageHandleService {

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

    @Autowired
    private CodeHandleService codeHandleService;

    /*
     * 对来自微信的消息作出响应(包含消息和事件)
     * 
     * @param inputStream
     * 
     * @return
     * 
     * @throws Exception
     */
    @Override
    public String handleMessage(Map<String, String> params) throws Exception {

        logger.info("开始处理【message】信息");

        String result = null;

        if (params != null && params.size() > 0) {
            BaseMessage msgInfo = new BaseMessage();
            String createTime = params.get("CreateTime");
            String msgId = params.get("MsgId");
            msgInfo.setCreateTime((createTime != null && !"".equals(createTime)) ? Integer.parseInt(createTime) : 0);
            msgInfo.setFromUserName(params.get("FromUserName"));
            msgInfo.setMsgId((msgId != null && !"".equals(msgId)) ? Long.parseLong(msgId) : 0);
            msgInfo.setToUserName(params.get("ToUserName"));
            WeChatResult resp = codeHandleService.handleCode(params, msgInfo);
            if (null == resp) {
//              String str = "<xml>\r\n" + "  <ToUserName><![CDATA[oWnU700PB2HTtgzReJva2Gz00000]]></ToUserName>\r\n"
//                      + "  <FromUserName><![CDATA[gh_23ba766d4f8c]]></FromUserName>\r\n"
//                      + "  <CreateTime><![CDATA[1534324363936]]></CreateTime>\r\n"
//                      + "  <MsgType><![CDATA[news]]></MsgType>\r\n" + "  <FuncFlag><![CDATA[0]]></FuncFlag>\r\n"
//                      + "  <ArticleCount><![CDATA[2]]></ArticleCount>\r\n" + "  <Articles>\r\n" + "    <item>\r\n"
//                      + "      <Title><![CDATA[飞狐互动:feefox]]></Title>\r\n"
//                      + "      <Description><![CDATA[欢迎关注我的个人博客<a href=\"http://118.25.62.232/images/showreel.jpg\">图片</a><a href=\"http://118.25.62.232/images/showreel.jpg\">图片</a>]]></Description>\r\n"
//                      + "      <PicUrl><![CDATA[http://118.25.62.232/images/showreel.jpg]]></PicUrl>\r\n"
//                      + "      <Url><![CDATA[http://118.25.62.232/apple/]]></Url>\r\n" + "    </item>\r\n"
//                      + "    <item>\r\n" + "      <Title><![CDATA[飞狐互动:feefox]]></Title>\r\n"
//                      + "      <Description><![CDATA[欢迎关注我的个人博客<a href=\"http://118.25.62.232/images/showreel.jpg\">图片</a><a href=\"http://118.25.62.232/images/showreel.jpg\">图片</a>]]></Description>\r\n"
//                      + "      <PicUrl><![CDATA[http://118.25.62.232/images/showreel.jpg]]></PicUrl>\r\n"
//                      + "      <Url><![CDATA[http://118.25.62.232/apple/]]></Url>\r\n" + "    </item>\r\n"
//                      + "  </Articles>\r\n" + "</xml>";
//              return str;
                return null;
            }
            boolean success = resp.isSuccess(); // 如果 为true,则表示返回xml文件, 直接转换即可,否则按类型
            if (success) {
                result = resp.getObject().toString();
            } else {
                int type = resp.getType(); // 这里规定 1 图文消息 否则直接转换
                if (type == WeChatResult.NEWSMSG) {
                    NewsMessage message = (NewsMessage) resp.getObject();
                    result = MessageUtil.newsMessagegToXml(message);
                    return result;
                } else {
                    BaseMessage baseMessage = (BaseMessage) resp.getObject();
                    result = MessageUtil.baseMessageToXml(baseMessage);
                    return result;
                }
            }
        } else {
            result = "msg is wrong";

        }
        return result;
    }

}

CodeHandleService层完成消息的分类后,将数据传递给实现层WechatMsgService进行逻辑处理

/**
  * @ClassName: CodeHandleServiceImpl 
  * @Description: TODO
  * @author: cc
  * @date: 2018年8月18日 下午2:14:06
  * 
  */
package cc.feefox.wechat.message.service.impl;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cc.feefox.wechat.common.constant.MessagegConstant;
import cc.feefox.wechat.common.result.WeChatResult;
import cc.feefox.wechat.common.util.StringUtils;
import cc.feefox.wechat.message.model.BaseMessage;
import cc.feefox.wechat.message.service.interfaces.CodeHandleService;
import cc.feefox.wechat.message.service.interfaces.WechatMsgService;

/**
 * @Package: cc.feefox.wechat.message.service.impl
 * @author: cc
 * @date: 2018年8月18日 下午2:14:06
 */
@Service
public class CodeHandleServiceImpl implements CodeHandleService {

    @Autowired
    private WechatMsgService wechatMsgService;

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult handleCode(Map<String, String> params, BaseMessage msgInfo) {

        WeChatResult result = new WeChatResult();

        String msgInfoType = params.get("MsgType");
        if (!StringUtils.isBlank(msgInfoType)) {
            switch (msgInfoType) {
            case MessagegConstant.REQ_MESSAGE_TYPE_TEXT: // 文本消息
                result = wechatMsgService.textMsgInfo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_IMAGE: // 图片消息
                result = wechatMsgService.imageMsgInfo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_LINK: // 链接消息
                result = wechatMsgService.linkMsgInfo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_LOCATION: // 地理位置
                result = wechatMsgService.locationMsgInfo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_VOICE: // 音频消息
                result = wechatMsgService.voiceMsgInfo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_SHORTVIDEO: // 短视频消息
                result = wechatMsgService.shortVideo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_VIDEO: // 视频消息
                result = wechatMsgService.videoMsgInfo(params, msgInfo);
                break;
            case MessagegConstant.REQ_MESSAGE_TYPE_EVENT: // 事件消息
                String eventType = params.get("Event"); //
                if (eventType != null && !"".equals(eventType)) {
                    switch (eventType) {
                    case MessagegConstant.EVENT_TYPE_SUBSCRIBE:
                        result = wechatMsgService.subscribe(params, msgInfo);
                        break;
                    case MessagegConstant.EVENT_TYPE_UNSUBSCRIBE:
                        result = wechatMsgService.unsubscribe(params, msgInfo);
                        break;
                    case MessagegConstant.EVENT_TYPE_SCAN:
                        result = wechatMsgService.scan(params, msgInfo);
                        break;
                    case MessagegConstant.EVENT_TYPE_LOCATION:
                        result = wechatMsgService.eventLocation(params, msgInfo);
                        break;
                    case MessagegConstant.EVENT_TYPE_CLICK:
                        result = wechatMsgService.eventClick(params, msgInfo);
                        break;
                    case MessagegConstant.EVENT_TYPE_VIEW:
                        result = wechatMsgService.eventView(params, msgInfo);
                        break;
                    case MessagegConstant.KF_CREATE_SESSION:
                        result = wechatMsgService.kfCreateSession(params, msgInfo);
                        break;
                    case MessagegConstant.KF_CLOSE_SESSION:
                        result = wechatMsgService.kfCloseSession(params, msgInfo);
                        break;
                    case MessagegConstant.KF_SWITCH_SESSION:
                        result = wechatMsgService.kfSwitchSession(params, msgInfo);
                        break;
                    default:
                        wechatMsgService.eventDefaultReply(params, msgInfo);
                        break;
                    }
                }
                break;
            default:
                wechatMsgService.defaultMsgInfo(params, msgInfo);
            }
        }
        return result;
    }

}

最后,WechatMsgService层作出相应的响应,组织回复消息。

/**
  * @ClassName: WechatMsgServiceImpl 
  * @Description: TODO
  * @author: cc
  * @date: 2018年8月18日 下午3:19:48
  * 
  */
package cc.feefox.wechat.message.service.impl;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cc.feefox.wechat.common.json.JsonConfig;
import cc.feefox.wechat.common.result.WeChatResult;
import cc.feefox.wechat.common.util.DateTimeUtil;
import cc.feefox.wechat.customer.service.interfaces.CustomService;
import cc.feefox.wechat.main.WeChatController;
import cc.feefox.wechat.message.model.BaseMessage;
import cc.feefox.wechat.message.model.TextMessage;
import cc.feefox.wechat.message.service.interfaces.WechatMsgService;
import net.sf.json.JSONObject;

/**
 * @Package: cc.feefox.wechat.message.service.impl
 * @author: cc
 * @date: 2018年8月18日 下午3:19:48
 */
@Service
public class WechatMsgServiceImpl implements WechatMsgService {

    @Autowired
    private CustomService customService;

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

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult textMsgInfo(Map<String, String> params, BaseMessage msgInfo) {

        logger.info("文本消息");
        WeChatResult result = new WeChatResult();
        TextMessage text = new TextMessage();
        text.setContent(params.get("Content").trim());// 自动回复
        text.setCreateTime(DateTimeUtil.currentTime());
        text.setToUserName(msgInfo.getFromUserName());
        text.setFromUserName(msgInfo.getToUserName());
        text.setMsgId(msgInfo.getMsgId());
        text.setMsgType("text");
        result.setObject(text);
        return result;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult imageMsgInfo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult linkMsgInfo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult locationMsgInfo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult voiceMsgInfo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult shortVideo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult videoMsgInfo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult subscribe(Map<String, String> params, BaseMessage msgInfo) {

        logger.info("开始调用关注回复服务");
        // 关注回复,使用客服接口
        customService.handlerCustomerMessage_subscribe(params);
        // 发送小程序卡片
        customService.handlerCustomerMessage_miniprogrampage(params);
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult unsubscribe(Map<String, String> params, BaseMessage msgInfo) {

        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult scan(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult eventLocation(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult eventClick(Map<String, String> params, BaseMessage msgInfo) {

        logger.info("CLICK_RGKF事件");
        if (params.get("EventKey").equals("CLICK_BUG")) {
            String text = JsonConfig.getJsonResource("datas/BUG").toString();

            JSONObject json = JSONObject.fromObject(text);
            json.put("touser", params.get("FromUserName"));
            customService.handleKefuMessage(json);
        }
        if (params.get("EventKey").equals("CLICK_RGKF")) {
            String text = JsonConfig.getJsonResource("datas/rgkf").toString();

            JSONObject json = JSONObject.fromObject(text);
            json.put("touser", params.get("FromUserName"));
            customService.handleKefuMessage(json);
        }
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult eventView(Map<String, String> params, BaseMessage msgInfo) {

        logger.info("view事件,人工客服");

//      String content = "欢迎关注我的微信公众号";
//      WeChatResult result = new WeChatResult();
//      String text = JsonConfig.getJsonResource("datas/rgkf").toString();

//      TextMessage text = new TextMessage();
//      text.setContent(content);// 自动回复
//      text.setCreateTime(DateTimeUtil.currentTime());
//      text.setToUserName(msgInfo.getFromUserName());
//      text.setFromUserName(msgInfo.getToUserName());
//      text.setMsgId(msgInfo.getMsgId());
//      text.setMsgType("text");
//      result.setObject(text);
        return null;

    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult kfCreateSession(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult kfCloseSession(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     * 
     * @return
     */
    @Override
    public WeChatResult kfSwitchSession(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     */
    @Override
    public void eventDefaultReply(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub

    }

    /*
     * TODO
     * 
     * @param params
     * 
     * @param msgInfo
     */
    @Override
    public void defaultMsgInfo(Map<String, String> params, BaseMessage msgInfo) {
        // TODO Auto-generated method stub

    }

}

如有错漏请指出,欢迎加群 581817132
Java 微信公众平台开发(二)——事件推送与被动回复

相关标签: 微信公众号开发