fix(tasks): improve user active task controls

This commit is contained in:
meoacgx committed 2026-03-08 21:20:23 +08:00
1 parent ddbec4c34c
commit 0fead785a9
4 files changed
+202 -27

No files matched your search

@@ -1,4 +1,3 @@
@namespace TelegramPanel.Web.Components.Dialogs
@inject AccountCategoryManagementService CategoryManagement
@using System.Text.Json
@using TelegramPanel.Data.Entities
@@ -18,8 +17,14 @@
}
else
{
<MudSelect T="int" @bind-Value="selectedCategoryId" @bind-Value:after="OnInputsChanged"
Label="执行账号分类" Variant="Variant.Outlined" Dense="true">
<MudSelect T="int"
Label="执行账号分类(可多选)"
Variant="Variant.Outlined"
Dense="true"
MultiSelection="true"
SelectedValues="selectedCategoryIds"
SelectedValuesChanged="OnSelectedCategoriesChanged"
HelperText="可勾选多个分类,任务会从所有选中分类中抽取可用账号。">
@foreach (var c in categories)
{
<MudSelectItem Value="@c.Id">@c.Name</MudSelectItem>
@@ -91,7 +96,7 @@
private bool loading = true;
private bool initializedFromConfig;
private int selectedCategoryId;
private IEnumerable<int> selectedCategoryIds = Array.Empty<int>();
private string targetsText = "";
private string dictionaryText = "";
private int delayMinMs = 15;
@@ -113,8 +118,11 @@
.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
.ToList();
if (selectedCategoryId <= 0)
selectedCategoryId = categories.FirstOrDefault()?.Id ?? 0;
if (!selectedCategoryIds.Any())
{
var firstCategoryId = categories.FirstOrDefault()?.Id ?? 0;
selectedCategoryIds = firstCategoryId > 0 ? new[] { firstCategoryId } : Array.Empty<int>();
}
}
finally
{
@@ -140,8 +148,9 @@
if (cfg == null)
return;
if (cfg.CategoryId > 0)
selectedCategoryId = cfg.CategoryId;
var categoryIds = NormalizeCategoryIds(cfg.CategoryIds, cfg.CategoryId);
if (categoryIds.Count > 0)
selectedCategoryIds = categoryIds;
targetsText = string.Join(Environment.NewLine, cfg.Targets ?? new List<string>());
dictionaryText = string.Join(Environment.NewLine, cfg.Dictionary ?? new List<string>());
@@ -158,6 +167,12 @@
}
}
private async Task OnSelectedCategoriesChanged(IEnumerable<int> values)
{
selectedCategoryIds = NormalizeCategoryIds(values, 0);
await OnInputsChanged();
}
private async Task OnInputsChanged()
{
await PushDraftAsync();
@@ -167,6 +182,8 @@
{
var targets = ParseTargetLines(targetsText);
var dictionary = ParseMessageLines(dictionaryText);
var selectedIds = NormalizeCategoryIds(selectedCategoryIds, 0);
var selectedCategories = categories.Where(x => selectedIds.Contains(x.Id)).ToList();
if (loading)
{
@@ -180,9 +197,9 @@
return;
}
if (selectedCategoryId <= 0 || categories.All(x => x.Id != selectedCategoryId))
if (selectedCategories.Count == 0)
{
await DraftChanged.InvokeAsync(new ModuleTaskDraft(0, null, false, "请选择执行账号分类"));
await DraftChanged.InvokeAsync(new ModuleTaskDraft(0, null, false, "请至少选择一个执行账号分类"));
return;
}
@@ -222,11 +239,13 @@
return;
}
var category = categories.First(x => x.Id == selectedCategoryId);
var categoryNames = selectedCategories.Select(x => x.Name).ToList();
var cfg = new UserChatActiveTaskConfig
{
CategoryId = selectedCategoryId,
CategoryName = category.Name,
CategoryId = selectedCategories[0].Id,
CategoryName = selectedCategories[0].Name,
CategoryIds = selectedCategories.Select(x => x.Id).ToList(),
CategoryNames = categoryNames,
Targets = targets,
Dictionary = dictionary,
DelayMinMs = delayMinMs * 1000,
@@ -242,6 +261,19 @@
await DraftChanged.InvokeAsync(new ModuleTaskDraft(total, json, true, null));
}
private static List<int> NormalizeCategoryIds(IEnumerable<int>? values, int fallback)
{
var ids = (values ?? Array.Empty<int>())
.Where(x => x > 0)
.Distinct()
.ToList();
if (ids.Count == 0 && fallback > 0)
ids.Add(fallback);
return ids;
}
private static int ConvertMillisecondsToSeconds(int value)
{
if (value <= 0)
@@ -288,5 +320,3 @@
.ToList();
}
}
@@ -110,6 +110,8 @@
<MudIconButton Icon="@Icons.Material.Filled.Cancel" Size="Size.Small" Color="Color.Warning"
OnClick="@(() => CancelTask(context.Id))" />
}
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error"
OnClick="@(() => DeleteTask(context))" />
</MudTd>
</RowTemplate>
<NoRecordsContent>
@@ -175,7 +177,7 @@
OnClick="@(() => RerunTask(context))" />
}
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error"
OnClick="@(() => DeleteTask(context.Id))" />
OnClick="@(() => DeleteTask(context))" />
</MudTd>
</RowTemplate>
<NoRecordsContent>
@@ -538,8 +540,7 @@
using var doc = JsonDocument.Parse(task.Config);
var root = doc.RootElement;
var categoryName = root.TryGetProperty("category_name", out var categoryNameEl) ? (categoryNameEl.GetString() ?? "").Trim() : "";
var categoryId = root.TryGetProperty("category_id", out var categoryIdEl) && categoryIdEl.TryGetInt32(out var cid) ? cid : 0;
var categoryText = BuildSelectedCategorySummary(root);
var maxMessages = root.TryGetProperty("max_messages", out var maxMessagesEl) && maxMessagesEl.TryGetInt32(out var mm) ? mm : 0;
var accountMode = root.TryGetProperty("account_mode", out var accountModeEl) ? (accountModeEl.GetString() ?? "").Trim() : "";
var targetMode = root.TryGetProperty("target_mode", out var targetModeEl) ? (targetModeEl.GetString() ?? "").Trim() : "";
@@ -573,7 +574,6 @@
}
}
var categoryText = categoryName.Length > 0 ? $"{categoryName} (#{categoryId})" : $"#{categoryId}";
details += "\n配置摘要:\n";
details += $"分类: {categoryText}\n";
details += $"目标数: {targetCount}\n";
@@ -607,6 +607,72 @@
return seconds.ToString("0.###");
}
private static string BuildSelectedCategorySummary(JsonElement root)
{
var ids = ExtractSelectedCategoryIds(root);
var names = ExtractSelectedCategoryNames(root);
var count = Math.Max(ids.Count, names.Count);
if (count == 0)
return "-";
var parts = new List<string>();
for (var i = 0; i < count; i++)
{
var id = i < ids.Count ? ids[i] : 0;
var name = i < names.Count ? names[i] : string.Empty;
if (id > 0 && name.Length > 0)
parts.Add($"{name} (#{id})");
else if (id > 0)
parts.Add($"#{id}");
else if (name.Length > 0)
parts.Add(name);
}
return parts.Count == 0 ? "-" : string.Join(", ", parts);
}
private static List<int> ExtractSelectedCategoryIds(JsonElement root)
{
var ids = new List<int>();
if (root.TryGetProperty("category_ids", out var idsEl) && idsEl.ValueKind == JsonValueKind.Array)
{
foreach (var item in idsEl.EnumerateArray())
{
if (item.TryGetInt32(out var id) && id > 0)
ids.Add(id);
}
}
if (ids.Count == 0 && root.TryGetProperty("category_id", out var categoryIdEl) && categoryIdEl.TryGetInt32(out var categoryId) && categoryId > 0)
ids.Add(categoryId);
return ids.Distinct().ToList();
}
private static List<string> ExtractSelectedCategoryNames(JsonElement root)
{
var names = new List<string>();
if (root.TryGetProperty("category_names", out var namesEl) && namesEl.ValueKind == JsonValueKind.Array)
{
foreach (var item in namesEl.EnumerateArray())
{
var name = (item.GetString() ?? string.Empty).Trim();
if (name.Length > 0)
names.Add(name);
}
}
if (names.Count == 0 && root.TryGetProperty("category_name", out var categoryNameEl))
{
var fallbackName = (categoryNameEl.GetString() ?? string.Empty).Trim();
if (fallbackName.Length > 0)
names.Add(fallbackName);
}
return names.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
}
private static string NormalizeTaskModeDisplay(string? mode)
{
var text = (mode ?? "").Trim();
@@ -688,20 +754,26 @@
}
}
private async Task DeleteTask(int id)
private async Task DeleteTask(BatchTask task)
{
var status = GetDisplayStatus(task);
var isActive = IsActiveStatus(status);
var message = isActive
? $"确定要删除任务 #{task.Id} 吗?删除后将停止后台执行并移除记录,此操作不可恢复。"
: $"确定要删除任务 #{task.Id} 的记录吗?此操作不可恢复。";
bool? result = await DialogService.ShowMessageBox(
"确认删除",
"确定要删除这个任务记录吗?此操作不可恢复。",
message,
yesText: "删除", cancelText: "取消");
if (result == true)
{
try
{
await TaskManagement.DeleteTaskAsync(id);
tasks.RemoveAll(t => t.Id == id);
Snackbar.Add("任务已删除", Severity.Success);
await TaskManagement.DeleteTaskAsync(task.Id);
await LoadTasks();
Snackbar.Add(isActive ? "任务已删除,后台执行将在下一次状态检查时停止" : "任务已删除", Severity.Success);
}
catch (Exception ex)
{
@@ -885,9 +957,14 @@
throw new InvalidOperationException($"任务配置 JSON 无效:{ex.Message}");
}
if (cfg.CategoryId <= 0)
cfg.CategoryIds = NormalizeCategoryIdsForRerun(cfg.CategoryIds, cfg.CategoryId);
if (cfg.CategoryIds.Count == 0)
throw new InvalidOperationException("任务缺少账号分类,无法重新运行");
cfg.CategoryId = cfg.CategoryIds[0];
cfg.CategoryNames = NormalizeCategoryNamesForRerun(cfg.CategoryNames, cfg.CategoryName);
cfg.CategoryName = cfg.CategoryNames.FirstOrDefault() ?? cfg.CategoryName;
cfg.Targets = (cfg.Targets ?? new List<string>())
.Select(x => (x ?? string.Empty).Trim())
.Where(x => x.Length > 0)
@@ -923,6 +1000,34 @@
return cfg;
}
private static List<int> NormalizeCategoryIdsForRerun(IEnumerable<int>? values, int fallback)
{
var ids = (values ?? Array.Empty<int>())
.Where(x => x > 0)
.Distinct()
.ToList();
if (ids.Count == 0 && fallback > 0)
ids.Add(fallback);
return ids;
}
private static List<string> NormalizeCategoryNamesForRerun(IEnumerable<string>? values, string? fallback)
{
var names = (values ?? Array.Empty<string>())
.Select(x => (x ?? string.Empty).Trim())
.Where(x => x.Length > 0)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var fallbackName = (fallback ?? string.Empty).Trim();
if (names.Count == 0 && fallbackName.Length > 0)
names.Add(fallbackName);
return names;
}
private static string NormalizeModeValue(string? mode)
{
return string.Equals((mode ?? string.Empty).Trim(), UserChatActiveTaskModes.Queue, StringComparison.OrdinalIgnoreCase)
@@ -25,9 +25,10 @@ public sealed class UserChatActiveTaskHandler : IModuleTaskHandler
var config = DeserializeConfig(host.Config);
ValidateAndNormalizeConfig(config);
var selectedCategoryIds = NormalizeSelectedCategoryIds(config).ToHashSet();
var allAccounts = (await accountManagement.GetAllAccountsAsync())
.Where(x => x.IsActive && x.UserId > 0 && x.Category?.ExcludeFromOperations != true)
.Where(x => x.CategoryId == config.CategoryId)
.Where(x => x.CategoryId.HasValue && selectedCategoryIds.Contains(x.CategoryId.Value))
.OrderBy(x => x.Id)
.ToList();
@@ -196,9 +197,14 @@ public sealed class UserChatActiveTaskHandler : IModuleTaskHandler
private static void ValidateAndNormalizeConfig(UserChatActiveTaskConfig config)
{
if (config.CategoryId <= 0)
config.CategoryIds = NormalizeSelectedCategoryIds(config);
if (config.CategoryIds.Count == 0)
throw new InvalidOperationException("任务缺少账号分类");
config.CategoryId = config.CategoryIds[0];
config.CategoryNames = NormalizeSelectedCategoryNames(config);
config.CategoryName = config.CategoryNames.FirstOrDefault() ?? config.CategoryName;
config.Targets = config.Targets
.Select(x => (x ?? string.Empty).Trim())
.Where(x => x.Length > 0)
@@ -231,6 +237,34 @@ public sealed class UserChatActiveTaskHandler : IModuleTaskHandler
config.RecentFailures ??= new List<UserChatActiveTaskRuntimeFailure>();
}
private static List<int> NormalizeSelectedCategoryIds(UserChatActiveTaskConfig config)
{
var ids = (config.CategoryIds ?? new List<int>())
.Where(x => x > 0)
.Distinct()
.ToList();
if (ids.Count == 0 && config.CategoryId > 0)
ids.Add(config.CategoryId);
return ids;
}
private static List<string> NormalizeSelectedCategoryNames(UserChatActiveTaskConfig config)
{
var names = (config.CategoryNames ?? new List<string>())
.Select(x => (x ?? string.Empty).Trim())
.Where(x => x.Length > 0)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var fallbackName = (config.CategoryName ?? string.Empty).Trim();
if (names.Count == 0 && fallbackName.Length > 0)
names.Add(fallbackName);
return names;
}
private static string NormalizeMode(string? mode)
{
return string.Equals((mode ?? string.Empty).Trim(), UserChatActiveTaskModes.Queue, StringComparison.OrdinalIgnoreCase)
@@ -16,6 +16,12 @@ public sealed class UserChatActiveTaskConfig
[JsonPropertyName("category_name")]
public string? CategoryName { get; set; }
[JsonPropertyName("category_ids")]
public List<int> CategoryIds { get; set; } = new();
[JsonPropertyName("category_names")]
public List<string> CategoryNames { get; set; } = new();
[JsonPropertyName("targets")]
public List<string> Targets { get; set; } = new();