diff --git a/src/TelegramPanel.Core/Services/AccountRiskService.cs b/src/TelegramPanel.Core/Services/AccountRiskService.cs index 95774ee..01e8074 100644 --- a/src/TelegramPanel.Core/Services/AccountRiskService.cs +++ b/src/TelegramPanel.Core/Services/AccountRiskService.cs @@ -17,9 +17,12 @@ public class AccountRiskService /// public RiskCheckResult CheckLoginDuration(Account account) { - if (account.LastLoginAt == null) + var referenceAt = account.GetRiskReferenceAtUtc(); + var loginHours = account.GetRiskReferenceHours(); + + if (referenceAt == null || loginHours == null) { - // 无登录记录,视为高风险(刚导入的账号) + // 兜底:完全缺少可参考的时间,视为高风险 return new RiskCheckResult { IsRisky = true, @@ -28,16 +31,30 @@ public class AccountRiskService }; } - var loginHours = (DateTime.UtcNow - account.LastLoginAt.Value).TotalHours; - - if (loginHours < RISK_THRESHOLD_HOURS) + // 兼容历史数据:LastLoginAt 为空时,使用导入时间(CreatedAt)作为估算,避免永久误报 + var isEstimated = account.IsRiskReferenceEstimated(); + if (isEstimated && loginHours >= RISK_THRESHOLD_HOURS) { - var remainingHours = RISK_THRESHOLD_HOURS - loginHours; + return new RiskCheckResult + { + IsRisky = false, + Message = "账号登录时长已满足要求(按导入时间估算)", + DetailedMessage = $"当前已导入时长:{loginHours.Value:F1} 小时" + }; + } + + if (loginHours.Value < RISK_THRESHOLD_HOURS) + { + var remainingHours = RISK_THRESHOLD_HOURS - loginHours.Value; return new RiskCheckResult { IsRisky = true, - Message = $"账号登录时长不足 24 小时(当前:{loginHours:F1} 小时)", - DetailedMessage = $"建议等待 {remainingHours:F1} 小时后再进行敏感操作,以降低被 Telegram 风控的风险" + Message = isEstimated + ? $"账号未记录登录时间(按导入时间估算:{loginHours.Value:F1} 小时)" + : $"账号登录时长不足 24 小时(当前:{loginHours.Value:F1} 小时)", + DetailedMessage = isEstimated + ? $"该账号尚未记录登录时间(可能是较早导入的账号),已按导入时间估算;建议等待 {remainingHours:F1} 小时后再进行敏感操作,以降低被 Telegram 风控的风险" + : $"建议等待 {remainingHours:F1} 小时后再进行敏感操作,以降低被 Telegram 风控的风险" }; } @@ -45,7 +62,7 @@ public class AccountRiskService { IsRisky = false, Message = "账号登录时长已满足要求", - DetailedMessage = $"当前登录时长:{loginHours:F1} 小时" + DetailedMessage = $"当前登录时长:{loginHours.Value:F1} 小时" }; } @@ -145,8 +162,12 @@ public class BatchRiskCheckResult var riskyPhones = string.Join("、", RiskyAccounts.Select(a => { - var hours = a.GetLoginHours(); - return hours.HasValue ? $"{a.Phone}({hours.Value:F1}h)" : a.Phone; + var hours = a.GetRiskReferenceHours(); + if (!hours.HasValue) + return a.Phone; + + var tag = a.IsRiskReferenceEstimated() ? "导入" : "登录"; + return $"{a.Phone}({tag}{hours.Value:F1}h)"; })); return $"以下账号登录时长不足 24 小时:{riskyPhones}"; diff --git a/src/TelegramPanel.Data/Entities/AccountExtensions.cs b/src/TelegramPanel.Data/Entities/AccountExtensions.cs index 8785fca..f2efc28 100644 --- a/src/TelegramPanel.Data/Entities/AccountExtensions.cs +++ b/src/TelegramPanel.Data/Entities/AccountExtensions.cs @@ -5,6 +5,42 @@ namespace TelegramPanel.Data.Entities; /// public static class AccountExtensions { + /// + /// 获取风控检查用的参考时间(优先 LastLoginAt,其次 CreatedAt/LastSyncAt 作为估算兜底) + /// + public static DateTime? GetRiskReferenceAtUtc(this Account account) + { + if (account.LastLoginAt != null) + return account.LastLoginAt.Value; + + // 兼容历史数据:早期导入的账号可能没有写入 LastLoginAt + if (account.CreatedAt != default) + return account.CreatedAt; + + if (account.LastSyncAt != default) + return account.LastSyncAt; + + return null; + } + + /// + /// 风控参考时间是否为估算值(非真实登录时间) + /// + public static bool IsRiskReferenceEstimated(this Account account) + => account.LastLoginAt == null && account.GetRiskReferenceAtUtc() != null; + + /// + /// 获取风控检查用的参考小时数 + /// + public static double? GetRiskReferenceHours(this Account account) + { + var at = account.GetRiskReferenceAtUtc(); + if (at == null) + return null; + + return (DateTime.UtcNow - at.Value).TotalHours; + } + /// /// 获取登录小时数的格式化字符串 /// diff --git a/src/TelegramPanel.Web/Components/Pages/AccountLogin.razor b/src/TelegramPanel.Web/Components/Pages/AccountLogin.razor index 0a7d086..86cdd7a 100644 --- a/src/TelegramPanel.Web/Components/Pages/AccountLogin.razor +++ b/src/TelegramPanel.Web/Components/Pages/AccountLogin.razor @@ -354,6 +354,7 @@ existingAccount.ApiId = telegramApiId; existingAccount.ApiHash = telegramApiHash; existingAccount.LastSyncAt = DateTime.UtcNow; + existingAccount.LastLoginAt = DateTime.UtcNow; await AccountManagement.UpdateAccountAsync(existingAccount); Snackbar.Add("账号信息已更新", Severity.Success); @@ -372,7 +373,8 @@ ApiHash = telegramApiHash, IsActive = true, CreatedAt = DateTime.UtcNow, - LastSyncAt = DateTime.UtcNow + LastSyncAt = DateTime.UtcNow, + LastLoginAt = DateTime.UtcNow }; await AccountManagement.CreateAccountAsync(account);