using CSRedis; using dodohold.core; namespace molilian.core { /// /// 隐藏号判断:隐藏号命中的转链只返回 deeplink,不产生任何日志、统计、上报数据 /// public static class HiddenAccountCore { // 同一隐藏号当天同 ip / oaid 只转成功一次 private const int OnceDoneExpireSeconds = 86400; // 转链进行中的占位,防止同一用户并发请求重复转成功 private const int OncePendingExpireSeconds = 120; public static Task IsHiddenAsync(TkChannelEnum channel, int accountId) { return IsHiddenAsync(channel.ToString(), accountId); } public static async Task IsHiddenAsync(string channel, int accountId) { if (accountId <= 0) return false; switch ((channel ?? string.Empty).ToLowerInvariant()) { case "tb": var tbAccount = await TkPoolCore.ALLGetOneAsync(accountId); return tbAccount != null && tbAccount.is_hide; case "pdd": var pddAccounts = await PddPoolCore.AllListAsync(); var pddAccount = pddAccounts?.FirstOrDefault(e => e.id == accountId); return pddAccount != null && pddAccount.is_hide; default: return false; } } /// /// 占用隐藏号当天的 ip / oaid 名额。当天已转成功或正在转链时返回 false。 /// public static bool TryReserveOnce(TkChannelEnum channel, int accountId, string ip, string oaid) { var keys = BuildOnceKeys(channel, accountId, ip, oaid); var reserved = new List(); foreach (var key in keys) { if (!RedisHelper.Set(key, "pending", OncePendingExpireSeconds, RedisExistence.Nx)) { if (reserved.Count > 0) RedisHelper.Del(reserved.ToArray()); return false; } reserved.Add(key); } return true; } /// /// 转链结束:成功则标记当天已转(同步到所有节点),失败则释放占位 /// public static async Task CompleteOnceAsync(TkChannelEnum channel, int accountId, string ip, string oaid, bool success) { var keys = BuildOnceKeys(channel, accountId, ip, oaid); if (keys.Count == 0) return; try { if (!success) { RedisHelper.Del(keys.ToArray()); return; } foreach (var key in keys) RedisHelper.Set(key, "done", OnceDoneExpireSeconds); await EndPointCore.ProcessEndPointNodesAsync(node => { if (string.IsNullOrEmpty(node.redis_server)) return Task.CompletedTask; if (!node.is_public_api) return Task.CompletedTask; try { var redis = RedisClientManager.GetRedisClient(node.redis_server); foreach (var key in keys) redis.Set(key, "done", OnceDoneExpireSeconds); } catch (Exception ex) { _ = new LoggerLibrary("HiddenAccountCore", "CompleteOnce") .Info(node.name, ex.Message) .SaveAsync(); } return Task.CompletedTask; }); } catch (Exception ex) { _ = new LoggerLibrary("HiddenAccountCore", "CompleteOnce") .Info(ex.Message, ex.StackTrace) .SaveAsync(); } } private static List BuildOnceKeys(TkChannelEnum channel, int accountId, string ip, string oaid) { string prefix = $":hide_once:{channel}_{accountId}:{DateTime.Now:yyyyMMdd}"; var keys = new List(); if (!string.IsNullOrEmpty(ip)) keys.Add($"{prefix}:ip:{ip}"); if (!string.IsNullOrEmpty(oaid)) keys.Add($"{prefix}:oaid:{oaid}"); return keys; } } }