Browse Source
1、使作业调度完成时调度用户定义Action,移除污染作业参数的异常处理参数; 2、重构用户定义作业注册方式,通过IJobDefinitionProvider注册; 3、增加IJobActionDefinitionProvider接口,用户可自行注册作业调度后处理程序; 4、增加LINGYUN.Abp.Notifications.Jobs模块,使通知相关作业独立; 5、增加LINGYUN.Abp.BackgroundTasks.DistributedLocking模块,让用户决定是否使用分布式锁定任务; 6、应用服务层增加作业Action管理接口, 便于从前端快速管理作业Action; 7、应用服务层增加作业定义查询接口, 便于从前端快速绑定作业名称、参数等.pull/620/head
140 changed files with 2966 additions and 417 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,24 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\Notifications\Jobs\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Notifications\Jobs\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\task-management\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,27 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
using LINGYUN.Abp.Notifications.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Jobs; |
||||
|
|
||||
|
[DependsOn(typeof(AbpNotificationModule))] |
||||
|
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))] |
||||
|
public class AbpNotificationsJobsModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpNotificationsJobsModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<NotificationsResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Abp/Notifications/Jobs/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using LINGYUN.Abp.Notifications.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Jobs; |
||||
|
|
||||
|
internal static class LocalizableStatic |
||||
|
{ |
||||
|
public static ILocalizableString Create(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<NotificationsResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"NotificationCleanupJob": "Notification Cleanup Job", |
||||
|
"Notification:BatchCount": "Batch Count" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"NotificationCleanupJob": "清理过期通知作业", |
||||
|
"Notification:BatchCount": "批次数量" |
||||
|
} |
||||
|
} |
||||
@ -1,17 +1,26 @@ |
|||||
using LINGYUN.Abp.BackgroundTasks; |
using LINGYUN.Abp.BackgroundTasks; |
||||
using LINGYUN.Abp.Notifications; |
using System.Collections.Generic; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
|
|
||||
namespace LY.MicroService.RealtimeMessage.BackgroundJobs; |
namespace LINGYUN.Abp.Notifications.Jobs; |
||||
|
|
||||
public class NotificationCleanupJob : IJobRunnable |
public class NotificationCleanupJob : IJobRunnable |
||||
{ |
{ |
||||
|
#region Definition Paramters
|
||||
|
|
||||
|
public readonly static IReadOnlyList<JobDefinitionParamter> Paramters = |
||||
|
new List<JobDefinitionParamter> |
||||
|
{ |
||||
|
new JobDefinitionParamter(PropertyBatchCount, LocalizableStatic.Create("Notification:BatchCount")) |
||||
|
}; |
||||
|
|
||||
|
#endregion
|
||||
|
public const string Name = "NotificationCleanupJob"; |
||||
/// <summary>
|
/// <summary>
|
||||
/// 每次清除记录大小
|
/// 每次清除记录大小
|
||||
/// </summary>
|
/// </summary>
|
||||
public const string PropertyBatchCount = "BatchCount"; |
public const string PropertyBatchCount = "BatchCount"; |
||||
|
|
||||
|
|
||||
public async virtual Task ExecuteAsync(JobRunnableContext context) |
public async virtual Task ExecuteAsync(JobRunnableContext context) |
||||
{ |
{ |
||||
var count = context.GetJobData<int>(PropertyBatchCount); |
var count = context.GetJobData<int>(PropertyBatchCount); |
||||
@ -0,0 +1,16 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Jobs; |
||||
|
|
||||
|
public class NotificationJobDefinitionProvider : JobDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IJobDefinitionContext context) |
||||
|
{ |
||||
|
context.Add( |
||||
|
new JobDefinition( |
||||
|
NotificationCleanupJob.Name, |
||||
|
typeof(NotificationCleanupJob), |
||||
|
LocalizableStatic.Create("NotificationCleanupJob"), |
||||
|
NotificationCleanupJob.Paramters)); |
||||
|
} |
||||
|
} |
||||
@ -1,7 +1,43 @@ |
|||||
using Volo.Abp.Modularity; |
using LINGYUN.Abp.BackgroundTasks.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks; |
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
[DependsOn(typeof(AbpLocalizationModule))] |
||||
public class AbpBackgroundTasksAbstractionsModule : AbpModule |
public class AbpBackgroundTasksAbstractionsModule : AbpModule |
||||
{ |
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
AutoAddDefinitionProviders(context.Services); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources.Add<BackgroundTasksResource>("en"); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static void AutoAddDefinitionProviders(IServiceCollection services) |
||||
|
{ |
||||
|
var definitionProviders = new List<Type>(); |
||||
|
|
||||
|
services.OnRegistred(context => |
||||
|
{ |
||||
|
if (typeof(IJobDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
definitionProviders.Add(context.ImplementationType); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
services.Configure<AbpBackgroundTasksOptions>(options => |
||||
|
{ |
||||
|
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
||||
|
}); |
||||
|
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,8 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
[AttributeUsage(AttributeTargets.Class)] |
||||
|
public class DisableJobActionAttribute : Attribute |
||||
|
{ |
||||
|
} |
||||
@ -1,9 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks; |
|
||||
|
|
||||
public interface IJobCompletedNotifierProvider |
|
||||
{ |
|
||||
Task NotifyComplateAsync([NotNull] JobEventContext context); |
|
||||
} |
|
||||
@ -0,0 +1,12 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public interface IJobDefinitionContext |
||||
|
{ |
||||
|
JobDefinition GetOrNull(string name); |
||||
|
|
||||
|
IReadOnlyList<JobDefinition> GetAll(); |
||||
|
|
||||
|
void Add(params JobDefinition[] definitions); |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public interface IJobDefinitionManager |
||||
|
{ |
||||
|
[NotNull] |
||||
|
JobDefinition Get([NotNull] string name); |
||||
|
|
||||
|
IReadOnlyList<JobDefinition> GetAll(); |
||||
|
|
||||
|
JobDefinition GetOrNull(string name); |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public interface IJobDefinitionProvider |
||||
|
{ |
||||
|
void Define(IJobDefinitionContext context); |
||||
|
} |
||||
@ -1,11 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks; |
|
||||
|
|
||||
public interface IJobExecutedNotifier |
|
||||
{ |
|
||||
Task NotifyErrorAsync([NotNull] JobEventContext context); |
|
||||
Task NotifySuccessAsync([NotNull] JobEventContext context); |
|
||||
Task NotifyComplateAsync([NotNull] JobEventContext context); |
|
||||
} |
|
||||
@ -1,9 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks; |
|
||||
|
|
||||
public interface IJobFailedNotifierProvider |
|
||||
{ |
|
||||
Task NotifyErrorAsync([NotNull] JobEventContext context); |
|
||||
} |
|
||||
@ -1,9 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks; |
|
||||
|
|
||||
public interface IJobSuccessNotifierProvider |
|
||||
{ |
|
||||
Task NotifySuccessAsync([NotNull] JobEventContext context); |
|
||||
} |
|
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public enum JobActionType |
||||
|
{ |
||||
|
Failed = -1, |
||||
|
Successed = 0, |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public class JobDefinition |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; } |
||||
|
/// <summary>
|
||||
|
/// 作业类型
|
||||
|
/// </summary>
|
||||
|
public Type JobType { get; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public ILocalizableString DisplayName { get; } |
||||
|
/// <summary>
|
||||
|
/// 描述
|
||||
|
/// </summary>
|
||||
|
public ILocalizableString Description { get; } |
||||
|
/// <summary>
|
||||
|
/// 参数列表
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<JobDefinitionParamter> Paramters { get; } |
||||
|
public JobDefinition( |
||||
|
[NotNull] string name, |
||||
|
[NotNull] Type jobType, |
||||
|
[NotNull] ILocalizableString displayName, |
||||
|
[CanBeNull] IReadOnlyList<JobDefinitionParamter> paramters = null, |
||||
|
[CanBeNull] ILocalizableString description = null) |
||||
|
{ |
||||
|
Name = name; |
||||
|
JobType = jobType; |
||||
|
DisplayName = displayName; |
||||
|
Description = description; |
||||
|
Paramters = paramters ?? new JobDefinitionParamter[0]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public class JobDefinitionContext : IJobDefinitionContext |
||||
|
{ |
||||
|
protected Dictionary<string, JobDefinition> Jobs { get; } |
||||
|
|
||||
|
public JobDefinitionContext(Dictionary<string, JobDefinition> jobs) |
||||
|
{ |
||||
|
Jobs = jobs; |
||||
|
} |
||||
|
|
||||
|
public virtual JobDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return Jobs.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<JobDefinition> GetAll() |
||||
|
{ |
||||
|
return Jobs.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual void Add(params JobDefinition[] definitions) |
||||
|
{ |
||||
|
if (definitions.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var definition in definitions) |
||||
|
{ |
||||
|
Jobs[definition.Name] = definition; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public class JobDefinitionManager : IJobDefinitionManager, ISingletonDependency |
||||
|
{ |
||||
|
protected Lazy<IDictionary<string, JobDefinition>> JobDefinitions { get; } |
||||
|
|
||||
|
protected AbpBackgroundTasksOptions Options { get; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
|
||||
|
public JobDefinitionManager( |
||||
|
IOptions<AbpBackgroundTasksOptions> options, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options.Value; |
||||
|
|
||||
|
JobDefinitions = new Lazy<IDictionary<string, JobDefinition>>(CreateJobDefinitions, true); |
||||
|
} |
||||
|
|
||||
|
public virtual JobDefinition Get(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
var action = GetOrNull(name); |
||||
|
|
||||
|
if (action == null) |
||||
|
{ |
||||
|
throw new AbpException("Undefined job: " + name); |
||||
|
} |
||||
|
|
||||
|
return action; |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<JobDefinition> GetAll() |
||||
|
{ |
||||
|
return JobDefinitions.Value.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual JobDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return JobDefinitions.Value.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
protected virtual IDictionary<string, JobDefinition> CreateJobDefinitions() |
||||
|
{ |
||||
|
var jobs = new Dictionary<string, JobDefinition>(); |
||||
|
|
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var providers = Options |
||||
|
.DefinitionProviders |
||||
|
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IJobDefinitionProvider) |
||||
|
.ToList(); |
||||
|
|
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
provider.Define(new JobDefinitionContext(jobs)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return jobs; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public class JobDefinitionParamter |
||||
|
{ |
||||
|
public string Name { get; } |
||||
|
|
||||
|
public bool Required { get; } |
||||
|
|
||||
|
public ILocalizableString DisplayName { get; } |
||||
|
|
||||
|
public ILocalizableString Description { get; } |
||||
|
|
||||
|
public JobDefinitionParamter( |
||||
|
[NotNull] string name, |
||||
|
[NotNull] ILocalizableString displayName, |
||||
|
[CanBeNull] ILocalizableString description = null, |
||||
|
bool required = false) |
||||
|
{ |
||||
|
Name = name; |
||||
|
Required = required; |
||||
|
DisplayName = displayName; |
||||
|
Description = description; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
public abstract class JobDefinitionProvider : IJobDefinitionProvider, ITransientDependency |
||||
|
{ |
||||
|
public abstract void Define(IJobDefinitionContext context); |
||||
|
} |
||||
@ -1,67 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Logging.Abstractions; |
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks; |
|
||||
|
|
||||
public class JobExecutedNotifier : IJobExecutedNotifier, ISingletonDependency |
|
||||
{ |
|
||||
public ILogger<JobExecutedNotifier> Logger { protected get; set; } |
|
||||
|
|
||||
public JobExecutedNotifier() |
|
||||
{ |
|
||||
Logger = NullLogger<JobExecutedNotifier>.Instance; |
|
||||
} |
|
||||
|
|
||||
public async Task NotifyComplateAsync([NotNull] JobEventContext context) |
|
||||
{ |
|
||||
var notifier = context.ServiceProvider.GetService<IJobCompletedNotifierProvider>(); |
|
||||
if (notifier != null) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
await notifier.NotifyComplateAsync(context); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
Logger.LogWarning($"An exception thow with job complete notify: {ex.Message}"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task NotifyErrorAsync([NotNull] JobEventContext context) |
|
||||
{ |
|
||||
var notifier = context.ServiceProvider.GetService<IJobFailedNotifierProvider>(); |
|
||||
if (notifier != null) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
await notifier.NotifyErrorAsync(context); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
Logger.LogWarning($"An exception thow with job error notify: {ex.Message}"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task NotifySuccessAsync([NotNull] JobEventContext context) |
|
||||
{ |
|
||||
var notifier = context.ServiceProvider.GetService<IJobSuccessNotifierProvider>(); |
|
||||
if (notifier != null) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
await notifier.NotifySuccessAsync(context); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
Logger.LogWarning($"An exception thow with job success notify: {ex.Message}"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,19 @@ |
|||||
|
# LINGYUN.Abp.BackgroundTasks.Abstractions |
||||
|
|
||||
|
后台任务(队列)模块抽象层,定义一些基本构造与接口 |
||||
|
|
||||
|
## 特性参数 |
||||
|
|
||||
|
* DisableJobActionAttribute 标记此特性不处理作业触发后行为 |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
} |
||||
|
``` |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,27 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\BackgroundTasks\Activities\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\BackgroundTasks\Activities\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Localization" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,57 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
[DependsOn(typeof(AbpLocalizationModule))] |
||||
|
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))] |
||||
|
public class AbpBackgroundTasksActivitiesModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
AutoAddDefinitionProviders(context.Services); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpBackgroundTasksActivitiesModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<BackgroundTasksResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Abp/BackgroundTasks/Activities/Localization/Resources"); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpBackgroundTasksOptions>(options => |
||||
|
{ |
||||
|
options.JobMonitors.AddIfNotContains(typeof(JobActionEvent)); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static void AutoAddDefinitionProviders(IServiceCollection services) |
||||
|
{ |
||||
|
var definitionProviders = new List<Type>(); |
||||
|
|
||||
|
services.OnRegistred(context => |
||||
|
{ |
||||
|
if (typeof(IJobActionDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
definitionProviders.Add(context.ImplementationType); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
services.Configure<AbpBackgroundTasksActivitiesOptions>(options => |
||||
|
{ |
||||
|
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp.Collections; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class AbpBackgroundTasksActivitiesOptions |
||||
|
{ |
||||
|
public ITypeList<IJobActionDefinitionProvider> DefinitionProviders { get; } |
||||
|
|
||||
|
public AbpBackgroundTasksActivitiesOptions() |
||||
|
{ |
||||
|
DefinitionProviders = new TypeList<IJobActionDefinitionProvider>(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
[Dependency(TryRegister = true)] |
||||
|
public class DefaultJobActionStore : IJobActionStore, ISingletonDependency |
||||
|
{ |
||||
|
public Task<List<JobAction>> GetActionsAsync(string id, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Task.FromResult(new List<JobAction>()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public interface IJobActionDefinitionContext |
||||
|
{ |
||||
|
JobActionDefinition GetOrNull(string name); |
||||
|
|
||||
|
IReadOnlyList<JobActionDefinition> GetAll(); |
||||
|
|
||||
|
void Add(params JobActionDefinition[] definitions); |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public interface IJobActionDefinitionManager |
||||
|
{ |
||||
|
[NotNull] |
||||
|
JobActionDefinition Get([NotNull] string name); |
||||
|
|
||||
|
IReadOnlyList<JobActionDefinition> GetAll(); |
||||
|
|
||||
|
JobActionDefinition GetOrNull(string name); |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public interface IJobActionDefinitionProvider |
||||
|
{ |
||||
|
void Define(IJobActionDefinitionContext context); |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public interface IJobActionStore |
||||
|
{ |
||||
|
Task<List<JobAction>> GetActionsAsync(string id, CancellationToken cancellationToken = default); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public interface IJobExecutedProvider |
||||
|
{ |
||||
|
Task NotifyErrorAsync([NotNull] JobActionExecuteContext context); |
||||
|
Task NotifySuccessAsync([NotNull] JobActionExecuteContext context); |
||||
|
Task NotifyComplateAsync([NotNull] JobActionExecuteContext context); |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class JobAction |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
public Dictionary<string, object> Paramters { get; set; } |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Collections; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class JobActionDefinition |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; } |
||||
|
/// <summary>
|
||||
|
/// 类型
|
||||
|
/// </summary>
|
||||
|
public JobActionType Type { get; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public ILocalizableString DisplayName { get; } |
||||
|
/// <summary>
|
||||
|
/// 描述
|
||||
|
/// </summary>
|
||||
|
public ILocalizableString Description { get; } |
||||
|
/// <summary>
|
||||
|
/// 参数列表
|
||||
|
/// </summary>
|
||||
|
public IList<JobActionParamter> Paramters { get; } |
||||
|
/// <summary>
|
||||
|
/// 通知提供者
|
||||
|
/// </summary>
|
||||
|
public ITypeList<IJobExecutedProvider> Providers { get; } |
||||
|
public JobActionDefinition( |
||||
|
[NotNull] string name, |
||||
|
[NotNull] JobActionType type, |
||||
|
[NotNull] ILocalizableString displayName, |
||||
|
[NotNull] IList<JobActionParamter> paramters, |
||||
|
ILocalizableString description = null) |
||||
|
{ |
||||
|
Name = name; |
||||
|
Type = type; |
||||
|
DisplayName = displayName; |
||||
|
Paramters = paramters; |
||||
|
Description = description; |
||||
|
|
||||
|
Providers = new TypeList<IJobExecutedProvider>(); |
||||
|
} |
||||
|
|
||||
|
public virtual JobActionDefinition WithProvider<TProvider>() |
||||
|
where TProvider : IJobExecutedProvider |
||||
|
{ |
||||
|
Providers.Add(typeof(TProvider)); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
public class JobActionDefinitionContext : IJobActionDefinitionContext |
||||
|
{ |
||||
|
protected Dictionary<string, JobActionDefinition> Actions { get; } |
||||
|
|
||||
|
public JobActionDefinitionContext(Dictionary<string, JobActionDefinition> actions) |
||||
|
{ |
||||
|
Actions = actions; |
||||
|
} |
||||
|
|
||||
|
public virtual JobActionDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return Actions.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<JobActionDefinition> GetAll() |
||||
|
{ |
||||
|
return Actions.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual void Add(params JobActionDefinition[] definitions) |
||||
|
{ |
||||
|
if (definitions.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var definition in definitions) |
||||
|
{ |
||||
|
Actions[definition.Name] = definition; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class JobActionDefinitionManager : IJobActionDefinitionManager, ISingletonDependency |
||||
|
{ |
||||
|
protected Lazy<IDictionary<string, JobActionDefinition>> ActionDefinitions { get; } |
||||
|
|
||||
|
protected AbpBackgroundTasksActivitiesOptions Options { get; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
|
||||
|
public JobActionDefinitionManager( |
||||
|
IOptions<AbpBackgroundTasksActivitiesOptions> options, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options.Value; |
||||
|
|
||||
|
ActionDefinitions = new Lazy<IDictionary<string, JobActionDefinition>>(CreateSettingDefinitions, true); |
||||
|
} |
||||
|
|
||||
|
public virtual JobActionDefinition Get(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
var action = GetOrNull(name); |
||||
|
|
||||
|
if (action == null) |
||||
|
{ |
||||
|
throw new AbpException("Undefined action: " + name); |
||||
|
} |
||||
|
|
||||
|
return action; |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<JobActionDefinition> GetAll() |
||||
|
{ |
||||
|
return ActionDefinitions.Value.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual JobActionDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return ActionDefinitions.Value.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
protected virtual IDictionary<string, JobActionDefinition> CreateSettingDefinitions() |
||||
|
{ |
||||
|
var actions = new Dictionary<string, JobActionDefinition>(); |
||||
|
|
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var providers = Options |
||||
|
.DefinitionProviders |
||||
|
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IJobActionDefinitionProvider) |
||||
|
.ToList(); |
||||
|
|
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
provider.Define(new JobActionDefinitionContext(actions)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return actions; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public abstract class JobActionDefinitionProvider : IJobActionDefinitionProvider, ITransientDependency |
||||
|
{ |
||||
|
public abstract void Define(IJobActionDefinitionContext context); |
||||
|
} |
||||
@ -0,0 +1,89 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class JobActionEvent : JobEventBase<JobActionEvent>, ITransientDependency |
||||
|
{ |
||||
|
protected async override Task OnJobAfterExecutedAsync(JobEventContext context) |
||||
|
{ |
||||
|
if (context.EventData.Type.IsDefined(typeof(DisableJobActionAttribute), true)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var actions = await GetJobActions(context); |
||||
|
if (!actions.Any()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var provider = context.ServiceProvider.GetRequiredService<IJobActionDefinitionManager>(); |
||||
|
|
||||
|
foreach (var action in actions) |
||||
|
{ |
||||
|
var definition = provider.GetOrNull(action.Name); |
||||
|
|
||||
|
if (definition == null) |
||||
|
{ |
||||
|
Logger.LogWarning($"Cannot execute job action {definition.Name}, Because it's not registered."); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
await ExecuteAction(context, definition, action); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task<List<JobAction>> GetJobActions(JobEventContext context) |
||||
|
{ |
||||
|
var store = context.ServiceProvider.GetRequiredService<IJobActionStore>(); |
||||
|
|
||||
|
return await store.GetActionsAsync(context.EventData.Key); |
||||
|
} |
||||
|
|
||||
|
private async Task ExecuteAction(JobEventContext context, JobActionDefinition actionDefinition, JobAction action) |
||||
|
{ |
||||
|
var missingRequiredParams = actionDefinition.Paramters |
||||
|
.Where(p => p.Required) |
||||
|
.Where(rp => !action.Paramters.Any(p => string.Equals(rp.Name, p.Key))) |
||||
|
.Select(rp => rp.Name); |
||||
|
|
||||
|
if (missingRequiredParams.Any()) |
||||
|
{ |
||||
|
Logger.LogWarning($"Cannot execute job action {actionDefinition.Name}, The required parameters are missing: {missingRequiredParams.JoinAsString(Environment.NewLine)}"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var notifierContext = new JobActionExecuteContext(action, context); |
||||
|
|
||||
|
foreach (var notifierType in actionDefinition.Providers) |
||||
|
{ |
||||
|
if (context.ServiceProvider.GetService(notifierType) is IJobExecutedProvider notifier) |
||||
|
{ |
||||
|
if (context.EventData.Exception != null) |
||||
|
{ |
||||
|
if (actionDefinition.Type == JobActionType.Failed) |
||||
|
{ |
||||
|
await notifier.NotifyErrorAsync(notifierContext); |
||||
|
Logger.LogInformation($"Executed Failed action with {notifierType.Name} was Successed."); |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (actionDefinition.Type == JobActionType.Successed) |
||||
|
{ |
||||
|
await notifier.NotifySuccessAsync(notifierContext); |
||||
|
Logger.LogInformation($"Executed Successed action with {notifierType.Name} was Successed."); |
||||
|
} |
||||
|
|
||||
|
await notifier.NotifyComplateAsync(notifierContext); |
||||
|
Logger.LogInformation($"Executed Complated action with {notifierType.Name} was Successed."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class JobActionExecuteContext |
||||
|
{ |
||||
|
public JobAction Action { get; } |
||||
|
public JobEventContext Event { get; } |
||||
|
public JobActionExecuteContext( |
||||
|
[NotNull] JobAction action, |
||||
|
[NotNull] JobEventContext @event) |
||||
|
{ |
||||
|
Action = action; |
||||
|
Event = @event; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public class JobActionParamter |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
public bool Required { get; set; } |
||||
|
public ILocalizableString DisplayName { get; set; } |
||||
|
public ILocalizableString Description { get; set; } |
||||
|
|
||||
|
public JobActionParamter( |
||||
|
[NotNull] string name, |
||||
|
[NotNull] ILocalizableString displayName, |
||||
|
[CanBeNull] ILocalizableString description = null, |
||||
|
bool required = false) |
||||
|
{ |
||||
|
Name = name; |
||||
|
DisplayName = displayName; |
||||
|
Description = description; |
||||
|
Required = required; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
|
||||
|
public abstract class JobExecutedProvider : IJobExecutedProvider |
||||
|
{ |
||||
|
public virtual Task NotifyComplateAsync([NotNull] JobActionExecuteContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public virtual Task NotifyErrorAsync([NotNull] JobActionExecuteContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public virtual Task NotifySuccessAsync([NotNull] JobActionExecuteContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
# LINGYUN.Abp.BackgroundTasks.Activities |
||||
|
|
||||
|
后台任务(队列)模块行为处理模块 |
||||
|
|
||||
|
## 接口参数 |
||||
|
|
||||
|
* IJobActionStore 实现此接口获取作业管理行为 |
||||
|
* JobActionDefinitionProvider 实现此接口自定义作业行为 |
||||
|
* JobExecutedProvider 实现此接口扩展作业行为 |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpBackgroundTasksActivitiesModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
} |
||||
|
``` |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.DistributedLocking.Abstractions" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks\LINGYUN.Abp.BackgroundTasks.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,11 @@ |
|||||
|
using Volo.Abp.DistributedLocking; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.DistributedLocking; |
||||
|
|
||||
|
[DependsOn(typeof(AbpBackgroundTasksModule))] |
||||
|
[DependsOn(typeof(AbpDistributedLockingAbstractionsModule))] |
||||
|
public class AbpBackgroundTasksDistributedLockingModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
# LINGYUN.Abp.BackgroundTasks.DistributedLocking |
||||
|
|
||||
|
后台任务(队列)模块分布式锁模块 |
||||
|
|
||||
|
See: [Distributed-Locking](https://docs.abp.io/en/abp/latest/Distributed-Locking) |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpBackgroundTasksDistributedLockingModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
} |
||||
|
``` |
||||
@ -0,0 +1,15 @@ |
|||||
|
# LINGYUN.Abp.BackgroundTasks.EventBus |
||||
|
|
||||
|
后台任务(队列)模块分布式事件模块,集成模块使应用程序具备处理作业事件能力 |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpBackgroundTasksEventBusModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
} |
||||
|
``` |
||||
@ -0,0 +1,25 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
using LINGYUN.Abp.BackgroundTasks.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; |
||||
|
|
||||
|
public class ExceptionHandlingJobActionDefinitionProvider : JobActionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IJobActionDefinitionContext context) |
||||
|
{ |
||||
|
context.Add( |
||||
|
new JobActionDefinition( |
||||
|
JobExecutedFailedProvider.Name, |
||||
|
JobActionType.Failed, |
||||
|
L("JobExceptionNotifier"), |
||||
|
JobExecutedFailedProvider.Paramters, |
||||
|
L("JobExceptionNotifier")) |
||||
|
.WithProvider<JobExecutedFailedProvider>()); |
||||
|
} |
||||
|
|
||||
|
private static ILocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<BackgroundTasksResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,144 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using LINGYUN.Abp.BackgroundTasks.Activities; |
||||
|
using LINGYUN.Abp.BackgroundTasks.ExceptionHandling.Templates; |
||||
|
using LINGYUN.Abp.BackgroundTasks.Localization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Newtonsoft.Json; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Emailing; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.TextTemplating; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; |
||||
|
|
||||
|
public class JobExecutedFailedProvider : JobExecutedProvider, ITransientDependency |
||||
|
{ |
||||
|
public const string Name = "JobExecutedFailedProvider"; |
||||
|
public readonly static IList<JobActionParamter> Paramters = new List<JobActionParamter> |
||||
|
{ |
||||
|
new JobActionParamter(PropertyTo, L("DisplayName:PropertyTo"), L("Description:PropertyTo"), true), |
||||
|
|
||||
|
new JobActionParamter(PropertySubject, L("DisplayName:PropertySubject"), L("Description:PropertySubject")), |
||||
|
new JobActionParamter(PropertyFrom, L("DisplayName:PropertyFrom"), L("Description:PropertyFrom")), |
||||
|
new JobActionParamter(PropertyBody, L("DisplayName:PropertyBody"), L("Description:PropertyBody")), |
||||
|
new JobActionParamter(PropertyTemplate, L("DisplayName:PropertyTemplate"), L("Description:PropertyTemplate")), |
||||
|
new JobActionParamter(PropertyContext, L("DisplayName:PropertyContext"), L("Description:PropertyContext")), |
||||
|
new JobActionParamter(PropertyCulture, L("DisplayName:PropertyCulture"), L("Description:PropertyCulture")), |
||||
|
}; |
||||
|
|
||||
|
public const string Prefix = "exception."; |
||||
|
public const string JobGroup = "ExceptionNotifier"; |
||||
|
|
||||
|
public const string PropertyFrom = Prefix + "from"; |
||||
|
/// <summary>
|
||||
|
/// 接收者
|
||||
|
/// </summary>
|
||||
|
public const string PropertyTo = Prefix + "to"; |
||||
|
/// <summary>
|
||||
|
/// 必须,邮件主体
|
||||
|
/// </summary>
|
||||
|
public const string PropertySubject = Prefix + "subject"; |
||||
|
/// <summary>
|
||||
|
/// 消息内容, 文本消息时必须
|
||||
|
/// </summary>
|
||||
|
public const string PropertyBody = Prefix + "body"; |
||||
|
/// <summary>
|
||||
|
/// 发送模板消息
|
||||
|
/// </summary>
|
||||
|
public const string PropertyTemplate = Prefix + "template"; |
||||
|
/// <summary>
|
||||
|
/// 可选, 模板消息中的上下文参数
|
||||
|
/// </summary>
|
||||
|
public const string PropertyContext = Prefix + "context"; |
||||
|
/// <summary>
|
||||
|
/// 可选, 模板消息中的区域性
|
||||
|
/// </summary>
|
||||
|
public const string PropertyCulture = Prefix + "culture"; |
||||
|
|
||||
|
public ILogger<JobExecutedFailedProvider> Logger { protected get; set; } |
||||
|
|
||||
|
protected IEmailSender EmailSender { get; } |
||||
|
protected ITemplateRenderer TemplateRenderer { get; } |
||||
|
public JobExecutedFailedProvider( |
||||
|
IEmailSender emailSender, |
||||
|
ITemplateRenderer templateRenderer) |
||||
|
{ |
||||
|
EmailSender = emailSender; |
||||
|
TemplateRenderer = templateRenderer; |
||||
|
|
||||
|
Logger = NullLogger<JobExecutedFailedProvider>.Instance; |
||||
|
} |
||||
|
|
||||
|
public override async Task NotifyErrorAsync([NotNull] JobActionExecuteContext context) |
||||
|
{ |
||||
|
if (context.Action.Paramters.TryGetValue(PropertyTo, out var exceptionTo) && |
||||
|
exceptionTo is string to) |
||||
|
{ |
||||
|
var template = context.Action.Paramters.GetOrDefault(PropertyTemplate)?.ToString() ?? ""; |
||||
|
var subject = context.Action.Paramters.GetOrDefault(PropertySubject)?.ToString() ?? "From job execute exception"; |
||||
|
var from = context.Action.Paramters.GetOrDefault(PropertyFrom)?.ToString() ?? ""; |
||||
|
var errorMessage = context.Event.EventData.Exception.GetBaseException().Message; |
||||
|
|
||||
|
if (template.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
// var message = eventData.Args.GetOrDefault(SendEmailJob.PropertyBody)?.ToString() ?? "";
|
||||
|
// await EmailSender.SendAsync(from, to, subject, message, false);
|
||||
|
// return;
|
||||
|
// 默认使用内置模板发送错误
|
||||
|
template = JobExceptionHandlingTemplates.JobExceptionNotifier; |
||||
|
} |
||||
|
|
||||
|
var footer = context.Action.Paramters.GetOrDefault("footer")?.ToString() ?? $"Copyright to LY Colin © {context.Event.EventData.RunTime.Year}"; |
||||
|
var model = new |
||||
|
{ |
||||
|
Title = subject, |
||||
|
Id = context.Event.EventData.Key, |
||||
|
Group = context.Action.Paramters.GetOrDefault(nameof(JobInfo.Group)) ?? context.Event.EventData.Group, |
||||
|
Name = context.Action.Paramters.GetOrDefault(nameof(JobInfo.Name)) ?? context.Event.EventData.Name, |
||||
|
Type = context.Action.Paramters.GetOrDefault(nameof(JobInfo.Type)) ?? context.Event.EventData.Type.Name, |
||||
|
Triggertime = context.Event.EventData.RunTime.ToString("yyyy-MM-dd HH:mm:ss"), |
||||
|
Message = errorMessage, |
||||
|
Tenantname = context.Action.Paramters.GetOrDefault(nameof(IMultiTenant.TenantId)), |
||||
|
Footer = footer, |
||||
|
}; |
||||
|
|
||||
|
var globalContext = new Dictionary<string, object>(); |
||||
|
if (context.Action.Paramters.TryGetValue(PropertyContext, out var ctx) && |
||||
|
ctx is string ctxStr && !ctxStr.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
globalContext = JsonConvert.DeserializeObject<Dictionary<string, object>>(ctxStr); |
||||
|
} |
||||
|
catch { } |
||||
|
} |
||||
|
globalContext.AddIfNotContains(context.Action.Paramters); |
||||
|
|
||||
|
var culture = context.Action.Paramters.GetOrDefault(PropertyCulture)?.ToString() ?? CultureInfo.CurrentCulture.Name; |
||||
|
|
||||
|
var content = await TemplateRenderer.RenderAsync( |
||||
|
templateName: template, |
||||
|
model: model, |
||||
|
cultureName: culture, |
||||
|
globalContext: globalContext); |
||||
|
|
||||
|
if (from.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
await EmailSender.SendAsync(to, subject, content, true); |
||||
|
return; |
||||
|
} |
||||
|
await EmailSender.SendAsync(from, to, subject, content, true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static ILocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<BackgroundTasksResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -1,132 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using LINGYUN.Abp.BackgroundTasks.ExceptionHandling.Templates; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Logging.Abstractions; |
|
||||
using Newtonsoft.Json; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Globalization; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Emailing; |
|
||||
using Volo.Abp.MultiTenancy; |
|
||||
using Volo.Abp.TextTemplating; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; |
|
||||
|
|
||||
public class JobFailedNotifierProvider : IJobFailedNotifierProvider, ITransientDependency |
|
||||
{ |
|
||||
public const string Prefix = "exception."; |
|
||||
public const string JobGroup = "ExceptionNotifier"; |
|
||||
|
|
||||
public const string PropertyFrom = "from"; |
|
||||
/// <summary>
|
|
||||
/// 接收者
|
|
||||
/// </summary>
|
|
||||
public const string PropertyTo = "to"; |
|
||||
/// <summary>
|
|
||||
/// 必须,邮件主体
|
|
||||
/// </summary>
|
|
||||
public const string PropertySubject = "subject"; |
|
||||
/// <summary>
|
|
||||
/// 消息内容, 文本消息时必须
|
|
||||
/// </summary>
|
|
||||
public const string PropertyBody = "body"; |
|
||||
/// <summary>
|
|
||||
/// 发送模板消息
|
|
||||
/// </summary>
|
|
||||
public const string PropertyTemplate = "template"; |
|
||||
/// <summary>
|
|
||||
/// 可选, 模板消息中的上下文参数
|
|
||||
/// </summary>
|
|
||||
public const string PropertyContext = "context"; |
|
||||
/// <summary>
|
|
||||
/// 可选, 模板消息中的区域性
|
|
||||
/// </summary>
|
|
||||
public const string PropertyCulture = "culture"; |
|
||||
|
|
||||
public ILogger<JobFailedNotifierProvider> Logger { protected get; set; } |
|
||||
|
|
||||
protected IEmailSender EmailSender { get; } |
|
||||
protected ITemplateRenderer TemplateRenderer { get; } |
|
||||
|
|
||||
public JobFailedNotifierProvider( |
|
||||
IEmailSender emailSender, |
|
||||
ITemplateRenderer templateRenderer) |
|
||||
{ |
|
||||
EmailSender = emailSender; |
|
||||
TemplateRenderer = templateRenderer; |
|
||||
|
|
||||
Logger = NullLogger<JobFailedNotifierProvider>.Instance; |
|
||||
} |
|
||||
|
|
||||
public virtual async Task NotifyErrorAsync([NotNull] JobEventContext context) |
|
||||
{ |
|
||||
var eventData = context.EventData; |
|
||||
// 异常所属分组不处理, 防止死循环
|
|
||||
if (string.Equals(eventData.Group, JobGroup)) |
|
||||
{ |
|
||||
Logger.LogWarning($"There is a problem executing the job, reason: {eventData.Exception.Message}"); |
|
||||
return; |
|
||||
} |
|
||||
var notifyKey = Prefix + PropertyTo; |
|
||||
if (eventData.Args.TryGetValue(notifyKey, out var exceptionTo) && |
|
||||
exceptionTo is string to) |
|
||||
{ |
|
||||
var template = eventData.Args.GetOrDefault(Prefix + PropertyTemplate)?.ToString() ?? ""; |
|
||||
var subject = eventData.Args.GetOrDefault(Prefix + PropertySubject)?.ToString() ?? "From job execute exception"; |
|
||||
var from = eventData.Args.GetOrDefault(Prefix + PropertyFrom)?.ToString() ?? ""; |
|
||||
var errorMessage = eventData.Exception.GetBaseException().Message; |
|
||||
|
|
||||
if (template.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
// var message = eventData.Args.GetOrDefault(Prefix + SendEmailJob.PropertyBody)?.ToString() ?? "";
|
|
||||
// await EmailSender.SendAsync(from, to, subject, message, false);
|
|
||||
// return;
|
|
||||
// 默认使用内置模板发送错误
|
|
||||
template = JobExceptionHandlingTemplates.JobExceptionNotifier; |
|
||||
} |
|
||||
|
|
||||
var footer = eventData.Args.GetOrDefault("footer")?.ToString() ?? $"Copyright to LY Colin © {eventData.RunTime.Year}"; |
|
||||
var model = new |
|
||||
{ |
|
||||
Title = subject, |
|
||||
Id = eventData.Key, |
|
||||
Group = eventData.Args.GetOrDefault(nameof(JobInfo.Group)) ?? eventData.Group, |
|
||||
Name = eventData.Args.GetOrDefault(nameof(JobInfo.Name)) ?? eventData.Name, |
|
||||
Type = eventData.Args.GetOrDefault(nameof(JobInfo.Type)) ?? eventData.Type.Name, |
|
||||
Triggertime = eventData.RunTime.ToString("yyyy-MM-dd HH:mm:ss"), |
|
||||
Message = errorMessage, |
|
||||
Tenantname = eventData.Args.GetOrDefault(nameof(IMultiTenant.TenantId)), |
|
||||
Footer = footer, |
|
||||
}; |
|
||||
|
|
||||
var globalContext = new Dictionary<string, object>(); |
|
||||
if (eventData.Args.TryGetValue(Prefix + PropertyContext, out var ctx) && |
|
||||
ctx is string ctxStr && !ctxStr.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
globalContext = JsonConvert.DeserializeObject<Dictionary<string, object>>(ctxStr); |
|
||||
} |
|
||||
catch { } |
|
||||
} |
|
||||
globalContext.AddIfNotContains(eventData.Args); |
|
||||
|
|
||||
var culture = eventData.Args.GetOrDefault(Prefix + PropertyCulture)?.ToString() ?? CultureInfo.CurrentCulture.Name; |
|
||||
|
|
||||
var content = await TemplateRenderer.RenderAsync( |
|
||||
templateName: template, |
|
||||
model: model, |
|
||||
cultureName: culture, |
|
||||
globalContext: globalContext); |
|
||||
|
|
||||
if (from.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
await EmailSender.SendAsync(to, subject, content, true); |
|
||||
return; |
|
||||
} |
|
||||
await EmailSender.SendAsync(from, to, subject, content, true); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,51 @@ |
|||||
|
namespace LINGYUN.Abp.BackgroundTasks.Jobs; |
||||
|
|
||||
|
public class DefaultJobDefinitionProvider : JobDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IJobDefinitionContext context) |
||||
|
{ |
||||
|
context.Add(CreateDefaultJobs()); |
||||
|
} |
||||
|
|
||||
|
private static JobDefinition[] CreateDefaultJobs() |
||||
|
{ |
||||
|
return new[] |
||||
|
{ |
||||
|
new JobDefinition( |
||||
|
DefaultJobNames.SleepJob, |
||||
|
typeof(SleepJob), |
||||
|
LocalizableStatic.Create("SleepJob"), |
||||
|
SleepJob.Paramters), |
||||
|
|
||||
|
new JobDefinition( |
||||
|
DefaultJobNames.ConsoleJob, |
||||
|
typeof(ConsoleJob), |
||||
|
LocalizableStatic.Create("ConsoleJob"), |
||||
|
ConsoleJob.Paramters), |
||||
|
|
||||
|
new JobDefinition( |
||||
|
DefaultJobNames.SendSmsJob, |
||||
|
typeof(SendSmsJob), |
||||
|
LocalizableStatic.Create("SendSmsJob"), |
||||
|
SendSmsJob.Paramters), |
||||
|
|
||||
|
new JobDefinition( |
||||
|
DefaultJobNames.SendEmailJob, |
||||
|
typeof(SendEmailJob), |
||||
|
LocalizableStatic.Create("SendEmailJob"), |
||||
|
SendEmailJob.Paramters), |
||||
|
|
||||
|
new JobDefinition( |
||||
|
DefaultJobNames.HttpRequestJob, |
||||
|
typeof(HttpRequestJob), |
||||
|
LocalizableStatic.Create("HttpRequestJob"), |
||||
|
HttpRequestJob.Paramters), |
||||
|
|
||||
|
new JobDefinition( |
||||
|
DefaultJobNames.ServiceInvocationJob, |
||||
|
typeof(ServiceInvocationJob), |
||||
|
LocalizableStatic.Create("ServiceInvocationJob"), |
||||
|
ServiceInvocationJob.Paramters), |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Jobs; |
||||
|
|
||||
|
internal static class LocalizableStatic |
||||
|
{ |
||||
|
public static ILocalizableString Create(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<BackgroundTasksResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"SleepJob": "Sleep Job", |
||||
|
"ConsoleJob": "Console Job", |
||||
|
"SendSmsJob": "Sms Job", |
||||
|
"SendEmailJob": "Email Job", |
||||
|
"HttpRequestJob": "Http Request Job", |
||||
|
"ServiceInvocationJob": "Service Invocation Job", |
||||
|
"Console:Message": "Message", |
||||
|
"Http:Url": "Url", |
||||
|
"Http:Method": "Method", |
||||
|
"Http:MethodDesc": "The request method can be GET, PUT, POST, PATCH, OPTIONS, or DELETE.", |
||||
|
"Http:Data": "Data", |
||||
|
"Http:ContentType": "Content Type", |
||||
|
"Http:Headers": "Headers", |
||||
|
"Http:Culture": "Culture", |
||||
|
"Http:Service": "Service", |
||||
|
"Http:Tenant": "Tenant", |
||||
|
"Http:Provider": "Provider", |
||||
|
"Http:ProviderDesc": "Interservice invocation program, optional scope: http、dapr.", |
||||
|
"Http:AppId": "App Id", |
||||
|
"Http:AppIdDesc": "App Id, This parameter takes effect only when the provider is dapr.", |
||||
|
"Sms:PhoneNumber": "Phone Number", |
||||
|
"Sms:Message": "Message", |
||||
|
"Sms:Properties": "Properties", |
||||
|
"Emailing:To": "To", |
||||
|
"Emailing:Subject": "Subject", |
||||
|
"Emailing:From": "From", |
||||
|
"Emailing:Body": "Body", |
||||
|
"Emailing:Template": "Template", |
||||
|
"Emailing:Model": "Model", |
||||
|
"Emailing:Context": "Context", |
||||
|
"Emailing:Culture": "Culture", |
||||
|
"Sleep:Delay": "Delay(ms)" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"SleepJob": "休眠作业", |
||||
|
"ConsoleJob": "控制台作业", |
||||
|
"SendSmsJob": "短信作业", |
||||
|
"SendEmailJob": "邮件作业", |
||||
|
"HttpRequestJob": "Http请求作业", |
||||
|
"ServiceInvocationJob": "远程调用作业", |
||||
|
"Console:Message": "消息", |
||||
|
"Http:Url": "请求路径", |
||||
|
"Http:Method": "请求方法", |
||||
|
"Http:MethodDesc": "请求方法,支持的格式为GET、PUT、POST、PATCH、OPTIONS、DELETE.", |
||||
|
"Http:Data": "传输数据", |
||||
|
"Http:ContentType": "内容类型", |
||||
|
"Http:Headers": "请求头", |
||||
|
"Http:Culture": "文化名称", |
||||
|
"Http:Service": "服务名称", |
||||
|
"Http:Tenant": "租户", |
||||
|
"Http:Provider": "调用方", |
||||
|
"Http:ProviderDesc": "服务间调用程序,可选范围: http、dapr.", |
||||
|
"Http:AppId": "应用标识", |
||||
|
"Http:AppIdDesc": "应用标识,仅限调用方为dapr时生效.", |
||||
|
"Sms:PhoneNumber": "手机号码", |
||||
|
"Sms:Message": "消息内容", |
||||
|
"Sms:Properties": "消息参数", |
||||
|
"Emailing:To": "收件地址", |
||||
|
"Emailing:Subject": "邮件标题", |
||||
|
"Emailing:From": "发送方名称", |
||||
|
"Emailing:Body": "发送内容", |
||||
|
"Emailing:Template": "邮件模板", |
||||
|
"Emailing:Model": "数据", |
||||
|
"Emailing:Context": "全局参数", |
||||
|
"Emailing:Culture": "文化名称", |
||||
|
"Sleep:Delay": "延迟(ms)" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
# LINGYUN.Abp.BackgroundTasks.Jobs |
||||
|
|
||||
|
后台任务(队列)常用作业模块 |
||||
|
|
||||
|
## 作业列表 |
||||
|
|
||||
|
* [ConsoleJob](./LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob): 控制台输出 |
||||
|
* [HttpRequestJob](./LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob): Http请求 |
||||
|
* [SendEmailJob](./LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob): 发送邮件 |
||||
|
* [SendSmsJob](./LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob): 发送短信 |
||||
|
* [ServiceInvocationJob](./LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob): 服务间调用(Http请求的扩展) |
||||
|
* [SleepJob](./LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob): 休眠,使作业延期执行 |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpBackgroundTasksJobsModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
} |
||||
|
``` |
||||
@ -1,46 +1,50 @@ |
|||||
using Microsoft.Extensions.Logging; |
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Logging.Abstractions; |
using Microsoft.Extensions.Logging.Abstractions; |
||||
using Microsoft.Extensions.Options; |
|
||||
using Quartz; |
using Quartz; |
||||
using System; |
using System; |
||||
using System.Collections.Generic; |
|
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.Timing; |
using Volo.Abp.Timing; |
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks.Quartz; |
namespace LINGYUN.Abp.BackgroundTasks.Quartz; |
||||
|
|
||||
public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonDependency |
public class QuartzJobCreator : IQuartzJobCreator, ISingletonDependency |
||||
{ |
{ |
||||
public ILogger<QuartzJobExecutorProvider> Logger { protected get; set; } |
public ILogger<QuartzJobCreator> Logger { protected get; set; } |
||||
|
|
||||
protected IClock Clock { get; } |
protected IClock Clock { get; } |
||||
protected AbpBackgroundTasksOptions Options { get; } |
|
||||
protected IQuartzKeyBuilder KeyBuilder { get; } |
protected IQuartzKeyBuilder KeyBuilder { get; } |
||||
public QuartzJobExecutorProvider( |
protected IJobDefinitionManager JobDefinitionManager { get; } |
||||
|
public QuartzJobCreator( |
||||
IClock clock, |
IClock clock, |
||||
IQuartzKeyBuilder keyBuilder, |
IQuartzKeyBuilder keyBuilder, |
||||
IOptions<AbpBackgroundTasksOptions> options) |
IJobDefinitionManager jobDefinitionManager) |
||||
{ |
{ |
||||
Clock = clock; |
Clock = clock; |
||||
Options = options.Value; |
|
||||
KeyBuilder = keyBuilder; |
KeyBuilder = keyBuilder; |
||||
|
JobDefinitionManager = jobDefinitionManager; |
||||
|
|
||||
Logger = NullLogger<QuartzJobExecutorProvider>.Instance; |
Logger = NullLogger<QuartzJobCreator>.Instance; |
||||
} |
} |
||||
|
|
||||
public IJobDetail CreateJob(JobInfo job) |
public IJobDetail CreateJob(JobInfo job) |
||||
{ |
{ |
||||
var jobType = Options.JobProviders.GetOrDefault(job.Type) ?? Type.GetType(job.Type); |
var jobDefinition = JobDefinitionManager.GetOrNull(job.Type); |
||||
|
var jobType = jobDefinition?.JobType ?? Type.GetType(job.Type); |
||||
if (jobType == null) |
if (jobType == null) |
||||
{ |
{ |
||||
Logger.LogWarning($"The task: {job.Group} - {job.Name}: {job.Type} is not registered and cannot create an instance of the performer type."); |
//Logger.LogWarning($"The task: {job.Group} - {job.Name}: {job.Type} is not registered and cannot create an instance of the performer type.");
|
||||
return null; |
//return null;
|
||||
} |
|
||||
|
|
||||
if (!typeof(IJob).IsAssignableFrom(jobType)) |
// 运行时搜寻本地作业
|
||||
|
jobType = typeof(QuartzJobSearchJobAdapter); |
||||
|
} |
||||
|
else |
||||
{ |
{ |
||||
var adapterType = typeof(QuartzJobSimpleAdapter<>); |
if (!typeof(IJob).IsAssignableFrom(jobType)) |
||||
jobType = adapterType.MakeGenericType(jobType); |
{ |
||||
|
var adapterType = typeof(QuartzJobSimpleAdapter<>); |
||||
|
jobType = adapterType.MakeGenericType(jobType); |
||||
|
} |
||||
} |
} |
||||
|
|
||||
// 改为 JobId作为名称
|
// 改为 JobId作为名称
|
||||
@ -0,0 +1,40 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Quartz; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundTasks.Quartz; |
||||
|
|
||||
|
public class QuartzJobSearchJobAdapter : IJob |
||||
|
{ |
||||
|
protected IServiceScopeFactory ServiceScopeFactory { get; } |
||||
|
protected IJobDefinitionManager JobDefinitionManager { get; } |
||||
|
|
||||
|
public QuartzJobSearchJobAdapter( |
||||
|
IServiceScopeFactory serviceScopeFactory, |
||||
|
IJobDefinitionManager jobDefinitionManager) |
||||
|
{ |
||||
|
ServiceScopeFactory = serviceScopeFactory; |
||||
|
JobDefinitionManager = jobDefinitionManager; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task Execute(IJobExecutionContext context) |
||||
|
{ |
||||
|
var jobType = context.MergedJobDataMap.GetString(nameof(JobInfo.Type)); |
||||
|
var jobDefinition = JobDefinitionManager.Get(jobType); |
||||
|
|
||||
|
using var scope = ServiceScopeFactory.CreateScope(); |
||||
|
var jobExecuter = scope.ServiceProvider.GetRequiredService<IJobRunnableExecuter>(); |
||||
|
|
||||
|
var jobContext = new JobRunnableContext( |
||||
|
jobDefinition.JobType, |
||||
|
scope.ServiceProvider, |
||||
|
context.MergedJobDataMap.ToImmutableDictionary(), |
||||
|
getCache: (key) => context.Get(key), |
||||
|
setCache: context.Put); |
||||
|
|
||||
|
await jobExecuter.ExecuteAsync(jobContext); |
||||
|
|
||||
|
context.Result = jobContext.Result; |
||||
|
} |
||||
|
} |
||||
@ -1,32 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundTasks.Internal; |
|
||||
|
|
||||
public class JobNotifierEvent : JobEventBase<JobNotifierEvent>, ITransientDependency |
|
||||
{ |
|
||||
protected async override Task OnJobAfterExecutedAsync(JobEventContext context) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
var notifier = context.ServiceProvider.GetRequiredService<IJobExecutedNotifier>(); |
|
||||
if (context.EventData.Exception != null) |
|
||||
{ |
|
||||
await notifier.NotifyErrorAsync(context); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
await notifier.NotifySuccessAsync(context); |
|
||||
} |
|
||||
|
|
||||
await notifier.NotifyComplateAsync(context); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
Logger.LogWarning($"An exception thow with job notify: {ex.Message}"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TaskManagement; |
||||
|
|
||||
|
public class BackgroundJobActionCreateDto : BackgroundJobActionCreateOrUpdateDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(BackgroundJobActionConsts), nameof(BackgroundJobActionConsts.MaxNameLength))] |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TaskManagement; |
||||
|
|
||||
|
public abstract class BackgroundJobActionCreateOrUpdateDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 是否启用
|
||||
|
/// </summary>
|
||||
|
public bool IsEnabled { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 参数
|
||||
|
/// </summary>
|
||||
|
public ExtraPropertyDictionary Paramters { get; set; } |
||||
|
|
||||
|
public BackgroundJobActionCreateOrUpdateDto() |
||||
|
{ |
||||
|
Paramters = new ExtraPropertyDictionary(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TaskManagement; |
||||
|
|
||||
|
public class BackgroundJobActionDefinitionDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 类型
|
||||
|
/// </summary>
|
||||
|
public JobActionType Type { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 描述
|
||||
|
/// </summary>
|
||||
|
public string Description { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 参数列表
|
||||
|
/// </summary>
|
||||
|
public IList<BackgroundJobActionParamterDto> Paramters { get; set; } |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TaskManagement; |
||||
|
|
||||
|
public class BackgroundJobActionDto : EntityDto<Guid> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 作业标识
|
||||
|
/// </summary>
|
||||
|
public string JobId { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 是否启用
|
||||
|
/// </summary>
|
||||
|
public bool IsEnabled { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 参数
|
||||
|
/// </summary>
|
||||
|
public ExtraPropertyDictionary Paramters { get; set; } |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TaskManagement; |
||||
|
|
||||
|
public class BackgroundJobActionGetDefinitionsInput |
||||
|
{ |
||||
|
public JobActionType? Type { get; set; } |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue