diff --git a/src/TelegramPanel.Web/Components/Pages/Tasks.razor b/src/TelegramPanel.Web/Components/Pages/Tasks.razor
index bceb1d0..a69dc8d 100644
--- a/src/TelegramPanel.Web/Components/Pages/Tasks.razor
+++ b/src/TelegramPanel.Web/Components/Pages/Tasks.razor
@@ -6,6 +6,8 @@
@inject IDialogService DialogService
@using TelegramPanel.Data.Entities
@using TelegramPanel.Core.BatchTasks
+@using System.Text.Json
+@using TelegramPanel.Web.Services
@implements IDisposable
任务中心 - Telegram Panel
@@ -62,10 +64,10 @@
@GetTaskTypeName(context.TaskType)
- @GetStatusName(context.Status)
+ @GetStatusName(GetDisplayStatus(context))
-
+
@context.Completed / @context.Total (@context.Failed 失败)
@@ -82,12 +84,12 @@
- @if (context.Status == "pending" || context.Status == "running")
+ @if (GetDisplayStatus(context) == "pending" || GetDisplayStatus(context) == "running")
{
}
- @if (context.Status == "completed" || context.Status == "failed")
+ @if (GetDisplayStatus(context) == "completed" || GetDisplayStatus(context) == "failed")
{
@@ -112,7 +114,7 @@
private IEnumerable filteredTasks =>
tasks.Where(t =>
- (filterStatus == "all" || t.Status == filterStatus) &&
+ (filterStatus == "all" || GetDisplayStatus(t) == filterStatus) &&
(filterCategory == "all" || GetCategory(t.TaskType) == filterCategory));
private IEnumerable AvailableCategories =>
@@ -277,11 +279,20 @@
_ => Color.Default
};
+ private static string GetDisplayStatus(BatchTask task)
+ {
+ if (task.Status == "failed" && task.CompletedAt.HasValue && task.Total > 0 && task.Completed >= task.Total)
+ return "completed";
+
+ return task.Status;
+ }
+
private void ShowTaskDetails(BatchTask task)
{
var createdAt = TimeZone.Format(task.CreatedAt, "yyyy-MM-dd HH:mm");
+ var displayStatus = GetDisplayStatus(task);
var details = $"任务类型: {GetTaskTypeName(task.TaskType)}\n" +
- $"状态: {GetStatusName(task.Status)}\n" +
+ $"状态: {GetStatusName(displayStatus)}\n" +
$"总数: {task.Total}\n" +
$"已完成: {task.Completed}\n" +
$"失败: {task.Failed}\n" +
@@ -293,7 +304,10 @@
if (task.CompletedAt.HasValue)
details += $"完成时间: {TimeZone.Format(task.CompletedAt.Value, "yyyy-MM-dd HH:mm")}\n";
- if (!string.IsNullOrEmpty(task.Config))
+ var failureLines = ExtractBotAdminFailureLines(task);
+ if (failureLines.Count > 0)
+ details += $"\n失败频道:\n{string.Join('\n', failureLines)}";
+ else if (!IsBotAdminTask(task.TaskType) && !string.IsNullOrEmpty(task.Config))
details += $"\n配置信息:\n{task.Config}";
var html = System.Net.WebUtility.HtmlEncode(details)
@@ -302,6 +316,67 @@
DialogService.ShowMessageBox("任务详情", (MarkupString)html);
}
+ private static bool IsBotAdminTask(string taskType) =>
+ taskType is BatchTaskTypes.BotChannelSetAdminsByAccount or BatchTaskTypes.BotSetAdmins;
+
+ private static List ExtractBotAdminFailureLines(BatchTask task)
+ {
+ var lines = new List();
+ if (!IsBotAdminTask(task.TaskType) || string.IsNullOrWhiteSpace(task.Config) || task.Failed <= 0)
+ return lines;
+
+ try
+ {
+ using var doc = JsonDocument.Parse(task.Config);
+ var root = doc.RootElement;
+
+ if (root.TryGetProperty("FailureLines", out var failureLinesElement)
+ && failureLinesElement.ValueKind == JsonValueKind.Array)
+ {
+ foreach (var lineElement in failureLinesElement.EnumerateArray())
+ {
+ var line = (lineElement.GetString() ?? string.Empty).Trim();
+ if (line.Length > 0)
+ lines.Add(line);
+ }
+
+ if (lines.Count > 0)
+ return lines;
+ }
+
+ if (root.TryGetProperty("Failures", out var failuresElement)
+ && failuresElement.ValueKind == JsonValueKind.Array)
+ {
+ var failures = new List();
+ foreach (var item in failuresElement.EnumerateArray())
+ {
+ if (item.ValueKind != JsonValueKind.Object)
+ continue;
+
+ var failure = new BotAdminTaskFailureItem
+ {
+ ChannelTelegramId = item.TryGetProperty("ChannelTelegramId", out var channelId) && channelId.TryGetInt64(out var id) ? id : 0,
+ ChannelTitle = item.TryGetProperty("ChannelTitle", out var channelTitle) ? channelTitle.GetString() ?? string.Empty : string.Empty,
+ Username = item.TryGetProperty("Username", out var username) ? username.GetString() : null,
+ UserId = item.TryGetProperty("UserId", out var userId) && userId.TryGetInt64(out var uid) ? uid : null,
+ Reason = item.TryGetProperty("Reason", out var reason) ? reason.GetString() ?? string.Empty : string.Empty
+ };
+
+ failures.Add(failure);
+ }
+
+ if (failures.Count > 0)
+ lines = BotAdminTaskFailureFormatter.BuildLines(failures);
+ }
+ }
+ catch
+ {
+ // 配置异常时回退到默认展示,不中断详情弹窗。
+ }
+
+ return lines;
+ }
+
private async Task CancelTask(int id)
{
bool? result = await DialogService.ShowMessageBox(
diff --git a/src/TelegramPanel.Web/Services/BatchTaskBackgroundService.cs b/src/TelegramPanel.Web/Services/BatchTaskBackgroundService.cs
index 0ab3d14..8217592 100644
--- a/src/TelegramPanel.Web/Services/BatchTaskBackgroundService.cs
+++ b/src/TelegramPanel.Web/Services/BatchTaskBackgroundService.cs
@@ -160,9 +160,9 @@ public sealed class BatchTaskBackgroundService : BackgroundService
if (latest != null && latest.Status != "running")
return;
- await taskManagement.CompleteTaskAsync(pending.Id, success: failed == 0);
- _logger.LogInformation("Batch task completed: {TaskId} {TaskType} (ok={Ok}, completed={Completed}, failed={Failed})",
- pending.Id, pending.TaskType, failed == 0, completed, failed);
+ await taskManagement.CompleteTaskAsync(pending.Id, success: true);
+ _logger.LogInformation("Batch task completed: {TaskId} {TaskType} (completed={Completed}, failed={Failed})",
+ pending.Id, pending.TaskType, completed, failed);
}
catch (Exception ex)
{