From 48d17b7d31f9c8007c18e90afc573ee0fb37f94d Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 2 Jul 2025 16:16:09 +0800 Subject: [PATCH] feat(tasks): enhance background tasks - Add the job action filter 'IJobActionFilter' - Calculate the execution duration in the job event --- .../DefaultJobExceptionTypeFinder.cs | 5 +++ .../Abp/BackgroundTasks/JobEventBase.cs | 36 +++++++++++++------ .../Abp/BackgroundTasks/JobEventData.cs | 8 ++++- .../Activities/IJobActionFilter.cs | 21 +++++++++++ .../Activities/JobActionEvent.cs | 11 ++++++ ...pBackgroundTasksExceptionHandlingModule.cs | 1 + ...sNotificationTemplateDefinitionProvider.cs | 17 ++++----- .../Quartz/QuartzJobListener.cs | 8 +++++ .../Quartz/QuartzJobSearchJobAdapter.cs | 4 +++ .../Quartz/QuartzJobSimpleAdapter.cs | 4 +++ .../Internal/JobExecutedEvent.cs | 9 +++-- .../BackgroundTasks/Internal/JobLogEvent.cs | 11 ++++-- 12 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/IJobActionFilter.cs diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobExceptionTypeFinder.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobExceptionTypeFinder.cs index ac5be807d..1a68732c0 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobExceptionTypeFinder.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobExceptionTypeFinder.cs @@ -48,6 +48,11 @@ public class DefaultJobExceptionTypeFinder : IJobExceptionTypeFinder, ITransient return JobExceptionType.Network; } + if (httpStatusCode.HttpStatusCode >= 500) + { + return JobExceptionType.System; + } + return JobExceptionType.Application; } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs index 2bcd4e59b..716c3d0bf 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs @@ -19,12 +19,15 @@ public abstract class JobEventBase : IJobEvent { try { - var currentTenant = context.ServiceProvider.GetRequiredService(); - using (currentTenant.Change(context.EventData.TenantId)) + if (await CanAfterExecuted(context)) { - Logger.LogInformation("Job {Group}-{Name} after event with {Event} has executing.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); - await OnJobAfterExecutedAsync(context); - Logger.LogInformation("Job {Group}-{Name} after event with {Event} was executed.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); + var currentTenant = context.ServiceProvider.GetRequiredService(); + using (currentTenant.Change(context.EventData.TenantId)) + { + Logger.LogInformation("Job {Group}-{Name} after event with {Event} has executing.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); + await OnJobAfterExecutedAsync(context); + Logger.LogInformation("Job {Group}-{Name} after event with {Event} was executed.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); + } } } catch (Exception ex) @@ -37,12 +40,15 @@ public abstract class JobEventBase : IJobEvent { try { - var currentTenant = context.ServiceProvider.GetRequiredService(); - using (currentTenant.Change(context.EventData.TenantId)) + if (await CanBeforeExecuted(context)) { - Logger.LogInformation("Job {Group}-{Name} before event with {Event} executing.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); - await OnJobBeforeExecutedAsync(context); - Logger.LogInformation("Job {Group}-{Name} before event with {Event} was executed.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); + var currentTenant = context.ServiceProvider.GetRequiredService(); + using (currentTenant.Change(context.EventData.TenantId)) + { + Logger.LogInformation("Job {Group}-{Name} before event with {Event} executing.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); + await OnJobBeforeExecutedAsync(context); + Logger.LogInformation("Job {Group}-{Name} before event with {Event} was executed.", context.EventData.Group, context.EventData.Name, typeof(TEvent).Name); + } } } catch (Exception ex) @@ -51,11 +57,21 @@ public abstract class JobEventBase : IJobEvent } } + protected virtual Task CanAfterExecuted(JobEventContext context) + { + return Task.FromResult(true); + } + protected virtual Task OnJobAfterExecutedAsync(JobEventContext context) { return Task.CompletedTask; } + protected virtual Task CanBeforeExecuted(JobEventContext context) + { + return Task.FromResult(true); + } + protected virtual Task OnJobBeforeExecutedAsync(JobEventContext context) { return Task.CompletedTask; diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs index c00ecf50a..55f52a99e 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs @@ -59,6 +59,10 @@ public class JobEventData /// public DateTime RunTime { get; set; } /// + /// 执行时间(ms) + /// + public int? ExecutionDuration { get; set; } + /// /// 上次运行时间 /// public DateTime? LastRunTime { get; set; } @@ -66,7 +70,9 @@ public class JobEventData /// 下次运行时间 /// public DateTime? NextRunTime { get; set; } - + /// + /// 作业取消令牌 + /// public CancellationToken CancellationToken { get; } public JobEventData( string key, diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/IJobActionFilter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/IJobActionFilter.cs new file mode 100644 index 000000000..6341416c5 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/IJobActionFilter.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.BackgroundTasks.Activities; +/// +/// 作业行为过滤器 +/// +public interface IJobActionFilter +{ + /// + /// 是否允许触发后操作 + /// + /// + /// + Task CanAfterExecuted(JobEventContext context); + /// + /// 是否允许触发前操作 + /// + /// + /// + Task CanBeforeExecuted(JobEventContext context); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/JobActionEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/JobActionEvent.cs index b809cfac3..8bcbffc97 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/JobActionEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Activities/LINGYUN/Abp/BackgroundTasks/Activities/JobActionEvent.cs @@ -10,6 +10,17 @@ namespace LINGYUN.Abp.BackgroundTasks.Activities; public class JobActionEvent : JobEventBase, ITransientDependency { + protected override Task CanAfterExecuted(JobEventContext context) + { + var filter = context.ServiceProvider.GetService(); + if (filter != null) + { + return filter.CanAfterExecuted(context); + } + + return base.CanAfterExecuted(context); + } + protected async override Task OnJobAfterExecutedAsync(JobEventContext context) { if (context.EventData.Type.IsDefined(typeof(DisableJobActionAttribute), true)) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs index a626ff61d..72bb39f83 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs @@ -7,6 +7,7 @@ using Volo.Abp.VirtualFileSystem; namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; +[System.Obsolete("Please use the `AbpBackgroundTasksNotificationsModule` module")] [DependsOn(typeof(AbpBackgroundTasksActivitiesModule))] [DependsOn(typeof(AbpEmailingModule))] public class AbpBackgroundTasksExceptionHandlingModule : AbpModule diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Notifications/LINGYUN/Abp/BackgroundTasks/Notifications/Templates/BackgroundTasksNotificationTemplateDefinitionProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Notifications/LINGYUN/Abp/BackgroundTasks/Notifications/Templates/BackgroundTasksNotificationTemplateDefinitionProvider.cs index 9c6e69b95..46d4d6c04 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Notifications/LINGYUN/Abp/BackgroundTasks/Notifications/Templates/BackgroundTasksNotificationTemplateDefinitionProvider.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Notifications/LINGYUN/Abp/BackgroundTasks/Notifications/Templates/BackgroundTasksNotificationTemplateDefinitionProvider.cs @@ -1,7 +1,4 @@ using LINGYUN.Abp.BackgroundTasks.Localization; -using System; -using System.Collections.Generic; -using System.Text; using Volo.Abp.Localization; using Volo.Abp.TextTemplating; @@ -17,13 +14,13 @@ public class BackgroundTasksNotificationTemplateDefinitionProvider : TemplateDef { return new[] { - new TemplateDefinition( - BackgroundTasksNotificationTemplates.JobExecutedNotification, - displayName: L("TextTemplate:JobExecutedNotification"), - localizationResource: typeof(BackgroundTasksResource) - ).WithVirtualFilePath( - "/LINGYUN/Abp/BackgroundTasks/Notifications/Templates/JobExecutedNotification.tpl", - isInlineLocalized: true) + new TemplateDefinition( + BackgroundTasksNotificationTemplates.JobExecutedNotification, + displayName: L("TextTemplate:JobExecutedNotification"), + localizationResource: typeof(BackgroundTasksResource) + ).WithVirtualFilePath( + "/LINGYUN/Abp/BackgroundTasks/Notifications/Templates/JobExecutedNotification.tpl", + isInlineLocalized: true) }; } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs index 4dab915e0..dec636421 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs @@ -129,6 +129,14 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency } jobEventData.Description = context.JobDetail.Description; jobEventData.RunTime = Clock.Normalize(context.FireTimeUtc.LocalDateTime); + + var runTimeCache = context.Get(nameof(JobEventData.RunTime)); + if (runTimeCache != null && runTimeCache is DateTime runTime) + { + jobEventData.RunTime = runTime; + jobEventData.ExecutionDuration = (Clock.Now - runTime).Milliseconds; + } + var lastRunTime = context.PreviousFireTimeUtc?.LocalDateTime ?? context.Trigger.GetPreviousFireTimeUtc()?.LocalDateTime; if (lastRunTime.HasValue) { diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSearchJobAdapter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSearchJobAdapter.cs index c0ea3720a..a5fad3abf 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSearchJobAdapter.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSearchJobAdapter.cs @@ -2,6 +2,7 @@ using Quartz; using System.Collections.Immutable; using System.Threading.Tasks; +using Volo.Abp.Timing; namespace LINGYUN.Abp.BackgroundTasks.Quartz; @@ -24,8 +25,11 @@ public class QuartzJobSearchJobAdapter : IJob var jobDefinition = JobDefinitionManager.Get(jobType); using var scope = ServiceScopeFactory.CreateScope(); + var clock = scope.ServiceProvider.GetRequiredService(); var jobExecuter = scope.ServiceProvider.GetRequiredService(); + context.Put(nameof(JobEventData.RunTime), clock.Now); + var jobContext = new JobRunnableContext( jobDefinition.JobType, scope.ServiceProvider, diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs index dca926eda..19d519c75 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs @@ -2,6 +2,7 @@ using Quartz; using System.Collections.Immutable; using System.Threading.Tasks; +using Volo.Abp.Timing; namespace LINGYUN.Abp.BackgroundTasks.Quartz; @@ -19,8 +20,11 @@ public class QuartzJobSimpleAdapter : IJob public async virtual Task Execute(IJobExecutionContext context) { using var scope = ServiceScopeFactory.CreateScope(); + var clock = scope.ServiceProvider.GetRequiredService(); var jobExecuter = scope.ServiceProvider.GetRequiredService(); + context.Put(nameof(JobEventData.RunTime), clock.Now); + var jobContext = new JobRunnableContext( typeof(TJobRunnable), scope.ServiceProvider, diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs index ff4c36fe8..6882391af 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs @@ -9,13 +9,18 @@ namespace LINGYUN.Abp.BackgroundTasks.Internal; public class JobExecutedEvent : JobEventBase, ITransientDependency { - protected override async Task OnJobAfterExecutedAsync(JobEventContext context) + protected override Task CanAfterExecuted(JobEventContext context) { if (context.EventData.Type.IsDefined(typeof(DisableJobStatusAttribute), true)) { Logger.LogWarning("The job change event could not be processed because the job marked the DisableJobStatus attribute!"); - return; + return Task.FromResult(false); } + return base.CanAfterExecuted(context); + } + + protected override async Task OnJobAfterExecutedAsync(JobEventContext context) + { var store = context.ServiceProvider.GetRequiredService(); var job = await store.FindAsync(context.EventData.Key, context.EventData.CancellationToken); if (job == null) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs index a517c7f9b..b1a1b1bed 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using System.Threading.Tasks; using Volo.Abp.Auditing; using Volo.Abp.DependencyInjection; @@ -13,12 +14,18 @@ namespace LINGYUN.Abp.BackgroundTasks.Internal; /// public class JobLogEvent : JobEventBase, ITransientDependency { - protected async override Task OnJobAfterExecutedAsync(JobEventContext context) + protected override Task CanAfterExecuted(JobEventContext context) { if (context.EventData.Type.IsDefined(typeof(DisableAuditingAttribute), true)) { - return; + Logger.LogWarning("The job change event could not be processed because the job marked the DisableAuditing attribute!"); + return Task.FromResult(false); } + return base.CanAfterExecuted(context); + } + + protected async override Task OnJobAfterExecutedAsync(JobEventContext context) + { var store = context.ServiceProvider.GetRequiredService(); await store.StoreLogAsync(context.EventData); }