实现账号风控系统:24小时登录时长检查
核心功能: - 新增 AccountRiskService 风控检查服务 - 添加 Account.LastLoginAt 字段记录登录时间 - 实现 24 小时风控阈值检查逻辑 - 新增 RiskWarningDialog 风控警告对话框 风控集成点: - 创建频道(ChannelCreate) - 深度探测账号状态(Accounts - 深度探测) - 批量修改昵称(BatchChangeNicknameDialog) - 批量修改个性签名(BatchChangeBioDialog) - 编辑用户资料(EditUserProfileDialog) 风控逻辑: - 刚导入账号(LastLoginAt 为 null)→ 高风险 - 登录时长 < 24 小时 → 高风险,提示剩余等待时间 - 登录时长 ≥ 24 小时 → 安全,允许操作 数据库变更: - 添加 Accounts.LastLoginAt (nullable DateTime) 字段 - 创建数据库迁移 20251220000000_AddLastLoginAt 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
1e09449431
commit
ccb3f3e8a2
13 files changed
+550
-2
No files matched your search
@@ -35,6 +35,9 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<BatchTaskManagementService>();
|
||||
services.AddScoped<BotManagementService>();
|
||||
|
||||
// 注册风控服务
|
||||
services.AddScoped<AccountRiskService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using TelegramPanel.Data.Entities;
|
||||
|
||||
namespace TelegramPanel.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 账号风控检查服务
|
||||
/// </summary>
|
||||
public class AccountRiskService
|
||||
{
|
||||
/// <summary>
|
||||
/// 风控阈值:登录后需满 24 小时才能进行敏感操作
|
||||
/// </summary>
|
||||
private const double RISK_THRESHOLD_HOURS = 24.0;
|
||||
|
||||
/// <summary>
|
||||
/// 检查单个账号的登录时长是否满足风控要求
|
||||
/// </summary>
|
||||
public RiskCheckResult CheckLoginDuration(Account account)
|
||||
{
|
||||
if (account.LastLoginAt == null)
|
||||
{
|
||||
// 无登录记录,视为高风险(刚导入的账号)
|
||||
return new RiskCheckResult
|
||||
{
|
||||
IsRisky = true,
|
||||
Message = "账号未记录登录时间",
|
||||
DetailedMessage = "该账号尚未记录登录时间(可能是刚导入的新账号),建议等待 24 小时后再进行敏感操作"
|
||||
};
|
||||
}
|
||||
|
||||
var loginHours = (DateTime.UtcNow - account.LastLoginAt.Value).TotalHours;
|
||||
|
||||
if (loginHours < RISK_THRESHOLD_HOURS)
|
||||
{
|
||||
var remainingHours = RISK_THRESHOLD_HOURS - loginHours;
|
||||
return new RiskCheckResult
|
||||
{
|
||||
IsRisky = true,
|
||||
Message = $"账号登录时长不足 24 小时(当前:{loginHours:F1} 小时)",
|
||||
DetailedMessage = $"建议等待 {remainingHours:F1} 小时后再进行敏感操作,以降低被 Telegram 风控的风险"
|
||||
};
|
||||
}
|
||||
|
||||
return new RiskCheckResult
|
||||
{
|
||||
IsRisky = false,
|
||||
Message = "账号登录时长已满足要求",
|
||||
DetailedMessage = $"当前登录时长:{loginHours:F1} 小时"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量检查账号的登录时长
|
||||
/// </summary>
|
||||
public BatchRiskCheckResult CheckBatchAccounts(IEnumerable<Account> accounts)
|
||||
{
|
||||
var accountList = accounts.ToList();
|
||||
var riskyAccounts = new List<Account>();
|
||||
var safeAccounts = new List<Account>();
|
||||
|
||||
foreach (var account in accountList)
|
||||
{
|
||||
var check = CheckLoginDuration(account);
|
||||
if (check.IsRisky)
|
||||
riskyAccounts.Add(account);
|
||||
else
|
||||
safeAccounts.Add(account);
|
||||
}
|
||||
|
||||
return new BatchRiskCheckResult
|
||||
{
|
||||
TotalCount = accountList.Count,
|
||||
RiskyCount = riskyAccounts.Count,
|
||||
SafeCount = safeAccounts.Count,
|
||||
RiskyAccounts = riskyAccounts,
|
||||
SafeAccounts = safeAccounts,
|
||||
HasRiskyAccounts = riskyAccounts.Count > 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单账号风控检查结果
|
||||
/// </summary>
|
||||
public class RiskCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否存在风险
|
||||
/// </summary>
|
||||
public bool IsRisky { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 风险消息
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 详细说明
|
||||
/// </summary>
|
||||
public string DetailedMessage { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量账号风控检查结果
|
||||
/// </summary>
|
||||
public class BatchRiskCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 总账号数
|
||||
/// </summary>
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 风险账号数
|
||||
/// </summary>
|
||||
public int RiskyCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 安全账号数
|
||||
/// </summary>
|
||||
public int SafeCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 风险账号列表
|
||||
/// </summary>
|
||||
public List<Account> RiskyAccounts { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 安全账号列表
|
||||
/// </summary>
|
||||
public List<Account> SafeAccounts { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 是否存在风险账号
|
||||
/// </summary>
|
||||
public bool HasRiskyAccounts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取风险摘要信息
|
||||
/// </summary>
|
||||
public string GetRiskySummary()
|
||||
{
|
||||
if (RiskyCount == 0)
|
||||
return "所有账号均已满足登录时长要求";
|
||||
|
||||
var riskyPhones = string.Join("、", RiskyAccounts.Select(a =>
|
||||
{
|
||||
var hours = a.GetLoginHours();
|
||||
return hours.HasValue ? $"{a.Phone}({hours.Value:F1}h)" : a.Phone;
|
||||
}));
|
||||
|
||||
return $"以下账号登录时长不足 24 小时:{riskyPhones}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TelegramPanel.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 风控警告对话框的用户操作
|
||||
/// </summary>
|
||||
public enum RiskWarningAction
|
||||
{
|
||||
/// <summary>
|
||||
/// 继续操作(包含风险账号)
|
||||
/// </summary>
|
||||
Continue,
|
||||
|
||||
/// <summary>
|
||||
/// 排除风险账号后继续
|
||||
/// </summary>
|
||||
ExcludeRisky
|
||||
}
|
||||
@@ -21,6 +21,11 @@ public class Account
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime LastSyncAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 最后一次登录 Telegram 的时间(UTC),用于风控检查
|
||||
/// </summary>
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Telegram 状态检测结果摘要(用于页面刷新后仍可展示上次检测结论)
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace TelegramPanel.Data.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Account 实体扩展方法
|
||||
/// </summary>
|
||||
public static class AccountExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取登录小时数的格式化字符串
|
||||
/// </summary>
|
||||
public static string GetLoginHoursFormatted(this Account account)
|
||||
{
|
||||
if (account.LastLoginAt == null)
|
||||
return "未知";
|
||||
|
||||
var hours = (DateTime.UtcNow - account.LastLoginAt.Value).TotalHours;
|
||||
return hours.ToString("F1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取登录小时数
|
||||
/// </summary>
|
||||
public static double? GetLoginHours(this Account account)
|
||||
{
|
||||
if (account.LastLoginAt == null)
|
||||
return null;
|
||||
|
||||
return (DateTime.UtcNow - account.LastLoginAt.Value).TotalHours;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TelegramPanel.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20251220000000_AddLastLoginAt")]
|
||||
/// <inheritdoc />
|
||||
public partial class AddLastLoginAt : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "LastLoginAt",
|
||||
table: "Accounts",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastLoginAt",
|
||||
table: "Accounts");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,16 @@ namespace TelegramPanel.Data.Migrations
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastSyncAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
@@ -53,6 +60,18 @@ namespace TelegramPanel.Data.Migrations
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("TelegramStatusCheckedAtUtc")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TelegramStatusDetails")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("TelegramStatusOk")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TelegramStatusSummary")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@namespace TelegramPanel.Web.Components.Dialogs
|
||||
@inject AccountManagementService AccountManagement
|
||||
@inject TelegramPanel.Core.Services.Telegram.AccountTelegramToolsService AccountTelegramTools
|
||||
@inject TelegramPanel.Core.Services.AccountRiskService RiskService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@using TelegramPanel.Data.Entities
|
||||
@@ -101,6 +102,38 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量风控检查:修改 Bio 是敏感操作
|
||||
var batchCheck = RiskService.CheckBatchAccounts(accounts);
|
||||
if (batchCheck.HasRiskyAccounts)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["Message"] = $"检测到 {batchCheck.RiskyCount} 个风险账号",
|
||||
["DetailedMessage"] = batchCheck.GetRiskySummary(),
|
||||
["ShowRecommendations"] = true,
|
||||
["ShowExcludeOption"] = true,
|
||||
["RiskyAccounts"] = batchCheck.RiskyAccounts.ToList()
|
||||
};
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large };
|
||||
var dialog = DialogService.Show<TelegramPanel.Web.Components.Dialogs.RiskWarningDialog>("批量操作风控警告", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (result.Canceled)
|
||||
return; // 用户取消操作
|
||||
|
||||
if (result.Data is TelegramPanel.Core.Services.RiskWarningAction action && action == TelegramPanel.Core.Services.RiskWarningAction.ExcludeRisky)
|
||||
{
|
||||
// 排除风险账号后继续
|
||||
accounts = batchCheck.SafeAccounts.ToList();
|
||||
if (accounts.Count == 0)
|
||||
{
|
||||
Snackbar.Add("排除风险账号后无剩余账号", Severity.Info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// else: 用户选择继续所有账号
|
||||
}
|
||||
|
||||
bool? confirm = await DialogService.ShowMessageBox(
|
||||
"确认修改",
|
||||
$"将对 {accounts.Count} 个账号批量修改 Bio(简介)。是否继续?",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@namespace TelegramPanel.Web.Components.Dialogs
|
||||
@inject AccountManagementService AccountManagement
|
||||
@inject TelegramPanel.Core.Services.Telegram.AccountTelegramToolsService AccountTelegramTools
|
||||
@inject TelegramPanel.Core.Services.AccountRiskService RiskService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@using TelegramPanel.Data.Entities
|
||||
@@ -112,6 +113,38 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量风控检查:修改昵称是敏感操作
|
||||
var batchCheck = RiskService.CheckBatchAccounts(accounts);
|
||||
if (batchCheck.HasRiskyAccounts)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["Message"] = $"检测到 {batchCheck.RiskyCount} 个风险账号",
|
||||
["DetailedMessage"] = batchCheck.GetRiskySummary(),
|
||||
["ShowRecommendations"] = true,
|
||||
["ShowExcludeOption"] = true,
|
||||
["RiskyAccounts"] = batchCheck.RiskyAccounts.ToList()
|
||||
};
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large };
|
||||
var dialog = DialogService.Show<TelegramPanel.Web.Components.Dialogs.RiskWarningDialog>("批量操作风控警告", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (result.Canceled)
|
||||
return; // 用户取消操作
|
||||
|
||||
if (result.Data is TelegramPanel.Core.Services.RiskWarningAction action && action == TelegramPanel.Core.Services.RiskWarningAction.ExcludeRisky)
|
||||
{
|
||||
// 排除风险账号后继续
|
||||
accounts = batchCheck.SafeAccounts.ToList();
|
||||
if (accounts.Count == 0)
|
||||
{
|
||||
Snackbar.Add("排除风险账号后无剩余账号", Severity.Info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// else: 用户选择继续所有账号
|
||||
}
|
||||
|
||||
bool? confirm = await DialogService.ShowMessageBox(
|
||||
"确认修改",
|
||||
$"将对 {accounts.Count} 个账号批量修改昵称。是否继续?",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@namespace TelegramPanel.Web.Components.Dialogs
|
||||
@inject AccountManagementService AccountManagement
|
||||
@inject TelegramPanel.Core.Services.Telegram.AccountTelegramToolsService AccountTelegramTools
|
||||
@inject TelegramPanel.Core.Services.AccountRiskService RiskService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@using TelegramPanel.Data.Entities
|
||||
@@ -156,6 +157,26 @@
|
||||
if (confirm != true)
|
||||
return;
|
||||
|
||||
// 风控检查:修改资料是敏感操作
|
||||
var riskCheck = RiskService.CheckLoginDuration(account);
|
||||
if (riskCheck.IsRisky)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["Message"] = riskCheck.Message,
|
||||
["DetailedMessage"] = riskCheck.DetailedMessage,
|
||||
["ShowRecommendations"] = true
|
||||
};
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Medium };
|
||||
var dialog = DialogService.Show<TelegramPanel.Web.Components.Dialogs.RiskWarningDialog>("风控警告", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
if (result.Canceled)
|
||||
{
|
||||
saving = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
saving = true;
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
@namespace TelegramPanel.Web.Components.Dialogs
|
||||
@using TelegramPanel.Core.Services
|
||||
@using TelegramPanel.Data.Entities
|
||||
|
||||
@* 账号风控警告对话框 *@
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudAlert Severity="Severity.Warning" Icon="@Icons.Material.Filled.Warning" Class="mb-4">
|
||||
<MudText Typo="Typo.body1" Class="mb-2">
|
||||
<strong>⚠️ 风控警告</strong>
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2">
|
||||
@Message
|
||||
</MudText>
|
||||
@if (!string.IsNullOrWhiteSpace(DetailedMessage))
|
||||
{
|
||||
<MudText Typo="Typo.body2" Class="mt-2">
|
||||
@DetailedMessage
|
||||
</MudText>
|
||||
}
|
||||
</MudAlert>
|
||||
|
||||
@if (ShowRecommendations)
|
||||
{
|
||||
<MudText Typo="Typo.subtitle2" Class="mb-2">
|
||||
<strong>建议操作:</strong>
|
||||
</MudText>
|
||||
<MudList T="string" Dense="true" Class="pl-2">
|
||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Schedule">
|
||||
等待满 24 小时后再进行敏感操作
|
||||
</MudListItem>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Filled.SlowMotionVideo">
|
||||
降低操作频率,避免被 Telegram 风控系统标记
|
||||
</MudListItem>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Filled.SafetyCheck">
|
||||
优先使用低风险操作替代
|
||||
</MudListItem>
|
||||
</MudList>
|
||||
}
|
||||
|
||||
@if (RiskyAccounts != null && RiskyAccounts.Any())
|
||||
{
|
||||
<MudText Typo="Typo.subtitle2" Class="mt-3 mb-2">
|
||||
<strong>风险账号列表:</strong>
|
||||
</MudText>
|
||||
<MudPaper Class="pa-2" Elevation="0" Style="max-height: 200px; overflow-y: auto;">
|
||||
@foreach (var account in RiskyAccounts)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Warning" Class="ma-1">
|
||||
@account.Phone
|
||||
@if (account.LastLoginAt != null)
|
||||
{
|
||||
<text> (登录 @account.GetLoginHoursFormatted() 小时)</text>
|
||||
}
|
||||
</MudChip>
|
||||
}
|
||||
</MudPaper>
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel" Color="Color.Default">
|
||||
取消操作
|
||||
</MudButton>
|
||||
@if (ShowExcludeOption && RiskyAccounts != null && RiskyAccounts.Any())
|
||||
{
|
||||
<MudButton OnClick="ExcludeRisky" Color="Color.Info">
|
||||
排除风险账号后继续
|
||||
</MudButton>
|
||||
}
|
||||
<MudButton OnClick="Continue" Color="Color.Warning" Variant="Variant.Filled">
|
||||
我了解风险,继续操作
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter]
|
||||
MudDialogInstance? MudDialog { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Message { get; set; } = "存在风控风险";
|
||||
|
||||
[Parameter]
|
||||
public string? DetailedMessage { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ShowRecommendations { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool ShowExcludeOption { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public List<Account>? RiskyAccounts { get; set; }
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog?.Close(DialogResult.Cancel());
|
||||
}
|
||||
|
||||
private void Continue()
|
||||
{
|
||||
MudDialog?.Close(DialogResult.Ok(RiskWarningAction.Continue));
|
||||
}
|
||||
|
||||
private void ExcludeRisky()
|
||||
{
|
||||
MudDialog?.Close(DialogResult.Ok(RiskWarningAction.ExcludeRisky));
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,12 @@
|
||||
@inject AccountManagementService AccountManagement
|
||||
@inject AccountCategoryManagementService CategoryManagement
|
||||
@inject AccountTelegramToolsService AccountTelegramTools
|
||||
@inject TelegramPanel.Core.Services.AccountRiskService RiskService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@inject NavigationManager Navigation
|
||||
@using TelegramPanel.Data.Entities
|
||||
@using TelegramPanel.Web.Components.Dialogs
|
||||
@implements IDisposable
|
||||
|
||||
<PageTitle>账号列表 - Telegram Panel</PageTitle>
|
||||
@@ -370,11 +372,35 @@
|
||||
loading = true;
|
||||
bool? probeResult = await DialogService.ShowMessageBox(
|
||||
"刷新 Telegram 状态",
|
||||
"是否进行深度探测(将创建并删除一个测试频道,用于判断“创建频道接口是否被冻结”)?",
|
||||
"是否进行深度探测(将创建并删除一个测试频道,用于判断【创建频道接口是否被冻结】)?",
|
||||
yesText: "深度探测",
|
||||
cancelText: "普通刷新");
|
||||
|
||||
var probe = probeResult == true;
|
||||
|
||||
// 风控检查:深度探测是敏感操作
|
||||
if (probe)
|
||||
{
|
||||
var riskCheck = RiskService.CheckLoginDuration(account);
|
||||
if (riskCheck.IsRisky)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["Message"] = riskCheck.Message,
|
||||
["DetailedMessage"] = riskCheck.DetailedMessage,
|
||||
["ShowRecommendations"] = true
|
||||
};
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Medium };
|
||||
var dialog = DialogService.Show<RiskWarningDialog>("风控警告", parameters, options);
|
||||
var dialogResult = await dialog.Result;
|
||||
if (dialogResult.Canceled)
|
||||
{
|
||||
loading = false;
|
||||
return; // 用户取消操作
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = await AccountTelegramTools.RefreshAccountStatusAsync(account.Id, probeCreateChannel: probe, cancellationToken: _pageCts.Token);
|
||||
telegramStatus[account.Id] = result;
|
||||
|
||||
@@ -406,12 +432,53 @@
|
||||
{
|
||||
bool? probeResult = await DialogService.ShowMessageBox(
|
||||
"批量刷新 Telegram 状态",
|
||||
"是否进行深度探测(将对每个账号创建并删除一个测试频道,用于判断“创建频道接口是否被冻结”)?",
|
||||
"是否进行深度探测(将对每个账号创建并删除一个测试频道,用于判断【创建频道接口是否被冻结】)?",
|
||||
yesText: "深度探测",
|
||||
cancelText: "普通刷新");
|
||||
|
||||
var probe = probeResult == true;
|
||||
var targets = filteredAccounts.ToList();
|
||||
|
||||
// 批量风控检查:深度探测是敏感操作
|
||||
if (probe && targets.Count > 0)
|
||||
{
|
||||
var batchCheck = RiskService.CheckBatchAccounts(targets);
|
||||
if (batchCheck.HasRiskyAccounts)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["Message"] = $"检测到 {batchCheck.RiskyCount} 个风险账号",
|
||||
["DetailedMessage"] = batchCheck.GetRiskySummary(),
|
||||
["ShowRecommendations"] = true,
|
||||
["ShowExcludeOption"] = true,
|
||||
["RiskyAccounts"] = batchCheck.RiskyAccounts.ToList()
|
||||
};
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large };
|
||||
var dialog = DialogService.Show<RiskWarningDialog>("批量操作风控警告", parameters, options);
|
||||
var dialogResult = await dialog.Result;
|
||||
|
||||
if (dialogResult.Canceled)
|
||||
{
|
||||
loading = false;
|
||||
return; // 用户取消操作
|
||||
}
|
||||
|
||||
if (dialogResult.Data is TelegramPanel.Core.Services.RiskWarningAction action
|
||||
&& action == TelegramPanel.Core.Services.RiskWarningAction.ExcludeRisky)
|
||||
{
|
||||
// 排除风险账号后继续
|
||||
targets = batchCheck.SafeAccounts.ToList();
|
||||
if (targets.Count == 0)
|
||||
{
|
||||
Snackbar.Add("排除风险账号后无剩余账号", Severity.Info);
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// else: 用户选择继续所有账号
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var a in targets)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
@page "/channels/create"
|
||||
@inject IChannelService ChannelService
|
||||
@inject AccountManagementService AccountManagement
|
||||
@inject TelegramPanel.Core.Services.AccountRiskService RiskService
|
||||
@inject ChannelManagementService ChannelManagement
|
||||
@inject ChannelGroupManagementService ChannelGroupManagement
|
||||
@inject ISnackbar Snackbar
|
||||
@inject NavigationManager Navigation
|
||||
@inject IDialogService DialogService
|
||||
@using TelegramPanel.Data.Entities
|
||||
@using TelegramPanel.Web.Components.Dialogs
|
||||
|
||||
<PageTitle>创建频道 - Telegram Panel</PageTitle>
|
||||
|
||||
@@ -139,6 +142,27 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 风控检查:创建频道是敏感操作
|
||||
var account = await AccountManagement.GetAccountAsync(selectedAccountId);
|
||||
if (account != null)
|
||||
{
|
||||
var riskCheck = RiskService.CheckLoginDuration(account);
|
||||
if (riskCheck.IsRisky)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["Message"] = riskCheck.Message,
|
||||
["DetailedMessage"] = riskCheck.DetailedMessage,
|
||||
["ShowRecommendations"] = true
|
||||
};
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Medium };
|
||||
var dialog = DialogService.Show<RiskWarningDialog>("风控警告", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
if (result.Canceled)
|
||||
return; // 用户取消操作
|
||||
}
|
||||
}
|
||||
|
||||
creating = true;
|
||||
|
||||
try
|
||||
|
||||
Reference in new issue
Block a user