TkEndpointCore.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. using dodohold.core;
  2. using molilian.core;
  3. using Org.BouncyCastle.Crypto;
  4. using System;
  5. using System.Collections.Concurrent;
  6. using System.Linq;
  7. using YunhuiKit;
  8. using static molilian.core.TkPoolCore;
  9. public partial class TkEndpointCore
  10. {
  11. private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
  12. private static IEnumerable<TkEndpointConfigDTO> _cached;
  13. // 添加到 TkEndpointCore 类中
  14. private static readonly ConcurrentDictionary<(int, bool?, string), List<TkEndpointConfigDTO>> _accountEndpointCache = new();
  15. private static async Task<IEnumerable<TkEndpointConfigDTO>> ListAsync(bool force = false)
  16. {
  17. // 内存缓存检查
  18. if (!force && _cached != null) return _cached;
  19. string cache_key = "cache:tk_endpoint_config";
  20. if (!force)
  21. {
  22. var cachedList = await RedisHelper.GetAsync<IEnumerable<TkEndpointConfigDTO>>(cache_key);
  23. if (cachedList != null)
  24. {
  25. _cached = cachedList;
  26. return cachedList;
  27. }
  28. }
  29. // 获取新数据
  30. await _semaphore.WaitAsync();
  31. try
  32. {
  33. // 双重检查,防止并发情况下重复加载
  34. if (!force)
  35. {
  36. var cachedList = await RedisHelper.GetAsync<IEnumerable<TkEndpointConfigDTO>>(cache_key);
  37. if (cachedList != null)
  38. {
  39. _cached = cachedList;
  40. return cachedList;
  41. }
  42. }
  43. // 从数据库加载数据
  44. var list = new DBContext.Table("tk_endpoint_config")
  45. .Where("status=@status", new { status = 1 })
  46. .Select<TkEndpointConfigDTO>();
  47. if (list == null) return default;
  48. // 更新调用次数
  49. //foreach (var item in list)
  50. //{
  51. // item.current_daily_calls = await RiskControlCore.GetAllNodesTkEndpointCallsAsync(item.id, DateTime.Now.ToString("yyyyMMdd"));
  52. // item.current_hourly_calls = await RiskControlCore.GetAllNodesTkEndpointCallsAsync(item.id, DateTime.Now.ToString("yyyyMMddHH"));
  53. //}
  54. // 更新缓存
  55. await RedisHelper.SetAsync(cache_key, list, 30 * 86400);
  56. _cached = list;
  57. _accountEndpointCache.Clear();
  58. return list;
  59. }
  60. finally
  61. {
  62. _semaphore.Release();
  63. }
  64. }
  65. public static Task<List<TkEndpointConfigDTO>> GetEndpointsByAccountAsync(int accountId, bool? isTaobaoUrl = null, string parseEndpoints = null)
  66. {
  67. return GetEndpointsByAccountInternalAsync(accountId, isTaobaoUrl, parseEndpoints, cloneResult: true);
  68. }
  69. public static Task<List<TkEndpointConfigDTO>> GetEndpointsByAccountReadonlyAsync(int accountId, bool? isTaobaoUrl = null, string parseEndpoints = null)
  70. {
  71. return GetEndpointsByAccountInternalAsync(accountId, isTaobaoUrl, parseEndpoints, cloneResult: false);
  72. }
  73. private static async Task<List<TkEndpointConfigDTO>> GetEndpointsByAccountInternalAsync(int accountId, bool? isTaobaoUrl, string parseEndpoints, bool cloneResult)
  74. {
  75. string normalizedParseEndpoints = NormalizeParseEndpoints(parseEndpoints);
  76. var cacheKey = (accountId, isTaobaoUrl, normalizedParseEndpoints);
  77. if (_accountEndpointCache.TryGetValue(cacheKey, out var cached))
  78. return cloneResult ? cached.Select(x => x.DeepCopy()).ToList() : cached;
  79. // 2. 获取所有端点(包含全局和专属)
  80. var allEndpoints = (await ListAsync())?.Where(e => e.status).ToList() ?? [];
  81. // 3. 合并逻辑:专属配置覆盖全局配置(关键!)
  82. var mergedEndpoints = allEndpoints
  83. .GroupBy(e => e.endpoint)
  84. .Select(g =>
  85. {
  86. // 优先取专属配置
  87. var accountSpecific = g.FirstOrDefault(e => e.tk_pool_id == accountId);
  88. if (accountSpecific != null)
  89. return accountSpecific.DeepCopy(); // 深拷贝专属配置
  90. // 否则取全局配置(tk_pool_id=0)
  91. var global = g.FirstOrDefault(e => e.tk_pool_id == 0);
  92. return global?.DeepCopy(); // 深拷贝全局配置
  93. })
  94. .Where(e => e != null)
  95. .ToList();
  96. // 4. 根据isTaobaoUrl过滤
  97. if (isTaobaoUrl.HasValue)
  98. {
  99. mergedEndpoints = mergedEndpoints.Where(e =>
  100. {
  101. if (e.taobao_id_handling == 0) return true;
  102. if (e.taobao_id_handling == 1 && isTaobaoUrl.Value) return true;
  103. if (e.taobao_id_handling == 2 && !isTaobaoUrl.Value) return true;
  104. return false;
  105. }).ToList();
  106. }
  107. // 5. 排除指定的端点
  108. if (!string.IsNullOrEmpty(normalizedParseEndpoints))
  109. {
  110. var parseEndpointIds = normalizedParseEndpoints.Split(',')
  111. .Select(idStr => int.TryParse(idStr.Trim(), out var id) ? id : (int?)null)
  112. .Where(id => id.HasValue)
  113. .Select(id => id.Value)
  114. .ToHashSet();
  115. mergedEndpoints = mergedEndpoints.Where(e => parseEndpointIds.Contains(e.ep_id)).ToList();
  116. }
  117. // 6. 存入缓存
  118. var cachedValue = mergedEndpoints.Select(x => x.DeepCopy()).ToList();
  119. _accountEndpointCache.TryAdd(cacheKey, cachedValue);
  120. return cloneResult ? cachedValue.Select(x => x.DeepCopy()).ToList() : cachedValue;
  121. }
  122. // 添加缓存清理方法
  123. public static void ClearEndpointCache(int accountId)
  124. {
  125. foreach (var key in _accountEndpointCache.Keys.Where(key => key.Item1 == accountId).ToList())
  126. {
  127. _accountEndpointCache.TryRemove(key, out _);
  128. }
  129. }
  130. public static void Refresh()
  131. {
  132. _ = AllListAsync(true);
  133. _ = ListAsync(true);
  134. _accountEndpointCache.Clear();
  135. }
  136. private static string NormalizeParseEndpoints(string? parseEndpoints)
  137. {
  138. if (string.IsNullOrWhiteSpace(parseEndpoints)) return string.Empty;
  139. return string.Join(",",
  140. parseEndpoints.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
  141. .Where(item => !string.IsNullOrWhiteSpace(item))
  142. .Distinct(StringComparer.Ordinal)
  143. .OrderBy(item => item, StringComparer.Ordinal));
  144. }
  145. }