搜尋
首頁微信小程式微信開發微信開發之接收事件推播與訊息排重

在上一篇的博文中講到,微信的訊息可以大體分為兩種類型,一種是包括:文本,語音,圖片等的普通訊息,另一種就是本篇要將的事件類型。包括:追蹤/取消關注事件,掃描帶參數二維碼事件,上報地理位置事件,自訂選單相關事件等。本篇一一進行講解。

上一篇也提到了,微信伺服器在5秒內收不到回應會斷掉連接,並且重新發起請求,總共重試三次。 這樣的話,問題就來了。有這樣一個場景:當用戶關注微信帳號時,獲取當前用戶信息,然後將信息寫到資料庫中。類似pc端網站的註冊。可能由於這個關注事件中,我們需要處理的業務邏輯比較複雜。如送積分啊,寫用戶日誌啊,分配用戶群組啊。等等……一系列的邏輯需要執行,或者網路環境比較複雜,無法保證5秒內回應當前用戶的操作,那如果當操作尚未完成,微信伺服器又給我們的伺服器推送了一條相同的關注事件,我們將再次執行我們的那些邏輯,這樣就有可能導致資料庫中出現重複的資料(有的童鞋就會說了,我在插入資料之前先判斷目前是否已經存在了,如果存在了就不執行插入的操作。 我想說的是,我當初也是這樣想的,但真實的運行環境和我們的調試環境還是有差距的,直到發現數據庫中有不少重複的用戶資訊時,我才發現消息去重的重要性。

訊息的去重普通訊息和事件訊息是有區別的。普通訊息使用msgid,而事件訊息使用FromUserName + CreateTime。我的想法是:

  1. 新類別BaseMsg,有三個屬性分別是FromUser,MsgFlag,CreateTime。程式碼如下:

  2. public class BaseMsg
        {        /// <summary>
            /// 发送者标识        /// </summary>
            public string FromUser { get; set; }        /// <summary>
            /// 消息表示。普通消息时,为msgid,事件消息时,为事件的创建时间        /// </summary>
            public string MsgFlag { get; set; }        /// <summary>
            /// 添加到队列的时间        /// </summary>
            public DateTime CreateTime { get; set; }
        }
  3. 建立個靜態列表_queue,用來儲存訊息列表,列表的型別是List.

  4. 在處理微信訊息體前,先判斷列表是否實例化,如果沒有實例化則實例化,否則判斷列表的長度是否大於或等於50(這個可以自定義,用處就是微信並發的訊息量),如果大於或等於50,則保留20秒內未回應的訊息(5秒重試一次,總共重試3次,就是15秒,保險起見這裡寫20秒)。

  5. 取得目前訊息體的訊息類型,並根據_queue判斷目前訊息是否已經要求了。如果是事件則儲存FromUser和建立時間。如果是普通訊息則儲存MsgFlag。下面是程式碼:

if (_queue == null)
            {
                _queue = new List<BaseMsg>();
            }            else if(_queue.Count>=50)
            {
                _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息            }
            XElement xdoc = XElement.Parse(xml);            var msgtype = xdoc.Element("MsgType").Value.ToUpper();            var FromUserName = xdoc.Element("FromUserName").Value;            var MsgId = xdoc.Element("MsgId").Value;            var CreateTime = xdoc.Element("CreateTime").Value;
            MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype);            if (type!=MsgType.EVENT)
            {                if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = MsgId
                    });
                }                else
                {                    return null;
                }
               
            }            else
            {                if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = CreateTime
                    });
                }                else
                {                    return null;
                }
            }

當訊息已經存在佇列中時,則不轉換目前的訊息為實體了,直接傳回null,呼叫的時候,當傳回null時就不做任何處理。

下面開始講解事件訊息。接上篇講。所有的訊息都繼承BaseMessage,而所有的事件類型都包含一個Event的屬性。這裡為了方便調用,將訊息類型定義為枚舉,程式碼如下:

/// <summary>
    /// 事件类型枚举    /// </summary>
    public enum Event
    {        /// <summary>
        /// 非事件类型        /// </summary>        NOEVENT,        /// <summary>
        /// 订阅        /// </summary>        SUBSCRIBE,        /// <summary>
        /// 取消订阅        /// </summary>        UNSUBSCRIBE,        /// <summary>
        /// 扫描带参数的二维码        /// </summary>        SCAN,        /// <summary>
        /// 地理位置        /// </summary>        LOCATION,        /// <summary>
        /// 单击按钮        /// </summary>        CLICK,        /// <summary>
        /// 链接按钮        /// </summary>        VIEW,        /// <summary>
        /// 扫码推事件        /// </summary>        SCANCODE_PUSH,        /// <summary>
        /// 扫码推事件且弹出“消息接收中”提示框        /// </summary>        SCANCODE_WAITMSG,        /// <summary>
        /// 弹出系统拍照发图        /// </summary>        PIC_SYSPHOTO,        /// <summary>
        /// 弹出拍照或者相册发图        /// </summary>        PIC_PHOTO_OR_ALBUM,        /// <summary>
        /// 弹出微信相册发图器        /// </summary>        PIC_WEIXIN,        /// <summary>
        /// 弹出地理位置选择器        /// </summary>        LOCATION_SELECT,        /// <summary>
        /// 模板消息推送        /// </summary>        TEMPLATESENDJOBFINISH
    }

定義好枚舉後,就是定義訊息實體了。

追蹤/取消追蹤事件

xml封包如下:

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

對應的實體:

/// <summary>
    /// 订阅/取消订阅事件    /// </summary>
    public class SubEventMessage : EventMessage
    {        private string _eventkey;        /// <summary>
        /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值(已去掉前缀,可以直接使用)        /// </summary>
        public string EventKey
        {            get { return _eventkey; }            set { _eventkey = value.Replace("qrscene_", ""); }
        }        /// <summary>
        /// 二维码的ticket,可用来换取二维码图片        /// </summary>
        public string Ticket { get; set; }

    }

這裡要注意的是,當使用者掃描帶有參數的二維碼時,如果使用者沒有關注目前公眾號,使用者追蹤時,會在訊息體中帶上qrscene_參數,和Ticket,所以這裡定義了兩個屬性:EventKey,Ticket 。當EventKey被賦值時,替換掉qrscene_,因為我們真正需要的就是後面的參數。

掃描帶參數二維碼事件

使用者掃描帶場景值二維碼時,可能推送兩種事件:

  1. #如果使用者尚未追蹤公眾號,則使用者可以追蹤公眾號,追蹤後微信會將帶場景值關注事件推送給開發者。

  2. 如果使用者已經關注公眾號,則微信會將帶場景值掃描事件推送給開發者。 、

第一種上面已經講了,這裡就只說明下第二種。

使用者已關注時的事件推送

xml套件如下:

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[SCAN]]></Event><EventKey><![CDATA[SCENE_VALUE]]></EventKey><Ticket><![CDATA[TICKET]]></Ticket></xml>

對應的實體如下:

/// <summary>
    /// 扫描带参数的二维码实体    /// </summary>
    public class ScanEventMessage : EventMessage
    {        /// <summary>
        /// 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id        /// </summary>
        public string EventKey { get; set; }        /// <summary>
        /// 二维码的ticket,可用来换取二维码图片        /// </summary>
        public string Ticket { get; set; }

    }

上報地理位置事件

當公眾號開啟上報地理位置功能後,每次進入公眾號會話時,用戶同意上報地理位置後,都會在進入時上報地理位置,或在進入回話後每5秒上報一次地理位置,公眾號可以再公眾平台的後台中修改設定。回報地理位置時,微信會將上報地理位置事件推播到開發者填寫的url。

xml封包如下:

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[LOCATION]]></Event><Latitude>23.137466</Latitude><Longitude>113.352425</Longitude><Precision>119.385040</Precision></xml>

對應的實體如下:

/// <summary>
    /// 上报地理位置实体    /// </summary>
    public class LocationEventMessage : EventMessage
    {        /// <summary>
        /// 地理位置纬度        /// </summary>
        public string Latitude { get; set; }        /// <summary>
        /// 地理位置经度        /// </summary>
        public string Longitude { get; set; }       /// <summary>
        /// 地理位置精度       /// </summary>
        public string Precision { get; set; }

    }

自定义菜单事件常用的事件有:click,view,scancode_puth,scancode_waitmsg,location_select。另外还有三种发图的事件,由于并不常用,笔者也没想到使用场景,再次就不一一讲述了,有兴趣的可以自己研究下,或者和我进行交流。

click事件推送的xml数据包:

<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>

view事件推送的xml数据包和click的格式是一样的,所以定义一个类就可以了,如下:

/// <summary>
    /// 普通菜单事件,包括click和view    /// </summary>
    public class NormalMenuEventMessage : EventMessage
    {        /// <summary>
        /// 事件KEY值,设置的跳转URL        /// </summary>
        public string EventKey { get; set; }
  

    }

scancode事件的xml数据包如下:

<xml><ToUserName><![CDATA[ToUserName]]></ToUserName><FromUserName><![CDATA[FromUserName]]></FromUserName><CreateTime>1419265698</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[scancode_push]]></Event><EventKey><![CDATA[EventKey]]></EventKey><ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType><ScanResult><![CDATA[http://weixin.qq.com/r/JEy5oRLE0U_urVbC9xk2]]></ScanResult></ScanCodeInfo></xml>

对应的实体如下:

/// <summary>
    /// 菜单扫描事件    /// </summary>
    public class ScanMenuEventMessage : EventMessage
    {        /// <summary>
        /// 事件KEY值        /// </summary>
        public string EventKey { get; set; }        /// <summary>
        /// 扫码类型。qrcode是二维码,其他的是条码        /// </summary>
        public string ScanType { get; set; }        /// <summary>
        /// 扫描结果        /// </summary>
        public string ScanResult { get; set; }
    }

至此,当前常用的事件类型消息都已定义完毕,结合上一篇所讲的,将xml数据包转换成对象的完整代码如下:

public class MessageFactory
    {        private static List _queue; 
        public static BaseMessage CreateMessage(string xml)
        {            if (_queue == null)
            {
                _queue = new List<BaseMsg>();
            }            else if(_queue.Count>=50)
            {
                _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息            }
            XElement xdoc = XElement.Parse(xml);            var msgtype = xdoc.Element("MsgType").Value.ToUpper();            var FromUserName = xdoc.Element("FromUserName").Value;            var MsgId = xdoc.Element("MsgId").Value;            var CreateTime = xdoc.Element("CreateTime").Value;
            MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype);            if (type!=MsgType.EVENT)
            {                if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = MsgId
                    });
                }                else
                {                    return null;
                }
               
            }            else
            {                if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = CreateTime
                    });
                }                else
                {                    return null;
                }
            }            switch (type)
            {                case MsgType.TEXT: return Utils.ConvertObj(xml);                case MsgType.IMAGE: return Utils.ConvertObj(xml);                case MsgType.VIDEO: return Utils.ConvertObj(xml);                case MsgType.VOICE: return Utils.ConvertObj(xml);                case MsgType.LINK:                    return Utils.ConvertObj(xml);                case MsgType.LOCATION:                    return Utils.ConvertObj(xml);                case MsgType.EVENT://事件类型                {                    var eventtype = (Event)Enum.Parse(typeof(Event), xdoc.Element("Event").Value.ToUpper());                    switch (eventtype)
                    {                        case Event.CLICK:                            return Utils.ConvertObj(xml);                        case Event.VIEW: return Utils.ConvertObj(xml);                        case Event.LOCATION: return Utils.ConvertObj(xml);                        case Event.LOCATION_SELECT: return Utils.ConvertObj(xml);                        case Event.SCAN: return Utils.ConvertObj(xml);                        case Event.SUBSCRIBE: return Utils.ConvertObj(xml);                        case Event.UNSUBSCRIBE: return Utils.ConvertObj(xml);                        case Event.SCANCODE_WAITMSG: return Utils.ConvertObj(xml);                        default:                            return Utils.ConvertObj(xml);
                    }
                } break;                default:                    return Utils.ConvertObj(xml);
            }
        }
    }

【相关推荐】

1.微信公众号平台源码下载

2.微信啦啦外卖2.2.4解密开源版 微信魔方源码

以上是微信開發之接收事件推播與訊息排重的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版