base.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. using dodohold.core;
  2. using CSRedis;
  3. using System.Data;
  4. using System.Diagnostics;
  5. using YunhuiKit;
  6. using static ICSharpCode.SharpZipLib.Zip.ExtendedUnixData;
  7. namespace molilian.core
  8. {
  9. public partial class TkLogCore
  10. {
  11. public static bool save_dailys_log = false;
  12. static TkLogCore()
  13. {
  14. int flag = RedisHelper.Get<int>("turn:save_dailys_log");
  15. if (flag == 1) save_dailys_log = true;
  16. }
  17. private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(10, 10);
  18. private const int StatsHourlyExpireSeconds = 7 * 86400;
  19. private const int StatsDailyExpireSeconds = 90 * 86400;
  20. private const int StatsMonthlyExpireSeconds = 366 * 86400;
  21. public static async Task<int> BatchInsertLogDBAsync(int limit)
  22. {
  23. await semaphore.WaitAsync().ConfigureAwait(false);
  24. try
  25. {
  26. var tasks = EndPointCore.List()
  27. .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
  28. .Where(node => CenterHub.IsCenter ? !node.is_coupon_api : node.is_coupon_api)
  29. .Select(async node =>
  30. {
  31. try
  32. {
  33. var redisServer = EndPointCore.GetRedisServer(node);
  34. if (string.IsNullOrEmpty(redisServer)) return 0;
  35. await using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
  36. return BatchInsertLogDB(limit, scope.Client);
  37. }
  38. catch (Exception ex)
  39. {
  40. // TODO: 添加日志记录
  41. return 0;
  42. }
  43. });
  44. var results = await Task.WhenAll(tasks).ConfigureAwait(false);
  45. return results.Sum();
  46. }
  47. catch (Exception)
  48. {
  49. return 0;
  50. }
  51. finally
  52. {
  53. semaphore.Release();
  54. }
  55. }
  56. public static int BatchInsertLogDB(int limit, YunhuiKit.RedisClient redis)
  57. {
  58. int total = 0;
  59. //using var connection = DBContext.GetOpenConnection();
  60. //connection.Open();
  61. //using var transaction = connection.BeginTransaction();
  62. using IDbTransaction transaction = null;
  63. using IDbConnection connection = null;
  64. try
  65. {
  66. Stopwatch stopwatch = new Stopwatch(); // 创建一个计时器
  67. var tasks = new List<Task<int>>
  68. {
  69. RunTaskWithLoggingAsync(() => InsertPromotionImgAsync(limit, redis), "task_insert_promotion_img_logs"),
  70. RunTaskWithLoggingAsync(() => InsertParseTkLogAsync(limit, redis), "task_insert_parse_tb_logs"),
  71. RunTaskWithLoggingAsync(() => InsertParseJDLogAsync(limit, redis), "task_insert_parse_jd_logs"),
  72. RunTaskWithLoggingAsync(() => InsertParsePddLogAsync(limit, redis), "task_insert_parse_pdd_logs"),
  73. RunTaskWithLoggingAsync(() => InsertParseDyLogAsync(limit, redis), "task_insert_parse_dy_logs"),
  74. RunTaskWithLoggingAsync(() => InsertParseKsLogAsync(limit, redis), "task_insert_parse_ks_logs"),
  75. RunTaskWithLoggingAsync(() => InsertToolLogAsync(limit, redis), "task_insert_parse_tool_logs"),
  76. RunTaskWithLoggingAsync(() => InsertDeeplinkLogAsync(limit, redis), "task_insert_parse_deeplink_logs"),
  77. RunTaskWithLoggingAsync(() => InsertCouponLogAsync(limit, redis), "task_insert_parse_coupon_logs"),
  78. RunTaskWithLoggingAsync(() => InsertActivityLogAsync(limit, redis), "task_insert_activity_logs"),
  79. RunTaskWithLoggingAsync(() => InsertCpsLogAsync(limit, redis), "task_insert_parse_cps_logs"),
  80. RunTaskWithLoggingAsync(() => TracksCore.InsertTrackRequestLogAsync(limit, redis), "task_insert_track_request_logs"),
  81. //Task.Run(() => RunTaskWithLogging(() => task_insert_tk_logs(limit, redis), "task_insert_tk_logs")),
  82. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_tb_logs(limit, redis), "task_insert_parse_tb_logs")),
  83. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_jd_logs(limit, redis), "task_insert_parse_jd_logs")),
  84. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_pdd_logs(limit, redis), "task_insert_parse_pdd_logs")),
  85. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_dy_logs(limit, redis), "task_insert_parse_dy_logs")),
  86. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_tool_logs(limit, redis), "task_insert_parse_tool_logs")),
  87. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_deeplink_logs(limit, redis), "task_insert_parse_deeplink_logs")),
  88. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_coupon_logs(limit, redis), "task_insert_parse_coupon_logs")),
  89. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_cps_logs(limit, redis), "task_insert_parse_cps_logs")),
  90. //Task.Run(() => RunTaskWithLogging(() => task_insert_promotion_img_logs(limit, redis), "task_insert_promotion_img_logs")),
  91. //Task.Run(() => RunTaskWithLogging(() => task_insert_parse_ks_logs(limit, redis), "task_insert_parse_ks_logs"))
  92. };
  93. // 等待所有任务完成
  94. Task.WhenAll(tasks).Wait();//252行
  95. // 计算所有任务的结果总和
  96. total = tasks.Select(t => t.Result).Sum();
  97. //transaction.Commit();
  98. }
  99. catch (Exception ex)
  100. {
  101. //transaction.Rollback();
  102. // 构建异常详细信息字符串
  103. string detailedError = $"{ex.Message}\n" +
  104. $"堆栈跟踪: {ex.StackTrace}\n";
  105. if (ex.InnerException != null)
  106. {
  107. detailedError += $"内部异常: {ex.InnerException.Message}\n" +
  108. $"内部堆栈跟踪: {ex.InnerException.StackTrace}\n";
  109. }
  110. // 如果异常包含其他数据,也可以记录下来
  111. if (ex.Data != null && ex.Data.Count > 0)
  112. {
  113. detailedError += "附加数据:\n";
  114. foreach (var key in ex.Data.Keys)
  115. {
  116. detailedError += $"{key}: {ex.Data[key]}\n";
  117. }
  118. }
  119. _ = new LoggerLibrary("database_error", "parse_log")
  120. .Info(detailedError)
  121. .SaveAsync();
  122. NotifyCore.Notify(new NifyMessage
  123. {
  124. message = $"【Exception】\n{detailedError}",
  125. priority = NifyMessagePriority.high,
  126. tags = ["red_circle"]
  127. });
  128. }
  129. finally
  130. {
  131. //connection.Close();
  132. }
  133. return total;
  134. }
  135. private static async Task<int> RunTaskWithLoggingAsync(Func<Task<int>> taskFunc, string taskName)
  136. {
  137. LoggerLibrary log = new LoggerLibrary("debug", "BatchInsertLogDB"); // 创建日志对象
  138. Stopwatch stopwatch = new Stopwatch();
  139. stopwatch.Start();
  140. int taskTotal = await taskFunc();
  141. stopwatch.Stop();
  142. log.Info($"{taskName} 耗时: {stopwatch.ElapsedMilliseconds} ms,\t插入记录数: {taskTotal}");
  143. log.SaveAsync();
  144. return taskTotal;
  145. }
  146. /// <summary>
  147. /// 过时方法 随时删除
  148. /// </summary>
  149. /// <param name="channel"></param>
  150. /// <param name="accountId"></param>
  151. /// <param name="accountName"></param>
  152. /// <param name="success"></param>
  153. /// <param name="message"></param>
  154. /// <param name="reason"></param>
  155. private static void saveCache(string channel, int accountId, string accountName, bool success, string message, string reason)
  156. {
  157. saveAccountCache("all", success, message, reason);
  158. saveAccountCache($"{channel}", success, message, reason);
  159. saveAccountCache($"{accountName}", success, message, reason);
  160. if (accountId != 0)
  161. {
  162. //todo 放着跑两天,要将读取的地方改成读取accountid
  163. saveAccountCache($"{channel}_{accountId}", success, message, reason);
  164. }
  165. }
  166. private static void saveAccountCache(string accountName, bool success, string message, string reason)
  167. {
  168. SaveStatsAccountCache("total", accountName, success, message, reason);
  169. }
  170. private static async Task saveUnionCouponParseCacheAsync(TkDataDTO data)
  171. {
  172. string cacheKey = $":cache:parse:{data.ip}_{data.oaid}_{data.itemId}";
  173. await EndPointCore.ProcessEndPointNodesAsync(node =>
  174. {
  175. if (!node.is_coupon_api) return Task.CompletedTask;
  176. if (string.IsNullOrEmpty(node.redis_server)) return Task.CompletedTask;
  177. var redis = RedisClientManager.GetRedisClient(node.redis_server);
  178. redis.Set(cacheKey, 1, 2 * 86400);
  179. return Task.CompletedTask;
  180. });
  181. }
  182. private async static Task saveClientRequestTotalAsync(TkChannelEnum channel, string ip, string oaid)
  183. {
  184. await saveClientRequestTotalAsync(channel.ToString(), ip, oaid);
  185. }
  186. private static async Task saveClientRequestTotalAsync(string channel, string ip, string oaid)
  187. {
  188. await EndPointCore.ProcessEndPointNodesAsync(node =>
  189. {
  190. if (string.IsNullOrEmpty(node.redis_server)) return Task.CompletedTask;
  191. if (!node.is_public_api) return Task.CompletedTask;
  192. #if DEBUG
  193. switch (node.name)
  194. {
  195. case "bj":
  196. node.redis_server = "101.200.152.61:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook";
  197. break;
  198. case "gz":
  199. node.redis_server = "8.138.110.158:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook";
  200. break;
  201. case "coupon1":
  202. node.redis_server = "123.56.185.166:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=coupon";
  203. break;
  204. default: return Task.CompletedTask;
  205. }
  206. #endif
  207. var redis = RedisClientManager.GetRedisClient(node.redis_server);
  208. string cacheKey = $":cache:{channel}:ip:{DateTime.Now:yyyyMMdd}:{ip}";
  209. redis.IncrBy(cacheKey);
  210. redis.Expire(cacheKey, 86400);
  211. if (!string.IsNullOrEmpty(oaid))
  212. {
  213. cacheKey = $":cache:{channel}:oaid:{DateTime.Now:yyyyMMdd}:{oaid}";
  214. redis.IncrBy(cacheKey);
  215. redis.Expire(cacheKey, 86400);
  216. }
  217. return Task.CompletedTask;
  218. });
  219. }
  220. public static bool InBlacklist(string blacklist, string oaid)
  221. {
  222. if (string.IsNullOrEmpty(blacklist)) return false;
  223. var arr = blacklist.Split(new[] { "\r\n" }, StringSplitOptions.None)
  224. .Select(s => s.Trim()).ToArray();
  225. return arr.Contains(oaid);
  226. }
  227. public static int getClientRequestTotalByOAID(TkChannelEnum channel, string oaid)
  228. {
  229. if (string.IsNullOrEmpty(oaid)) return 0;
  230. string cacheKey = $":cache:{channel}:oaid:{DateTime.Now:yyyyMMdd}:{oaid}";
  231. return RedisHelper.Get<int>(cacheKey);
  232. }
  233. public static int getClientRequestTotalByIp(TkChannelEnum channel, string ip)
  234. {
  235. if (string.IsNullOrEmpty(ip)) return 0;
  236. string cacheKey = $":cache:{channel}:ip:{DateTime.Now:yyyyMMdd}:{ip}";
  237. return RedisHelper.Get<int>(cacheKey);
  238. }
  239. public static int getClientRequestTotalByOAID(string channel, string oaid)
  240. {
  241. if (string.IsNullOrEmpty(oaid)) return 0;
  242. string cacheKey = $":cache:{channel}:oaid:{DateTime.Now:yyyyMMdd}:{oaid}";
  243. return RedisHelper.Get<int>(cacheKey);
  244. }
  245. public static int getClientRequestTotalByIp(string channel, string ip)
  246. {
  247. if (string.IsNullOrEmpty(ip)) return 0;
  248. string cacheKey = $":cache:{channel}:ip:{DateTime.Now:yyyyMMdd}:{ip}";
  249. return RedisHelper.Get<int>(cacheKey);
  250. }
  251. private static void saveClientRequestTotal(CpsChannelEnum channel, string ip, string oaid)
  252. {
  253. string cacheKey = $":cache:cps_{channel}:ip:{DateTime.Now:yyyyMMdd}:{ip}";
  254. RedisHelper.IncrBy(cacheKey);
  255. RedisHelper.Expire(cacheKey, 86400);
  256. if (!string.IsNullOrEmpty(oaid))
  257. {
  258. cacheKey = $":cache:cps_{channel}:oaid:{DateTime.Now:yyyyMMdd}:{oaid}";
  259. RedisHelper.IncrBy(cacheKey);
  260. RedisHelper.Expire(cacheKey, 86400);
  261. }
  262. }
  263. public static int getClientRequestTotalByOAID(CpsChannelEnum channel, string oaid)
  264. {
  265. if (string.IsNullOrEmpty(oaid)) return 0;
  266. string cacheKey = $":cache:cps_{channel}:oaid:{DateTime.Now:yyyyMMdd}:{oaid}";
  267. return RedisHelper.Get<int>(cacheKey);
  268. }
  269. public static int getClientRequestTotalByIp(CpsChannelEnum channel, string ip)
  270. {
  271. if (string.IsNullOrEmpty(ip)) return 0;
  272. string cacheKey = $":cache:cps_{channel}:ip:{DateTime.Now:yyyyMMdd}:{ip}";
  273. return RedisHelper.Get<int>(cacheKey);
  274. }
  275. private static async Task SaveParseCacheAsync(string channel, int accountId, string accountName,
  276. bool success, string message, string reason, string deeplink)
  277. {
  278. string dp_flag = deeplink switch
  279. {
  280. "" => "none",
  281. "tbopen://m.taobao.com/tbopen/index.html" or
  282. "pinduoduo://com.xunmeng.pinduoduo/" or
  283. "snssdk1128://feed?refer=web" or
  284. "bdnetdisk://n/action.EXTERNAL_ACTIVITY" or
  285. "openapp.jdmobile://virtual?params=" or "openapp.jdmobile://" => "home",
  286. _ => success ? "success" : "fail",
  287. };
  288. //关于dp的缓存
  289. await SaveParseAccountCacheAsync($"dp_{dp_flag}:all", success, message, reason);
  290. await SaveParseAccountCacheAsync($"dp_{dp_flag}:{channel}", success, message, reason);
  291. if (accountId == 0 && !string.IsNullOrEmpty(accountName))
  292. {
  293. await SaveParseAccountCacheAsync($"dp_{dp_flag}:{accountName}", success, message, reason);
  294. }
  295. if (accountId != 0)
  296. {
  297. await SaveParseAccountCacheAsync($"dp_{dp_flag}:{channel}_{accountId}", success, message, reason);
  298. }
  299. await SaveParseAccountCacheAsync("all", success, message, reason);
  300. await SaveParseAccountCacheAsync($"dp_channel:{channel}", success, message, reason);
  301. await SaveParseAccountCacheAsync($"{channel}", success, message, reason);
  302. if (accountId == 0 && !string.IsNullOrEmpty(accountName))
  303. {
  304. await SaveParseAccountCacheAsync($"{accountName}", success, message, reason);
  305. }
  306. if (accountId != 0)
  307. {
  308. await SaveParseAccountCacheAsync($"{channel}_{accountId}", success, message, reason);
  309. }
  310. }
  311. private static async Task SaveParseAccountCacheAsync(string accountName, bool success,
  312. string message, string reason)
  313. {
  314. await SaveStatsAccountCacheAsync("parse_total", accountName, success, message, reason);
  315. }
  316. private static void saveParseCache(string channel, int accountId, string accountName,
  317. bool success, string message, string reason, string deeplink)
  318. {
  319. string dp_flag = deeplink switch
  320. {
  321. "" => "none",
  322. "tbopen://m.taobao.com/tbopen/index.html" or
  323. "pinduoduo://com.xunmeng.pinduoduo/" or
  324. "snssdk1128://feed?refer=web" or
  325. "bdnetdisk://n/action.EXTERNAL_ACTIVITY" or
  326. "openapp.jdmobile://virtual?params=" or "openapp.jdmobile://" => "home",
  327. _ => success ? "success" : "fail",
  328. };
  329. //关于dp的缓存
  330. saveParseAccountCache($"dp_{dp_flag}:all", success, message, reason);
  331. saveParseAccountCache($"dp_{dp_flag}:{channel}", success, message, reason);
  332. if (accountId == 0 && !string.IsNullOrEmpty(accountName))
  333. {
  334. saveParseAccountCache($"dp_{dp_flag}:{accountName}", success, message, reason);
  335. }
  336. if (accountId != 0)
  337. {
  338. saveParseAccountCache($"dp_{dp_flag}:{channel}_{accountId}", success, message, reason);
  339. }
  340. saveParseAccountCache("all", success, message, reason);
  341. saveParseAccountCache($"{channel}", success, message, reason);
  342. if (accountId == 0 && !string.IsNullOrEmpty(accountName))
  343. {
  344. saveParseAccountCache($"{accountName}", success, message, reason);
  345. }
  346. if (accountId != 0)
  347. {
  348. saveParseAccountCache($"{channel}_{accountId}", success, message, reason);
  349. }
  350. }
  351. private static void saveParseAccountCache(string accountName, bool success,
  352. string message, string reason)
  353. {
  354. SaveStatsAccountCache("parse_total", accountName, success, message, reason);
  355. }
  356. private static void SaveStatsAccountCache(string prefix, string accountName, bool success,
  357. string message, string reason)
  358. {
  359. var now = DateTime.Now;
  360. string month = now.ToString("yyyyMM");
  361. string day = now.ToString("yyyyMMdd");
  362. string hour = now.ToString("yyyyMMddHH");
  363. SaveStatsCount(prefix, accountName, month);
  364. SaveStatsCount(prefix, accountName, day);
  365. SaveStatsCount(prefix, accountName, hour);
  366. string result = success ? "success" : "fail";
  367. SaveStatsCount(prefix, accountName, month, result);
  368. SaveStatsCount(prefix, accountName, day, result);
  369. SaveStatsCount(prefix, accountName, hour, result);
  370. SaveStatsDimension(prefix, accountName, "message", message, month);
  371. SaveStatsDimension(prefix, accountName, "message", message, day);
  372. SaveStatsDimension(prefix, accountName, "message", message, hour);
  373. SaveStatsDimension(prefix, accountName, "reason", reason, month);
  374. SaveStatsDimension(prefix, accountName, "reason", reason, day);
  375. SaveStatsDimension(prefix, accountName, "reason", reason, hour);
  376. }
  377. private static async Task SaveStatsAccountCacheAsync(string prefix, string accountName, bool success,
  378. string message, string reason)
  379. {
  380. var now = DateTime.Now;
  381. string month = now.ToString("yyyyMM");
  382. string day = now.ToString("yyyyMMdd");
  383. string hour = now.ToString("yyyyMMddHH");
  384. await SaveStatsCountAsync(prefix, accountName, month);
  385. await SaveStatsCountAsync(prefix, accountName, day);
  386. await SaveStatsCountAsync(prefix, accountName, hour);
  387. string result = success ? "success" : "fail";
  388. await SaveStatsCountAsync(prefix, accountName, month, result);
  389. await SaveStatsCountAsync(prefix, accountName, day, result);
  390. await SaveStatsCountAsync(prefix, accountName, hour, result);
  391. await SaveStatsDimensionAsync(prefix, accountName, "message", message, month);
  392. await SaveStatsDimensionAsync(prefix, accountName, "message", message, day);
  393. await SaveStatsDimensionAsync(prefix, accountName, "message", message, hour);
  394. await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, month);
  395. await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, day);
  396. await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, hour);
  397. }
  398. private static void SaveStatsCount(string prefix, string accountName, string timeKey, string? dimension = null)
  399. {
  400. string key = string.IsNullOrEmpty(dimension)
  401. ? $":{prefix}:{accountName}:{timeKey}"
  402. : $":{prefix}:{accountName}:{dimension}:{timeKey}";
  403. RedisHelper.IncrBy(key);
  404. RedisHelper.Expire(key, GetStatsExpireSeconds(timeKey));
  405. }
  406. private static async Task SaveStatsCountAsync(string prefix, string accountName, string timeKey, string? dimension = null)
  407. {
  408. string key = string.IsNullOrEmpty(dimension)
  409. ? $":{prefix}:{accountName}:{timeKey}"
  410. : $":{prefix}:{accountName}:{dimension}:{timeKey}";
  411. await RedisKit.IncrByAsync(key);
  412. await RedisKit.ExpireAsync(key, GetStatsExpireSeconds(timeKey));
  413. }
  414. private static void SaveStatsDimension(string prefix, string accountName, string name, string value, string timeKey)
  415. {
  416. if (string.IsNullOrEmpty(value)) return;
  417. int expireSeconds = GetStatsExpireSeconds(timeKey);
  418. string setKey = $":{prefix}:{accountName}:{name}:{timeKey}";
  419. RedisHelper.SAdd(setKey, value);
  420. RedisHelper.Expire(setKey, expireSeconds);
  421. string valueKey = $":{prefix}:{accountName}:{value}:{timeKey}";
  422. RedisHelper.IncrBy(valueKey);
  423. RedisHelper.Expire(valueKey, expireSeconds);
  424. string namedValueKey = $":{prefix}:{accountName}:{name}:{value}:{timeKey}";
  425. RedisHelper.IncrBy(namedValueKey);
  426. RedisHelper.Expire(namedValueKey, expireSeconds);
  427. }
  428. private static async Task SaveStatsDimensionAsync(string prefix, string accountName, string name, string value, string timeKey)
  429. {
  430. if (string.IsNullOrEmpty(value)) return;
  431. int expireSeconds = GetStatsExpireSeconds(timeKey);
  432. string setKey = $":{prefix}:{accountName}:{name}:{timeKey}";
  433. await RedisKit.SAddAsync(setKey, value);
  434. await RedisKit.ExpireAsync(setKey, expireSeconds);
  435. string valueKey = $":{prefix}:{accountName}:{value}:{timeKey}";
  436. await RedisKit.IncrByAsync(valueKey);
  437. await RedisKit.ExpireAsync(valueKey, expireSeconds);
  438. string namedValueKey = $":{prefix}:{accountName}:{name}:{value}:{timeKey}";
  439. await RedisKit.IncrByAsync(namedValueKey);
  440. await RedisKit.ExpireAsync(namedValueKey, expireSeconds);
  441. }
  442. private static int GetStatsExpireSeconds(string timeKey)
  443. {
  444. return timeKey.Length switch
  445. {
  446. 10 => StatsHourlyExpireSeconds,
  447. 8 => StatsDailyExpireSeconds,
  448. 6 => StatsMonthlyExpireSeconds,
  449. _ => StatsDailyExpireSeconds,
  450. };
  451. }
  452. public static async Task<int> GetTotalAsync(string keyname, bool all_node = true)
  453. {
  454. try
  455. {
  456. var tasks = EndPointCore.List()
  457. .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
  458. .Where(node => all_node || (CenterHub.IsCenter ? !node.is_coupon_api : node.is_coupon_api))
  459. .Select(async node =>
  460. {
  461. try
  462. {
  463. #if DEBUG
  464. switch (node.name)
  465. {
  466. case "bj":
  467. node.redis_server = "101.200.152.61:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook";
  468. break;
  469. case "gz":
  470. node.redis_server = "8.138.110.158:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook";
  471. break;
  472. case "coupon1":
  473. node.redis_server = "123.56.185.166:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=coupon";
  474. break;
  475. }
  476. #endif
  477. var redisServer = EndPointCore.GetRedisServer(node);
  478. if (string.IsNullOrEmpty(redisServer))
  479. return 0;
  480. await using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
  481. return await scope.Client.GetAsync<int>(keyname);
  482. }
  483. catch (Exception)
  484. {
  485. return 0;
  486. }
  487. });
  488. var results = await Task.WhenAll(tasks);
  489. return results.Sum();
  490. }
  491. catch (Exception)
  492. {
  493. return 0;
  494. }
  495. }
  496. public static async Task<string[]> GetTotalKeysAsync(string keyname, bool all_node = true)
  497. {
  498. var tasks = EndPointCore.List()
  499. .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
  500. .Where(node => all_node || (CenterHub.IsCenter ? !node.is_coupon_api : node.is_coupon_api))
  501. .Select(async node =>
  502. {
  503. try
  504. {
  505. try
  506. {
  507. var redisServer = EndPointCore.GetRedisServer(node);
  508. if (string.IsNullOrEmpty(redisServer)) return [];
  509. using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
  510. return await scope.Client.SMembersAsync<string>(keyname);
  511. }
  512. catch (Exception ex) { }
  513. return [];
  514. }
  515. catch (Exception ex)
  516. {
  517. return [];
  518. }
  519. });
  520. var results = await Task.WhenAll(tasks);
  521. return results.SelectMany(x => x).Distinct().ToArray();
  522. }
  523. }
  524. }