CpsPoolCore.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. using dodohold.core;
  2. using System.Threading.Channels;
  3. namespace molilian.core
  4. {
  5. public partial class CpsPoolCore
  6. {
  7. private static string _end_point;
  8. private static Dictionary<string, decimal> _incomeAmt = new();
  9. private static Dictionary<string, int> _calls = new();
  10. static Random _random = new Random();
  11. static CpsPoolCore()
  12. {
  13. _end_point = Environment.GetEnvironmentVariable("EndPoint");
  14. }
  15. private static readonly object _lockObj = new();
  16. private static IEnumerable<CpsLinksDTO> _links_cached;
  17. private static IEnumerable<CpsApiStyleDTO> _api_style_cached;
  18. private static IEnumerable<CpsPoolDTO> _cached;
  19. public static CpsLinksDTO? GetOne(string channel)
  20. {
  21. var list = List();
  22. if (!list.Any()) return null;
  23. var pool = list.Where(e => e.channel_code == channel && e.status).FirstOrDefault();
  24. if (pool == null) return null;
  25. var links = LinksList();
  26. if (!links.Any()) return null;
  27. // 获取符合条件的链接
  28. var eligibleLinks = links.Where(e => IsEligible(e, channel)).ToList();
  29. if (!eligibleLinks.Any()) return null;
  30. // 计算总权重
  31. int totalWeight = eligibleLinks.Sum(item => item.priority_weight);
  32. if (totalWeight == 0) return null;
  33. int randomValue = _random.Next(totalWeight);
  34. // 按权重查找
  35. int accumulatedWeight = 0;
  36. foreach (var link in eligibleLinks.OrderByDescending(e => e.priority_weight))
  37. {
  38. accumulatedWeight += link.priority_weight;
  39. if (randomValue < accumulatedWeight)
  40. return link;
  41. }
  42. // 保险起见返回第一个
  43. return eligibleLinks.First();
  44. }
  45. public static CpsLinksDTO? GetOne2(string channel)
  46. {
  47. var list = List();
  48. if (!list.Any()) return null;
  49. var pool = list.Where(e => e.channel_code == channel && e.status).FirstOrDefault();
  50. if (pool == null) return null;
  51. var links = LinksList();
  52. if (!links.Any()) return null;
  53. //int totalWeight = links.Sum(item => item.priority_weight);
  54. //int randomValue = _random.Next(0,totalWeight);
  55. //// 累积权重直到找到选中项
  56. //double accumulatedWeight = 0;
  57. //foreach (var item in items)
  58. //{
  59. // accumulatedWeight += Math.Pow(2, weightSelector(item));
  60. // if (randomValue <= accumulatedWeight)
  61. // return item;
  62. //}
  63. return links.Where(e => IsEligible(e, channel))
  64. .OrderByDescending(e => e.priority_weight)
  65. .ThenBy(l => Guid.NewGuid())
  66. .FirstOrDefault();
  67. }
  68. private static bool IsEligible(CpsLinksDTO item, string channel)
  69. {
  70. if (item.channel_code != channel) return false;
  71. // 使用初始化参数创建工作时间表
  72. if (!new WorkSchedule(item.time_range).IsWorkHour()) return false;
  73. var now = DateTime.Now;
  74. if (item.start_time > now || now > item.end_time) return false;
  75. if (item.daily_calls_limit > 0)
  76. {
  77. int daily_num = RiskControlCore.GetCalls(TkChannelEnum.cps, item.id, DateTime.Now.ToString("yyyyMMdd"));
  78. if (daily_num >= item.daily_calls_limit) return false;
  79. }
  80. if (item.hourly_calls_limit > 0)
  81. {
  82. int hourly_num = RiskControlCore.GetCalls(TkChannelEnum.cps, item.id, DateTime.Now.ToString("yyyyMMddHH"));
  83. if (hourly_num >= item.hourly_calls_limit) return false;
  84. }
  85. return true;
  86. }
  87. public static IEnumerable<CpsPoolDTO> List(bool force = false)
  88. {
  89. if (!force && _cached != null) return _cached;
  90. string cache_key = $"cache:cps_pool";
  91. var list = RedisHelper.Get<IEnumerable<CpsPoolDTO>>(cache_key);
  92. if (force || list == null)
  93. {
  94. lock (_lockObj)
  95. {
  96. list = new DBContext.Table("cps_pool")
  97. .Where("status=@status", new { status = 1 })
  98. .Select<CpsPoolDTO>();
  99. if (list == null) return default;
  100. RedisHelper.Set(cache_key, list, 30 * 86400);
  101. }
  102. }
  103. _cached = list;
  104. return list;
  105. }
  106. public static IEnumerable<CpsLinksDTO> LinksList(bool force = false)
  107. {
  108. if (!force && _links_cached != null) return _links_cached;
  109. string cache_key = $"cache:cps_links";
  110. var list = RedisHelper.Get<IEnumerable<CpsLinksDTO>>(cache_key);
  111. if (force || list == null)
  112. {
  113. lock (_lockObj)
  114. {
  115. list = new DBContext.Table("cps_links")
  116. .Where("status=@status", new { status = 1 })
  117. .Select<CpsLinksDTO>();
  118. if (list == null) return default;
  119. foreach (var item in list)
  120. {
  121. RiskControlCore.SetCallsAsync(TkChannelEnum.cps, item.id, DateTime.Now.ToString("yyyyMMddHH"), item.current_hourly_calls);
  122. RiskControlCore.SetCallsAsync(TkChannelEnum.cps, item.id, DateTime.Now.ToString("yyyyMMdd"), item.current_daily_calls);
  123. }
  124. RedisHelper.Set(cache_key, list, 30 * 86400);
  125. }
  126. }
  127. _links_cached = list;
  128. return list;
  129. }
  130. public static IEnumerable<CpsApiStyleDTO> ApiStyleList(bool force = false)
  131. {
  132. if (!force && _api_style_cached != null) return _api_style_cached;
  133. string cache_key = $"cache:cps_api_style";
  134. var list = RedisHelper.Get<IEnumerable<CpsApiStyleDTO>>(cache_key);
  135. if (force || list == null)
  136. {
  137. lock (_lockObj)
  138. {
  139. list = new DBContext.Table("cps_api_syle")
  140. .Where("status=@status", new { status = 1 })
  141. .Select<CpsApiStyleDTO>();
  142. if (list == null) return default;
  143. RedisHelper.Set(cache_key, list, 30 * 86400);
  144. }
  145. }
  146. _api_style_cached = list;
  147. return list;
  148. }
  149. public static int Update(CpsLinksDTO account)
  150. {
  151. return new DBContext.Table("cps_links")
  152. .Add("current_hourly_calls", account.current_hourly_calls)
  153. .Add("current_daily_calls", account.current_daily_calls)
  154. .Where("id=@id", new { account.id })
  155. .Update();
  156. }
  157. public static void Refresh()
  158. {
  159. _ = List(true);
  160. _ = LinksList(true);
  161. _ = ApiStyleList(true);
  162. }
  163. internal static void AccountExhausted()
  164. {
  165. string cache_key = $"cache:tk_pool:account:exhausted";
  166. long count = RedisHelper.IncrBy(cache_key);
  167. if (count > 1) return;
  168. RedisHelper.Expire(cache_key, 3600);
  169. NotifyCore.Notify(new NifyMessage
  170. {
  171. message = $"【CPS优惠券】没有匹配账号",
  172. priority = NifyMessagePriority.high,
  173. tags = ["red_circle"]
  174. });
  175. _ = NotifyCore.QYWeixinPushNotifyAsync("没账号", $"【CPS优惠券】没有匹配账号");
  176. }
  177. public static CpsApiStyleDTO? GetTitle(CpsLinksDTO account, string style)
  178. {
  179. CpsApiStyleDTO result = null;
  180. try
  181. {
  182. if (string.IsNullOrEmpty(account.style_code)) return null;
  183. var list = ApiStyleList();
  184. var record = list?.Where(e => e.style_code == account.style_code && e.style_name == style).FirstOrDefault();
  185. if (record != null) result = record;
  186. }
  187. catch { }
  188. return result;
  189. }
  190. }
  191. }