diff --git a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln
index 0ae43b0bb..1691e1e80 100644
--- a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln
+++ b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln
@@ -36,6 +36,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.TaskManagem
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BackgroundTasks.Abstractions", "modules\task-management\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj", "{4A049C32-55F2-4A5F-954A-C8A977C2D87F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BackgroundTasks.Jobs", "modules\task-management\LINGYUN.Abp.BackgroundTasks.Jobs\LINGYUN.Abp.BackgroundTasks.Jobs.csproj", "{4C59F590-AA6C-4C46-8060-DA65D8305980}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -82,6 +84,10 @@ Global
{4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4C59F590-AA6C-4C46-8060-DA65D8305980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4C59F590-AA6C-4C46-8060-DA65D8305980}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4C59F590-AA6C-4C46-8060-DA65D8305980}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4C59F590-AA6C-4C46-8060-DA65D8305980}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -97,6 +103,7 @@ Global
{7051C251-11D0-4971-B13E-F6929AE6DE89} = {385578CC-C0F1-4377-A7A2-682B8F416234}
{E8022994-A19F-4540-B9D1-7EF4AA85D18A} = {8DA8A2EE-0B26-487E-A6C4-518906E92B1B}
{4A049C32-55F2-4A5F-954A-C8A977C2D87F} = {C38EB7EF-BAE9-4129-862A-71C652B81775}
+ {4C59F590-AA6C-4C46-8060-DA65D8305980} = {C38EB7EF-BAE9-4129-862A-71C652B81775}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E1FD1F4C-D344-408B-97CF-B6F1F6D7D293}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs
index a4f65d170..02acfafd6 100644
--- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs
@@ -1,17 +1,15 @@
using System;
-using Volo.Abp;
namespace LINGYUN.Abp.BackgroundTasks;
-public class AbpBackgroundTaskConcurrentException : AbpException
+public class AbpBackgroundTaskConcurrentException : AbpJobExecutionException
{
- public Type JobType { get; }
///
/// Creates a new object.
///
/// Inner exception
public AbpBackgroundTaskConcurrentException(Type jobType)
- : this(
+ : base(
jobType,
$"This job {jobType.Name} cannot be performed because it has been locked by another performer",
null)
@@ -24,7 +22,7 @@ public class AbpBackgroundTaskConcurrentException : AbpException
/// Execute job type
/// Inner exception
public AbpBackgroundTaskConcurrentException(Type jobType, Exception innerException)
- : this(
+ : base(
jobType,
$"This job {jobType.Name} cannot be performed because it has been locked by another performer",
innerException)
@@ -38,8 +36,7 @@ public class AbpBackgroundTaskConcurrentException : AbpException
/// Exception message
/// Inner exception
public AbpBackgroundTaskConcurrentException(Type jobType, string message, Exception innerException)
- : base(message, innerException)
+ : base(jobType, message, innerException)
{
- JobType = jobType;
}
}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs
index 3e349c78c..5ad3b3a60 100644
--- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs
@@ -1,7 +1,33 @@
-using Volo.Abp.Modularity;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using Volo.Abp.Modularity;
+using Volo.Abp.Reflection;
namespace LINGYUN.Abp.BackgroundTasks;
public class AbpBackgroundTasksAbstractionsModule : AbpModule
{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ AutoAddJobMonitors(context.Services);
+ }
+
+ private static void AutoAddJobMonitors(IServiceCollection services)
+ {
+ var jobMonitors = new List();
+
+ services.OnRegistred(context =>
+ {
+ if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(JobEventBase<>)))
+ {
+ jobMonitors.Add(context.ImplementationType);
+ }
+ });
+
+ services.Configure(options =>
+ {
+ options.JobMonitors.AddIfNotContains(jobMonitors);
+ });
+ }
}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs
similarity index 78%
rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs
rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs
index 69457817b..788102a83 100644
--- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using Volo.Abp.Collections;
namespace LINGYUN.Abp.BackgroundTasks;
@@ -13,6 +14,13 @@ public class AbpBackgroundTasksOptions
///
public ITypeList JobMonitors { get; }
///
+ /// 作业提供者列表
+ ///
+ ///
+ /// 用户实现的作业可以添加在集合中
+ ///
+ public IDictionary JobProviders { get; }
+ ///
/// 任务过期时间
/// 默认: 15 days
///
@@ -65,5 +73,12 @@ public class AbpBackgroundTasksOptions
JobCleanCronExpression = "0 0/10 * * * ? *";
JobMonitors = new TypeList();
+ JobProviders = new Dictionary();
+ }
+
+ public void AddProvider(string name)
+ where TJobRunnable : IJobRunnable
+ {
+ JobProviders[name] = typeof(TJobRunnable);
}
}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs
new file mode 100644
index 000000000..3de421a9b
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs
@@ -0,0 +1,44 @@
+using System;
+using Volo.Abp;
+
+namespace LINGYUN.Abp.BackgroundTasks;
+
+public class AbpJobExecutionException : AbpException
+{
+ public Type JobType { get; }
+ ///
+ /// Creates a new object.
+ ///
+ /// Inner exception
+ public AbpJobExecutionException(Type jobType)
+ : this(
+ jobType,
+ $"Unable to execute job {jobType.Name}.",
+ null)
+ {
+ }
+
+ ///
+ /// Creates a new object.
+ ///
+ /// Execute job type
+ /// Inner exception
+ public AbpJobExecutionException(Type jobType, Exception innerException)
+ : this(
+ jobType,
+ $"Unable to execute job {jobType.Name} because it: {innerException.Message}",
+ innerException)
+ {
+ }
+ ///
+ /// Creates a new object.
+ ///
+ /// Execute job type
+ /// Exception message
+ /// Inner exception
+ public AbpJobExecutionException(Type jobType, string message, Exception innerException)
+ : base(message, innerException)
+ {
+ JobType = jobType;
+ }
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs
similarity index 100%
rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs
rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs
similarity index 100%
rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs
rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs
index 75af712ad..96f366af8 100644
--- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs
@@ -8,6 +8,7 @@ public class JobRunnableContext
public Type JobType { get; }
public IServiceProvider ServiceProvider { get; }
public IReadOnlyDictionary JobData { get; }
+ public object Result { get; private set; }
public JobRunnableContext(
Type jobType,
IServiceProvider serviceProvider,
@@ -17,4 +18,9 @@ public class JobRunnableContext
ServiceProvider = serviceProvider;
JobData = jobData;
}
+
+ public void SetResult(object result)
+ {
+ Result = result;
+ }
}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs
new file mode 100644
index 000000000..f2fe7510b
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs
@@ -0,0 +1,85 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+
+namespace LINGYUN.Abp.BackgroundTasks;
+
+public static class JobRunnableContextExtensions
+{
+ public static T GetService(this JobRunnableContext context)
+ {
+ return context.ServiceProvider.GetService();
+ }
+
+ public static object GetService(this JobRunnableContext context, Type serviceType)
+ {
+ return context.ServiceProvider.GetService(serviceType);
+ }
+
+ public static T GetRequiredService(this JobRunnableContext context)
+ {
+ return context.ServiceProvider.GetRequiredService();
+ }
+
+ public static object GetRequiredService(this JobRunnableContext context, Type serviceType)
+ {
+ return context.ServiceProvider.GetRequiredService(serviceType);
+ }
+
+ public static string GetString(this JobRunnableContext context, string key)
+ {
+ return context.GetJobData(key).ToString();
+ }
+
+ public static bool TryGetString(this JobRunnableContext context, string key, out string value)
+ {
+ if (context.TryGetJobData(key, out var data) && data != null)
+ {
+ value = data.ToString();
+ return true;
+ }
+ value = default;
+ return false;
+ }
+
+ public static T GetJobData(this JobRunnableContext context, string key) where T : struct
+ {
+ var value = context.GetJobData(key);
+
+ return value.To();
+ }
+
+ public static bool TryGetJobData(this JobRunnableContext context, string key, out T value) where T : struct
+ {
+ if (context.TryGetJobData(key, out var data) && data != null)
+ {
+ try
+ {
+ value = data.To();
+ return true;
+ }
+ catch
+ {
+ }
+ }
+ value = default;
+ return false;
+ }
+
+ public static object GetJobData(this JobRunnableContext context, string key)
+ {
+ if (context.TryGetJobData(key, out var value) && value != null)
+ {
+ return value;
+ }
+ throw new ArgumentException(key + " not specified.");
+ }
+
+ public static bool TryGetJobData(this JobRunnableContext context, string key, out object value)
+ {
+ if (context.JobData.TryGetValue(key, out value))
+ {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml
new file mode 100644
index 000000000..ac6b5b292
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj
new file mode 100644
index 000000000..f7afc48e8
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs
new file mode 100644
index 000000000..b0ccd972a
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs
@@ -0,0 +1,25 @@
+using Volo.Abp.Emailing;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Modularity;
+using Volo.Abp.Sms;
+
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
+
+[DependsOn(typeof(AbpEmailingModule))]
+[DependsOn(typeof(AbpSmsModule))]
+[DependsOn(typeof(AbpHttpClientModule))]
+public class AbpBackgroundTasksJobsModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.AddProvider(DefaultJobNames.ConsoleJob);
+ options.AddProvider(DefaultJobNames.SendEmailJob);
+ options.AddProvider(DefaultJobNames.SendSmsJob);
+ options.AddProvider(DefaultJobNames.SleepJob);
+ options.AddProvider(DefaultJobNames.ServiceInvocationJob);
+ options.AddProvider(DefaultJobNames.HttpRequestJob);
+ });
+ }
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs
similarity index 81%
rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs
rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs
index a108d492c..52377cb0e 100644
--- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs
@@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
-namespace LINGYUN.Abp.BackgroundTasks.Primitives;
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
public class ConsoleJob : IJobRunnable
{
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs
new file mode 100644
index 000000000..b8527766d
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs
@@ -0,0 +1,29 @@
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
+
+public static class DefaultJobNames
+{
+ ///
+ /// 发送邮件
+ ///
+ public const string SendEmailJob = "SendEmail";
+ ///
+ /// 发送短信
+ ///
+ public const string SendSmsJob = "SendSms";
+ ///
+ /// 控制台输出
+ ///
+ public const string ConsoleJob = "Console";
+ ///
+ /// 休眠
+ ///
+ public const string SleepJob = "Sleep";
+ ///
+ /// 服务间调用
+ ///
+ public const string ServiceInvocationJob = "ServiceInvocation";
+ ///
+ /// Http请求
+ ///
+ public const string HttpRequestJob = "HttpRequest";
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs
new file mode 100644
index 000000000..c3849914a
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Http;
+using Volo.Abp.Json;
+
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
+
+public class HttpRequestJob : IJobRunnable
+{
+ public const string PropertyUrl = "url";
+ public const string PropertyMethod = "method";
+ public const string PropertyData = "data";
+ public const string PropertyContentType = "contentType";
+ public const string PropertyHeaders = "headers";
+ public const string PropertyToken = "token";
+
+ public virtual async Task ExecuteAsync(JobRunnableContext context)
+ {
+ var clientFactory = context.GetRequiredService();
+
+ var client = clientFactory.CreateClient();
+ var requestMessage = BuildRequestMessage(context);
+
+ var response = await client.SendAsync(
+ requestMessage,
+ HttpCompletionOption.ResponseHeadersRead);
+
+ var stringContent = await response.Content.ReadAsStringAsync();
+
+ if (!response.IsSuccessStatusCode && stringContent.IsNullOrWhiteSpace())
+ {
+ context.SetResult($"HttpStatusCode: {(int)response.StatusCode}, Reason: {response.ReasonPhrase}");
+ return;
+ }
+ context.SetResult(stringContent);
+ }
+
+ protected virtual HttpRequestMessage BuildRequestMessage(JobRunnableContext context)
+ {
+ var url = context.GetString(PropertyUrl);
+ var method = context.GetString(PropertyMethod);
+ context.TryGetJobData(PropertyData, out var data);
+ context.TryGetJobData(PropertyContentType, out var contentType);
+
+ var jsonSerializer = context.GetRequiredService();
+
+ var httpRequestMesasge = new HttpRequestMessage(new HttpMethod(method), url);
+ if (data != null)
+ {
+ // TODO: 需要支持表单类型
+
+ // application/json 支持
+ httpRequestMesasge.Content = new StringContent(
+ jsonSerializer.Serialize(data),
+ Encoding.UTF8,
+ contentType?.ToString() ?? MimeTypes.Application.Json);
+ }
+ if (context.TryGetJobData(PropertyHeaders, out var headers) &&
+ headers is IDictionary headersDic)
+ {
+ foreach (var header in headersDic)
+ {
+ httpRequestMesasge.Headers.Add(header.Key, header.Value);
+ }
+ }
+ // TODO: 和 headers 一起?
+ if (context.TryGetString(PropertyToken, out var accessToken))
+ {
+ httpRequestMesasge.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
+ }
+
+ return httpRequestMesasge;
+ }
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs
new file mode 100644
index 000000000..765c35db6
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs
@@ -0,0 +1,26 @@
+using System.Threading.Tasks;
+using Volo.Abp.Emailing;
+
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
+
+public class SendEmailJob : IJobRunnable
+{
+ public const string PropertyFrom = "from";
+ public const string PropertyTo = "to";
+ public const string PropertySubject = "subject";
+ public const string PropertyBody = "body";
+ public const string PropertyIsBodyHtml = "isBodyHtml";
+
+ public virtual async Task ExecuteAsync(JobRunnableContext context)
+ {
+ context.TryGetString(PropertyFrom, out var from);
+ var to = context.GetString(PropertyTo);
+ var subject = context.GetString(PropertySubject);
+ var body = context.GetString(PropertyBody);
+ context.TryGetJobData(PropertyIsBodyHtml, out var isBodyHtml);
+
+ var emailSender = context.GetRequiredService();
+
+ await emailSender.QueueAsync(from, to, subject, body, isBodyHtml);
+ }
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs
new file mode 100644
index 000000000..7d3cc2463
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Sms;
+
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
+
+public class SendSmsJob : IJobRunnable
+{
+ public const string PropertyPhoneNumber = "phoneNumber";
+ public const string PropertyMessage = "message";
+ public const string PropertyProperties = "properties";
+
+ public virtual async Task ExecuteAsync(JobRunnableContext context)
+ {
+ var phoneNumber = context.GetString(PropertyPhoneNumber);
+ var message = context.GetString(PropertyMessage);
+
+ var smsMessage = new SmsMessage(phoneNumber, message);
+ if (context.TryGetJobData(PropertyProperties, out var data) &&
+ data is IDictionary properties)
+ {
+ smsMessage.Properties.AddIfNotContains(properties);
+ }
+
+ var smsSender = context.GetRequiredService();
+
+ await smsSender.SendAsync(smsMessage);
+ }
+}
diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs
new file mode 100644
index 000000000..19ba601c4
--- /dev/null
+++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs
@@ -0,0 +1,117 @@
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Http.Client.ClientProxying;
+using Volo.Abp.Http.Client.DynamicProxying;
+using Volo.Abp.Http.Client.Proxying;
+using Volo.Abp.Http.Modeling;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.BackgroundTasks.Jobs;
+
+public class ServiceInvocationJob : IJobRunnable
+{
+ public const string PropertyService = "service";
+ public const string PropertyMethod = "method";
+ public const string PropertyCulture = "culture";
+
+ public virtual async Task ExecuteAsync(JobRunnableContext context)
+ {
+ // 获取参数列表
+ var type = context.GetString(PropertyService);
+ var method = context.GetString(PropertyMethod);
+ var serviceType = Type.GetType(type, true);
+ var serviceMethod = serviceType.GetMethod(method);
+ context.TryGetString(PropertyCulture, out var culture);
+
+ using (CultureHelper.Use(culture ?? CultureInfo.CurrentCulture.Name))
+ {
+ // 反射所必须的参数
+ var callRequestMethod = nameof(DynamicHttpProxyInterceptorClientProxy