using dodohold.core; using System.Collections.Generic; using System.Data; using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Web; using YunhuiKit; namespace molilian.core { public partial class OverrideRuleCore { private static IEnumerable _cached; private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); public static async Task> ListAsync(bool force = false) { #if DEBUG return new DBContext.Table("tk_override_rules") .Where("status=@status", new { status = 1 }) .Order("sort DESC, id DESC") .Select(); #endif if (!force && _cached != null) return _cached; try { await _semaphore.WaitAsync(); // 如果缓存存在且未强制刷新,直接返回 if (!force && _cached != null) return _cached; string cache_key = $"cache:tk_override_rules"; IEnumerable? list = null; // 尝试从Redis获取数据 try { list = await RedisKit.GetAsync>(cache_key); } catch (Exception ex) { // 记录Redis错误 _ = new LoggerLibrary("OverrideRule", "Redis").Info(ex.Message, ex.StackTrace).SaveAsync(); } // 如果Redis获取失败或需要强制刷新 if (force || list == null) { try { list = new DBContext.Table("tk_override_rules") .Where("status=@status", new { status = 1 }) .Order("sort DESC, id DESC") .Select(); if (list != null && list.Any()) { // 尝试更新Redis缓存 try { await RedisKit.SetAsync(cache_key, list, 30 * 86400); } catch (Exception ex) { // 记录Redis更新错误 _ = new LoggerLibrary("OverrideRule", "Redis").Info(ex.Message, ex.StackTrace).SaveAsync(); } } } catch (Exception ex) { // 记录数据库查询错误 _ = new LoggerLibrary("OverrideRule", "Database").Info(ex.Message, ex.StackTrace).SaveAsync(); // 如果数据库查询失败但缓存还在,继续使用缓存 if (_cached != null) return _cached; throw; // 如果没有任何可用数据,则抛出异常 } } _cached = list; return list ?? []; } finally { _semaphore.Release(); } } public static async Task ProcessAsync(UnionParseRequest request) { if (string.IsNullOrEmpty(request.Channel)) return null; var list = await ListAsync(); if (list == default) return null; string content = request.Content; if (request.SpecialText == 1) content = request.QueryText; if (string.IsNullOrEmpty(content)) return null; foreach (var item in list) { // if (!request.Channel.Equals(item.platform)) continue; if (item.use_special_text == 1 && request.SpecialText != 1) continue; string original_text = item.original_text; if (item.use_special_text == 1) original_text = request.QueryText; if (string.IsNullOrEmpty(original_text)) continue; switch (item.rule) { case "regex": Match match = Regex.Match(content, original_text); if (!match.Success) continue; break; case "text": if (!content.Equals(original_text)) continue; break; default: if (!content.Contains(original_text)) continue; break; } if (item.use_special_text == 1 && request.SpecialText != 1) continue; if (item.use_risk_control) { bool is_ignore = AlimamaPlus.ShouldIgnoreRequest(request, out _); if (is_ignore) continue; is_ignore = AlimamaPlus.FlowControlIgnoreRequest(request, out _); if (is_ignore) continue; } // 每小时调用次数统计(无论是否设置限制都要记录) string hourlyKey = $":override_rule_calls:{item.id}:{DateTime.Now:yyyyMMddHH}"; try { long currentHourlyCalls = await RedisKit.IncrByAsync(hourlyKey); // 如果是第一次调用,设置过期时间为2小时 if (currentHourlyCalls < 10) { await RedisKit.ExpireAsync(hourlyKey, 7200); // 2小时 } // 如果设置了每小时限制且超过限制,跳过该规则 if (item.hourly_calls_limit > 0 && currentHourlyCalls > item.hourly_calls_limit) continue; } catch (Exception ex) { // Redis错误时记录日志但不阻塞业务 _ = new LoggerLibrary("OverrideRule", "Redis").Info($"Hourly limit check failed: {ex.Message}", ex.StackTrace).SaveAsync(); } // 每日调用次数统计(无论是否设置限制都要记录) string dailyKey = $":override_rule_calls:{item.id}:{DateTime.Now:yyyyMMdd}"; try { long currentDailyCalls = await RedisKit.IncrByAsync(dailyKey); if (currentDailyCalls < 10) { await RedisKit.ExpireAsync(dailyKey, 259200); // 3天 } // 如果设置了每日限制且超过限制,跳过该规则 if (item.daily_calls_limit > 0 && currentDailyCalls >= item.daily_calls_limit) continue; } catch (Exception ex) { // Redis错误时记录日志但不阻塞业务 _ = new LoggerLibrary("OverrideRule", "Redis").Info($"Daily limit check failed: {ex.Message}", ex.StackTrace).SaveAsync(); } // 关键词统计 - 长期缓存(6个月)用于历史数据分析 await SaveKeywordStatisticsAsync(item.id, item.platform, original_text); string output_text = item.output_text; if (output_text.Contains("{url:query_text}")) output_text = output_text.Replace("{url:query_text}", HttpUtility.UrlEncode(original_text)); if (output_text.Contains("{url2:query_text}")) output_text = output_text.Replace("{url2:query_text}", HttpUtility.UrlEncode(HttpUtility.UrlEncode(original_text))); if (output_text.Contains("{url3:query_text}")) output_text = output_text.Replace("{url3:query_text}", HttpUtility.UrlEncode(HttpUtility.UrlEncode(HttpUtility.UrlEncode(original_text)))); if (output_text.Contains("{query_text}")) output_text = output_text.Replace("{query_text}", original_text); item.output_text = output_text; return item; } return null; } /// /// 保存关键词统计数据到Redis,用于长期数据分析 /// 缓存周期:6个月 /// private static async Task SaveKeywordStatisticsAsync(int ruleId, string platform, string keyword) { try { // 6个月的秒数 const int sixMonthsInSeconds = 180 * 86400; // 关键词总请求次数统计 - 按月份 string monthlyKey = $":keyword_stats:{platform}:{ruleId}:{DateTime.Now:yyyyMM}"; await RedisKit.IncrByAsync(monthlyKey); await RedisKit.ExpireAsync(monthlyKey, sixMonthsInSeconds); // 关键词总请求次数统计 - 按日 string dailyKey = $":keyword_stats:{platform}:{ruleId}:{DateTime.Now:yyyyMMdd}"; await RedisKit.IncrByAsync(dailyKey); await RedisKit.ExpireAsync(dailyKey, sixMonthsInSeconds); // 关键词总请求次数统计 - 按小时 string hourlyKey = $":keyword_stats:{platform}:{ruleId}:{DateTime.Now:yyyyMMddHH}"; await RedisKit.IncrByAsync(hourlyKey); await RedisKit.ExpireAsync(hourlyKey, sixMonthsInSeconds); // 平台维度统计 string platformMonthlyKey = $":keyword_stats:platform:{platform}:{DateTime.Now:yyyyMM}"; await RedisKit.IncrByAsync(platformMonthlyKey); await RedisKit.ExpireAsync(platformMonthlyKey, sixMonthsInSeconds); // 全局统计 string globalMonthlyKey = $":keyword_stats:global:{DateTime.Now:yyyyMM}"; await RedisKit.IncrByAsync(globalMonthlyKey); await RedisKit.ExpireAsync(globalMonthlyKey, sixMonthsInSeconds); // 记录关键词集合(用于后续查询有哪些关键词) string keywordSetKey = $":keyword_stats:set:{platform}:{DateTime.Now:yyyyMM}"; await RedisKit.SAddAsync(keywordSetKey, keyword); await RedisKit.ExpireAsync(keywordSetKey, sixMonthsInSeconds); } catch (Exception ex) { // Redis错误时记录日志但不阻塞业务 _ = new LoggerLibrary("OverrideRule", "KeywordStats").Info($"Keyword statistics save failed: {ex.Message}", ex.StackTrace).SaveAsync(); } } public static void Refresh() { _cached = null; _ = ListAsync(true); } public static int Update(OverrideRuleDTO data, IDbConnection conn) { var result = (int)conn.Update(data, new { data.id }); _ = ListAsync(true); #if DEBUG #else EndPointCore.NotifyReload(); #endif return result; } public static int Create(OverrideRuleDTO data, IDbConnection conn) { var result = (int)conn.Insert(data); _ = ListAsync(true); #if DEBUG #else EndPointCore.NotifyReload(); #endif return result; } } }