Browse Source

enhance(tasks): add support task management

pull/457/head
cKey 4 years ago
parent
commit
b708afbd88
  1. 7
      aspnet-core/.editorconfig
  2. 2
      aspnet-core/LINGYUN.MicroService.All.sln
  3. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xml
  4. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xsd
  5. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN.Abp.BackgroundTasks.Abstractions.csproj
  6. 7
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs
  7. 11
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnable.cs
  8. 12
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs
  9. 17
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventContext.cs
  10. 90
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs
  11. 104
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs
  12. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobPriority.cs
  13. 20
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs
  14. 25
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobStatus.cs
  15. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobType.cs
  16. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xml
  17. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xsd
  18. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN.Abp.BackgroundTasks.Quartz.csproj
  19. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs
  20. 12
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs
  21. 102
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs
  22. 144
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs
  23. 164
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs
  24. 33
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs
  25. 27
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/Quartz/IJobExecutionContextExtensions.cs
  26. 16
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md
  27. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xml
  28. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xsd
  29. 22
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj
  30. 49
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs
  31. 69
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs
  32. 39
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs
  33. 82
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs
  34. 22
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs
  35. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEventProvider.cs
  36. 72
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobScheduler.cs
  37. 27
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobStore.cs
  38. 18
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs
  39. 26
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundKeepAliveJob.cs
  40. 31
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundPollingJob.cs
  41. 108
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs
  42. 87
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/InMemoryJobStore.cs
  43. 41
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobEventProvider.cs
  44. 60
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs
  45. 26
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs
  46. 58
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs
  47. 55
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs
  48. 13
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs
  49. 14
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs
  50. 116
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md
  51. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xml
  52. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xsd
  53. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj
  54. 17
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateDto.cs
  55. 61
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateOrUpdateDto.cs
  56. 99
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoDto.cs
  57. 65
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoGetListInput.cs
  58. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoUpdateDto.cs
  59. 14
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogDto.cs
  60. 36
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs
  61. 20
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs
  62. 11
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs
  63. 17
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs
  64. 10
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementApplicationContractsModule.cs
  65. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xml
  66. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xsd
  67. 20
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN.Abp.TaskManagement.Application.csproj
  68. 161
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs
  69. 11
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs
  70. 22
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationModule.cs
  71. 13
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationService.cs
  72. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xml
  73. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xsd
  74. 27
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN.Abp.TaskManagement.Domain.Shared.csproj
  75. 10
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs
  76. 7
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobLogConsts.cs
  77. 6
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json
  78. 6
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json
  79. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/TaskManagementResource.cs
  80. 34
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementDomainSharedModule.cs
  81. 9
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementErrorCodes.cs
  82. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xml
  83. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xsd
  84. 21
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN.Abp.TaskManagement.Domain.csproj
  85. 162
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs
  86. 67
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoFilter.cs
  87. 39
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs
  88. 35
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLogFilter.cs
  89. 86
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs
  90. 135
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs
  91. 66
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs
  92. 39
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobLogRepository.cs
  93. 11
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDbProperties.cs
  94. 12
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainMapperProfile.cs
  95. 24
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainModule.cs
  96. 3
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xml
  97. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xsd
  98. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj
  99. 124
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs
  100. 63
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs

7
aspnet-core/.editorconfig

@ -1,4 +1,5 @@
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\Projects\MicroService\CRM\Vue\abp-next-admin\aspnet-core codebase based on best match to current usage at 2021-12-27
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\Projects\MicroService\CRM\Vue\abp-next-admin\aspnet-core codebase based on best match to current usage at 2022-01-07
# There already existed an .editorconfig file in this directory. Copy rules from this .editorconfig.inferred file to the existing .editorconfig file as desired to have them take effect at this location.
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]
@ -26,8 +27,8 @@ csharp_new_line_before_else = true
csharp_new_line_before_members_in_anonymous_types = true
#require members of object initializers to be on the same line
csharp_new_line_before_members_in_object_initializers = false
#require braces to be on a new line for anonymous_types, control_blocks, types, lambdas, object_collection_array_initializers, methods, and anonymous_methods (also known as "Allman" style)
csharp_new_line_before_open_brace = anonymous_types, control_blocks, types, lambdas, object_collection_array_initializers, methods, anonymous_methods
#require braces to be on a new line for methods, control_blocks, types, lambdas, object_collection_array_initializers, anonymous_methods, and anonymous_types (also known as "Allman" style)
csharp_new_line_before_open_brace = methods, control_blocks, types, lambdas, object_collection_array_initializers, anonymous_methods, anonymous_types
#Formatting - organize using options

2
aspnet-core/LINGYUN.MicroService.All.sln

@ -378,7 +378,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Sms.Tencent", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.BlobStoring.Tencent", "modules\cloud-tencent\LINGYUN.Abp.BlobStoring.Tencent\LINGYUN.Abp.BlobStoring.Tencent.csproj", "{A4B972EC-9F0B-4405-9965-766FABC9B07E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.OssManagement.Tencent", "modules\oss-management\LINGYUN.Abp.OssManagement.Tencent\LINGYUN.Abp.OssManagement.Tencent.csproj", "{31E60E23-FD98-4D5E-A137-2B3F2968BA09}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.OssManagement.Tencent", "modules\oss-management\LINGYUN.Abp.OssManagement.Tencent\LINGYUN.Abp.OssManagement.Tencent.csproj", "{31E60E23-FD98-4D5E-A137-2B3F2968BA09}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

3
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xsd

@ -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>

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN.Abp.BackgroundTasks.Abstractions.csproj

@ -0,0 +1,15 @@
<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.Core" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
</Project>

7
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs

@ -0,0 +1,7 @@
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.BackgroundTasks;
public class AbpBackgroundTasksAbstractionsModule : AbpModule
{
}

11
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnable.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 任务类需要实现此接口
/// </summary>
public interface IJobRunnable
{
Task ExecuteAsync(JobRunnableContext context);
}

12
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 定义任务执行者接口
/// 可以通过它实现一些限制(例如分布式锁)
/// </summary>
public interface IJobRunnableExecuter
{
Task ExecuteAsync(JobRunnableContext context);
}

17
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventContext.cs

@ -0,0 +1,17 @@
using System;
namespace LINGYUN.Abp.BackgroundTasks;
public class JobEventContext
{
public IServiceProvider ServiceProvider { get; }
public JobEventData EventData { get; }
public JobEventContext(
IServiceProvider serviceProvider,
JobEventData jobEventData)
{
ServiceProvider = serviceProvider;
EventData = jobEventData;
}
}

90
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs

@ -0,0 +1,90 @@
using System;
namespace LINGYUN.Abp.BackgroundTasks;
public class JobEventData
{
/// <summary>
/// 任务类别
/// </summary>
public Type Type { get; }
/// <summary>
/// 任务组别
/// </summary>
public string Group { get; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; }
/// <summary>
/// 任务标识
/// </summary>
public Guid Key { get; }
/// <summary>
/// 任务状态
/// </summary>
public JobStatus Status { get; set; }
/// <summary>
/// 执行者租户
/// </summary>
public Guid? TenantId { get; set; }
/// <summary>
/// 错误明细
/// </summary>
public Exception Exception { get; }
/// <summary>
/// 任务描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 返回参数
/// </summary>
public string Result { get; set; }
/// <summary>
/// 触发次数
/// </summary>
public int Triggered { get; set; }
/// <summary>
/// 最大可执行次数
/// </summary>
public int RepeatCount { get; set; }
/// <summary>
/// 失败重试上限
/// 默认:50
/// </summary>
public int TryCount { get; set; }
/// <summary>
/// 最大执行次数
/// 默认:0, 无限制
/// </summary>
public int MaxCount { get; set; }
/// <summary>
/// 运行时间
/// </summary>
public DateTime RunTime { get; set; }
/// <summary>
/// 上次运行时间
/// </summary>
public DateTime? LastRunTime { get; set; }
/// <summary>
/// 下次运行时间
/// </summary>
public DateTime? NextRunTime { get; set; }
/// <summary>
/// 连续失败且不会再次执行
/// </summary>
public bool IsAbandoned { get; set; }
public JobEventData(
Guid key,
Type type,
string group,
string name,
Exception exception = null)
{
Key = key;
Type = type;
Group = group;
Name = name;
Exception = exception;
}
}

104
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.BackgroundTasks;
public class JobInfo
{
/// <summary>
/// 任务标识
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 返回参数
/// </summary>
public string Result { get; set; }
/// <summary>
/// 任务参数
/// </summary>
public IDictionary<string, object> Args { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public JobStatus Status { get; set; } = JobStatus.None;
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreationTime { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 上次运行时间
/// </summary>
public DateTime? LastRunTime { get; set; }
/// <summary>
/// 下一次执行时间
/// </summary>
public DateTime? NextRunTime { get; set; }
/// <summary>
/// 任务类别
/// </summary>
public JobType JobType { get; set; } = JobType.Once;
/// <summary>
/// Cron表达式,如果是持续任务需要指定
/// </summary>
public string Cron { get; set; }
/// <summary>
/// 触发次数
/// </summary>
public int TriggerCount { get; set; }
/// <summary>
/// 失败重试次数
/// </summary>
public int TryCount { get; set; }
/// <summary>
/// 失败重试上限
/// 默认:50
/// </summary>
public int MaxTryCount { get; set; } = 50;
/// <summary>
/// 最大执行次数
/// 默认:0, 无限制
/// </summary>
public int MaxCount { get; set; }
/// <summary>
/// 连续失败且不会再次执行
/// </summary>
public bool IsAbandoned { get; set; }
/// <summary>
/// 间隔时间,单位秒,与Cron表达式冲突
/// 默认: 300
/// </summary>
public int Interval { get; set; } = 300;
/// <summary>
/// 任务优先级
/// </summary>
public JobPriority Priority { get; set; } = JobPriority.Normal;
/// <summary>
/// 任务独占超时时长(秒)
/// 0或更小不生效
/// </summary>
public int LockTimeOut { get; set; }
}

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobPriority.cs

@ -0,0 +1,15 @@
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 任务优先级
/// </summary>
/// <remarks>
/// 与框架保持一致
/// </remarks>
public enum JobPriority
{
Low = 5,
BelowNormal = 10,
Normal = 0xF,
AboveNormal = 20,
High = 25
}

20
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.BackgroundTasks;
public class JobRunnableContext
{
public Type JobType { get; }
public IServiceProvider ServiceProvider { get; }
public IReadOnlyDictionary<string, object> JobData { get; }
public JobRunnableContext(
Type jobType,
IServiceProvider serviceProvider,
IReadOnlyDictionary<string, object> jobData)
{
JobType = jobType;
ServiceProvider = serviceProvider;
JobData = jobData;
}
}

25
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobStatus.cs

@ -0,0 +1,25 @@
namespace LINGYUN.Abp.BackgroundTasks;
public enum JobStatus
{
/// <summary>
/// 未知
/// </summary>
None = -1,
/// <summary>
/// 已完成
/// </summary>
Completed = 0,
/// <summary>
/// 运行中
/// </summary>
Running = 10,
/// <summary>
/// 已暂停
/// </summary>
Paused = 20,
/// <summary>
/// 已停止
/// </summary>
Stopped = 30
}

19
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobType.cs

@ -0,0 +1,19 @@
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 任务类别
/// </summary>
public enum JobType
{
/// <summary>
/// 一次性
/// </summary>
Once,
/// <summary>
/// 周期性
/// </summary>
Period,
/// <summary>
/// 持续性
/// </summary>
Persistent
}

3
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xsd

@ -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>

19
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN.Abp.BackgroundTasks.Quartz.csproj

@ -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.Quartz" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks\LINGYUN.Abp.BackgroundTasks.csproj" />
</ItemGroup>
</Project>

19
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Quartz;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
[DependsOn(typeof(AbpBackgroundTasksModule))]
[DependsOn(typeof(AbpQuartzModule))]
public class AbpBackgroundTasksQuartzModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var _scheduler = context.ServiceProvider.GetRequiredService<IScheduler>();
_scheduler.ListenerManager.AddJobListener(context.ServiceProvider.GetRequiredService<QuartzJobListener>());
}
}

12
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs

@ -0,0 +1,12 @@
using Quartz;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
public interface IQuartzJobExecutorProvider
{
#nullable enable
IJobDetail? CreateJob(JobInfo job);
ITrigger CreateTrigger(JobInfo job);
#nullable disable
}

102
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs

@ -0,0 +1,102 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Quartz;
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonDependency
{
public ILogger<QuartzJobExecutorProvider> Logger { protected get; set; }
protected IClock Clock { get; }
protected AbpBackgroundTasksOptions Options { get; }
public QuartzJobExecutorProvider(
IClock clock,
IOptions<AbpBackgroundTasksOptions> options)
{
Clock = clock;
Options = options.Value;
Logger = NullLogger<QuartzJobExecutorProvider>.Instance;
}
public IJobDetail CreateJob(JobInfo job)
{
var jobType = Type.GetType(job.Type);
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.");
return null;
}
var adapterType = typeof(QuartzJobSimpleAdapter<>);
if (!typeof(IJob).IsAssignableFrom(jobType))
{
jobType = adapterType.MakeGenericType(jobType);
}
var jobBuilder = JobBuilder.Create(jobType)
.WithIdentity(job.Name, job.Group)
.WithDescription(job.Description);
jobBuilder.UsingJobData(nameof(JobInfo.Id), job.Id);
jobBuilder.UsingJobData(nameof(JobInfo.LockTimeOut), job.LockTimeOut);
jobBuilder.UsingJobData(new JobDataMap(job.Args));
return jobBuilder.Build();
}
public ITrigger CreateTrigger(JobInfo job)
{
var triggerBuilder = TriggerBuilder.Create();
switch (job.JobType)
{
case JobType.Period:
if (!CronExpression.IsValidExpression(job.Cron))
{
Logger.LogWarning($"The task: {job.Group} - {job.Name} periodic task Cron expression was invalid and the task trigger could not be created.");
return null;
}
triggerBuilder
.WithIdentity(job.Name, job.Group)
.WithDescription(job.Description)
.EndAt(job.EndTime)
.ForJob(job.Name, job.Group)
.WithPriority((int)job.Priority)
.WithCronSchedule(job.Cron);
if (job.BeginTime > Clock.Now)
{
triggerBuilder = triggerBuilder.StartAt(job.BeginTime);
}
break;
case JobType.Once:
case JobType.Persistent:
default:
// Quartz 需要减一位
var maxCount = job.MaxCount <= 0 ? -1 : job.MaxCount - 1;
if (job.JobType == JobType.Once)
{
maxCount = 0;
}
triggerBuilder
.WithIdentity(job.Name, job.Group)
.WithDescription(job.Description)
.EndAt(job.EndTime)
.ForJob(job.Name, job.Group)
.WithPriority((int)job.Priority)
.WithSimpleSchedule(x =>
x.WithIntervalInSeconds(job.Interval)
.WithRepeatCount(maxCount));
break;
}
return triggerBuilder.Build();
}
}

144
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs

@ -0,0 +1,144 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Quartz;
using Quartz.Listener;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
public class QuartzJobListener : JobListenerSupport, ISingletonDependency
{
public ILogger<QuartzJobListener> Logger { protected get; set; }
public override string Name => "QuartzJobListener";
protected IJobEventProvider EventProvider { get; }
protected IServiceProvider ServiceProvider { get; }
public QuartzJobListener(
IServiceProvider serviceProvider,
IJobEventProvider eventProvider)
{
ServiceProvider = serviceProvider;
EventProvider = eventProvider;
Logger = NullLogger<QuartzJobListener>.Instance;
}
public override Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
var jobType = context.JobDetail.JobType;
if (jobType.IsGenericType)
{
jobType = jobType.GetGenericTypeDefinition();
}
Logger.LogInformation($"The task {jobType.Name} could not be performed...");
return Task.FromResult(-1);
}
public override async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
var jobId = context.GetString(nameof(JobInfo.Id));
if (Guid.TryParse(jobId, out var jobUUId))
{
try
{
using var scope = ServiceProvider.CreateScope();
var jobEventData = new JobEventData(
jobUUId,
context.JobDetail.JobType,
context.JobDetail.Key.Group,
context.JobDetail.Key.Name)
{
Result = context.Result?.ToString()
};
var jobEventList = EventProvider.GetAll();
var eventContext = new JobEventContext(
scope.ServiceProvider,
jobEventData);
var index = 0;
var taskList = new Task[jobEventList.Count];
foreach (var jobEvent in jobEventList)
{
taskList[index] = jobEvent.OnJobBeforeExecuted(eventContext);
index++;
}
await Task.WhenAll(taskList);
}
catch (Exception ex)
{
Logger.LogError($"The event before the task execution is abnormal:{ex}");
}
}
}
[UnitOfWork]
public override async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
{
try
{
using var scope = ServiceProvider.CreateScope();
var jobId = context.GetString(nameof(JobInfo.Id));
if (Guid.TryParse(jobId, out var jobUUId))
{
var jobEventData = new JobEventData(
jobUUId,
context.JobDetail.JobType,
context.JobDetail.Key.Group,
context.JobDetail.Key.Name,
jobException)
{
Status = JobStatus.Running
};
if (context.Trigger is ISimpleTrigger simpleTrigger)
{
jobEventData.Triggered = simpleTrigger.TimesTriggered;
jobEventData.RepeatCount = simpleTrigger.RepeatCount;
}
jobEventData.Description = context.JobDetail.Description;
jobEventData.RunTime = context.FireTimeUtc.LocalDateTime;
jobEventData.LastRunTime = context.PreviousFireTimeUtc?.LocalDateTime;
jobEventData.NextRunTime = context.NextFireTimeUtc?.LocalDateTime;
if (context.Result != null)
{
jobEventData.Result = context.Result.ToString();
}
var tenantIdString = context.GetString(nameof(IMultiTenant.TenantId));
if (Guid.TryParse(tenantIdString, out var tenantId))
{
jobEventData.TenantId = tenantId;
}
var jobEventList = EventProvider.GetAll();
var eventContext = new JobEventContext(
scope.ServiceProvider,
jobEventData);
var index = 0;
var taskList = new Task[jobEventList.Count];
foreach (var jobEvent in jobEventList)
{
taskList[index] = jobEvent.OnJobAfterExecuted(eventContext);
index++;
}
await Task.WhenAll(taskList);
}
}
catch (Exception ex)
{
Logger.LogError($"The event is abnormal after the task is executed:{ex}");
}
}
}

164
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs

@ -0,0 +1,164 @@
using Quartz;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
[Dependency(ReplaceServices = true)]
public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
{
protected IJobStore JobStore { get; }
protected IScheduler Scheduler { get; }
protected IQuartzJobExecutorProvider QuartzJobExecutor { get; }
public QuartzJobScheduler(
IJobStore jobStore,
IScheduler scheduler,
IQuartzJobExecutorProvider quartzJobExecutor)
{
JobStore = jobStore;
Scheduler = scheduler;
QuartzJobExecutor = quartzJobExecutor;
}
public virtual async Task<bool> ExistsAsync(JobInfo job)
{
var jobKey = new JobKey(job.Name, job.Group);
return await Scheduler.CheckExists(jobKey);
}
public virtual async Task PauseAsync(JobInfo job)
{
var jobKey = new JobKey(job.Name, job.Group);
if (await Scheduler.CheckExists(jobKey))
{
var triggers = await Scheduler.GetTriggersOfJob(jobKey);
foreach (var trigger in triggers)
{
await Scheduler.PauseTrigger(trigger.Key);
}
}
}
public virtual async Task<bool> QueueAsync(JobInfo job)
{
var jobKey = new JobKey(job.Name, job.Group);
if (await Scheduler.CheckExists(jobKey))
{
return false;
}
var jobDetail = QuartzJobExecutor.CreateJob(job);
if (jobDetail == null)
{
return false;
}
var jobTrigger = QuartzJobExecutor.CreateTrigger(job);
if (jobTrigger == null)
{
return false;
}
await Scheduler.ScheduleJob(jobDetail, jobTrigger);
return await Scheduler.CheckExists(jobTrigger.Key);
}
public virtual async Task QueuesAsync(IEnumerable<JobInfo> jobs)
{
var jobDictionary = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>();
foreach (var job in jobs)
{
var jobDetail = QuartzJobExecutor.CreateJob(job);
if (jobDetail == null)
{
continue;
}
var jobTrigger = QuartzJobExecutor.CreateTrigger(job);
if (jobTrigger == null)
{
continue;
}
jobDictionary.Add(jobDetail, new ITrigger[] { jobTrigger });
}
await Scheduler.ScheduleJobs(jobDictionary, false);
}
public virtual async Task<bool> RemoveAsync(JobInfo job)
{
var jobKey = new JobKey(job.Name, job.Group);
if (!await Scheduler.CheckExists(jobKey))
{
return false;
}
var triggers = await Scheduler.GetTriggersOfJob(jobKey);
foreach (var trigger in triggers)
{
await Scheduler.PauseTrigger(trigger.Key);
}
await Scheduler.DeleteJob(jobKey);
return !await Scheduler.CheckExists(jobKey);
}
public virtual async Task ResumeAsync(JobInfo job)
{
var jobKey = new JobKey(job.Name, job.Group);
if (await Scheduler.CheckExists(jobKey))
{
var triggers = await Scheduler.GetTriggersOfJob(jobKey);
foreach (var trigger in triggers)
{
await Scheduler.ResumeTrigger(trigger.Key);
}
}
}
public virtual async Task<bool> ShutdownAsync()
{
await StopAsync();
await Scheduler.Shutdown(true);
return Scheduler.IsShutdown;
}
public virtual async Task<bool> StartAsync()
{
if (Scheduler.InStandbyMode)
{
await Scheduler.Start();
}
return Scheduler.InStandbyMode;
}
public virtual async Task<bool> StopAsync()
{
if (!Scheduler.InStandbyMode)
{
//等待任务运行完成
await Scheduler.Standby();
}
return !Scheduler.InStandbyMode;
}
public virtual async Task TriggerAsync(JobInfo job)
{
var jobKey = new JobKey(job.Name, job.Group);
if (await Scheduler.CheckExists(jobKey))
{
await Scheduler.TriggerJob(jobKey);
}
else
{
throw new AbpException("This task could not be found in task scheduler, please confirm that it is enabled?");
}
}
}

33
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs

@ -0,0 +1,33 @@
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
public class QuartzJobSimpleAdapter<TJobRunnable> : IJob
where TJobRunnable : IJobRunnable
{
protected IServiceProvider ServiceProvider { get; }
public QuartzJobSimpleAdapter(
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public async virtual Task Execute(IJobExecutionContext context)
{
// 任务已经在一个作用域中
// using var scope = ServiceProvider.CreateScope();
var jobExecuter = ServiceProvider.GetRequiredService<IJobRunnableExecuter>();
var jobContext = new JobRunnableContext(
typeof(TJobRunnable),
ServiceProvider,
context.MergedJobDataMap.ToImmutableDictionary());
await jobExecuter.ExecuteAsync(jobContext);
}
}

27
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/Quartz/IJobExecutionContextExtensions.cs

@ -0,0 +1,27 @@
using System;
namespace Quartz;
public static class IJobExecutionContextExtensions
{
public static TValue GetData<TValue>(this IJobExecutionContext context, string key)
{
var value = context.MergedJobDataMap.GetString(key);
return (TValue)Convert.ChangeType(value, typeof(TValue));
}
public static string GetString(this IJobExecutionContext context, string key)
{
var value = context.MergedJobDataMap.Get(key);
return value != null ? value.ToString() : "";
}
public static int GetInt(this IJobExecutionContext context, string key)
{
var value = context.MergedJobDataMap.GetInt(key);
return value;
}
}

16
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md

@ -0,0 +1,16 @@
# LINGYUN.Abp.BackgroundTasks.Quartz
后台任务(队列)模块的Quartz实现, 使用任务适配器来做到任务的幂等性控制.
并添加一个监听器用于通知管理者任务状态
## 配置使用
模块按需引用,具体配置参考Volo.Abp.Quartz模块
```csharp
[DependsOn(typeof(AbpBackgroundTasksQuartzModule))]
public class YouProjectModule : AbpModule
{
// other
}
```

3
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xsd

@ -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>

22
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj

@ -0,0 +1,22 @@
<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.Auditing" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.DistributedLocking" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj" />
</ItemGroup>
</Project>

49
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs

@ -0,0 +1,49 @@
using LINGYUN.Abp.BackgroundTasks.Internal;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using Volo.Abp.Auditing;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
namespace LINGYUN.Abp.BackgroundTasks;
[DependsOn(typeof(AbpAuditingModule))]
[DependsOn(typeof(AbpDistributedLockingModule))]
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))]
[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))]
[DependsOn(typeof(AbpGuidsModule))]
public class AbpBackgroundTasksModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddJobMonitors(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient(typeof(BackgroundJobAdapter<>));
context.Services.AddHostedService<DefaultBackgroundWorker>();
}
private static void AutoAddJobMonitors(IServiceCollection services)
{
var jobMonitors = new List<Type>();
services.OnRegistred(context =>
{
if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(JobEventBase<>)))
{
jobMonitors.Add(context.ImplementationType);
}
});
services.Configure<AbpBackgroundTasksOptions>(options =>
{
options.JobMonitors.AddIfNotContains(jobMonitors);
});
}
}

69
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs

@ -0,0 +1,69 @@
using System;
using Volo.Abp.Collections;
namespace LINGYUN.Abp.BackgroundTasks;
public class AbpBackgroundTasksOptions
{
/// <summary>
/// 任务监听类型列表
/// </summary>
/// <remarks>
/// 用户可以实现事件监听实现自定义逻辑
/// </remarks>
public ITypeList<IJobEvent> JobMonitors { get; }
/// <summary>
/// 任务过期时间
/// 默认: 15 days
/// </summary>
/// <remarks>
/// 任务过期时间,超出时间段清理
/// </remarks>
public TimeSpan JobExpiratime { get; set; }
/// <summary>
/// 每次清理任务批次大小
/// 默认: 1000
/// </summary>
public int MaxJobCleanCount { get; set; }
/// <summary>
/// 清理过期任务批次Cron表达式
/// 默认: 600秒(0 0/10 * * * ? * )
/// </summary>
/// <remarks>
/// Cron表达式
/// </remarks>
public string JobCleanCronExpression { get; set; }
/// <summary>
/// 每次轮询任务批次大小
/// 默认: 1000
/// </summary>
public int MaxJobFetchCount { get; set; }
/// <summary>
/// 轮询任务批次Cron表达式
/// 默认: 30秒(0/30 * * * * ? )
/// </summary>
/// <remarks>
/// Cron表达式
/// </remarks>
public string JobFetchCronExpression { get; set; }
/// <summary>
/// 轮询任务批次时锁定任务超时时长(秒)
/// 默认:120
/// </summary>
/// <remarks>
/// 轮询任务也属于一个后台任务, 需要对每一次轮询加锁,防止重复任务入库
/// </remarks>
public int JobFetchLockTimeOut { get; set; }
public AbpBackgroundTasksOptions()
{
MaxJobFetchCount = 1000;
JobFetchLockTimeOut = 120;
JobFetchCronExpression = "0/30 * * * * ? ";
MaxJobCleanCount = 1000;
JobExpiratime = TimeSpan.FromDays(15d);
JobCleanCronExpression = "0 0/10 * * * ? *";
JobMonitors = new TypeList<IJobEvent>();
}
}

39
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs

@ -0,0 +1,39 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
namespace LINGYUN.Abp.BackgroundTasks;
public class BackgroundJobAdapter<TArgs> : IJobRunnable
{
public ILogger<BackgroundJobAdapter<TArgs>> Logger { protected get; set; }
protected AbpBackgroundJobOptions Options { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; }
public BackgroundJobAdapter(
IOptions<AbpBackgroundJobOptions> options,
IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory)
{
JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
Logger = NullLogger<BackgroundJobAdapter<TArgs>>.Instance;
}
public virtual async Task ExecuteAsync(JobRunnableContext context)
{
using var scope = ServiceScopeFactory.CreateScope();
var args = context.JobData.GetOrDefault(nameof(TArgs));
var jobType = Options.GetJob(typeof(TArgs)).JobType;
var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args);
await JobExecuter.ExecuteAsync(jobContext);
}
}

82
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs

@ -0,0 +1,82 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks;
[Dependency(ReplaceServices = true)]
public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
{
protected IClock Clock { get; }
protected IJobStore JobStore { get; }
protected IGuidGenerator GuidGenerator { get; }
protected AbpBackgroundJobOptions Options { get; }
public BackgroundJobManager(
IClock clock,
IJobStore jobStore,
IGuidGenerator guidGenerator,
IOptions<AbpBackgroundJobOptions> options)
{
Clock = clock;
JobStore = jobStore;
GuidGenerator = guidGenerator;
Options = options.Value;
}
public virtual async Task<string> EnqueueAsync<TArgs>(
TArgs args,
BackgroundJobPriority priority = BackgroundJobPriority.Normal,
TimeSpan? delay = null)
{
var jobConfiguration = Options.GetJob<TArgs>();
var interval = 60;
if (delay.HasValue)
{
interval = delay.Value.Seconds;
}
var jobId = GuidGenerator.Create();
var jobArgs = new Dictionary<string, object>
{
{ nameof(TArgs), args },
{ "ArgsType", jobConfiguration.ArgsType.AssemblyQualifiedName },
{ "JobType", typeof(BackgroundJobAdapter<TArgs>).AssemblyQualifiedName },
};
var jobInfo = new JobInfo
{
Id = jobId,
Name = jobConfiguration.JobName,
Group = "BackgroundJobs",
Priority = ConverForm(priority),
BeginTime = DateTime.Now,
Args = jobArgs,
Description = "From the framework background jobs",
JobType = JobType.Once,
Interval = interval,
CreationTime = Clock.Now,
Status = JobStatus.Running,
Type = typeof(BackgroundJobAdapter<TArgs>).AssemblyQualifiedName,
};
// 作为一次性任务持久化
await JobStore.StoreAsync(jobInfo);
return jobId.ToString();
}
private JobPriority ConverForm(BackgroundJobPriority priority)
{
return priority switch
{
BackgroundJobPriority.Low => JobPriority.Low,
BackgroundJobPriority.High => JobPriority.High,
BackgroundJobPriority.BelowNormal => JobPriority.BelowNormal,
BackgroundJobPriority.AboveNormal => JobPriority.AboveNormal,
_ => JobPriority.Normal,
};
}
}

22
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs

@ -0,0 +1,22 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 挂载任务事件接口
/// </summary>
public interface IJobEvent
{
/// <summary>
/// 任务启动前事件
/// </summary>
/// <param name="jobEventData"></param>
/// <returns></returns>
Task OnJobBeforeExecuted(JobEventContext context);
/// <summary>
/// 任务完成后事件
/// </summary>
/// <param name="jobEventData"></param>
/// <returns></returns>
Task OnJobAfterExecuted(JobEventContext context);
}

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEventProvider.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 任务事件提供者
/// </summary>
public interface IJobEventProvider
{
/// <summary>
/// 返回所有任务事件注册接口
/// </summary>
/// <returns></returns>
IReadOnlyCollection<IJobEvent> GetAll();
}

72
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobScheduler.cs

@ -0,0 +1,72 @@
using System.Threading.Tasks;
using System.Collections.Generic;
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 作业调度接口
/// </summary>
public interface IJobScheduler
{
/// <summary>
/// 任务入队
/// </summary>
/// <param name="job"></param>
/// <returns></returns>
Task<bool> QueueAsync(JobInfo job);
/// <summary>
/// 任务入队
/// </summary>
/// <param name="jobs"></param>
/// <returns></returns>
Task QueuesAsync(IEnumerable<JobInfo> jobs);
/// <summary>
/// 任务是否存在
/// </summary>
/// <param name="group"></param>
/// <param name="name"></param>
/// <returns></returns>
Task<bool> ExistsAsync(JobInfo job);
/// <summary>
/// 触发任务
/// </summary>
/// <param name="group"></param>
/// <param name="name"></param>
/// <returns></returns>
Task TriggerAsync(JobInfo job);
/// <summary>
/// 暂停任务
/// </summary>
/// <param name="group"></param>
/// <param name="name"></param>
/// <returns></returns>
Task PauseAsync(JobInfo job);
/// <summary>
/// 恢复暂停的任务
/// </summary>
/// <param name="group"></param>
/// <param name="name"></param>
/// <returns></returns>
Task ResumeAsync(JobInfo job);
/// <summary>
/// 移除任务
/// </summary>
/// <param name="group"></param>
/// <param name="name"></param>
/// <returns></returns>
Task<bool> RemoveAsync(JobInfo job);
/// <summary>
/// 启动任务协调器
/// </summary>
/// <returns></returns>
Task<bool> StartAsync();
/// <summary>
/// 停止任务协调器
/// </summary>
/// <returns></returns>
Task<bool> StopAsync();
/// <summary>
/// 释放任务协调器
/// </summary>
/// <returns></returns>
Task<bool> ShutdownAsync();
}

27
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobStore.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks;
public interface IJobStore
{
Task<List<JobInfo>> GetWaitingListAsync(
int maxResultCount,
CancellationToken cancellationToken = default);
Task<List<JobInfo>> GetAllPeriodTasksAsync(
CancellationToken cancellationToken = default);
Task<JobInfo> FindAsync(Guid jobId);
Task StoreAsync(JobInfo jobInfo);
Task StoreLogAsync(JobEventData eventData);
Task CleanupAsync(
int maxResultCount,
TimeSpan jobExpiratime,
CancellationToken cancellationToken = default);
}

18
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
internal class BackgroundCleaningJob : IJobRunnable
{
public virtual async Task ExecuteAsync(JobRunnableContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpBackgroundTasksOptions>>().Value;
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
await store.CleanupAsync(
options.MaxJobCleanCount,
options.JobExpiratime);
}
}

26
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundKeepAliveJob.cs

@ -0,0 +1,26 @@
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Auditing;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
[DisableAuditing]
internal class BackgroundKeepAliveJob : IJobRunnable
{
public virtual async Task ExecuteAsync(JobRunnableContext context)
{
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var periodJobs = await store.GetAllPeriodTasksAsync();
if (!periodJobs.Any())
{
return;
}
var jobScheduler = context.ServiceProvider.GetRequiredService<IJobScheduler>();
await jobScheduler.QueuesAsync(periodJobs);
}
}

31
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundPollingJob.cs

@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Auditing;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
[DisableAuditing]
internal class BackgroundPollingJob : IJobRunnable
{
public virtual async Task ExecuteAsync(JobRunnableContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpBackgroundTasksOptions>>().Value;
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var waitingJobs = await store.GetWaitingListAsync(options.MaxJobFetchCount);
if (!waitingJobs.Any())
{
return;
}
var jobScheduler = context.ServiceProvider.GetRequiredService<IJobScheduler>();
foreach (var job in waitingJobs)
{
await jobScheduler.QueueAsync(job);
}
}
}

108
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs

@ -0,0 +1,108 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
internal class DefaultBackgroundWorker : BackgroundService
{
private readonly IJobStore _jobStore;
private readonly IJobScheduler _jobScheduler;
private readonly AbpBackgroundTasksOptions _options;
public DefaultBackgroundWorker(
IJobStore jobStore,
IJobScheduler jobScheduler,
IOptions<AbpBackgroundTasksOptions> options)
{
_jobStore = jobStore;
_jobScheduler = jobScheduler;
_options = options.Value;
}
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
await QueuePollingJob();
await QueueKeepAliveJob();
await QueueCleaningJob();
}
private async Task QueueKeepAliveJob()
{
var keepAliveJob = BuildKeepAliveJobInfo();
await _jobScheduler.QueueAsync(keepAliveJob);
}
private async Task QueuePollingJob()
{
var pollingJob = BuildPollingJobInfo();
await _jobScheduler.QueueAsync(pollingJob);
}
private async Task QueueCleaningJob()
{
var cleaningJob = BuildCleaningJobInfo();
await _jobScheduler.QueueAsync(cleaningJob);
}
private JobInfo BuildKeepAliveJobInfo()
{
return new JobInfo
{
Id = Guid.Parse("8F50C5D9-5691-4B99-A52B-CABD91D93C89"),
Name = nameof(BackgroundKeepAliveJob),
Group = "Default",
Description = "Add periodic tasks",
Args = new Dictionary<string, object>(),
Status = JobStatus.Running,
BeginTime = DateTime.Now,
CreationTime = DateTime.Now,
JobType = JobType.Once,
Priority = JobPriority.High,
MaxCount = 1,
Type = typeof(BackgroundKeepAliveJob).AssemblyQualifiedName,
};
}
private JobInfo BuildPollingJobInfo()
{
return new JobInfo
{
Id = Guid.Parse("C51152E9-F0B8-4252-8352-283BE46083CC"),
Name = nameof(BackgroundPollingJob),
Group = "Default",
Description = "Polling tasks to be executed",
Args = new Dictionary<string, object>(),
Status = JobStatus.Running,
BeginTime = DateTime.Now,
CreationTime = DateTime.Now,
Cron = _options.JobFetchCronExpression,
JobType = JobType.Period,
Priority = JobPriority.High,
LockTimeOut = _options.JobFetchLockTimeOut,
Type = typeof(BackgroundPollingJob).AssemblyQualifiedName,
};
}
private JobInfo BuildCleaningJobInfo()
{
return new JobInfo
{
Id = Guid.Parse("AAAF8783-FA06-4CF9-BDCA-11140FB2478F"),
Name = nameof(BackgroundCleaningJob),
Group = "Default",
Description = "Cleaning tasks to be executed",
Args = new Dictionary<string, object>(),
Status = JobStatus.Running,
BeginTime = DateTime.Now,
CreationTime = DateTime.Now,
Cron = _options.JobCleanCronExpression,
JobType = JobType.Period,
Priority = JobPriority.High,
Type = typeof(BackgroundCleaningJob).AssemblyQualifiedName,
};
}
}

87
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/InMemoryJobStore.cs

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
[Dependency(TryRegister = true)]
internal class InMemoryJobStore : IJobStore, ISingletonDependency
{
private readonly List<JobInfo> _memoryJobStore;
public InMemoryJobStore()
{
_memoryJobStore = new List<JobInfo>();
}
public Task<List<JobInfo>> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default)
{
var jobs = _memoryJobStore
.Where(x => x.JobType == JobType.Period && x.Status == JobStatus.Running)
.OrderByDescending(x => x.Priority)
.ToList();
return Task.FromResult(jobs);
}
public Task<List<JobInfo>> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default)
{
var now = DateTime.Now;
var jobs = _memoryJobStore
.Where(x => !x.IsAbandoned && x.JobType != JobType.Period && x.Status == JobStatus.Running)
.OrderByDescending(x => x.Priority)
.ThenBy(x => x.TryCount)
.ThenBy(x => x.NextRunTime)
.Take(maxResultCount)
.ToList();
return Task.FromResult(jobs);
}
public Task<JobInfo> FindAsync(Guid jobId)
{
var job = _memoryJobStore.FirstOrDefault(x => x.Id.Equals(jobId));
return Task.FromResult(job);
}
public Task StoreAsync(JobInfo jobInfo)
{
var job = _memoryJobStore.FirstOrDefault(x => x.Id.Equals(jobInfo.Id));
if (job != null)
{
job.NextRunTime = jobInfo.NextRunTime;
job.LastRunTime = jobInfo.LastRunTime;
job.Status = jobInfo.Status;
job.TriggerCount = jobInfo.TriggerCount;
job.TryCount = jobInfo.TryCount;
job.IsAbandoned = jobInfo.IsAbandoned;
}
else
{
_memoryJobStore.Add(jobInfo);
}
return Task.CompletedTask;
}
public Task StoreLogAsync(JobEventData eventData)
{
return Task.CompletedTask;
}
public Task CleanupAsync(int maxResultCount, TimeSpan jobExpiratime, CancellationToken cancellationToken = default)
{
var expiratime = DateTime.Now - jobExpiratime;
var expriaJobs = _memoryJobStore.Where(
x => x.Status == JobStatus.Completed &&
expiratime.CompareTo(x.LastRunTime ?? x.EndTime ?? x.CreationTime) <= 0)
.Take(maxResultCount);
_memoryJobStore.RemoveAll(expriaJobs);
return Task.CompletedTask;
}
}

41
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobEventProvider.cs

@ -0,0 +1,41 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
internal class JobEventProvider : IJobEventProvider, ISingletonDependency
{
private readonly Lazy<List<IJobEvent>> _lazyEvents;
private List<IJobEvent> _events => _lazyEvents.Value;
private readonly IServiceProvider _serviceProvider;
private readonly AbpBackgroundTasksOptions _options;
public JobEventProvider(
IOptions<AbpBackgroundTasksOptions> options,
IServiceProvider serviceProvider)
{
_options = options.Value;
_serviceProvider = serviceProvider;
_lazyEvents = new Lazy<List<IJobEvent>>(CreateJobEvents);
}
public IReadOnlyCollection<IJobEvent> GetAll()
{
return _events.ToImmutableList();
}
private List<IJobEvent> CreateJobEvents()
{
var jobEvents = _options
.JobMonitors
.Select(p => _serviceProvider.GetRequiredService(p) as IJobEvent)
.ToList();
return jobEvents;
}
}

60
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs

@ -0,0 +1,60 @@
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
internal class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDependency
{
protected override async Task OnJobAfterExecutedAsync(JobEventContext context)
{
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var job = await store.FindAsync(context.EventData.Key);
if (job != null)
{
job.TriggerCount += 1;
job.NextRunTime = context.EventData.NextRunTime;
job.LastRunTime = context.EventData.LastRunTime;
job.Result = context.EventData.Result;
// 一次性任务执行一次后标记为已完成
if (job.JobType == JobType.Once)
{
job.Status = JobStatus.Completed;
}
// 任务异常后可重试
if (context.EventData.Exception != null)
{
job.TryCount += 1;
job.Status = JobStatus.Running;
job.Result = context.EventData.Exception.Message;
if (job.TryCount > job.MaxTryCount)
{
job.Status = JobStatus.Stopped;
job.IsAbandoned = true;
await RemoveJobAsync(context, job);
}
}
// 所有任务达到上限则标记已完成
if (job.MaxCount > 0 && job.TriggerCount > job.MaxCount)
{
job.Status = JobStatus.Completed;
await RemoveJobAsync(context, job);
}
await store.StoreAsync(job);
}
}
private async Task RemoveJobAsync(JobEventContext context, JobInfo jobInfo)
{
var jobScheduler = context.ServiceProvider.GetRequiredService<IJobScheduler>();
await jobScheduler.RemoveAsync(jobInfo);
}
}

26
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs

@ -0,0 +1,26 @@
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
/// <summary>
/// 存储任务日志
/// </summary>
/// <remarks>
/// 任务类型标记了<see cref="DisableAuditingAttribute"/> 特性则不会记录日志
/// </remarks>
internal class JobLogEvent : JobEventBase<JobLogEvent>, ITransientDependency
{
protected async override Task OnJobAfterExecutedAsync(JobEventContext context)
{
if (context.EventData.Type.IsDefined(typeof(DisableAuditingAttribute), true))
{
return;
}
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
await store.StoreLogAsync(context.EventData);
}
}

58
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs

@ -0,0 +1,58 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks;
public abstract class JobEventBase<TEvent> : IJobEvent
{
public ILogger<TEvent> Logger { protected get; set; }
protected JobEventBase()
{
Logger = NullLogger<TEvent>.Instance;
}
public async Task OnJobAfterExecuted(JobEventContext context)
{
try
{
await OnJobAfterExecutedAsync(context);
}
catch (Exception ex)
{
Logger.LogError("Failed to execute event, error:" + GetSourceException(ex).Message);
}
}
public async Task OnJobBeforeExecuted(JobEventContext context)
{
try
{
await OnJobBeforeExecutedAsync(context);
}
catch (Exception ex)
{
Logger.LogError("Failed to execute preprocessing event, error:" + GetSourceException(ex).Message);
}
}
protected virtual Task OnJobAfterExecutedAsync(JobEventContext context)
{
return Task.CompletedTask;
}
protected virtual Task OnJobBeforeExecutedAsync(JobEventContext context)
{
return Task.CompletedTask;
}
protected virtual Exception GetSourceException(Exception exception)
{
if (exception.InnerException != null)
{
return GetSourceException(exception.InnerException);
}
return exception;
}
}

55
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs

@ -0,0 +1,55 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks;
public class JobRunnableExecuter : IJobRunnableExecuter, ISingletonDependency
{
protected const string LockKeyFormat = "job:{0},key:{1}";
public async virtual Task ExecuteAsync(JobRunnableContext context)
{
Guid? tenantId = null;
if (context.JobData.TryGetValue(nameof(IMultiTenant.TenantId), out var tenant) &&
Guid.TryParse(tenant?.ToString(), out var tid))
{
tenantId = tid;
}
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
using (currentTenant.Change(tenantId))
{
context.JobData.TryGetValue(nameof(JobInfo.LockTimeOut), out var lockTime);
if (lockTime != null && (lockTime is int time && time > 0))
{
var jobId = context.JobData.GetOrDefault(nameof(JobInfo.Id));
var jobLockKey = string.Format(LockKeyFormat, context.JobType.Name, jobId);
var distributedLock = context.ServiceProvider.GetRequiredService<IAbpDistributedLock>();
await using (await distributedLock.TryAcquireAsync(jobLockKey, TimeSpan.FromSeconds(time)))
{
await InternalExecuteAsync(context);
}
}
else
{
await InternalExecuteAsync(context);
}
}
}
private async Task InternalExecuteAsync(JobRunnableContext context)
{
var jobRunnable = context.ServiceProvider.GetService(context.JobType);
if (jobRunnable == null)
{
jobRunnable = Activator.CreateInstance(context.JobType);
}
await ((IJobRunnable)jobRunnable).ExecuteAsync(context);
}
}

13
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs

@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Primitives;
public class ConsoleJob : IJobRunnable
{
public Task ExecuteAsync(JobRunnableContext context)
{
Console.WriteLine($"This message comes from the job: {GetType()}");
return Task.CompletedTask;
}
}

14
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Primitives;
public class EmailingJob : IJobRunnable
{
public virtual async Task ExecuteAsync(JobRunnableContext context)
{
}
}

116
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md

@ -0,0 +1,116 @@
# LINGYUN.Abp.BackgroundTasks
后台任务(队列)模块,Abp提供的后台作业与后台工作者不支持Cron表达式, 提供可管理的后台任务(队列)功能.
实现了**Volo.Abp.BackgroundJobs.IBackgroundJobManager**, 意味着您也能通过框架后台作业接口添加新作业.
## 任务类别
* JobType.Once: 一次性任务, 此类型只会被执行一次, 适用于邮件通知等场景
* JobType.Period: 周期性任务, 此类型任务会根据Cron表达式来决定运行方式, 适用于报表分析等场景
* JobType.Persistent: 持续性任务, 此类型任务按照给定重复次数、重复间隔运行, 适用于接口压测等场景
## 配置使用
模块按需引用
```csharp
[DependsOn(typeof(AbpBackgroundTasksModule))]
public class YouProjectModule : AbpModule
{
// other
}
```
```csharp
public class DemoClass
{
protected IServiceProvider ServiceProvider { get; }
public DemoClass(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public async Task Some()
{
var scheduler = ServiceProvider.GetRequiredService<IJobScheduler>();
// 将周期性(5秒一次)任务添加到队列
await scheduler.QueueAsync(new JobInfo
{
Type = typeof(ConsoleJob).AssemblyQualifiedName,
Args = new Dictionary<string, object>(),
Name = "Test-Console-Period",
Group = "Test",
Description = "Test-Console",
Id = Guid.NewGuid(),
JobType = JobType.Period,
Priority = Volo.Abp.BackgroundJobs.BackgroundJobPriority.Low,
Cron = "0/5 * * * * ? ",
TryCount = 10,
Status = JobStatus.Running,
});
// 将一次性任务添加到队列, 将在10(Interval)秒后被执行
await scheduler.QueueAsync(new JobInfo
{
Type = typeof(ConsoleJob).AssemblyQualifiedName,
Args = new Dictionary<string, object>(),
Name = "Test-Console-Once",
Group = "Test",
Description = "Test-Console",
Id = Guid.NewGuid(),
JobType = JobType.Once,
Priority = Volo.Abp.BackgroundJobs.BackgroundJobPriority.Low,
Interval = 10,
TryCount = 10,
Status = JobStatus.Running,
});
// 将持续性任务添加到队列, 将在10(Interval)秒后被执行, 最大执行5(MaxCount)次
await scheduler.QueueAsync(new JobInfo
{
Type = typeof(ConsoleJob).AssemblyQualifiedName,
Args = new Dictionary<string, object>(),
Name = "Test-Console-Persistent",
Group = "Test",
Description = "Test-Console",
Id = Guid.NewGuid(),
JobType = JobType.Persistent,
Priority = Volo.Abp.BackgroundJobs.BackgroundJobPriority.Low,
Interval = 10,
TryCount = 10,
MaxCount = 5,
Status = JobStatus.Running,
});
// 同样可以把框架后台作业添加到作业调度器中, 不需要更改使用习惯
var backgroundJobManager = ServiceProvider.GetRequiredService<IBackgroundJobManager>();
await jobManager.EnqueueAsync(
new SmsJobArgs
{
PhoneNumber = "13800138000",
Message = "来自框架的后台工作者"
},
BackgroundJobPriority.High,
TimeSpan.FromSeconds(10));
}
}
public class SmsJobArgs
{
public string PhoneNumber { get; set; }
public string Message { get; set; }
}
public class SmsJob : AsyncBackgroundJob<SmsJobArgs>, ITransientDependency
{
public override Task ExecuteAsync(SmsJobArgs args)
{
Console.WriteLine($"Send sms message: {args.Message}");
return Task.CompletedTask;
}
}
```

3
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xsd

@ -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>

19
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj

@ -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.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.TaskManagement.Domain.Shared\LINGYUN.Abp.TaskManagement.Domain.Shared.csproj" />
</ItemGroup>
</Project>

17
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateDto.cs

@ -0,0 +1,17 @@
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfoCreateDto : BackgroundJobInfoCreateOrUpdateDto
{
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
}

61
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateOrUpdateDto.cs

@ -0,0 +1,61 @@
using LINGYUN.Abp.BackgroundTasks;
using System;
using Volo.Abp.Data;
namespace LINGYUN.Abp.TaskManagement;
public abstract class BackgroundJobInfoCreateOrUpdateDto
{
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 任务参数
/// </summary>
public ExtraPropertyDictionary Args { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 任务类别
/// </summary>
public JobType JobType { get; set; }
/// <summary>
/// Cron表达式,如果是持续任务需要指定
/// </summary>
public string Cron { get; set; }
/// <summary>
/// 失败重试上限
/// 默认:50
/// </summary>
public int MaxTryCount { get; set; }
/// <summary>
/// 最大执行次数
/// 默认:0, 无限制
/// </summary>
public int MaxCount { get; set; }
/// <summary>
/// 间隔时间,单位秒,与Cron表达式冲突
/// 默认: 300
/// </summary>
public int Interval { get; set; } = 300;
/// <summary>
/// 任务优先级
/// </summary>
public JobPriority Priority { get; set; }
/// <summary>
/// 任务独占超时时长(秒)
/// 0或更小不生效
/// </summary>
public int LockTimeOut { get; set; }
}

99
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoDto.cs

@ -0,0 +1,99 @@
using LINGYUN.Abp.BackgroundTasks;
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfoDto : ExtensibleAuditedEntityDto<Guid>
{
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 返回参数
/// </summary>
public string Result { get; set; }
/// <summary>
/// 任务参数
/// </summary>
public ExtraPropertyDictionary Args { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public JobStatus Status { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 上次运行时间
/// </summary>
public DateTime? LastRunTime { get; set; }
/// <summary>
/// 下一次执行时间
/// </summary>
public DateTime? NextRunTime { get; set; }
/// <summary>
/// 任务类别
/// </summary>
public JobType JobType { get; set; }
/// <summary>
/// Cron表达式,如果是持续任务需要指定
/// </summary>
public string Cron { get; set; }
/// <summary>
/// 触发次数
/// </summary>
public int TriggerCount { get; set; }
/// <summary>
/// 失败重试次数
/// </summary>
public int TryCount { get; set; }
/// <summary>
/// 失败重试上限
/// 默认:50
/// </summary>
public int MaxTryCount { get; set; }
/// <summary>
/// 最大执行次数
/// 默认:0, 无限制
/// </summary>
public int MaxCount { get; set; }
/// <summary>
/// 连续失败且不会再次执行
/// </summary>
public bool IsAbandoned { get; set; }
/// <summary>
/// 间隔时间,单位秒,与Cron表达式冲突
/// 默认: 300
/// </summary>
public int Interval { get; set; } = 300;
/// <summary>
/// 任务优先级
/// </summary>
public JobPriority Priority { get; set; }
/// <summary>
/// 任务独占超时时长(秒)
/// 0或更小不生效
/// </summary>
public int LockTimeOut { get; set; }
}

65
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoGetListInput.cs

@ -0,0 +1,65 @@
using LINGYUN.Abp.BackgroundTasks;
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfoGetListInput: PagedAndSortedResultRequestDto
{
/// <summary>
/// 其他过滤条件
/// </summary>
public string Filter { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public JobStatus? Status { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime? BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 上次起始触发时间
/// </summary>
public DateTime? BeginLastRunTime { get; set; }
/// <summary>
/// 上次截止触发时间
/// </summary>
public DateTime? EndLastRunTime { get; set; }
/// <summary>
/// 起始创建时间
/// </summary>
public DateTime? BeginCreationTime { get; set; }
/// <summary>
/// 截止创建时间
/// </summary>
public DateTime? EndCreationTime { get; set; }
/// <summary>
/// 是否已放弃任务
/// </summary>
public bool? IsAbandoned { get; set; }
/// <summary>
/// 是否持续性任务
/// </summary>
public bool? IsPeriod { get; set; }
/// <summary>
/// 优先级
/// </summary>
public JobPriority? Priority { get; set; }
}

8
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoUpdateDto.cs

@ -0,0 +1,8 @@
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfoUpdateDto : BackgroundJobInfoCreateOrUpdateDto, IHasConcurrencyStamp
{
public string ConcurrencyStamp { get; set; }
}

14
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogDto.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobLogDto : EntityDto<long>
{
public string JobName { get; set; }
public string JobGroup { get; set; }
public string JobType { get; set; }
public string Message { get; set; }
public DateTime RunTime { get; set; }
public string Exception { get; set; }
}

36
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs

@ -0,0 +1,36 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobLogGetListInput : PagedAndSortedResultRequestDto
{
/// <summary>
/// 其他过滤条件
/// </summary>
public string Filter { get; set; }
/// <summary>
/// 存在异常
/// </summary>
public bool? HasExceptions { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 开始触发时间
/// </summary>
public DateTime? BeginRunTime { get; set; }
/// <summary>
/// 结束触发时间
/// </summary>
public DateTime? EndRunTime { get; set; }
}

20
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs

@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.TaskManagement;
public interface IBackgroundJobInfoAppService :
ICrudAppService<
BackgroundJobInfoDto,
Guid,
BackgroundJobInfoGetListInput,
BackgroundJobInfoCreateDto,
BackgroundJobInfoUpdateDto>
{
Task TriggerAsync(Guid id);
Task PauseAsync(Guid id);
Task ResumeAsync(Guid id);
}

11
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs

@ -0,0 +1,11 @@
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.TaskManagement;
public interface IBackgroundJobLogAppService :
IReadOnlyAppService<
BackgroundJobLogDto,
long,
BackgroundJobLogGetListInput>
{
}

17
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs

@ -0,0 +1,17 @@
namespace LINGYUN.Abp.TaskManagement.Permissions;
public static class TaskManagementPermissions
{
public const string GroupName = "TaskManagement";
public static class BackgroundJobs
{
public const string Default = GroupName + ".BackgroundJobs";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string Trigger = Default + ".Trigger";
public const string Pause = Default + ".Pause";
public const string Resume = Default + ".Resume";
}
}

10
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementApplicationContractsModule.cs

@ -0,0 +1,10 @@
using Volo.Abp.Application;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.TaskManagement;
[DependsOn(typeof(TaskManagementDomainSharedModule))]
[DependsOn(typeof(AbpDddApplicationContractsModule))]
public class TaskManagementApplicationContractsModule : AbpModule
{
}

3
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xsd

@ -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>

20
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN.Abp.TaskManagement.Application.csproj

@ -0,0 +1,20 @@
<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.Ddd.Application" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.TaskManagement.Application.Contracts\LINGYUN.Abp.TaskManagement.Application.Contracts.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.TaskManagement.Domain\LINGYUN.Abp.TaskManagement.Domain.csproj" />
</ItemGroup>
</Project>

161
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs

@ -0,0 +1,161 @@
using LINGYUN.Abp.BackgroundTasks;
using LINGYUN.Abp.TaskManagement.Permissions;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
namespace LINGYUN.Abp.TaskManagement;
[Authorize(TaskManagementPermissions.BackgroundJobs.Default)]
public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBackgroundJobInfoAppService
{
protected BackgroundJobManager BackgroundJobManager { get; }
protected IBackgroundJobInfoRepository BackgroundJobInfoRepository { get; }
public BackgroundJobInfoAppService(
BackgroundJobManager backgroundJobManager,
IBackgroundJobInfoRepository backgroundJobInfoRepository)
{
BackgroundJobManager = backgroundJobManager;
BackgroundJobInfoRepository = backgroundJobInfoRepository;
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Create)]
public virtual async Task<BackgroundJobInfoDto> CreateAsync(BackgroundJobInfoCreateDto input)
{
if (await BackgroundJobInfoRepository.CheckNameAsync(input.Name, input.Group))
{
throw new BusinessException(TaskManagementErrorCodes.JobNameAlreadyExists)
.WithData("Group", input.Group)
.WithData("Name", input.Name);
}
var backgroundJobInfo = new BackgroundJobInfo(
GuidGenerator.Create(),
input.Name,
input.Group,
input.Type,
input.Args,
input.BeginTime,
input.EndTime,
input.Priority,
input.MaxCount,
input.MaxTryCount);
UpdateByInput(backgroundJobInfo, input);
await BackgroundJobInfoRepository.InsertAsync(backgroundJobInfo, autoSave: true);
if (backgroundJobInfo.IsEnabled && backgroundJobInfo.JobType == JobType.Period)
{
await BackgroundJobManager.QueueAsync(backgroundJobInfo);
}
return ObjectMapper.Map<BackgroundJobInfo, BackgroundJobInfoDto>(backgroundJobInfo);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Delete)]
public virtual async Task DeleteAsync(Guid id)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
await BackgroundJobManager.DeleteAsync(backgroundJobInfo);
}
public virtual async Task<BackgroundJobInfoDto> GetAsync(Guid id)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
return ObjectMapper.Map<BackgroundJobInfo, BackgroundJobInfoDto>(backgroundJobInfo);
}
public virtual async Task<PagedResultDto<BackgroundJobInfoDto>> GetListAsync(BackgroundJobInfoGetListInput input)
{
var filter = new BackgroundJobInfoFilter
{
IsAbandoned = input.IsAbandoned,
IsPeriod = input.IsPeriod,
BeginCreationTime = input.BeginCreationTime,
EndCreationTime = input.EndCreationTime,
BeginLastRunTime = input.BeginLastRunTime,
EndLastRunTime = input.EndLastRunTime,
BeginTime = input.BeginTime,
EndTime = input.EndTime,
Filter = input.Filter,
Group = input.Group,
Name = input.Name,
Priority = input.Priority,
Status = input.Status,
Type = input.Type
};
var totalCount = await BackgroundJobInfoRepository.GetCountAsync(filter);
var backgroundJobInfos = await BackgroundJobInfoRepository.GetListAsync(
filter, input.Sorting, input.MaxResultCount, input.SkipCount);
return new PagedResultDto<BackgroundJobInfoDto>(totalCount,
ObjectMapper.Map<List<BackgroundJobInfo>, List<BackgroundJobInfoDto>>(backgroundJobInfos));
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Pause)]
public virtual async Task PauseAsync(Guid id)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
await BackgroundJobManager.PauseAsync(backgroundJobInfo);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Resume)]
public virtual async Task ResumeAsync(Guid id)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
await BackgroundJobManager.ResumeAsync(backgroundJobInfo);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Trigger)]
public virtual async Task TriggerAsync(Guid id)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
await BackgroundJobManager.TriggerAsync(backgroundJobInfo);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Update)]
public virtual async Task<BackgroundJobInfoDto> UpdateAsync(Guid id, BackgroundJobInfoUpdateDto input)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
var resetJob = backgroundJobInfo.JobType == input.JobType;
UpdateByInput(backgroundJobInfo, input);
backgroundJobInfo.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
await BackgroundJobManager.UpdateAsync(backgroundJobInfo, resetJob);
return ObjectMapper.Map<BackgroundJobInfo, BackgroundJobInfoDto>(backgroundJobInfo);
}
protected virtual void UpdateByInput(BackgroundJobInfo backgroundJobInfo, BackgroundJobInfoCreateOrUpdateDto input)
{
backgroundJobInfo.IsEnabled = input.IsEnabled;
backgroundJobInfo.LockTimeOut = input.LockTimeOut;
backgroundJobInfo.Description = input.Description;
switch (input.JobType)
{
case JobType.Once:
backgroundJobInfo.SetOnceJob(input.Interval);
break;
case JobType.Persistent:
backgroundJobInfo.SetPersistentJob(input.Interval);
break;
case JobType.Period:
backgroundJobInfo.SetPeriodJob(input.Cron);
break;
}
}
}

11
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs

@ -0,0 +1,11 @@
using AutoMapper;
namespace LINGYUN.Abp.TaskManagement;
public class TaskManagementApplicationMapperProfile : Profile
{
public TaskManagementApplicationMapperProfile()
{
CreateMap<BackgroundJobInfo, BackgroundJobInfoDto>();
}
}

22
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationModule.cs

@ -0,0 +1,22 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Application;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.TaskManagement;
[DependsOn(typeof(TaskManagementApplicationContractsModule))]
[DependsOn(typeof(TaskManagementDomainModule))]
[DependsOn(typeof(AbpDddApplicationModule))]
public class TaskManagementApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<TaskManagementApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<TaskManagementApplicationMapperProfile>(validate: true);
});
}
}

13
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationService.cs

@ -0,0 +1,13 @@
using LINGYUN.Abp.TaskManagement.Localization;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.TaskManagement;
public abstract class TaskManagementApplicationService : ApplicationService
{
protected TaskManagementApplicationService()
{
LocalizationResource = typeof(TaskManagementResource);
ObjectMapperContext = typeof(TaskManagementApplicationModule);
}
}

3
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xsd

@ -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>

27
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN.Abp.TaskManagement.Domain.Shared.csproj

@ -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\TaskManagement\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\TaskManagement\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Validation" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj" />
</ItemGroup>
</Project>

10
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs

@ -0,0 +1,10 @@
namespace LINGYUN.Abp.TaskManagement;
public static class BackgroundJobInfoConsts
{
public static int MaxCronLength { get; set; } = 50;
public static int MaxNameLength { get; set; } = 100;
public static int MaxGroupLength { get; set; } = 50;
public static int MaxTypeLength { get; set; } = 200;
public static int MaxDescriptionLength { get; set; } = 255;
}

7
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobLogConsts.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Abp.TaskManagement;
public static class BackgroundJobLogConsts
{
public static int MaxMessageLength { get; set; } = 1000;
public static int MaxExceptionLength { get; set; } = 2000;
}

6
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json

@ -0,0 +1,6 @@
{
"culture": "en",
"texts": {
"Permission:TaskManagement": "TaskManagement"
}
}

6
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"Permission:TaskManagement": "任务管理"
}
}

8
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/TaskManagementResource.cs

@ -0,0 +1,8 @@
using Volo.Abp.Localization;
namespace LINGYUN.Abp.TaskManagement.Localization;
[LocalizationResourceName("TaskManagement")]
public class TaskManagementResource
{
}

34
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementDomainSharedModule.cs

@ -0,0 +1,34 @@
using LINGYUN.Abp.BackgroundTasks;
using LINGYUN.Abp.TaskManagement.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.TaskManagement;
[DependsOn(typeof(AbpValidationModule))]
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))]
public class TaskManagementDomainSharedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TaskManagementDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<TaskManagementResource>()
.AddVirtualJson("/LINGYUN/Abp/TaskManagement/Localization/Resources");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace(TaskManagementErrorCodes.Namespace, typeof(TaskManagementResource));
});
}
}

9
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementErrorCodes.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.TaskManagement
{
public static class TaskManagementErrorCodes
{
public const string Namespace = "TaskManagement";
public const string JobNameAlreadyExists = Namespace + ":01000";
}
}

3
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xsd

@ -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>

21
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN.Abp.TaskManagement.Domain.csproj

@ -0,0 +1,21 @@
<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.AutoMapper" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks\LINGYUN.Abp.BackgroundTasks.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.TaskManagement.Domain.Shared\LINGYUN.Abp.TaskManagement.Domain.Shared.csproj" />
</ItemGroup>
</Project>

162
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs

@ -0,0 +1,162 @@
using LINGYUN.Abp.BackgroundTasks;
using System;
using System.Collections.Generic;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfo : AuditedAggregateRoot<Guid>
{
/// <summary>
/// 任务名称
/// </summary>
public virtual string Name { get; protected set; }
/// <summary>
/// 任务分组
/// </summary>
public virtual string Group { get; protected set; }
/// <summary>
/// 任务类型
/// </summary>
public virtual string Type { get; protected set; }
/// <summary>
/// 任务参数
/// </summary>
public virtual ExtraPropertyDictionary Args { get; protected set; }
/// <summary>
/// 任务状态
/// </summary>
public virtual JobStatus Status { get; protected set; }
/// <summary>
/// 是否启用
/// </summary>
public virtual bool IsEnabled { get; set; }
/// <summary>
/// 描述
/// </summary>
public virtual string Description { get; set; }
/// <summary>
/// 任务独占超时时长(秒)
/// 0或更小不生效
/// </summary>
public virtual int LockTimeOut { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public virtual DateTime BeginTime { get; protected set; }
/// <summary>
/// 结束时间
/// </summary>
public virtual DateTime? EndTime { get; protected set; }
/// <summary>
/// 上次执行时间
/// </summary>
public virtual DateTime? LastRunTime { get; protected set; }
/// <summary>
/// 下次执行时间
/// </summary>
public virtual DateTime? NextRunTime { get; protected set; }
/// <summary>
/// 任务类别
/// </summary>
public virtual JobType JobType { get; protected set; }
/// <summary>
/// Cron表达式,如果是持续任务需要指定
/// </summary>
public virtual string Cron { get; protected set; }
/// <summary>
/// 任务优先级
/// </summary>
public virtual JobPriority Priority { get; protected set; }
/// <summary>
/// 触发次数
/// </summary>
public virtual int TriggerCount { get; set; }
/// <summary>
/// 失败重试次数
/// </summary>
public virtual int TryCount { get; set; }
/// <summary>
/// 失败重试上限
/// 默认:50
/// </summary>
public virtual int MaxTryCount { get; set; }
/// <summary>
/// 最大执行次数
/// 默认:0, 无限制
/// </summary>
public virtual int MaxCount { get; set; }
/// <summary>
/// 间隔时间,单位秒,与Cron表达式冲突
/// 默认: 300
/// </summary>
public virtual int Interval { get; protected set; }
/// <summary>
/// 连续失败且不会再次执行
/// </summary>
public virtual bool IsAbandoned { get; set; }
protected BackgroundJobInfo() { }
public BackgroundJobInfo(
Guid id,
string name,
string group,
string type,
IDictionary<string, object> args,
DateTime beginTime,
DateTime? endTime = null,
JobPriority priority = JobPriority.Normal,
int maxCount = 0,
int maxTryCount = 50) : base(id)
{
Name = name;
Group = group;
Type = type;
Priority = priority;
BeginTime = beginTime;
EndTime = endTime;
MaxCount = maxCount;
MaxTryCount = maxTryCount;
Status = JobStatus.Running;
Args = new ExtraPropertyDictionary();
Args.AddIfNotContains(args);
}
public void SetPeriodJob(string cron)
{
Cron = cron;
JobType = JobType.Period;
}
public void SetOnceJob(int interval)
{
Interval = interval;
JobType = JobType.Once;
}
public void SetPersistentJob(int interval)
{
Interval = interval;
JobType = JobType.Persistent;
}
public void SetLastRunTime(DateTime? lastRunTime)
{
LastRunTime = lastRunTime;
}
public void SetNextRunTime(DateTime? nextRunTime)
{
NextRunTime = nextRunTime;
}
public void SetStatus(JobStatus status)
{
Status = status;
}
}

67
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoFilter.cs

@ -0,0 +1,67 @@
using LINGYUN.Abp.BackgroundTasks;
using System;
namespace LINGYUN.Abp.TaskManagement;
/// <summary>
/// 后台任务过滤
/// </summary>
public class BackgroundJobInfoFilter
{
/// <summary>
/// 其他过滤条件
/// </summary>
public string Filter { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public JobStatus? Status { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime? BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 上次起始触发时间
/// </summary>
public DateTime? BeginLastRunTime { get; set; }
/// <summary>
/// 上次截止触发时间
/// </summary>
public DateTime? EndLastRunTime { get; set; }
/// <summary>
/// 起始创建时间
/// </summary>
public DateTime? BeginCreationTime { get; set; }
/// <summary>
/// 截止创建时间
/// </summary>
public DateTime? EndCreationTime { get; set; }
/// <summary>
/// 是否已放弃任务
/// </summary>
public bool? IsAbandoned { get; set; }
/// <summary>
/// 是否持续性任务
/// </summary>
public bool? IsPeriod { get; set; }
/// <summary>
/// 优先级
/// </summary>
public JobPriority? Priority { get; set; }
}

39
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs

@ -0,0 +1,39 @@
using System;
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobLog : Entity<long>
{
public virtual Guid? JobId { get; set; }
public virtual string JobName { get; protected set; }
public virtual string JobGroup { get; protected set; }
public virtual string JobType { get; protected set; }
public virtual string Message { get; protected set; }
public virtual DateTime RunTime { get; protected set; }
public virtual string Exception { get; protected set; }
protected BackgroundJobLog() { }
public BackgroundJobLog(string type, string group, string name)
{
JobType = type;
JobGroup = group;
JobName = name;
RunTime = DateTime.Now;
}
public BackgroundJobLog SetMessage(string message, Exception ex)
{
Message = message.Length > BackgroundJobLogConsts.MaxMessageLength
? message.Substring(0, BackgroundJobLogConsts.MaxMessageLength - 1)
: message;
if (ex != null)
{
var errMsg = ex.ToString();
Exception = errMsg.Length > BackgroundJobLogConsts.MaxExceptionLength
? errMsg.Substring(0, BackgroundJobLogConsts.MaxExceptionLength - 1)
: errMsg;
}
return this;
}
}

35
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLogFilter.cs

@ -0,0 +1,35 @@
using System;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobLogFilter
{
/// <summary>
/// 其他过滤条件
/// </summary>
public string Filter { get; set; }
/// <summary>
/// 存在异常
/// </summary>
public bool? HasExceptions { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 任务分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 开始触发时间
/// </summary>
public DateTime? BeginRunTime { get; set; }
/// <summary>
/// 结束触发时间
/// </summary>
public DateTime? EndRunTime { get; set; }
}

86
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs

@ -0,0 +1,86 @@
using LINGYUN.Abp.BackgroundTasks;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.ObjectMapping;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobManager : DomainService
{
protected IObjectMapper ObjectMapper { get; }
protected IJobScheduler JobScheduler { get; }
protected IBackgroundJobInfoRepository BackgroundJobInfoRepository { get; }
public BackgroundJobManager(
IObjectMapper objectMapper,
IJobScheduler jobScheduler,
IBackgroundJobInfoRepository backgroundJobInfoRepository)
{
ObjectMapper = objectMapper;
JobScheduler = jobScheduler;
BackgroundJobInfoRepository = backgroundJobInfoRepository;
}
public virtual async Task<BackgroundJobInfo> CreateAsync(BackgroundJobInfo jobInfo)
{
await BackgroundJobInfoRepository.InsertAsync(jobInfo);
if (jobInfo.IsEnabled && jobInfo.JobType == JobType.Period)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.QueueAsync(job);
}
return jobInfo;
}
public virtual async Task<BackgroundJobInfo> UpdateAsync(BackgroundJobInfo jobInfo, bool resetJob = false)
{
await BackgroundJobInfoRepository.UpdateAsync(jobInfo);
if (!jobInfo.IsEnabled || resetJob)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.RemoveAsync(job);
}
if (resetJob && jobInfo.JobType == JobType.Period)
{
await QueueAsync(jobInfo);
}
return jobInfo;
}
public virtual async Task DeleteAsync(BackgroundJobInfo jobInfo)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.RemoveAsync(job);
await BackgroundJobInfoRepository.DeleteAsync(jobInfo);
}
public virtual async Task QueueAsync(BackgroundJobInfo jobInfo)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.QueueAsync(job);
}
public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.TriggerAsync(job);
}
public virtual async Task PauseAsync(BackgroundJobInfo jobInfo)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.PauseAsync(job);
}
public virtual async Task ResumeAsync(BackgroundJobInfo jobInfo)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.ResumeAsync(job);
}
}

135
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs

@ -0,0 +1,135 @@
using LINGYUN.Abp.BackgroundTasks;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.TaskManagement;
[Dependency(ReplaceServices = true)]
public class BackgroundJobStore : IJobStore, ITransientDependency
{
protected IObjectMapper ObjectMapper { get; }
protected IBackgroundJobInfoRepository JobInfoRepository { get; }
protected IBackgroundJobLogRepository JobLogRepository { get; }
public BackgroundJobStore(
IObjectMapper objectMapper,
IBackgroundJobInfoRepository jobInfoRepository,
IBackgroundJobLogRepository jobLogRepository)
{
ObjectMapper = objectMapper;
JobInfoRepository = jobInfoRepository;
JobLogRepository = jobLogRepository;
}
public async virtual Task<List<JobInfo>> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default)
{
var jobInfos = await JobInfoRepository.GetAllPeriodTasksAsync(cancellationToken);
return ObjectMapper.Map<List<BackgroundJobInfo>, List<JobInfo>>(jobInfos);
}
public async virtual Task<List<JobInfo>> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default)
{
var jobInfos = await JobInfoRepository.GetWaitingListAsync(maxResultCount, cancellationToken);
return ObjectMapper.Map<List<BackgroundJobInfo>, List<JobInfo>>(jobInfos);
}
public async virtual Task<JobInfo> FindAsync(Guid jobId)
{
var jobInfo = await JobInfoRepository.FindAsync(jobId);
return ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
}
[UnitOfWork]
public async virtual Task StoreAsync(JobInfo jobInfo)
{
var backgroundJobInfo = await JobInfoRepository.FindAsync(jobInfo.Id);
if (backgroundJobInfo != null)
{
backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime);
backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime);
backgroundJobInfo.SetStatus(jobInfo.Status);
backgroundJobInfo.TriggerCount = jobInfo.TriggerCount;
backgroundJobInfo.TryCount = jobInfo.TryCount;
backgroundJobInfo.IsAbandoned = jobInfo.IsAbandoned;
await JobInfoRepository.UpdateAsync(backgroundJobInfo);
}
else
{
backgroundJobInfo = new BackgroundJobInfo(
jobInfo.Id,
jobInfo.Name,
jobInfo.Group,
jobInfo.Type,
jobInfo.Args,
jobInfo.BeginTime,
jobInfo.EndTime,
jobInfo.Priority,
jobInfo.MaxCount,
jobInfo.MaxTryCount);
backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime);
backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime);
backgroundJobInfo.SetStatus(jobInfo.Status);
backgroundJobInfo.TriggerCount = jobInfo.TriggerCount;
backgroundJobInfo.IsAbandoned = jobInfo.IsAbandoned;
backgroundJobInfo.TryCount = jobInfo.TryCount;
backgroundJobInfo.LockTimeOut = jobInfo.LockTimeOut;
backgroundJobInfo.Description = jobInfo.Description;
switch (jobInfo.JobType)
{
case JobType.Once:
backgroundJobInfo.SetOnceJob(jobInfo.Interval);
break;
case JobType.Persistent:
backgroundJobInfo.SetPersistentJob(jobInfo.Interval);
break;
case JobType.Period:
backgroundJobInfo.SetPeriodJob(jobInfo.Cron);
break;
}
await JobInfoRepository.InsertAsync(backgroundJobInfo);
}
}
[UnitOfWork]
public async virtual Task StoreLogAsync(JobEventData eventData)
{
var jogLog = new BackgroundJobLog(
eventData.Type.Name,
eventData.Group,
eventData.Name)
{
JobId = eventData.Key
};
jogLog.SetMessage(
eventData.Exception == null ? eventData.Result ?? "OK" : "Failed",
eventData.Exception);
await JobLogRepository.InsertAsync(jogLog);
}
[UnitOfWork]
public async virtual Task CleanupAsync(
int maxResultCount,
TimeSpan jobExpiratime,
CancellationToken cancellationToken = default)
{
var jobs = await JobInfoRepository.GetExpiredJobsAsync(
maxResultCount,
jobExpiratime,
cancellationToken);
await JobInfoRepository.DeleteManyAsync(jobs, cancellationToken: cancellationToken);
}
}

66
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.TaskManagement;
public interface IBackgroundJobInfoRepository : IRepository<BackgroundJobInfo, Guid>
{
Task<bool> CheckNameAsync(
string group,
string name,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取过期任务列表
/// </summary>
/// <param name="maxResultCount"></param>
/// <param name="jobExpiratime"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<BackgroundJobInfo>> GetExpiredJobsAsync(
int maxResultCount,
TimeSpan jobExpiratime,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取所有周期性任务
/// 指定了Cron表达式的任务需要作为持续性任务交给任务引擎
/// </summary>
/// <returns></returns>
Task<List<BackgroundJobInfo>> GetAllPeriodTasksAsync(
CancellationToken cancellationToken = default);
/// <summary>
/// 获取等待入队的任务列表
/// </summary>
/// <param name="maxResultCount"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<BackgroundJobInfo>> GetWaitingListAsync(
int maxResultCount,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取过滤后的任务数量
/// </summary>
/// <param name="filter"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<int> GetCountAsync(
BackgroundJobInfoFilter filter,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取过滤后的任务列表
/// </summary>
/// <param name="filter"></param>
/// <param name="sorting"></param>
/// <param name="maxResultCount"></param>
/// <param name="skipCount"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<BackgroundJobInfo>> GetListAsync(
BackgroundJobInfoFilter filter,
string sorting = nameof(BackgroundJobInfo.Name),
int maxResultCount = 10,
int skipCount = 0,
CancellationToken cancellationToken = default);
}

39
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobLogRepository.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.TaskManagement;
public interface IBackgroundJobLogRepository : IRepository<BackgroundJobLog, long>
{
/// <summary>
/// 获取过滤后的任务日志数量
/// </summary>
/// <param name="filter"></param>
/// <param name="jobId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<int> GetCountAsync(
BackgroundJobLogFilter filter,
Guid? jobId = null,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取过滤后的任务日志列表
/// </summary>
/// <param name="filter"></param>
/// <param name="jobId"></param>
/// <param name="sorting"></param>
/// <param name="maxResultCount"></param>
/// <param name="skipCount"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<BackgroundJobLog>> GetListAsync(
BackgroundJobLogFilter filter,
Guid? jobId = null,
string sorting = nameof(BackgroundJobLog.RunTime),
int maxResultCount = 10,
int skipCount = 0,
CancellationToken cancellationToken = default);
}

11
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDbProperties.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.TaskManagement;
public static class TaskManagementDbProperties
{
public static string DbTablePrefix { get; set; } = "TK_";
public static string DbSchema { get; set; } = null;
public const string ConnectionStringName = "TaskManagement";
}

12
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainMapperProfile.cs

@ -0,0 +1,12 @@
using AutoMapper;
using LINGYUN.Abp.BackgroundTasks;
namespace LINGYUN.Abp.TaskManagement;
public class TaskManagementDomainMapperProfile : Profile
{
public TaskManagementDomainMapperProfile()
{
CreateMap<BackgroundJobInfo, JobInfo>();
}
}

24
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainModule.cs

@ -0,0 +1,24 @@
using LINGYUN.Abp.BackgroundTasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.TaskManagement;
[DependsOn(typeof(TaskManagementDomainSharedModule))]
[DependsOn(typeof(AbpAutoMapperModule))]
[DependsOn(typeof(AbpDddDomainModule))]
[DependsOn(typeof(AbpBackgroundTasksModule))]
public class TaskManagementDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<TaskManagementDomainModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<TaskManagementDomainMapperProfile>(validate: true);
});
}
}

3
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xsd

@ -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>

19
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.TaskManagement.Domain\LINGYUN.Abp.TaskManagement.Domain.csproj" />
</ItemGroup>
</Project>

124
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs

@ -0,0 +1,124 @@
using LINGYUN.Abp.BackgroundTasks;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore;
public class EfCoreBackgroundJobInfoRepository :
EfCoreRepository<TaskManagementDbContext, BackgroundJobInfo, Guid>,
IBackgroundJobInfoRepository
{
protected IClock Clock { get; }
public EfCoreBackgroundJobInfoRepository(
IClock clock,
IDbContextProvider<TaskManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
Clock = clock;
}
public virtual async Task<bool> CheckNameAsync(
string group,
string name,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.AllAsync(x => x.Group.Equals(group) && x.Name.Equals(name),
GetCancellationToken(cancellationToken));
}
public virtual async Task<List<BackgroundJobInfo>> GetExpiredJobsAsync(
int maxResultCount,
TimeSpan jobExpiratime,
CancellationToken cancellationToken = default)
{
var expiratime = Clock.Now - jobExpiratime;
return await (await GetDbSetAsync())
.Where(x => x.Status == JobStatus.Completed &&
DateTime.Compare(x.LastRunTime.Value, expiratime) <= 0)
.OrderBy(x => x.CreationTime)
.Take(maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<List<BackgroundJobInfo>> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(x => x.IsEnabled && !x.IsAbandoned)
.Where(x => x.JobType == JobType.Period && x.Status == JobStatus.Running)
.Where(x => x.TriggerCount < x.MaxCount && x.TryCount < x.MaxTryCount)
.OrderByDescending(x => x.Priority)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<int> GetCountAsync(BackgroundJobInfoFilter filter, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(!filter.Type.IsNullOrWhiteSpace(), x => x.Type.Contains(filter.Type))
.WhereIf(!filter.Group.IsNullOrWhiteSpace(), x => x.Group.Equals(filter.Group))
.WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.Name.Equals(filter.Name))
.WhereIf(!filter.Filter.IsNullOrWhiteSpace(), x => x.Name.Contains(filter.Filter) ||
x.Group.Contains(filter.Filter) || x.Type.Contains(filter.Filter) || x.Description.Contains(filter.Filter))
.WhereIf(filter.IsPeriod.HasValue && filter.IsPeriod.Value, x => x.JobType == JobType.Period)
.WhereIf(filter.IsPeriod.HasValue && !filter.IsPeriod.Value, x => x.JobType != JobType.Period)
.WhereIf(filter.Priority.HasValue, x => x.Priority == filter.Priority.Value)
.WhereIf(filter.Status.HasValue, x => x.Status == filter.Status.Value)
.WhereIf(filter.IsAbandoned.HasValue, x => x.IsAbandoned == filter.IsAbandoned.Value)
.WhereIf(filter.BeginLastRunTime.HasValue, x => filter.BeginLastRunTime.Value.CompareTo(x.LastRunTime) <= 0)
.WhereIf(filter.EndLastRunTime.HasValue, x => filter.EndLastRunTime.Value.CompareTo(x.LastRunTime) >= 0)
.WhereIf(filter.BeginTime.HasValue, x => x.BeginTime.CompareTo(x.BeginTime) >= 0)
.WhereIf(filter.EndTime.HasValue, x => filter.EndTime.Value.CompareTo(x.EndTime) >= 0)
.WhereIf(filter.BeginCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.BeginCreationTime.Value) >= 0)
.WhereIf(filter.EndCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.EndCreationTime.Value) <= 0)
.CountAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<List<BackgroundJobInfo>> GetListAsync(BackgroundJobInfoFilter filter, string sorting = "Name", int maxResultCount = 10, int skipCount = 0, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(!filter.Type.IsNullOrWhiteSpace(), x => x.Type.Contains(filter.Type))
.WhereIf(!filter.Group.IsNullOrWhiteSpace(), x => x.Group.Equals(filter.Group))
.WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.Name.Equals(filter.Name))
.WhereIf(!filter.Filter.IsNullOrWhiteSpace(), x => x.Name.Contains(filter.Filter) ||
x.Group.Contains(filter.Filter) || x.Type.Contains(filter.Filter) || x.Description.Contains(filter.Filter))
.WhereIf(filter.IsPeriod.HasValue && filter.IsPeriod.Value, x => !string.IsNullOrWhiteSpace(x.Cron))
.WhereIf(filter.IsPeriod.HasValue && !filter.IsPeriod.Value, x => string.IsNullOrWhiteSpace(x.Cron))
.WhereIf(filter.Status.HasValue, x => x.Status == filter.Status.Value)
.WhereIf(filter.Priority.HasValue, x => x.Priority == filter.Priority.Value)
.WhereIf(filter.IsAbandoned.HasValue, x => x.IsAbandoned == filter.IsAbandoned.Value)
.WhereIf(filter.BeginLastRunTime.HasValue, x => filter.BeginLastRunTime.Value.CompareTo(x.LastRunTime) <= 0)
.WhereIf(filter.EndLastRunTime.HasValue, x => filter.EndLastRunTime.Value.CompareTo(x.LastRunTime) >= 0)
.WhereIf(filter.BeginTime.HasValue, x => x.BeginTime.CompareTo(x.BeginTime) >= 0)
.WhereIf(filter.EndTime.HasValue, x => filter.EndTime.Value.CompareTo(x.EndTime) >= 0)
.WhereIf(filter.BeginCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.BeginCreationTime.Value) >= 0)
.WhereIf(filter.EndCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.EndCreationTime.Value) <= 0)
.OrderBy(sorting ?? nameof(BackgroundJobInfo.Name))
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<List<BackgroundJobInfo>> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default)
{
var now = Clock.Now;
return await (await GetDbSetAsync())
.Where(x => x.IsEnabled && !x.IsAbandoned)
.Where(x => x.JobType != JobType.Period && x.Status == JobStatus.Running)
.Where(x => x.TriggerCount < x.MaxCount && x.TryCount < x.MaxTryCount)
.OrderByDescending(x => x.Priority)
.ThenBy(x => x.TryCount)
.ThenBy(x => x.NextRunTime)
.Take(maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
}

63
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs

@ -0,0 +1,63 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore;
public class EfCoreBackgroundJobLogRepository :
EfCoreRepository<TaskManagementDbContext, BackgroundJobLog, long>,
IBackgroundJobLogRepository
{
public EfCoreBackgroundJobLogRepository(
IDbContextProvider<TaskManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<int> GetCountAsync(
BackgroundJobLogFilter filter,
Guid? jobId = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(jobId.HasValue, x => x.JobId.Equals(jobId))
.WhereIf(!filter.Type.IsNullOrWhiteSpace(), x => x.JobType.Contains(filter.Type))
.WhereIf(!filter.Group.IsNullOrWhiteSpace(), x => x.JobGroup.Equals(filter.Group))
.WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.JobName.Equals(filter.Name))
.WhereIf(!filter.Filter.IsNullOrWhiteSpace(), x => x.JobName.Contains(filter.Filter) ||
x.JobGroup.Contains(filter.Filter) || x.JobType.Contains(filter.Filter) || x.Message.Contains(filter.Filter))
.WhereIf(filter.HasExceptions.HasValue, x => !string.IsNullOrWhiteSpace(x.Exception))
.WhereIf(filter.BeginRunTime.HasValue, x => x.RunTime.CompareTo(filter.BeginRunTime.Value) >= 0)
.WhereIf(filter.EndRunTime.HasValue, x => x.RunTime.CompareTo(filter.EndRunTime.Value) <= 0)
.CountAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<List<BackgroundJobLog>> GetListAsync(
BackgroundJobLogFilter filter,
Guid? jobId = null,
string sorting = nameof(BackgroundJobLog.RunTime),
int maxResultCount = 10,
int skipCount = 0,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(jobId.HasValue, x => x.JobId.Equals(jobId))
.WhereIf(!filter.Type.IsNullOrWhiteSpace(), x => x.JobType.Contains(filter.Type))
.WhereIf(!filter.Group.IsNullOrWhiteSpace(), x => x.JobGroup.Equals(filter.Group))
.WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.JobName.Equals(filter.Name))
.WhereIf(!filter.Filter.IsNullOrWhiteSpace(), x => x.JobName.Contains(filter.Filter) ||
x.JobGroup.Contains(filter.Filter) || x.JobType.Contains(filter.Filter) || x.Message.Contains(filter.Filter))
.WhereIf(filter.HasExceptions.HasValue, x => !string.IsNullOrWhiteSpace(x.Exception))
.WhereIf(filter.BeginRunTime.HasValue, x => x.RunTime.CompareTo(filter.BeginRunTime.Value) >= 0)
.WhereIf(filter.EndRunTime.HasValue, x => x.RunTime.CompareTo(filter.EndRunTime.Value) <= 0)
.OrderBy(sorting ?? nameof(BackgroundJobInfo.Name))
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save