EventSender.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. 
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using TapSDK.Core.Internal.Log;
  11. using TapSDK.Core.Standalone.Internal.Http;
  12. using UnityEngine;
  13. using UnityEngine.Networking;
  14. namespace TapSDK.Core.Standalone.Internal
  15. {
  16. public class EventSender
  17. {
  18. private const string OldEventFilePath = "events.json";
  19. private readonly TapLog log = new TapLog("TapEvent");
  20. private string persistentDataPath = Application.persistentDataPath;
  21. private Queue<Dictionary<string, object>> eventQueue = new Queue<Dictionary<string, object>>();
  22. private TapHttp tapHttp = TapHttp
  23. .NewBuilder("TapSDKCore", TapTapSDK.Version)
  24. .Sign(TapHttpSign.CreateNoneSign())
  25. .Parser(TapHttpParser.CreateEventParser())
  26. .Build();
  27. private const int MaxEvents = 50;
  28. private const int MaxBatchSize = 200;
  29. private const float SendInterval = 15f;
  30. private Timer timer;
  31. private DateTime lastSendTime;
  32. private string domain = Constants.SERVER_URL_CN;
  33. private int QueueCount => eventQueue.Count;
  34. public EventSender()
  35. {
  36. // 设置计时器
  37. timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromSeconds(SendInterval));
  38. lastSendTime = DateTime.Now;
  39. // 初始化 HttpClient
  40. var header = new Dictionary<string, string>
  41. {
  42. { "User-Agent", $"{TapTapSDK.SDKPlatform}/{TapTapSDK.Version}" }
  43. };
  44. var coreOptions = TapCoreStandalone.coreOptions;
  45. if (coreOptions.region == TapTapRegionType.CN)
  46. {
  47. domain = Constants.SERVER_URL_CN;
  48. }
  49. else
  50. {
  51. domain = Constants.SERVER_URL_IO;
  52. }
  53. // 加载未发送的事件
  54. LoadEvents();
  55. SendEventsAsync(null);
  56. }
  57. public async void SendEventsAsync(Action onSendComplete)
  58. {
  59. if (eventQueue.Count == 0)
  60. {
  61. onSendComplete?.Invoke();
  62. return;
  63. }
  64. var eventsToSend = new List<Dictionary<string, object>>();
  65. for (int i = 0; i < MaxBatchSize && eventQueue.Count > 0; i++)
  66. {
  67. eventsToSend.Add(eventQueue.Dequeue());
  68. }
  69. var body = new Dictionary<string, object> {
  70. { "data", eventsToSend }
  71. };
  72. var resonse = await tapHttp.PostJsonAsync<Boolean>(path: $"{domain}/v2/batch", json: body);
  73. if (resonse.IsSuccess)
  74. {
  75. // log.Log("Events sent successfully");
  76. }
  77. else
  78. {
  79. log.Warning("Failed to send events");
  80. // 将事件重新添加到队列
  81. foreach (var eventParams in eventsToSend)
  82. {
  83. eventQueue.Enqueue(eventParams);
  84. }
  85. }
  86. onSendComplete?.Invoke();
  87. SaveEvents();
  88. }
  89. public void Send(Dictionary<string, object> eventParams)
  90. {
  91. // 将事件添加到队列
  92. eventQueue.Enqueue(eventParams);
  93. SaveEvents();
  94. // 检查队列大小
  95. if (QueueCount >= MaxEvents)
  96. {
  97. SendEvents();
  98. ResetTimer();
  99. }
  100. }
  101. private void OnTimerElapsed(object state)
  102. {
  103. var offset = (DateTime.Now - lastSendTime).TotalSeconds;
  104. if (offset >= SendInterval)
  105. {
  106. SendEvents();
  107. ResetTimer();
  108. }
  109. }
  110. private void ResetTimer()
  111. {
  112. timer.Change(TimeSpan.FromSeconds(SendInterval), TimeSpan.FromSeconds(SendInterval));
  113. }
  114. private string GetEventCacheFileName(){
  115. if (TapTapSDK.taptapSdkOptions != null
  116. && !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId)){
  117. return "events_" + TapTapSDK.taptapSdkOptions.clientId + ".json";
  118. }
  119. return OldEventFilePath;
  120. }
  121. private void LoadEvents()
  122. {
  123. string filePath = Path.Combine(persistentDataPath, GetEventCacheFileName());
  124. if(!File.Exists(filePath)){
  125. string oldFilePath = Path.Combine(persistentDataPath, OldEventFilePath);
  126. // 兼容旧版本文件
  127. if (File.Exists(oldFilePath)) {
  128. File.Move(oldFilePath, filePath);
  129. }
  130. }
  131. if (File.Exists(filePath))
  132. {
  133. string jsonData = File.ReadAllText(filePath);
  134. if (string.IsNullOrEmpty(jsonData))
  135. {
  136. return;
  137. }
  138. var savedEvents = ConvertToListOfDictionaries(Json.Deserialize(jsonData));
  139. if (savedEvents == null)
  140. {
  141. return;
  142. }
  143. foreach (var eventParams in savedEvents)
  144. {
  145. eventQueue.Enqueue(eventParams);
  146. }
  147. }
  148. }
  149. private void SaveEvents()
  150. {
  151. try
  152. {
  153. if (eventQueue == null)
  154. {
  155. return;
  156. }
  157. var eventList = eventQueue.ToList();
  158. string jsonData = Json.Serialize(eventList);
  159. if (string.IsNullOrEmpty(GetEventCacheFileName()))
  160. {
  161. TapLog.Error("EventFilePath is null or empty");
  162. return;
  163. }
  164. string filePath = Path.Combine(persistentDataPath, GetEventCacheFileName());
  165. if (string.IsNullOrEmpty(filePath))
  166. {
  167. return;
  168. }
  169. File.WriteAllText(filePath, jsonData);
  170. }
  171. catch (Exception ex)
  172. {
  173. TapLog.Error("SaveEvents Exception - " + ex.Message);
  174. }
  175. }
  176. public void SendEvents()
  177. {
  178. SendEventsAsync(() => lastSendTime = DateTime.Now);
  179. }
  180. private Dictionary<string, object> ConvertToDictionary(Dictionary<string, object> original)
  181. {
  182. var result = new Dictionary<string, object>();
  183. foreach (var keyValuePair in original)
  184. {
  185. if (keyValuePair.Value is Dictionary<string, object> nestedDictionary)
  186. {
  187. result[keyValuePair.Key] = ConvertToDictionary(nestedDictionary);
  188. }
  189. else if (keyValuePair.Value is List<object> nestedList)
  190. {
  191. result[keyValuePair.Key] = ConvertToListOfDictionaries(nestedList);
  192. }
  193. else
  194. {
  195. result[keyValuePair.Key] = keyValuePair.Value;
  196. }
  197. }
  198. return result;
  199. }
  200. private List<Dictionary<string, object>> ConvertToListOfDictionaries(object deserializedData)
  201. {
  202. if (deserializedData is List<object> list)
  203. {
  204. var result = new List<Dictionary<string, object>>();
  205. foreach (var item in list)
  206. {
  207. if (item is Dictionary<string, object> dictionary)
  208. {
  209. result.Add(ConvertToDictionary(dictionary));
  210. }
  211. else
  212. {
  213. return null; // 数据格式不匹配
  214. }
  215. }
  216. return result;
  217. }
  218. return null; // 数据格式不匹配
  219. }
  220. [Serializable]
  221. private class Serialization<T>
  222. {
  223. public List<T> items;
  224. public Serialization(List<T> items)
  225. {
  226. this.items = items;
  227. }
  228. public List<T> ToList()
  229. {
  230. return items;
  231. }
  232. }
  233. }
  234. }