fix(tasks): improve user active task controls
This commit is contained in:
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();
|
||||
|
||||
|
||||
Reference in new issue
Block a user