이 글의 예시에서는 C# WeChat 공개 계정 개발에서 이벤트 푸시 수신 및 메시지 중복 제거 방법을 설명합니다. 참고할 수 있도록 모든 사람과 공유하세요. 구체적인 분석은 다음과 같습니다.
위챗 서버가 5초 이내에 응답을 받지 못하면 연결을 끊고 요청을 다시 시작하여 총 3번을 재시도합니다. 이 경우 문제가 발생합니다. 다음과 같은 시나리오가 있습니다. 사용자가 WeChat 계정을 팔로우하면 현재 사용자 정보를 얻은 다음 해당 정보가 데이터베이스에 기록됩니다. PC 홈페이지 회원가입과 유사합니다. 아마도 이러한 우려 때문에 우리가 처리해야 하는 비즈니스 로직은 상대적으로 복잡합니다. 포인트 보내기, 사용자 로그 작성, 사용자 그룹 할당 등이 있습니다. 잠깐... 일련의 로직을 실행해야 하거나, 네트워크 환경이 상대적으로 복잡하고 현재 사용자의 작업이 5초 이내에 응답할 것이라는 보장이 없습니다. 그러면 작업이 아직 완료되지 않으면 WeChat 서버가 작동합니다. 동일한 어텐션 이벤트를 서버에 푸시합니다. 로직을 다시 실행하면 데이터베이스에 데이터가 중복될 수 있습니다(일부 어린이 신발에서는 데이터를 삽입하기 전에 먼저 해당 데이터가 존재하는지 확인하고 존재하는 경우 삽입 작업은 수행되지 않습니다. 제가 말씀드리고 싶은 것은 처음에는 그렇게 생각했지만 실제 운영 환경과 우리의 디버깅 환경 사이에는 여전히 격차가 있다는 것입니다. 메시지 중복 제거의 중요성을 발견한 데이터베이스의 중복된 사용자 정보.)
메시지 중복 제거에서는 일반 메시지와 이벤트 메시지에 차이가 있습니다. 일반 메시지는 msgid를 사용하고, 이벤트 메시지는 FromUserName + CreateTime을 사용합니다. 내 생각은 다음과 같습니다.
FromUser, MsgFlag 및 CreateTime의 세 가지 속성을 사용하여 새로운 BaseMsg 클래스를 만듭니다. 코드는 다음과 같습니다.
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; } }
정적 list_queue를 생성하여 메시지 목록을 저장합니다. 목록 유형은 Listf822bda892e76d613fb5d807a4e9e7c8입니다.
WeChat 메시지 본문을 처리하기 전에 먼저 목록이 다음과 같은지 확인합니다. 인스턴스화가 없으면 인스턴스화하고, 그렇지 않으면 목록의 길이가 50보다 크거나 같은지 판단합니다. (이는 사용자 정의 가능하며 위챗의 동시 메시지 양에 사용됩니다.) 또는 50과 같으면 응답하지 않는 메시지를 20초 이내에 유지합니다(5초 한 번 재시도, 총 3회 재시도, 15초. 안전을 위해 여기에 20초를 씁니다).
현재 메시지 본문의 메시지 유형을 가져오고 _queue를 기반으로 현재 메시지가 요청되었는지 확인합니다. 이벤트인 경우 FromUser와 생성시간이 저장됩니다. 정상적인 메시지라면 MsgFlag가 저장됩니다. 다음은 코드입니다.
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; } }
메시지가 이미 대기열에 존재하는 경우 현재 메시지는 엔터티로 변환되지 않으며 호출 시 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 }
메시지가 열거형에 정의된 후 메시지 엔터티가 정의됩니다.
Follow/Unfollow event
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와 Ticket이라는 두 가지 속성이 정의됩니다. 표. EventKey에 값을 할당할 때 qrscene_을 교체하세요. 실제로 필요한 것은 다음 매개변수이기 때문입니다.
매개변수가 포함된 QR 코드 이벤트 스캔
사용자가 장면 값이 포함된 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입니다. , 위치_선택. 사진 올리기 이벤트도 3가지 있는데, 자주 사용되지 않는 내용이라 작성자가 일일이 설명하지 않겠습니다. 관심 있으신 분들은 직접 공부하시거나 저와 소통하시면 됩니다. .
클릭 이벤트에 의해 푸시된 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>
뷰 이벤트에 의해 푸시된 xml 데이터 패킷의 형식은 클릭과 동일하므로 다음과 같이 클래스를 정의하면 됩니다.
/// <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<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); } } }
이 기사가 모든 사람의 C# 기반 WeChat 개발에 도움이 되기를 바랍니다.
C# WeChat 공개 계정 개발에서 이벤트 푸시 수신 방법 및 메시지 중복 제거와 관련된 더 많은 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!