이전 블로그 게시물에서 언급했듯이 WeChat 메시지는 크게 두 가지 유형으로 나눌 수 있습니다. 하나는 문자, 음성, 사진 등의 일반적인 메시지이고, 다른 하나는 이 글에서 논의할 이벤트 유형입니다. . 포함: 이벤트 팔로우/언팔로우, 매개변수로 QR 코드 이벤트 스캔, 지리적 위치 이벤트 보고, 사용자 정의 메뉴 관련 이벤트 등 이 기사에서는 이를 하나씩 설명합니다.
이전 기사에서 언급했듯이 WeChat 서버가 5초 이내에 응답을 받지 못하면 연결을 끊고 요청을 다시 시작하여 총 3번을 다시 시도합니다. 이 경우 문제가 발생합니다. 다음과 같은 시나리오가 있습니다. 사용자가 WeChat 계정을 팔로우하면 현재 사용자 정보를 얻은 다음 해당 정보가 데이터베이스에 기록됩니다. PC 홈페이지 회원가입과 유사합니다. 아마도 이러한 우려 때문에 우리가 처리해야 하는 비즈니스 로직은 상대적으로 복잡합니다. 포인트 보내기, 사용자 로그 작성, 사용자 그룹 할당 등이 있습니다. 잠깐... 일련의 로직을 실행해야 하거나, 네트워크 환경이 상대적으로 복잡하고 현재 사용자의 작업이 5초 이내에 응답할 것이라는 보장이 없습니다. 그러면 작업이 아직 완료되지 않으면 WeChat 서버가 작동합니다. 동일한 어텐션 이벤트를 서버에 푸시합니다. 로직을 다시 실행하면 데이터베이스에 데이터가 중복될 수 있습니다. 일부 어린이 신발에서는 데이터를 삽입하기 전에 해당 이벤트가 이미 존재하는지 먼저 확인합니다. 삽입 작업이 수행되지 않습니다. 말씀드리고 싶은 것은 처음에는 그렇게 생각했지만 실제 운영 환경과 우리 디버깅 환경 사이에는 여전히 격차가 있다는 것입니다. 사용자 정보를 데이터베이스에 저장한 후에야 메시지 중복 제거의 중요성을 깨달았습니다.
메시지 중복 제거에서는 일반 메시지와 이벤트 메시지에 차이가 있습니다. 일반 메시지는 msgid를 사용하고, 이벤트 메시지는 FromUserName + CreateTime을 사용합니다. 내 생각은:
FromUser, MsgFlag 및 CreateTime이라는 세 가지 속성 을 사용하여 새로운 BaseMsg 클래스를 만듭니다. 코드는 다음과 같습니다.
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; } }
메시지 목록을 저장하기 위한 staticlist_queue를 생성합니다. 목록 유형은 List
WeChat 메시지 본문을 처리하기 전에 먼저 목록이 인스턴스화되었는지 확인하세요. 그렇지 않으면 목록 길이가 다음보다 크거나 같은지 확인하세요. 50(사용자 정의 가능, 목적은 WeChat의 동시 메시지 수) 50보다 크거나 같을 경우 20초 이내에 응답하지 않은 메시지는 보관됩니다(5초마다 한 번씩 재시도, 총 1회). 3회 재시도(15초). 안전을 위해 여기에 20초를 입력하세요.
현재 메시지 본문의 메시지 유형을 가져오고 _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; } }
필요한 사항 여기에 언급됨 예, 사용자가 매개변수가 포함된 QR 코드를 스캔할 때 사용자가 현재 공식 계정을 팔로우하지 않는 경우 사용자가 팔로우하면 메시지 본문에 qrscene_ 매개변수와 티켓이 포함되므로 여기서 두 가지 속성이 정의됩니다. : 이벤트키, 티켓 . EventKey에 값을 할당할 때 qrscene_을 교체하세요. 실제로 필요한 것은 다음 매개변수이기 때문입니다.
사용자가 장면 값이 포함된 QR 코드를 스캔하면 두 가지 이벤트가 푸시될 수 있습니다.
공식 계정을 팔로우하지 않은 사용자는 공식 계정을 팔로우한 후 WeChat에서 개발자에게 장면 값과 함께 팔로우 이벤트를 푸시합니다.
사용자가 공식 계정을 팔로우한 경우 WeChat은 개발자에게 장면 값 스캔 이벤트를 푸시합니다. ,
위에서 첫 번째 유형에 대해 설명했고 여기서는 두 번째 유형에 대해서만 설명하겠습니다.
사용자 팔로우 시 이벤트 푸시
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초마다 지리적 위치가 신고됩니다. 공식 계정은 공식 플랫폼 백그라운드에서 설정을 수정할 수 있습니다. 지리적 위치를 보고할 때 WeChat은 보고된 지리적 위치 이벤트를 개발자가 입력한 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); } } }
【相关推荐】
위 내용은 이벤트 푸시 수신 및 메시지 중복 제거 WeChat 개발의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!