feat: 增加任务管理
This commit is contained in:
parent
edf691a506
commit
5a61625f6f
|
@ -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<AdminTaskService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,5 @@ public static class TasksExtensions
|
|||
LastRunResult = i.LastRunResult,
|
||||
Status = i.Status,
|
||||
Trigger = i.Triggers.First().ToString()
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
|
|
|
@ -1,46 +1,56 @@
|
|||
@page "/Admin/Tasks"
|
||||
|
||||
<Card IsCollapsible="true" HeaderText="任务介绍" class="mb-3">
|
||||
<CardBody>
|
||||
<Alert class="mb-0">
|
||||
<p class="font-weight-bold">后台任务说明:</p>
|
||||
<p>1. 默认任务 (立即执行,仅执行一次)</p>
|
||||
<p>
|
||||
<code>
|
||||
TaskServicesManager.GetOrAdd("简单任务", token => Task.Delay(1000));
|
||||
</code>
|
||||
</p>
|
||||
<p>2. 周期性任务 (1 分钟后间隔 5 秒执行 2 次任务)</p>
|
||||
<p>
|
||||
<code>
|
||||
var trigger = TriggerBuilder.Default.WithInterval(TimeSpan.FromSeconds(5)).WithRepeatCount(2).WithStartTime(DateTimeOffset.Now.AddMinutes(1)).Build();
|
||||
<br />
|
||||
TaskServicesManager.GetOrAdd("测试任务", token => Task.Delay(1000), trigger);
|
||||
</code>
|
||||
</p>
|
||||
<p>3. Cron 表达式任务 (间隔 5 秒循环执行任务)</p>
|
||||
<div>
|
||||
<code>
|
||||
TaskServicesManager.GetOrAdd("Cron 表达式任务", token => Task.Delay(1000), TriggerBuilder.Build("*/5 * * * * *"));
|
||||
</code>
|
||||
</div>
|
||||
</Alert>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<div class="tab-scroll-body">
|
||||
<Card IsCollapsible="true" HeaderText="任务介绍" class="mb-3">
|
||||
<CardBody>
|
||||
<Alert class="mb-0">
|
||||
<p class="font-weight-bold">后台任务说明:</p>
|
||||
<p>1. 默认任务 (立即执行,仅执行一次)</p>
|
||||
<p>
|
||||
<code>
|
||||
TaskServicesManager.GetOrAdd("简单任务", token => Task.Delay(1000));
|
||||
</code>
|
||||
</p>
|
||||
<p>2. 周期性任务 (1 分钟后间隔 5 秒执行 2 次任务)</p>
|
||||
<p>
|
||||
<code>
|
||||
var trigger = TriggerBuilder.Default.WithInterval(TimeSpan.FromSeconds(5)).WithRepeatCount(2).WithStartTime(DateTimeOffset.Now.AddMinutes(1)).Build();
|
||||
<br />
|
||||
TaskServicesManager.GetOrAdd("测试任务", token => Task.Delay(1000), trigger);
|
||||
</code>
|
||||
</p>
|
||||
<p>3. Cron 表达式任务 (间隔 5 秒循环执行任务)</p>
|
||||
<div>
|
||||
<code>
|
||||
TaskServicesManager.GetOrAdd("Cron 表达式任务", token => Task.Delay(1000), TriggerBuilder.Build("*/5 * * * * *"));
|
||||
</code>
|
||||
</div>
|
||||
</Alert>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<AdminTable TItem="TasksModel" ExtendButtonColumnWidth="270" OnQueryAsync="OnQueryAsync">
|
||||
<ColumnsTemplete>
|
||||
<TableColumn @bind-Field="@context.Name" Sortable="true" Filterable="true" Searchable="true" Width="180"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.CreateTime" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.LastRuntime" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.NextRuntime" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.Trigger" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.LastRunResult" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.Status" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
</ColumnsTemplete>
|
||||
<RowButtonTemplate>
|
||||
<TableCellButton Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa fa-pause-circle" Text="暂停" OnClick="() => OnPause(context)" />
|
||||
<TableCellButton Size="Size.ExtraSmall" Color="Color.Success" Icon="fa fa-play-circle" Text="运行" OnClick="() => OnRun(context)" />
|
||||
<TableCellButton Size="Size.ExtraSmall" Color="Color.Info" Icon="fa fa-info-circle" Text="日志" OnClick="() => OnLog(context)" />
|
||||
</RowButtonTemplate>
|
||||
</AdminTable>
|
||||
<AdminTable TItem="TasksModel" ExtendButtonColumnWidth="270" OnQueryAsync="OnQueryAsync">
|
||||
<ColumnsTemplete>
|
||||
<TableColumn @bind-Field="@context.Name" Sortable="true" Filterable="true" Searchable="true" Width="80"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.CreateTime" Sortable="true" Filterable="true" Searchable="true" FormatString="yyyy-MM-dd HH:mm:ss"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.LastRuntime" Sortable="true" Filterable="true" Searchable="true" FormatString="yyyy-MM-dd HH:mm:ss"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.NextRuntime" Sortable="true" Filterable="true" Searchable="true" FormatString="yyyy-MM-dd HH:mm:ss"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.Trigger" Sortable="true" Filterable="true" Searchable="true"></TableColumn>
|
||||
<TableColumn @bind-Field="@context.LastRunResult" Sortable="true" Filterable="true" Searchable="true">
|
||||
<Template Context="v">
|
||||
<Tag Color="GetResultColor(v.Value)">@FormatResult(v.Value)</Tag>
|
||||
</Template>
|
||||
</TableColumn>
|
||||
<TableColumn @bind-Field="@context.Status" Sortable="true" Filterable="true" Searchable="true">
|
||||
<Template Context="v">
|
||||
<Tag Color="GetStatusColor(v.Value)" Icon="@GetStatusIcon(v.Value)">@FormatStatus(v.Value)</Tag>
|
||||
</Template>
|
||||
</TableColumn>
|
||||
</ColumnsTemplete>
|
||||
<RowButtonTemplate>
|
||||
<TableCellButton Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa fa-pause-circle" Text="暂停" OnClick="() => OnPause(context)" />
|
||||
<TableCellButton Size="Size.ExtraSmall" Color="Color.Success" Icon="fa fa-play-circle" Text="运行" OnClick="() => OnRun(context)" />
|
||||
<TableCellButton Size="Size.ExtraSmall" Color="Color.Info" Icon="fa fa-info-circle" Text="日志" OnClick="() => OnLog(context)" />
|
||||
</RowButtonTemplate>
|
||||
</AdminTable>
|
||||
</div>
|
|
@ -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<QueryData<TasksModel>> OnQueryAsync(QueryPageOptions options)
|
||||
[NotNull]
|
||||
private AdminTable<TasksModel>? TaskTable { get; set; }
|
||||
|
||||
private static Task<QueryData<TasksModel>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
var tasks = TaskServicesManager.ToList().ToTasksModelList();
|
||||
return Task.FromResult(new QueryData<TasksModel>()
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="dict"></param>
|
||||
public AdminTaskService(IDict dict) => DictService = dict;
|
||||
|
||||
/// <summary>
|
||||
/// 运行任务
|
||||
/// </summary>
|
||||
/// <param name="stoppingToken"></param>
|
||||
/// <returns></returns>
|
||||
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<DBLogTask>("SQL日志", TriggerBuilder.Build(Cron.Minutely()));
|
||||
|
||||
// 真实任务负责周期性设置健康检查结果开关为开启
|
||||
TaskServicesManager.GetOrAdd("健康检查", token => Task.FromResult(DictService.SaveHealthCheck()), TriggerBuilder.Build(Cron.Minutely(10)));
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue