Heim  >  Artikel  >  Backend-Entwicklung  >  C# WeChat-Entwicklungsmethode für öffentliche Konten zum Empfangen von Event-Push und Nachrichtendeduplizierung

C# WeChat-Entwicklungsmethode für öffentliche Konten zum Empfangen von Event-Push und Nachrichtendeduplizierung

高洛峰
高洛峰Original
2018-05-23 11:22:502790Durchsuche

Das Beispiel in diesem Artikel beschreibt die Methode zum Empfangen von Event-Push und Nachrichtendeduplizierung bei der Entwicklung öffentlicher C#-WeChat-Konten. Teilen Sie es als Referenz mit allen. Die spezifische Analyse lautet wie folgt:

Wenn der WeChat-Server innerhalb von 5 Sekunden keine Antwort erhält, trennt er die Verbindung und initiiert die Anfrage erneut, wobei es insgesamt dreimal erneut versucht wird. In diesem Fall tritt das Problem auf. Es gibt ein solches Szenario: Wenn ein Benutzer einem WeChat-Konto folgt, werden die aktuellen Benutzerinformationen abgerufen und diese Informationen dann in die Datenbank geschrieben. Ähnlich wie bei der Registrierung auf der PC-Website. Vielleicht ist die Geschäftslogik, mit der wir umgehen müssen, aufgrund dieser Bedenken relativ komplex. Zum Beispiel das Senden von Punkten, das Schreiben von Benutzerprotokollen und das Zuweisen von Benutzergruppen. Warten Sie ... Eine Reihe von Logik muss ausgeführt werden, oder die Netzwerkumgebung ist relativ komplex und es gibt keine Garantie dafür, dass der Vorgang des aktuellen Benutzers innerhalb von 5 Sekunden beantwortet wird. Wenn der Vorgang noch nicht abgeschlossen ist, wird der WeChat-Server verwendet schiebt das gleiche Aufmerksamkeitsereignis auf unseren Server. Wir werden unsere Logik erneut ausführen, was zu doppelten Daten in der Datenbank führen kann (einige Kinderschuhe sagen: Bevor ich die Daten einfüge, stelle ich zunächst fest, ob sie bereits vorhanden sind, und wenn sie vorhanden sind, Die Einfügung wird nicht durchgeführt. Ich habe das am Anfang gedacht, aber es gibt immer noch eine Lücke zwischen der realen Betriebsumgebung und unserer Debugging-Umgebung von doppelten Benutzerinformationen in der Datenbank, dass ich die Bedeutung der Nachrichtendeduplizierung entdeckt habe.

Bei der Nachrichtendeduplizierung gibt es einen Unterschied zwischen normalen Nachrichten und Ereignisnachrichten. Gewöhnliche Nachrichten verwenden msgid, während Ereignisnachrichten FromUserName + CreateTime verwenden. Meine Idee ist:
Erstellen Sie eine neue Klasse BaseMsg mit drei Attributen: FromUser, MsgFlag und CreateTime. Der Code lautet wie folgt:

public class BaseMsg
{
        /// 631fb227578dfffda61e1fa4d04b7d25
        /// 发送者标识
        /// 039f3e95db2a684c7b74365531eb6044
        public string FromUser { get; set; }
        /// 631fb227578dfffda61e1fa4d04b7d25
        /// 消息表示。普通消息时,为msgid,事件消息时,为事件的创建时间
        /// 039f3e95db2a684c7b74365531eb6044
        public string MsgFlag { get; set; }
        /// 631fb227578dfffda61e1fa4d04b7d25
        /// 添加到队列的时间
        /// 039f3e95db2a684c7b74365531eb6044
        public DateTime CreateTime { get; set; }
}

Erstellen Sie eine statische list_queue zum Speichern der Nachrichtenliste. Der Typ der Liste ist Listf822bda892e76d613fb5d807a4e9e7c8.
Bevor Sie den WeChat-Nachrichtentext verarbeiten, stellen Sie zunächst fest, ob Wenn die Liste nicht instanziiert ist, wird sie instanziiert. Andernfalls wird beurteilt, ob die Länge der Liste größer oder gleich 50 ist (dies kann angepasst werden und wird für das gleichzeitige Nachrichtenvolumen von WeChat verwendet). größer oder gleich 50, behalten Sie die Nachrichten bei, die nicht innerhalb von 20 Sekunden geantwortet haben (Alle 5 Sekunden erneut versuchen, insgesamt 3 Wiederholungsversuche, also 15 Sekunden. Um auf der sicheren Seite zu sein, schreibe ich hier 20 Sekunden).
Rufen Sie den Nachrichtentyp des aktuellen Nachrichtentexts ab und bestimmen Sie anhand von _queue, ob die aktuelle Nachricht angefordert wurde. Wenn es sich um ein Ereignis handelt, werden FromUser und Erstellungszeit gespeichert. Wenn es sich um eine normale Nachricht handelt, wird MsgFlag gespeichert. Das Folgende ist der Code:

if (_queue == null)
{
 _queue = new Listf822bda892e76d613fb5d807a4e9e7c8();
}
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;
 }
}

Wenn die Nachricht bereits in der Warteschlange vorhanden ist, wird die aktuelle Nachricht nicht in eine Entität umgewandelt und null wird direkt zurückgegeben. Beim Aufruf erfolgt keine Verarbeitung wenn null zurückgegeben wird.

Beginnen wir mit der Erläuterung der Veranstaltungsneuigkeiten. Fortsetzung des vorherigen Artikels. Alle Nachrichten erben BaseMessage und alle Ereignistypen enthalten eine Event-Eigenschaft. Um den Aufruf hier zu erleichtern, wird die Nachrichtenentität definiert, nachdem die Nachricht

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

in der Aufzählung definiert wurde.

Ereignis verfolgen/entfolgen
xml-Datenpaket lautet wie folgt:

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

Entsprechende Entität:

/// <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; }
}

Was hier beachtet werden muss, ist das Wann Der Benutzer scannt. Wenn der Benutzer bei Verwendung eines QR-Codes mit Parametern nicht dem aktuellen öffentlichen Konto folgt, dies jedoch der Fall ist, werden der Parameter qrscene_ und das Ticket in den Nachrichtentext aufgenommen, sodass hier zwei Attribute definiert werden: EventKey und Ticket . Wenn Sie EventKey einen Wert zuweisen, ersetzen Sie qrscene_, denn was wir wirklich brauchen, sind die folgenden Parameter.

Scannen eines QR-Code-Ereignisses mit Parametern
Wenn ein Benutzer einen QR-Code mit Szenenwert scannt, können zwei Ereignisse übertragen werden:

Wenn der Benutzer dem offiziellen Konto nicht gefolgt ist, wird der Benutzer Sie können dem offiziellen Konto folgen. Nach dem Verfolgen wird WeChat die folgenden Ereignisse mit Szenenwerten an die Entwickler weiterleiten.
Wenn der Benutzer dem offiziellen Konto gefolgt ist, leitet WeChat das Scan-Ereignis mit Szenenwert an den Entwickler weiter. ,
Der erste Typ wurde oben besprochen und nur der zweite Typ wird hier erläutert.

Ereignis-Push, wenn der Benutzer gefolgt ist

Das XML-Paket lautet wie folgt:

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

Die entsprechenden Entitäten lauten wie folgt:

/// <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; }
}

Geolocation-Ereignis melden
Wenn das offizielle Konto die Funktion zur Meldung des geografischen Standorts aktiviert, wird jedes Mal, wenn der Benutzer die offizielle Kontositzung betritt und der Meldung des geografischen Standorts zustimmt, bei der Eingabe der geografische Standort oder der geografische Standort gemeldet Der Standort wird alle 5 Sekunden nach Eingabe der Standortantwort gemeldet. Das öffentliche Konto kann die Einstellungen im Hintergrund der öffentlichen Plattform ändern. Beim Melden des geografischen Standorts sendet WeChat das gemeldete geografische Standortereignis an die vom Entwickler eingegebene URL.

Das XML-Datenpaket lautet wie folgt:

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

Die entsprechenden Entitäten lauten wie folgt:

/// <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; }
}

Häufig verwendete Ereignisse für benutzerdefinierte Menüereignisse sind: Klicken, Anzeigen , scancode_puth, scancode_waitmsg , location_select. Es gibt auch drei Ereignisse zum Posten von Bildern. Da sie nicht häufig verwendet werden, hat der Autor nicht an die Verwendungsszenarien gedacht, daher werde ich sie nicht einzeln beschreiben. Wenn Sie interessiert sind, können Sie sie selbst studieren oder mit mir kommunizieren .

Das vom Klickereignis gepushte XML-Datenpaket:

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

Das Format des vom Ansichtsereignis gepushten XML-Datenpakets ist das gleiche wie das von Click, also definieren Sie einfach eine Klasse , wie folgt:

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

Das XML-Datenpaket des Scancode-Ereignisses lautet wie folgt:

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

Die entsprechenden Entitäten lauten wie folgt:

/// <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; }
}

Zu diesem Zeitpunkt wurden die derzeit häufig verwendeten Ereignistypnachrichten definiert. In Kombination mit dem, was im vorherigen Artikel erwähnt wurde, lautet der vollständige Code zum Konvertieren von XML-Datenpaketen in Objekte wie folgt:

public class MessageFactory
{
        private static List<BaseMsg> _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<TextMessage>(xml);
                case MsgType.IMAGE: return Utils.ConvertObj<ImgMessage>(xml);
                case MsgType.VIDEO: return Utils.ConvertObj<VideoMessage>(xml);
                case MsgType.VOICE: return Utils.ConvertObj<VoiceMessage>(xml);
                case MsgType.LINK:
                    return Utils.ConvertObj<LinkMessage>(xml);
                case MsgType.LOCATION:
                    return Utils.ConvertObj<LocationMessage>(xml);
                case MsgType.EVENT://事件类型
                {
                    var eventtype = (Event)Enum.Parse(typeof(Event), xdoc.Element("Event").Value.ToUpper());
                    switch (eventtype)
                    {
                        case Event.CLICK:
                            return Utils.ConvertObj<NormalMenuEventMessage>(xml);
                        case Event.VIEW: return Utils.ConvertObj<NormalMenuEventMessage>(xml);
                        case Event.LOCATION: return Utils.ConvertObj<LocationEventMessage>(xml);
                        case Event.LOCATION_SELECT: return Utils.ConvertObj<LocationMenuEventMessage>(xml);
                        case Event.SCAN: return Utils.ConvertObj<ScanEventMessage>(xml);
                        case Event.SUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml);
                        case Event.UNSUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml);
                        case Event.SCANCODE_WAITMSG: return Utils.ConvertObj<ScanMenuEventMessage>(xml);
                        default:
                            return Utils.ConvertObj<EventMessage>(xml);
                    }
                } break;
                default:
                    return Utils.ConvertObj<BaseMessage>(xml);
            }
        }
}

Ich hoffe Dieser Artikel wird für die Entwicklung von WeChat auf Basis von C# hilfreich sein.

Weitere Artikel zu Methoden zum Empfangen von Event-Push und Nachrichtendeduplizierung bei der Entwicklung öffentlicher C#-WeChat-Konten finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn