微信开发四 接收事件推送

本例中有些引用的类与方法不做过多介绍,之后会提供完整源码下载,请自行查看。

本篇根据开发者文档-接收事件推送编写。请对照查看,一些传入与返回参数就不过多介绍。地址为:https://mp.weixin.qq.com/wiki/14/f79bdec63116f376113937e173652ba2.html


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

关注/取消关注事件

用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做帐号的解绑。

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

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

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

/*
 * 接收事件推送消息
 * http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
 */
@XmlRootElement
public class MsgBaseEvent {

    @XmlElement(name = "ToUserName")  
    private String ToUserName;      //开发者微信号

    @XmlElement(name = "FromUserName")  
    private String FromUserName;    //发送方帐号(一个OpenID)

    @XmlElement(name = "CreateTime")  
    private long CreateTime;        //消息创建时间 (整型)

    @XmlElement(name = "MsgType")  
    private String MsgType;         //消息类型,event

    @XmlElement(name = "Event")  
    private String Event;           //事件类型
}

扫描带参数二维码事件

用户扫描带场景值二维码时,可能推送以下两种事件:

  • 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
  • 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
    /*
     * 用户未关注时,进行关注后的事件推送
     * Event    事件类型,subscribe
     * EventKey 事件KEY值,qrscene_为前缀,后面为二维码的参数值
     * 
     * 用户已关注时的事件推送
     * Event    事件类型,SCAN
     * EventKey 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
     */
    @XmlRootElement
    public class MsgScanCodeEvent extends MsgBaseEvent {
    
        @XmlElement(name = "EventKey")
        private String EventKey;    //事件KEY值,qrscene_为前缀,后面为二维码的参数值 
    
        @XmlElement(name = "Ticket")
        private String Ticket;      //二维码的ticket,可用来换取二维码图片
    
        public String getEventKey() {
            return EventKey;
        }
        public void setEventKey(String eventKey) {
            EventKey = eventKey;
        }
    
        public String getTicket() {
            return Ticket;
        }
        public void setTicket(String ticket) {
            Ticket = ticket;
        }
    
        /*
         * 接收到的文本示例
         * <xml>
         *  <ToUserName><![CDATA[toUser]]></ToUserName>
         *  <FromUserName><![CDATA[FromUser]]></FromUserName>
         *  <CreateTime>123456789</CreateTime>
         *  <MsgType><![CDATA[event]]></MsgType>
         *  <Event><![CDATA[subscribe|SCAN]]></Event>
         *  <EventKey><![CDATA[qrscene_123123|SCENE_VALUE]]></EventKey>
         *  <Ticket><![CDATA[TICKET]]></Ticket>
         * </xml>
         */
        public static MsgScanCodeEvent requestMessage(String xmlStr) throws JAXBException {
            xmlStr = xmlStr.replace("xml", "msgScanCodeEvent");
            JAXBContext context = JAXBContext.newInstance(MsgScanCodeEvent.class);  
            Unmarshaller unmarshaller = context.createUnmarshaller();  
            return (MsgScanCodeEvent)unmarshaller.unmarshal(new StringReader(xmlStr));
        }
    }

上报地理位置事件

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

/*
 * Event--->
 * LOCATION
 */
@XmlRootElement
public class MsgLocationEvent extends MsgBaseEvent {

    @XmlElement(name = "Latitude") 
    private float Latitude;     //地理位置纬度

    @XmlElement(name = "Longitude") 
    private float Longitude;    //地理位置经度

    @XmlElement(name = "Precision") 
    private float Precision;    //地理位置精度

    public float getLatitude() {
        return Latitude;
    }
    public void setLatitude(float latitude) {
        Latitude = latitude;
    }

    public float getLongitude() {
        return Longitude;
    }
    public void setLongitude(float longitude) {
        Longitude = longitude;
    }

    public float getPrecision() {
        return Precision;
    }
    public void setPrecision(float precision) {
        Precision = precision;
    }

    /*
     * 接收到的文本示例
     * <xml>
     *   <ToUserName><![CDATA[toUser]]></ToUserName>
     *   <FromUserName><![CDATA[fromUser]]></FromUserName>
     *   <CreateTime>1348831860</CreateTime>
     *   <MsgType><![CDATA[event]]></MsgType>
     *   <Event><![CDATA[LOCATION]]></Event>
     *   <Latitude>23.137466</Latitude>
     *   <Longitude>113.352425</Longitude>
     *   <Precision>119.385040</Precision>
     * </xml>
     */
    public static MsgLocationEvent requestMessage(String xmlStr) throws JAXBException {
        xmlStr = xmlStr.replace("xml", "msgLocationEvent");
        JAXBContext context = JAXBContext.newInstance(MsgLocationEvent.class);  
        Unmarshaller unmarshaller = context.createUnmarshaller();  
        return (MsgLocationEvent)unmarshaller.unmarshal(new StringReader(xmlStr));
    }
}

自定义菜单事件

用户点击自定义菜单后,微信会把点击事件推送给开发者,请注意,点击菜单弹出子菜单,不会产生上报。

点击菜单拉取消息时的事件推送
/*
 * Event--->
 * click:点击菜单拉取消息时的事件推送
 */
@XmlRootElement
public class MenuClickEvent extends MenuBaseEvent {

    /*
     * 接收到的文本示例
     * <xml>
     *  <ToUserName><![CDATA[toUser]]></ToUserName>
     *  <FromUserName><![CDATA[FromUser]]></FromUserName>
     *  <CreateTime>123456789</CreateTime>
     *  <MsgType><![CDATA[event]]></MsgType>
     *  <Event><![CDATA[CLICK]]></Event>
     *  <EventKey><![CDATA[EVENTKEY]]></EventKey>
     * </xml>
     */
    public static MenuClickEvent requestMessage(String xmlStr) throws JAXBException {
        xmlStr = xmlStr.replace("xml", "menuClickEvent");
        JAXBContext context = JAXBContext.newInstance(MenuClickEvent.class);  
        Unmarshaller unmarshaller = context.createUnmarshaller();  
        return (MenuClickEvent)unmarshaller.unmarshal(new StringReader(xmlStr));
    }
}

点击菜单跳转链接时的事件推送

/*
 * Event--->
 * view:点击菜单跳转链接时的事件推送
 */
@XmlRootElement
public class MenuViewEvent extends MenuBaseEvent {

    @XmlElement(name = "MenuId")  
    private String MenuId ;         //指菜单ID,如果是个性化菜单,则可以通过这个字段,知道是哪个规则的菜单被点击了。

    public String getMenuId() {
        return MenuId;
    }
    public void setMenuId(String menuId) {
        MenuId = menuId;
    }

    /*
     * 接收到的文本示例
     * <xml>
     *  <ToUserName><![CDATA[toUser]]></ToUserName>
     *  <FromUserName><![CDATA[FromUser]]></FromUserName>
     *  <CreateTime>123456789</CreateTime>
     *  <MsgType><![CDATA[event]]></MsgType>
     *  <Event><![CDATA[VIEW]]></Event>
     *  <EventKey><![CDATA[www.qq.com]]></EventKey>
     *  <MenuId>MENUID</MenuId>
     * </xml>
     */
    public static MenuViewEvent requestMessage(String xmlStr) throws JAXBException {
        xmlStr = xmlStr.replace("xml", "menuViewEvent");
        JAXBContext context = JAXBContext.newInstance(MenuViewEvent.class);  
        Unmarshaller unmarshaller = context.createUnmarshaller();  
        return (MenuViewEvent)unmarshaller.unmarshal(new StringReader(xmlStr));
    }
}
<think>好的,我现在需要帮助用户解决关于微信硬件平台绑定事件推送实现的问题。首先,我得回顾之前的对话历史,确保我理解用户的需求。用户之前询问了微信硬件绑定事件的处理方法,我给出了详细的解决方案,包括事件识别、处理流程、设计要点等。现在用户进一步询问如何实现绑定事件推送功能,需要参考提供的引用内容。 首先,我需要检查用户提供的引用内容。引用1提到了微信硬件平台的开发流程,包括设备绑定事件推送和处理步骤。引用2和3讨论了微信小程序与硬件配网的实现,特别是脱离腾讯平台的解决方案。引用4提到了微信硬件云的两种接入方案。这些引用中,引用1和4最相关,因为它们直接涉及微信硬件平台的官方流程和接入方式。 接下来,我需要结合之前的回答和这些引用,整理出实现绑定事件推送的具体步骤。根据之前的回答,处理绑定事件的关键点包括事件识别、异步处理、安全验证等。而引用1提到,绑定事件微信推送至第三方服务器配置的URL开发者需要解密数据并根据消息类型处理。同时,引用4中的接入方案可能需要考虑,但用户的问题更侧重于绑定事件的处理,而不是接入方式的选择。 用户的问题是如何实现绑定事件推送功能,所以需要从服务器配置、事件接收、数据处理、响应机制等方面展开。需要确保回答覆盖以下方面: 1. 服务器配置:设置接收事件URL,并在微信平台配置。 2. 数据接收与解密:微信推送的是加密数据,需解密处理。 3. 事件类型判断:识别MsgType和Event字段,处理bind事件。 4. 异步处理机制:避免阻塞,使用线程池或消息队列。 5. 安全验证:签名验证、防重放攻击等。 6. 响应规范:按照微信要求返回ACK响应。 7. 数据存储与绑定关系维护:将设备与用户OpenID关联。 8. 异常处理与日志记录。 此外,引用3提到自定义回调参数,可能涉及到设备如何返回信息给微信,但用户的问题主要在于接收和处理绑定事件,因此可能需要简要提及后续的设备状态上报,但重点在绑定事件的处理流程。 需要确保回答结构清晰,分步骤说明,并参考官方文档(引用1和4)的建议。同时,用户之前的回答中提到的异步处理、线程池配置、安全策略等也需要整合进来。可能还需要提到如何防止重放攻击,如使用时间戳和nonce校验,这在之前的回答中有涉及,引用1也提到了解密和消息处理。 最后,生成相关问题,引导用户进一步了解安全措施、解绑处理或配网集成等内容,参考引用2和3中的小程序和配网相关主题。</think>根据微信硬件平台的技术规范和实践经验,实现设备绑定事件推送功能需遵循以下核心步骤,结合XML消息交互和异步处理机制: ### 一、基础配置流程 1. **服务端URL配置** - 在微信硬件平台开发者后台设置消息接收URL - 验证服务器有效性: $$ \text{签名验证}:\text{sha1}(timestamp + nonce + token) = signature $$ 2. **消息加解密配置** - 选择AES加密模式(推荐) - 配置消息加解密密钥: ```properties # 示例配置项 wechat.encodingAESKey=abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG wechat.token=your_secure_token ``` ### 二、事件处理架构 ![微信硬件事件处理流程图](https://via.placeholder.com/600x400?text=Binding+Event+Flow) #### 1. 消息解密处理(Python示例) ```python from Crypto.Cipher import AES import base64 def decrypt_msg(encrypt_msg, aes_key): aes_key = base64.b64decode(aes_key + "=") cipher = AES.new(aes_key, AES.MODE_CBC, aes_key[:16]) decrypted = cipher.decrypt(base64.b64decode(encrypt_msg)) return unpad(decrypted.decode('utf-8')) # 微信官方要求的消息充方式处理 def unpad(s): return s[:-ord(s[len(s)-1:])] ``` #### 2. 绑定事件判定逻辑 ```java public boolean isBindEvent(Map<String,String> msgMap) { return "device_event".equals(msgMap.get("MsgType")) && "bind".equals(msgMap.get("Event")); } ``` ### 三、核心实现模块 #### 1. 异步处理框架设计 ```java // 使用Spring的异步处理机制 @Async("deviceBindExecutor") public void handleBindAsync(DeviceBindEvent event) { try { // 1. 验证设备合法性 Device device = deviceService.validate(event.getDeviceId()); // 2. 建立用户绑定关系 userDeviceMapper.insert( new UserDevice(event.getOpenId(), device.getId(), System.currentTimeMillis()) ); // 3. 初始化设备状态 deviceStateService.initState(device.getId()); } catch (DuplicateBindingException e) { logger.warn("重复绑定设备: {}", event.getDeviceId()); } } // 线程池配置 @Bean(name = "deviceBindExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(500); executor.setThreadNamePrefix("DeviceBind-"); executor.initialize(); return executor; } ``` #### 2. 响应报文生成 ```xml <!-- 成功响应模板 --> <xml> <ToUserName><![CDATA[{{FromUserName}}]]></ToUserName> <FromUserName><![CDATA[{{ToUserName}}]]></FromUserName> <CreateTime>{{timestamp}}</CreateTime> <MsgType><![CDATA[device_event]]></MsgType> <DeviceID><![CDATA[{{deviceId}}]]></DeviceID> <DeviceStatus>3</DeviceStatus> </xml> <!-- 状态码说明: 0-设备离线 1-设备在线 3-指令已接收 --> ``` ### 、安全增强措施 1. **防重放攻击机制** - 时间窗口验证: $$ |T_{server} - T_{wechat}| < 5\text{分钟} $$ - Nonce缓存检测: ```redis SETEX nonce:{{value}} 300 1 ``` 2. **数据完整性验证** - 消息签名算法: $$ \text{Signature} = \text{sha1}(nonce + timestamp + encrypted\_msg) $$ - 验证流程图: ``` 接收消息 → 提取签名 → 本地计算签名 → 比对结果 → 异常日志记录 ``` ### 五、调试与监控 1. **微信开发者工具链** - 使用[微信硬件平台调试工具](https://iot.weixin.qq.com/wiki/doc/tools/en/device_debug_tool.pdf) - 模拟绑定事件报文: ```xml <xml> <ToUserName><![CDATA[gh_abcdef12345]]></ToUserName> <FromUserName><![CDATA[oV4L5j2Z3qy9I]]></FromUserName> <CreateTime>1640995200</CreateTime> <MsgType><![CDATA[device_event]]></MsgType> <Event><![CDATA[bind]]></Event> <DeviceID><![CDATA[DEVICE_001]]></DeviceID> <OpenID><![CDATA[oV4L5j2Z3qy9I]]></OpenID> </xml> ``` 2. **监控指标设置** ```prometheus # HELP wechat_bind_requests Total device bind requests wechat_bind_requests_total{status="success"} 238 wechat_bind_requests_total{status="failure"} 12 # HELP bind_processing_time_seconds Time spent processing bind requests bind_processing_time_seconds_bucket{le="0.1"} 215 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值