base.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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. await SaveParseAccountCacheAsync($"dp_{dp_flag}:{accountName}", success, message, reason);
  292. if (accountId != 0)
  293. {
  294. await SaveParseAccountCacheAsync($"dp_{dp_flag}:{channel}_{accountId}", success, message, reason);
  295. }
  296. await SaveParseAccountCacheAsync("all", success, message, reason);
  297. await SaveParseAccountCacheAsync($"dp_channel:{channel}", success, message, reason);
  298. await SaveParseAccountCacheAsync($"{channel}", success, message, reason);
  299. await SaveParseAccountCacheAsync($"{accountName}", success, message, reason);
  300. if (accountId != 0)
  301. {
  302. //todo 放着跑两天,要将读取的地方改成读取accountid
  303. await SaveParseAccountCacheAsync($"{channel}_{accountId}", success, message, reason);
  304. }
  305. }
  306. private static async Task SaveParseAccountCacheAsync(string accountName, bool success,
  307. string message, string reason)
  308. {
  309. await SaveStatsAccountCacheAsync("parse_total", accountName, success, message, reason);
  310. }
  311. private static void saveParseCache(string channel, int accountId, string accountName,
  312. bool success, string message, string reason, string deeplink)
  313. {
  314. string dp_flag = deeplink switch
  315. {
  316. "" => "none",
  317. "tbopen://m.taobao.com/tbopen/index.html" or
  318. "pinduoduo://com.xunmeng.pinduoduo/" or
  319. "snssdk1128://feed?refer=web" or
  320. "bdnetdisk://n/action.EXTERNAL_ACTIVITY" or
  321. "openapp.jdmobile://virtual?params=" or "openapp.jdmobile://" => "home",
  322. _ => success ? "success" : "fail",
  323. };
  324. //关于dp的缓存
  325. saveParseAccountCache($"dp_{dp_flag}:all", success, message, reason);
  326. saveParseAccountCache($"dp_{dp_flag}:{channel}", success, message, reason);
  327. saveParseAccountCache($"dp_{dp_flag}:{accountName}", success, message, reason);
  328. if (accountId != 0)
  329. {
  330. saveParseAccountCache($"dp_{dp_flag}:{channel}_{accountId}", success, message, reason);
  331. }
  332. saveParseAccountCache("all", success, message, reason);
  333. saveParseAccountCache($"{channel}", success, message, reason);
  334. saveParseAccountCache($"{accountName}", success, message, reason);
  335. if (accountId != 0)
  336. {
  337. //todo 放着跑两天,要将读取的地方改成读取accountid
  338. saveParseAccountCache($"{channel}_{accountId}", success, message, reason);
  339. }
  340. }
  341. private static void saveParseAccountCache(string accountName, bool success,
  342. string message, string reason)
  343. {
  344. SaveStatsAccountCache("parse_total", accountName, success, message, reason);
  345. }
  346. private static void SaveStatsAccountCache(string prefix, string accountName, bool success,
  347. string message, string reason)
  348. {
  349. var now = DateTime.Now;
  350. string month = now.ToString("yyyyMM");
  351. string day = now.ToString("yyyyMMdd");
  352. string hour = now.ToString("yyyyMMddHH");
  353. SaveStatsCount(prefix, accountName, month);
  354. SaveStatsCount(prefix, accountName, day);
  355. SaveStatsCount(prefix, accountName, hour);
  356. string result = success ? "success" : "fail";
  357. SaveStatsCount(prefix, accountName, month, result);
  358. SaveStatsCount(prefix, accountName, day, result);
  359. SaveStatsCount(prefix, accountName, hour, result);
  360. SaveStatsDimension(prefix, accountName, "message", message, month);
  361. SaveStatsDimension(prefix, accountName, "message", message, day);
  362. SaveStatsDimension(prefix, accountName, "message", message, hour);
  363. SaveStatsDimension(prefix, accountName, "reason", reason, month);
  364. SaveStatsDimension(prefix, accountName, "reason", reason, day);
  365. SaveStatsDimension(prefix, accountName, "reason", reason, hour);
  366. }
  367. private static async Task SaveStatsAccountCacheAsync(string prefix, string accountName, bool success,
  368. string message, string reason)
  369. {
  370. var now = DateTime.Now;
  371. string month = now.ToString("yyyyMM");
  372. string day = now.ToString("yyyyMMdd");
  373. string hour = now.ToString("yyyyMMddHH");
  374. await SaveStatsCountAsync(prefix, accountName, month);
  375. await SaveStatsCountAsync(prefix, accountName, day);
  376. await SaveStatsCountAsync(prefix, accountName, hour);
  377. string result = success ? "success" : "fail";
  378. await SaveStatsCountAsync(prefix, accountName, month, result);
  379. await SaveStatsCountAsync(prefix, accountName, day, result);
  380. await SaveStatsCountAsync(prefix, accountName, hour, result);
  381. await SaveStatsDimensionAsync(prefix, accountName, "message", message, month);
  382. await SaveStatsDimensionAsync(prefix, accountName, "message", message, day);
  383. await SaveStatsDimensionAsync(prefix, accountName, "message", message, hour);
  384. await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, month);
  385. await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, day);
  386. await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, hour);
  387. }
  388. private static void SaveStatsCount(string prefix, string accountName, string timeKey, string? dimension = null)
  389. {
  390. string key = string.IsNullOrEmpty(dimension)
  391. ? $":{prefix}:{accountName}:{timeKey}"
  392. : $":{prefix}:{accountName}:{dimension}:{timeKey}";
  393. RedisHelper.IncrBy(key);
  394. RedisHelper.Expire(key, GetStatsExpireSeconds(timeKey));
  395. }
  396. private static async Task SaveStatsCountAsync(string prefix, string accountName, string timeKey, string? dimension = null)
  397. {
  398. string key = string.IsNullOrEmpty(dimension)
  399. ? $":{prefix}:{accountName}:{timeKey}"
  400. : $":{prefix}:{accountName}:{dimension}:{timeKey}";
  401. await RedisKit.IncrByAsync(key);
  402. await RedisKit.ExpireAsync(key, GetStatsExpireSeconds(timeKey));
  403. }
  404. private static void SaveStatsDimension(string prefix, string accountName, string name, string value, string timeKey)
  405. {
  406. if (string.IsNullOrEmpty(value)) return;
  407. int expireSeconds = GetStatsExpireSeconds(timeKey);
  408. string setKey = $":{prefix}:{accountName}:{name}:{timeKey}";
  409. RedisHelper.SAdd(setKey, value);
  410. RedisHelper.Expire(setKey, expireSeconds);
  411. string valueKey = $":{prefix}:{accountName}:{value}:{timeKey}";
  412. RedisHelper.IncrBy(valueKey);
  413. RedisHelper.Expire(valueKey, expireSeconds);
  414. string namedValueKey = $":{prefix}:{accountName}:{name}:{value}:{timeKey}";
  415. RedisHelper.IncrBy(namedValueKey);
  416. RedisHelper.Expire(namedValueKey, expireSeconds);
  417. }
  418. private static async Task SaveStatsDimensionAsync(string prefix, string accountName, string name, string value, string timeKey)
  419. {
  420. if (string.IsNullOrEmpty(value)) return;
  421. int expireSeconds = GetStatsExpireSeconds(timeKey);
  422. string setKey = $":{prefix}:{accountName}:{name}:{timeKey}";
  423. await RedisKit.SAddAsync(setKey, value);
  424. await RedisKit.ExpireAsync(setKey, expireSeconds);
  425. string valueKey = $":{prefix}:{accountName}:{value}:{timeKey}";
  426. await RedisKit.IncrByAsync(valueKey);
  427. await RedisKit.ExpireAsync(valueKey, expireSeconds);
  428. string namedValueKey = $":{prefix}:{accountName}:{name}:{value}:{timeKey}";
  429. await RedisKit.IncrByAsync(namedValueKey);
  430. await RedisKit.ExpireAsync(namedValueKey, expireSeconds);
  431. }
  432. private static int GetStatsExpireSeconds(string timeKey)
  433. {
  434. return timeKey.Length switch
  435. {
  436. 10 => StatsHourlyExpireSeconds,
  437. 8 => StatsDailyExpireSeconds,
  438. 6 => StatsMonthlyExpireSeconds,
  439. _ => StatsDailyExpireSeconds,
  440. };
  441. }
  442. public static async Task<int> GetTotalAsync(string keyname, bool all_node = true)
  443. {
  444. try
  445. {
  446. var tasks = EndPointCore.List()
  447. .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
  448. .Where(node => all_node || (CenterHub.IsCenter ? !node.is_coupon_api : node.is_coupon_api))
  449. .Select(async node =>
  450. {
  451. try
  452. {
  453. #if DEBUG
  454. switch (node.name)
  455. {
  456. case "bj":
  457. node.redis_server = "101.200.152.61:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook";
  458. break;
  459. case "gz":
  460. node.redis_server = "8.138.110.158:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook";
  461. break;
  462. case "coupon1":
  463. node.redis_server = "123.56.185.166:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=coupon";
  464. break;
  465. }
  466. #endif
  467. var redisServer = EndPointCore.GetRedisServer(node);
  468. if (string.IsNullOrEmpty(redisServer))
  469. return 0;
  470. await using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
  471. return await scope.Client.GetAsync<int>(keyname);
  472. }
  473. catch (Exception)
  474. {
  475. return 0;
  476. }
  477. });
  478. var results = await Task.WhenAll(tasks);
  479. return results.Sum();
  480. }
  481. catch (Exception)
  482. {
  483. return 0;
  484. }
  485. }
  486. public static async Task<string[]> GetTotalKeysAsync(string keyname, bool all_node = true)
  487. {
  488. var tasks = EndPointCore.List()
  489. .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
  490. .Where(node => all_node || (CenterHub.IsCenter ? !node.is_coupon_api : node.is_coupon_api))
  491. .Select(async node =>
  492. {
  493. try
  494. {
  495. try
  496. {
  497. var redisServer = EndPointCore.GetRedisServer(node);
  498. if (string.IsNullOrEmpty(redisServer)) return [];
  499. using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
  500. return await scope.Client.SMembersAsync<string>(keyname);
  501. }
  502. catch (Exception ex) { }
  503. return [];
  504. }
  505. catch (Exception ex)
  506. {
  507. return [];
  508. }
  509. });
  510. var results = await Task.WhenAll(tasks);
  511. return results.SelectMany(x => x).Distinct().ToArray();
  512. }
  513. }
  514. }