From 5a61625f6feb4719e0c6e34ce41ddb4ddd7a9d74 Mon Sep 17 00:00:00 2001 From: Argo-Tianyi Date: Wed, 29 Dec 2021 11:21:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ServicesExtensions.cs | 5 + .../Extensions/TasksExtensions.cs | 1 - .../Pages/Admin/Tasks.razor | 96 ++++++++++--------- .../Pages/Admin/Tasks.razor.cs | 64 ++++++++++++- .../Services/AdminTaskService.cs | 45 +++++++++ 5 files changed, 162 insertions(+), 49 deletions(-) create mode 100644 src/blazor/admin/BootstrapAdmin.Web/Services/AdminTaskService.cs diff --git a/src/blazor/admin/BootstrapAdmin.Web/Extensions/ServicesExtensions.cs b/src/blazor/admin/BootstrapAdmin.Web/Extensions/ServicesExtensions.cs index 36b2c6a0..76dbd174 100644 --- a/src/blazor/admin/BootstrapAdmin.Web/Extensions/ServicesExtensions.cs +++ b/src/blazor/admin/BootstrapAdmin.Web/Extensions/ServicesExtensions.cs @@ -1,6 +1,7 @@ using BootstrapAdmin.Web.Services; using BootstrapAdmin.Web.Services.SMS; using BootstrapAdmin.Web.Services.SMS.Tencent; +using Longbow.Tasks; using System.Text; namespace Microsoft.Extensions.DependencyInjection @@ -83,6 +84,10 @@ namespace Microsoft.Extensions.DependencyInjection // 增加 PetaPoco 数据服务 services.AddPetaPocoDataAccessServices(); + // 增加后台任务 + services.AddTaskServices(); + services.AddHostedService(); + return services; } } diff --git a/src/blazor/admin/BootstrapAdmin.Web/Extensions/TasksExtensions.cs b/src/blazor/admin/BootstrapAdmin.Web/Extensions/TasksExtensions.cs index 58cbbdc7..deb69138 100644 --- a/src/blazor/admin/BootstrapAdmin.Web/Extensions/TasksExtensions.cs +++ b/src/blazor/admin/BootstrapAdmin.Web/Extensions/TasksExtensions.cs @@ -14,6 +14,5 @@ public static class TasksExtensions LastRunResult = i.LastRunResult, Status = i.Status, Trigger = i.Triggers.First().ToString() - }).ToList(); } diff --git a/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor b/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor index 91555c0d..82b1a126 100644 --- a/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor +++ b/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor @@ -1,46 +1,56 @@ @page "/Admin/Tasks" - - - -

后台任务说明:

-

1. 默认任务 (立即执行,仅执行一次)

-

- - TaskServicesManager.GetOrAdd("简单任务", token => Task.Delay(1000)); - -

-

2. 周期性任务 (1 分钟后间隔 5 秒执行 2 次任务)

-

- - var trigger = TriggerBuilder.Default.WithInterval(TimeSpan.FromSeconds(5)).WithRepeatCount(2).WithStartTime(DateTimeOffset.Now.AddMinutes(1)).Build(); -
- TaskServicesManager.GetOrAdd("测试任务", token => Task.Delay(1000), trigger); -
-

-

3. Cron 表达式任务 (间隔 5 秒循环执行任务)

-
- - TaskServicesManager.GetOrAdd("Cron 表达式任务", token => Task.Delay(1000), TriggerBuilder.Build("*/5 * * * * *")); - -
-
-
-
+
+ + + +

后台任务说明:

+

1. 默认任务 (立即执行,仅执行一次)

+

+ + TaskServicesManager.GetOrAdd("简单任务", token => Task.Delay(1000)); + +

+

2. 周期性任务 (1 分钟后间隔 5 秒执行 2 次任务)

+

+ + var trigger = TriggerBuilder.Default.WithInterval(TimeSpan.FromSeconds(5)).WithRepeatCount(2).WithStartTime(DateTimeOffset.Now.AddMinutes(1)).Build(); +
+ TaskServicesManager.GetOrAdd("测试任务", token => Task.Delay(1000), trigger); +
+

+

3. Cron 表达式任务 (间隔 5 秒循环执行任务)

+
+ + TaskServicesManager.GetOrAdd("Cron 表达式任务", token => Task.Delay(1000), TriggerBuilder.Build("*/5 * * * * *")); + +
+
+
+
- - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor.cs b/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor.cs index 1927da88..df4e3858 100644 --- a/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor.cs +++ b/src/blazor/admin/BootstrapAdmin.Web/Pages/Admin/Tasks.razor.cs @@ -1,4 +1,4 @@ -using BootstrapAdmin.DataAccess.Models; +using BootstrapAdmin.Web.Components; using BootstrapAdmin.Web.Extensions; using BootstrapAdmin.Web.Models; using Longbow.Tasks; @@ -7,7 +7,10 @@ namespace BootstrapAdmin.Web.Pages.Admin; public partial class Tasks { - private Task> OnQueryAsync(QueryPageOptions options) + [NotNull] + private AdminTable? TaskTable { get; set; } + + private static Task> OnQueryAsync(QueryPageOptions options) { var tasks = TaskServicesManager.ToList().ToTasksModelList(); return Task.FromResult(new QueryData() @@ -16,17 +19,68 @@ public partial class Tasks }); } - private Task OnPause(TasksModel model) + private static Color GetResultColor(TriggerResult result) => result switch { + TriggerResult.Success => Color.Success, + TriggerResult.Error => Color.Danger, + TriggerResult.Timeout => Color.Warning, + TriggerResult.Cancelled => Color.Dark, + _ => Color.Primary + }; + + private static string FormatResult(TriggerResult result) => result switch + { + TriggerResult.Success => "成功", + TriggerResult.Error => "故障", + TriggerResult.Timeout => "超时", + TriggerResult.Cancelled => "取消", + _ => "未知状态" + }; + + private static Color GetStatusColor(SchedulerStatus status) => status switch + { + SchedulerStatus.Running => Color.Success, + SchedulerStatus.Ready => Color.Danger, + SchedulerStatus.Disabled => Color.Danger, + _ => Color.Primary + }; + + private static string FormatStatus(SchedulerStatus status) => status switch + { + SchedulerStatus.Running => "运行中", + SchedulerStatus.Ready => "已停止", + SchedulerStatus.Disabled => "禁用", + _ => "未知状态" + }; + + private static string GetStatusIcon(SchedulerStatus status) => status switch + { + SchedulerStatus.Running => "fa fa-play-circle", + SchedulerStatus.Ready => "fa fa-times-circle", + _ => "未知状态" + }; + + private static Task OnPause(TasksModel model) + { + var task = TaskServicesManager.ToList().FirstOrDefault(i => i.Name == model.Name); + if (task != null) + { + task.Status = SchedulerStatus.Ready; + } return Task.CompletedTask; } - private Task OnRun(TasksModel model) + private static Task OnRun(TasksModel model) { + var task = TaskServicesManager.ToList().FirstOrDefault(i => i.Name == model.Name); + if (task != null) + { + task.Status = SchedulerStatus.Running; + } return Task.CompletedTask; } - private Task OnLog(TasksModel model) + private static Task OnLog(TasksModel model) { return Task.CompletedTask; } diff --git a/src/blazor/admin/BootstrapAdmin.Web/Services/AdminTaskService.cs b/src/blazor/admin/BootstrapAdmin.Web/Services/AdminTaskService.cs new file mode 100644 index 00000000..c965e79a --- /dev/null +++ b/src/blazor/admin/BootstrapAdmin.Web/Services/AdminTaskService.cs @@ -0,0 +1,45 @@ +using BootstrapAdmin.DataAccess.Models; +using BootstrapAdmin.Web.Core; +using BootstrapAdmin.Web.Jobs; +using Longbow.Tasks; + +namespace BootstrapAdmin.Web.Services; + +class AdminTaskService : BackgroundService +{ + private IDict DictService { get; set; } + + /// + /// + /// + /// + public AdminTaskService(IDict dict) => DictService = dict; + + /// + /// 运行任务 + /// + /// + /// + protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(() => + { + TaskServicesManager.GetOrAdd("单次任务", token => Task.Delay(1000)); + TaskServicesManager.GetOrAdd("周期任务", token => Task.Delay(1000), TriggerBuilder.Default.WithInterval(10000).Build()); + TaskServicesManager.GetOrAdd("Cron 任务", token => Task.Delay(1000), TriggerBuilder.Build(Cron.Secondly(5))); + TaskServicesManager.GetOrAdd("超时任务", token => Task.Delay(2000), TriggerBuilder.Default.WithTimeout(1000).WithInterval(1000).WithRepeatCount(2).Build()); + + // 本机调试时此处会抛出异常,配置文件中默认开启了任务持久化到物理文件,此处异常只有首次加载时会抛出 + // 此处异常是示例自定义任务内部未进行捕获异常时任务仍然能继续运行,不会导致整个进程崩溃退出 + // 此处代码可注释掉 + //TaskServicesManager.GetOrAdd("故障任务", token => throw new Exception("故障任务")); + TaskServicesManager.GetOrAdd("取消任务", token => Task.Delay(1000)).Triggers.First().Enabled = false; + + // 创建任务并禁用 + TaskServicesManager.GetOrAdd("禁用任务", token => Task.Delay(1000)).Status = SchedulerStatus.Disabled; + + // 真实任务负责批次写入数据执行脚本到日志中 + TaskServicesManager.GetOrAdd("SQL日志", TriggerBuilder.Build(Cron.Minutely())); + + // 真实任务负责周期性设置健康检查结果开关为开启 + TaskServicesManager.GetOrAdd("健康检查", token => Task.FromResult(DictService.SaveHealthCheck()), TriggerBuilder.Build(Cron.Minutely(10))); + }); +}