From b708afbd883184f9937f11449579cb5c1d99f890 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sat, 8 Jan 2022 22:36:25 +0800 Subject: [PATCH 01/10] enhance(tasks): add support task management --- aspnet-core/.editorconfig | 7 +- aspnet-core/LINGYUN.MicroService.All.sln | 2 +- .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ ...UN.Abp.BackgroundTasks.Abstractions.csproj | 15 ++ .../AbpBackgroundTasksAbstractionsModule.cs | 7 + .../Abp/BackgroundTasks/IJobRunnable.cs | 11 + .../BackgroundTasks/IJobRunnableExecuter.cs | 12 + .../Abp/BackgroundTasks/JobEventContext.cs | 17 ++ .../Abp/BackgroundTasks/JobEventData.cs | 90 ++++++++ .../LINGYUN/Abp/BackgroundTasks/JobInfo.cs | 104 +++++++++ .../Abp/BackgroundTasks/JobPriority.cs | 15 ++ .../Abp/BackgroundTasks/JobRunnableContext.cs | 20 ++ .../LINGYUN/Abp/BackgroundTasks/JobStatus.cs | 25 ++ .../LINGYUN/Abp/BackgroundTasks/JobType.cs | 19 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ .../LINGYUN.Abp.BackgroundTasks.Quartz.csproj | 19 ++ .../Quartz/AbpBackgroundTasksQuartzModule.cs | 19 ++ .../Quartz/IQuartzJobExecutorProvider.cs | 12 + .../Quartz/QuartzJobExecutorProvider.cs | 102 +++++++++ .../Quartz/QuartzJobListener.cs | 144 ++++++++++++ .../Quartz/QuartzJobScheduler.cs | 164 +++++++++++++ .../Quartz/QuartzJobSimpleAdapter.cs | 33 +++ .../Quartz/IJobExecutionContextExtensions.cs | 27 +++ .../README.md | 16 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ .../LINGYUN.Abp.BackgroundTasks.csproj | 22 ++ .../AbpBackgroundTasksModule.cs | 49 ++++ .../AbpBackgroundTasksOptions.cs | 69 ++++++ .../BackgroundTasks/BackgroundJobAdapter.cs | 39 ++++ .../BackgroundTasks/BackgroundJobManager.cs | 82 +++++++ .../LINGYUN/Abp/BackgroundTasks/IJobEvent.cs | 22 ++ .../Abp/BackgroundTasks/IJobEventProvider.cs | 15 ++ .../Abp/BackgroundTasks/IJobScheduler.cs | 72 ++++++ .../LINGYUN/Abp/BackgroundTasks/IJobStore.cs | 27 +++ .../Internal/BackgroundCleaningJob.cs | 18 ++ .../Internal/BackgroundKeepAliveJob.cs | 26 +++ .../Internal/BackgroundPollingJob.cs | 31 +++ .../Internal/DefaultBackgroundWorker.cs | 108 +++++++++ .../Internal/InMemoryJobStore.cs | 87 +++++++ .../Internal/JobEventProvider.cs | 41 ++++ .../Internal/JobExecutedEvent.cs | 60 +++++ .../BackgroundTasks/Internal/JobLogEvent.cs | 26 +++ .../Abp/BackgroundTasks/JobEventBase.cs | 58 +++++ .../BackgroundTasks/JobRunnableExecuter.cs | 55 +++++ .../BackgroundTasks/Primitives/ConsoleJob.cs | 13 ++ .../BackgroundTasks/Primitives/EmailingJob.cs | 14 ++ .../LINGYUN.Abp.BackgroundTasks/README.md | 116 ++++++++++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ ...askManagement.Application.Contracts.csproj | 19 ++ .../BackgroundJobInfoCreateDto.cs | 17 ++ .../BackgroundJobInfoCreateOrUpdateDto.cs | 61 +++++ .../TaskManagement/BackgroundJobInfoDto.cs | 99 ++++++++ .../BackgroundJobInfoGetListInput.cs | 65 ++++++ .../BackgroundJobInfoUpdateDto.cs | 8 + .../Abp/TaskManagement/BackgroundJobLogDto.cs | 14 ++ .../BackgroundJobLogGetListInput.cs | 36 +++ .../IBackgroundJobInfoAppService.cs | 20 ++ .../IBackgroundJobLogAppService.cs | 11 + .../Permissions/TaskManagementPermissions.cs | 17 ++ ...askManagementApplicationContractsModule.cs | 10 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ ...GYUN.Abp.TaskManagement.Application.csproj | 20 ++ .../BackgroundJobInfoAppService.cs | 161 +++++++++++++ .../TaskManagementApplicationMapperProfile.cs | 11 + .../TaskManagementApplicationModule.cs | 22 ++ .../TaskManagementApplicationService.cs | 13 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ ...UN.Abp.TaskManagement.Domain.Shared.csproj | 27 +++ .../TaskManagement/BackgroundJobInfoConsts.cs | 10 + .../TaskManagement/BackgroundJobLogConsts.cs | 7 + .../Localization/Resources/en.json | 6 + .../Localization/Resources/zh-Hans.json | 6 + .../Localization/TaskManagementResource.cs | 8 + .../TaskManagementDomainSharedModule.cs | 34 +++ .../TaskManagementErrorCodes.cs | 9 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ .../LINGYUN.Abp.TaskManagement.Domain.csproj | 21 ++ .../Abp/TaskManagement/BackgroundJobInfo.cs | 162 +++++++++++++ .../TaskManagement/BackgroundJobInfoFilter.cs | 67 ++++++ .../Abp/TaskManagement/BackgroundJobLog.cs | 39 ++++ .../TaskManagement/BackgroundJobLogFilter.cs | 35 +++ .../TaskManagement/BackgroundJobManager.cs | 86 +++++++ .../Abp/TaskManagement/BackgroundJobStore.cs | 135 +++++++++++ .../IBackgroundJobInfoRepository.cs | 66 ++++++ .../IBackgroundJobLogRepository.cs | 39 ++++ .../TaskManagementDbProperties.cs | 11 + .../TaskManagementDomainMapperProfile.cs | 12 + .../TaskManagementDomainModule.cs | 24 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ ....TaskManagement.EntityFrameworkCore.csproj | 19 ++ .../EfCoreBackgroundJobInfoRepository.cs | 124 ++++++++++ .../EfCoreBackgroundJobLogRepository.cs | 63 +++++ .../ITaskManagementDbContext.cs | 9 + .../TaskManagementDbContext.cs | 20 ++ ...agementDbContextModelCreatingExtensions.cs | 81 +++++++ ...TaskManagementEntityFrameworkCoreModule.cs | 19 ++ ...agementModelBuilderConfigurationOptions.cs | 17 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ .../LINGYUN.Abp.TaskManagement.HttpApi.csproj | 19 ++ .../TaskManagementHttpApiModule.cs | 39 ++++ .../LINGYUN.Abp.TaskManagement.Quartz.csproj | 19 ++ .../Quartz/IQuartzJobExecutorProvider.cs | 10 + .../Quartz/QuartzJobExecutorProvider.cs | 20 ++ .../Quartz/QuartzJobScheduler.cs | 63 +++++ .../Quartz/QuartzJobSchedulerOptions.cs | 9 + .../Controllers/HomeController.cs | 12 + .../Dockerfile | 15 ++ .../TaskManagementMigrationsDbContext.cs | 21 ++ ...askManagementMigrationsDbContextFactory.cs | 30 +++ ...Service.TaskManagement.HttpApi.Host.csproj | 65 ++++++ .../Program.cs | 44 ++++ .../Properties/launchSettings.json | 21 ++ .../Startup.cs | 30 +++ ...skManagementHttpApiHostModule.Configure.cs | 215 ++++++++++++++++++ .../TaskManagementHttpApiHostModule.cs | 114 ++++++++++ .../appsettings.Development.json | 118 ++++++++++ .../appsettings.json | 80 +++++++ .../dapr.sh | 1 + 127 files changed, 4928 insertions(+), 4 deletions(-) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN.Abp.BackgroundTasks.Abstractions.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnable.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventContext.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobPriority.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobStatus.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobType.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN.Abp.BackgroundTasks.Quartz.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/Quartz/IJobExecutionContextExtensions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEventProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobScheduler.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobStore.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundKeepAliveJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundPollingJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/InMemoryJobStore.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobEventProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateDto.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateOrUpdateDto.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoDto.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoGetListInput.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoUpdateDto.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogDto.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementApplicationContractsModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN.Abp.TaskManagement.Application.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationService.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN.Abp.TaskManagement.Domain.Shared.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobLogConsts.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/TaskManagementResource.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementErrorCodes.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN.Abp.TaskManagement.Domain.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoFilter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLogFilter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobLogRepository.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDbProperties.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainMapperProfile.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/ITaskManagementDbContext.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContext.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementEntityFrameworkCoreModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementModelBuilderConfigurationOptions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN.Abp.TaskManagement.HttpApi.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementHttpApiModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN.Abp.TaskManagement.Quartz.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/IQuartzJobExecutorProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobExecutorProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobScheduler.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobSchedulerOptions.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Controllers/HomeController.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContext.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContextFactory.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Program.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Properties/launchSettings.json create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Startup.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/dapr.sh diff --git a/aspnet-core/.editorconfig b/aspnet-core/.editorconfig index 2289110b2..9788d77d3 100644 --- a/aspnet-core/.editorconfig +++ b/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 diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln index 6448be090..cd76d0186 100644 --- a/aspnet-core/LINGYUN.MicroService.All.sln +++ b/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 diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN.Abp.BackgroundTasks.Abstractions.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN.Abp.BackgroundTasks.Abstractions.csproj new file mode 100644 index 000000000..958d8b36d --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN.Abp.BackgroundTasks.Abstractions.csproj @@ -0,0 +1,15 @@ + + + + + + + netstandard2.0 + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs new file mode 100644 index 000000000..3e349c78c --- /dev/null +++ b/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 +{ +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnable.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnable.cs new file mode 100644 index 000000000..e0471be11 --- /dev/null +++ b/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; + +/// +/// 任务类需要实现此接口 +/// +public interface IJobRunnable +{ + Task ExecuteAsync(JobRunnableContext context); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs new file mode 100644 index 000000000..67bceac9c --- /dev/null +++ b/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; + +/// +/// 定义任务执行者接口 +/// 可以通过它实现一些限制(例如分布式锁) +/// +public interface IJobRunnableExecuter +{ + Task ExecuteAsync(JobRunnableContext context); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventContext.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventContext.cs new file mode 100644 index 000000000..5fb3042e2 --- /dev/null +++ b/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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventData.cs new file mode 100644 index 000000000..fc1852921 --- /dev/null +++ b/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 +{ + /// + /// 任务类别 + /// + public Type Type { get; } + /// + /// 任务组别 + /// + public string Group { get; } + /// + /// 任务名称 + /// + public string Name { get; } + /// + /// 任务标识 + /// + public Guid Key { get; } + /// + /// 任务状态 + /// + public JobStatus Status { get; set; } + /// + /// 执行者租户 + /// + public Guid? TenantId { get; set; } + /// + /// 错误明细 + /// + public Exception Exception { get; } + /// + /// 任务描述 + /// + public string Description { get; set; } + /// + /// 返回参数 + /// + public string Result { get; set; } + /// + /// 触发次数 + /// + public int Triggered { get; set; } + /// + /// 最大可执行次数 + /// + public int RepeatCount { get; set; } + /// + /// 失败重试上限 + /// 默认:50 + /// + public int TryCount { get; set; } + /// + /// 最大执行次数 + /// 默认:0, 无限制 + /// + public int MaxCount { get; set; } + /// + /// 运行时间 + /// + public DateTime RunTime { get; set; } + /// + /// 上次运行时间 + /// + public DateTime? LastRunTime { get; set; } + /// + /// 下次运行时间 + /// + public DateTime? NextRunTime { get; set; } + /// + /// 连续失败且不会再次执行 + /// + 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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs new file mode 100644 index 000000000..ce724e44e --- /dev/null +++ b/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 +{ + /// + /// 任务标识 + /// + public Guid Id { get; set; } + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } + /// + /// 返回参数 + /// + public string Result { get; set; } + /// + /// 任务参数 + /// + public IDictionary Args { get; set; } + /// + /// 任务状态 + /// + public JobStatus Status { get; set; } = JobStatus.None; + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } + /// + /// 开始时间 + /// + public DateTime BeginTime { get; set; } + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + /// + /// 上次运行时间 + /// + public DateTime? LastRunTime { get; set; } + /// + /// 下一次执行时间 + /// + public DateTime? NextRunTime { get; set; } + /// + /// 任务类别 + /// + public JobType JobType { get; set; } = JobType.Once; + /// + /// Cron表达式,如果是持续任务需要指定 + /// + public string Cron { get; set; } + /// + /// 触发次数 + /// + public int TriggerCount { get; set; } + /// + /// 失败重试次数 + /// + public int TryCount { get; set; } + /// + /// 失败重试上限 + /// 默认:50 + /// + public int MaxTryCount { get; set; } = 50; + /// + /// 最大执行次数 + /// 默认:0, 无限制 + /// + public int MaxCount { get; set; } + /// + /// 连续失败且不会再次执行 + /// + public bool IsAbandoned { get; set; } + /// + /// 间隔时间,单位秒,与Cron表达式冲突 + /// 默认: 300 + /// + public int Interval { get; set; } = 300; + /// + /// 任务优先级 + /// + public JobPriority Priority { get; set; } = JobPriority.Normal; + /// + /// 任务独占超时时长(秒) + /// 0或更小不生效 + /// + public int LockTimeOut { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobPriority.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobPriority.cs new file mode 100644 index 000000000..dac91b3fa --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobPriority.cs @@ -0,0 +1,15 @@ +namespace LINGYUN.Abp.BackgroundTasks; +/// +/// 任务优先级 +/// +/// +/// 与框架保持一致 +/// +public enum JobPriority +{ + Low = 5, + BelowNormal = 10, + Normal = 0xF, + AboveNormal = 20, + High = 25 +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs new file mode 100644 index 000000000..75af712ad --- /dev/null +++ b/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 JobData { get; } + public JobRunnableContext( + Type jobType, + IServiceProvider serviceProvider, + IReadOnlyDictionary jobData) + { + JobType = jobType; + ServiceProvider = serviceProvider; + JobData = jobData; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobStatus.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobStatus.cs new file mode 100644 index 000000000..c46492592 --- /dev/null +++ b/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 +{ + /// + /// 未知 + /// + None = -1, + /// + /// 已完成 + /// + Completed = 0, + /// + /// 运行中 + /// + Running = 10, + /// + /// 已暂停 + /// + Paused = 20, + /// + /// 已停止 + /// + Stopped = 30 +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobType.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobType.cs new file mode 100644 index 000000000..2fb57a07f --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobType.cs @@ -0,0 +1,19 @@ +namespace LINGYUN.Abp.BackgroundTasks; +/// +/// 任务类别 +/// +public enum JobType +{ + /// + /// 一次性 + /// + Once, + /// + /// 周期性 + /// + Period, + /// + /// 持续性 + /// + Persistent +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN.Abp.BackgroundTasks.Quartz.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN.Abp.BackgroundTasks.Quartz.csproj new file mode 100644 index 000000000..386fea967 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN.Abp.BackgroundTasks.Quartz.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs new file mode 100644 index 000000000..983823e05 --- /dev/null +++ b/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(); + + _scheduler.ListenerManager.AddJobListener(context.ServiceProvider.GetRequiredService()); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs new file mode 100644 index 000000000..6fd447334 --- /dev/null +++ b/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 +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs new file mode 100644 index 000000000..ec3ab3501 --- /dev/null +++ b/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 Logger { protected get; set; } + + protected IClock Clock { get; } + protected AbpBackgroundTasksOptions Options { get; } + + public QuartzJobExecutorProvider( + IClock clock, + IOptions options) + { + Clock = clock; + Options = options.Value; + + Logger = NullLogger.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(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs new file mode 100644 index 000000000..7408ffcf2 --- /dev/null +++ b/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 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.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}"); + } + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs new file mode 100644 index 000000000..6b87ff4fd --- /dev/null +++ b/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 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 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 jobs) + { + var jobDictionary = new Dictionary>(); + 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 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 ShutdownAsync() + { + await StopAsync(); + + await Scheduler.Shutdown(true); + + return Scheduler.IsShutdown; + } + + public virtual async Task StartAsync() + { + if (Scheduler.InStandbyMode) + { + await Scheduler.Start(); + } + return Scheduler.InStandbyMode; + } + + public virtual async Task 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?"); + } + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs new file mode 100644 index 000000000..55b65a3af --- /dev/null +++ b/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 : 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(); + + var jobContext = new JobRunnableContext( + typeof(TJobRunnable), + ServiceProvider, + context.MergedJobDataMap.ToImmutableDictionary()); + + await jobExecuter.ExecuteAsync(jobContext); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/Quartz/IJobExecutionContextExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/Quartz/IJobExecutionContextExtensions.cs new file mode 100644 index 000000000..3d46e4614 --- /dev/null +++ b/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(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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md new file mode 100644 index 000000000..b1dd81e5b --- /dev/null +++ b/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 +} +``` diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj new file mode 100644 index 000000000..96b629b02 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj @@ -0,0 +1,22 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs new file mode 100644 index 000000000..205ccd0e9 --- /dev/null +++ b/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(); + } + + private static void AutoAddJobMonitors(IServiceCollection services) + { + var jobMonitors = new List(); + + services.OnRegistred(context => + { + if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(JobEventBase<>))) + { + jobMonitors.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + options.JobMonitors.AddIfNotContains(jobMonitors); + }); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs new file mode 100644 index 000000000..69457817b --- /dev/null +++ b/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 +{ + /// + /// 任务监听类型列表 + /// + /// + /// 用户可以实现事件监听实现自定义逻辑 + /// + public ITypeList JobMonitors { get; } + /// + /// 任务过期时间 + /// 默认: 15 days + /// + /// + /// 任务过期时间,超出时间段清理 + /// + public TimeSpan JobExpiratime { get; set; } + /// + /// 每次清理任务批次大小 + /// 默认: 1000 + /// + public int MaxJobCleanCount { get; set; } + /// + /// 清理过期任务批次Cron表达式 + /// 默认: 600秒(0 0/10 * * * ? * ) + /// + /// + /// Cron表达式 + /// + public string JobCleanCronExpression { get; set; } + /// + /// 每次轮询任务批次大小 + /// 默认: 1000 + /// + public int MaxJobFetchCount { get; set; } + /// + /// 轮询任务批次Cron表达式 + /// 默认: 30秒(0/30 * * * * ? ) + /// + /// + /// Cron表达式 + /// + public string JobFetchCronExpression { get; set; } + /// + /// 轮询任务批次时锁定任务超时时长(秒) + /// 默认:120 + /// + /// + /// 轮询任务也属于一个后台任务, 需要对每一次轮询加锁,防止重复任务入库 + /// + 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(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs new file mode 100644 index 000000000..10e2dfd3a --- /dev/null +++ b/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 : IJobRunnable +{ + public ILogger> Logger { protected get; set; } + + protected AbpBackgroundJobOptions Options { get; } + protected IServiceScopeFactory ServiceScopeFactory { get; } + protected IBackgroundJobExecuter JobExecuter { get; } + + public BackgroundJobAdapter( + IOptions options, + IBackgroundJobExecuter jobExecuter, + IServiceScopeFactory serviceScopeFactory) + { + JobExecuter = jobExecuter; + ServiceScopeFactory = serviceScopeFactory; + Options = options.Value; + + Logger = NullLogger>.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); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs new file mode 100644 index 000000000..9df770d40 --- /dev/null +++ b/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 options) + { + Clock = clock; + JobStore = jobStore; + GuidGenerator = guidGenerator; + Options = options.Value; + } + + public virtual async Task EnqueueAsync( + TArgs args, + BackgroundJobPriority priority = BackgroundJobPriority.Normal, + TimeSpan? delay = null) + { + var jobConfiguration = Options.GetJob(); + var interval = 60; + if (delay.HasValue) + { + interval = delay.Value.Seconds; + } + var jobId = GuidGenerator.Create(); + var jobArgs = new Dictionary + { + { nameof(TArgs), args }, + { "ArgsType", jobConfiguration.ArgsType.AssemblyQualifiedName }, + { "JobType", typeof(BackgroundJobAdapter).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).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, + }; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs new file mode 100644 index 000000000..3775644b4 --- /dev/null +++ b/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; + +/// +/// 挂载任务事件接口 +/// +public interface IJobEvent +{ + /// + /// 任务启动前事件 + /// + /// + /// + Task OnJobBeforeExecuted(JobEventContext context); + /// + /// 任务完成后事件 + /// + /// + /// + Task OnJobAfterExecuted(JobEventContext context); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEventProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEventProvider.cs new file mode 100644 index 000000000..9b3dc6f09 --- /dev/null +++ b/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; + +/// +/// 任务事件提供者 +/// +public interface IJobEventProvider +{ + /// + /// 返回所有任务事件注册接口 + /// + /// + IReadOnlyCollection GetAll(); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobScheduler.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobScheduler.cs new file mode 100644 index 000000000..4ec76be9a --- /dev/null +++ b/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; +/// +/// 作业调度接口 +/// +public interface IJobScheduler +{ + /// + /// 任务入队 + /// + /// + /// + Task QueueAsync(JobInfo job); + /// + /// 任务入队 + /// + /// + /// + Task QueuesAsync(IEnumerable jobs); + /// + /// 任务是否存在 + /// + /// + /// + /// + Task ExistsAsync(JobInfo job); + /// + /// 触发任务 + /// + /// + /// + /// + Task TriggerAsync(JobInfo job); + /// + /// 暂停任务 + /// + /// + /// + /// + Task PauseAsync(JobInfo job); + /// + /// 恢复暂停的任务 + /// + /// + /// + /// + Task ResumeAsync(JobInfo job); + /// + /// 移除任务 + /// + /// + /// + /// + Task RemoveAsync(JobInfo job); + /// + /// 启动任务协调器 + /// + /// + Task StartAsync(); + /// + /// 停止任务协调器 + /// + /// + Task StopAsync(); + /// + /// 释放任务协调器 + /// + /// + Task ShutdownAsync(); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobStore.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobStore.cs new file mode 100644 index 000000000..9dd69ca0c --- /dev/null +++ b/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> GetWaitingListAsync( + int maxResultCount, + CancellationToken cancellationToken = default); + + Task> GetAllPeriodTasksAsync( + CancellationToken cancellationToken = default); + + Task FindAsync(Guid jobId); + + Task StoreAsync(JobInfo jobInfo); + + Task StoreLogAsync(JobEventData eventData); + + Task CleanupAsync( + int maxResultCount, + TimeSpan jobExpiratime, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs new file mode 100644 index 000000000..ee767808d --- /dev/null +++ b/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>().Value; + var store = context.ServiceProvider.GetRequiredService(); + + await store.CleanupAsync( + options.MaxJobCleanCount, + options.JobExpiratime); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundKeepAliveJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundKeepAliveJob.cs new file mode 100644 index 000000000..1f47228c2 --- /dev/null +++ b/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(); + + var periodJobs = await store.GetAllPeriodTasksAsync(); + + if (!periodJobs.Any()) + { + return; + } + + var jobScheduler = context.ServiceProvider.GetRequiredService(); + + await jobScheduler.QueuesAsync(periodJobs); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundPollingJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundPollingJob.cs new file mode 100644 index 000000000..699c3bc72 --- /dev/null +++ b/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>().Value; + var store = context.ServiceProvider.GetRequiredService(); + + var waitingJobs = await store.GetWaitingListAsync(options.MaxJobFetchCount); + + if (!waitingJobs.Any()) + { + return; + } + + var jobScheduler = context.ServiceProvider.GetRequiredService(); + + foreach (var job in waitingJobs) + { + await jobScheduler.QueueAsync(job); + } + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs new file mode 100644 index 000000000..e86ae7879 --- /dev/null +++ b/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 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(), + 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(), + 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(), + Status = JobStatus.Running, + BeginTime = DateTime.Now, + CreationTime = DateTime.Now, + Cron = _options.JobCleanCronExpression, + JobType = JobType.Period, + Priority = JobPriority.High, + Type = typeof(BackgroundCleaningJob).AssemblyQualifiedName, + }; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/InMemoryJobStore.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/InMemoryJobStore.cs new file mode 100644 index 000000000..b6c654ebc --- /dev/null +++ b/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 _memoryJobStore; + + public InMemoryJobStore() + { + _memoryJobStore = new List(); + } + + public Task> 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> 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 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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobEventProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobEventProvider.cs new file mode 100644 index 000000000..b0e6e5da2 --- /dev/null +++ b/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> _lazyEvents; + private List _events => _lazyEvents.Value; + + private readonly IServiceProvider _serviceProvider; + private readonly AbpBackgroundTasksOptions _options; + public JobEventProvider( + IOptions options, + IServiceProvider serviceProvider) + { + _options = options.Value; + _serviceProvider = serviceProvider; + + _lazyEvents = new Lazy>(CreateJobEvents); + } + public IReadOnlyCollection GetAll() + { + return _events.ToImmutableList(); + } + + private List CreateJobEvents() + { + var jobEvents = _options + .JobMonitors + .Select(p => _serviceProvider.GetRequiredService(p) as IJobEvent) + .ToList(); + + return jobEvents; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs new file mode 100644 index 000000000..dc64301b2 --- /dev/null +++ b/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, ITransientDependency +{ + protected override async Task OnJobAfterExecutedAsync(JobEventContext context) + { + var store = context.ServiceProvider.GetRequiredService(); + + 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(); + await jobScheduler.RemoveAsync(jobInfo); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs new file mode 100644 index 000000000..6fb015762 --- /dev/null +++ b/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; + +/// +/// 存储任务日志 +/// +/// +/// 任务类型标记了 特性则不会记录日志 +/// +internal class JobLogEvent : JobEventBase, ITransientDependency +{ + protected async override Task OnJobAfterExecutedAsync(JobEventContext context) + { + if (context.EventData.Type.IsDefined(typeof(DisableAuditingAttribute), true)) + { + return; + } + var store = context.ServiceProvider.GetRequiredService(); + + await store.StoreLogAsync(context.EventData); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs new file mode 100644 index 000000000..a7174915f --- /dev/null +++ b/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 : IJobEvent +{ + public ILogger Logger { protected get; set; } + protected JobEventBase() + { + Logger = NullLogger.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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs new file mode 100644 index 000000000..24ad6721f --- /dev/null +++ b/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(); + 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(); + 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); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs new file mode 100644 index 000000000..a108d492c --- /dev/null +++ b/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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs new file mode 100644 index 000000000..b8e3f95e8 --- /dev/null +++ b/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) + { + + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md new file mode 100644 index 000000000..a636695dc --- /dev/null +++ b/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(); + + // 将周期性(5秒一次)任务添加到队列 + await scheduler.QueueAsync(new JobInfo + { + Type = typeof(ConsoleJob).AssemblyQualifiedName, + Args = new Dictionary(), + 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(), + 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(), + 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(); + 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, ITransientDependency +{ + public override Task ExecuteAsync(SmsJobArgs args) + { + Console.WriteLine($"Send sms message: {args.Message}"); + + return Task.CompletedTask; + } +} +``` diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj new file mode 100644 index 000000000..d7b51dbfa --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateDto.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateDto.cs new file mode 100644 index 000000000..fe774ecec --- /dev/null +++ b/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 +{ + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateOrUpdateDto.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoCreateOrUpdateDto.cs new file mode 100644 index 000000000..e2e17e87e --- /dev/null +++ b/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 +{ + /// + /// 是否启用 + /// + public bool IsEnabled { get; set; } + /// + /// 任务参数 + /// + public ExtraPropertyDictionary Args { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 开始时间 + /// + public DateTime BeginTime { get; set; } + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + /// + /// 任务类别 + /// + public JobType JobType { get; set; } + /// + /// Cron表达式,如果是持续任务需要指定 + /// + public string Cron { get; set; } + /// + /// 失败重试上限 + /// 默认:50 + /// + public int MaxTryCount { get; set; } + /// + /// 最大执行次数 + /// 默认:0, 无限制 + /// + public int MaxCount { get; set; } + /// + /// 间隔时间,单位秒,与Cron表达式冲突 + /// 默认: 300 + /// + public int Interval { get; set; } = 300; + /// + /// 任务优先级 + /// + public JobPriority Priority { get; set; } + /// + /// 任务独占超时时长(秒) + /// 0或更小不生效 + /// + public int LockTimeOut { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoDto.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoDto.cs new file mode 100644 index 000000000..7506f0b38 --- /dev/null +++ b/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 +{ + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } + /// + /// 返回参数 + /// + public string Result { get; set; } + /// + /// 任务参数 + /// + public ExtraPropertyDictionary Args { get; set; } + /// + /// 任务状态 + /// + public JobStatus Status { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 开始时间 + /// + public DateTime BeginTime { get; set; } + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + /// + /// 上次运行时间 + /// + public DateTime? LastRunTime { get; set; } + /// + /// 下一次执行时间 + /// + public DateTime? NextRunTime { get; set; } + /// + /// 任务类别 + /// + public JobType JobType { get; set; } + /// + /// Cron表达式,如果是持续任务需要指定 + /// + public string Cron { get; set; } + /// + /// 触发次数 + /// + public int TriggerCount { get; set; } + /// + /// 失败重试次数 + /// + public int TryCount { get; set; } + /// + /// 失败重试上限 + /// 默认:50 + /// + public int MaxTryCount { get; set; } + /// + /// 最大执行次数 + /// 默认:0, 无限制 + /// + public int MaxCount { get; set; } + /// + /// 连续失败且不会再次执行 + /// + public bool IsAbandoned { get; set; } + /// + /// 间隔时间,单位秒,与Cron表达式冲突 + /// 默认: 300 + /// + public int Interval { get; set; } = 300; + /// + /// 任务优先级 + /// + public JobPriority Priority { get; set; } + /// + /// 任务独占超时时长(秒) + /// 0或更小不生效 + /// + public int LockTimeOut { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoGetListInput.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoGetListInput.cs new file mode 100644 index 000000000..1151449dc --- /dev/null +++ b/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 +{ + /// + /// 其他过滤条件 + /// + public string Filter { get; set; } + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } + /// + /// 任务状态 + /// + public JobStatus? Status { get; set; } + /// + /// 开始时间 + /// + public DateTime? BeginTime { get; set; } + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + /// + /// 上次起始触发时间 + /// + public DateTime? BeginLastRunTime { get; set; } + /// + /// 上次截止触发时间 + /// + public DateTime? EndLastRunTime { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 截止创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 是否已放弃任务 + /// + public bool? IsAbandoned { get; set; } + /// + /// 是否持续性任务 + /// + public bool? IsPeriod { get; set; } + /// + /// 优先级 + /// + public JobPriority? Priority { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoUpdateDto.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoUpdateDto.cs new file mode 100644 index 000000000..17279a3b1 --- /dev/null +++ b/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; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogDto.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogDto.cs new file mode 100644 index 000000000..3411c5e9a --- /dev/null +++ b/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 +{ + 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; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs new file mode 100644 index 000000000..2967be0aa --- /dev/null +++ b/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 +{ + /// + /// 其他过滤条件 + /// + public string Filter { get; set; } + /// + /// 存在异常 + /// + public bool? HasExceptions { get; set; } + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } + /// + /// 开始触发时间 + /// + public DateTime? BeginRunTime { get; set; } + /// + /// 结束触发时间 + /// + public DateTime? EndRunTime { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs new file mode 100644 index 000000000..c0cf3a132 --- /dev/null +++ b/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); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs new file mode 100644 index 000000000..cc41aae14 --- /dev/null +++ b/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> +{ +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs new file mode 100644 index 000000000..8ca7655fe --- /dev/null +++ b/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"; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementApplicationContractsModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementApplicationContractsModule.cs new file mode 100644 index 000000000..4288d4fdf --- /dev/null +++ b/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 +{ +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN.Abp.TaskManagement.Application.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN.Abp.TaskManagement.Application.csproj new file mode 100644 index 000000000..df4140ea7 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN.Abp.TaskManagement.Application.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs new file mode 100644 index 000000000..222683669 --- /dev/null +++ b/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 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); + } + + [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 GetAsync(Guid id) + { + var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id); + + return ObjectMapper.Map(backgroundJobInfo); + } + + public virtual async Task> 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(totalCount, + ObjectMapper.Map, List>(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 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); + } + + 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; + } + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs new file mode 100644 index 000000000..11468eb23 --- /dev/null +++ b/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(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationModule.cs new file mode 100644 index 000000000..3bc12d61c --- /dev/null +++ b/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(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationService.cs new file mode 100644 index 000000000..6ca7ebba1 --- /dev/null +++ b/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); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN.Abp.TaskManagement.Domain.Shared.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN.Abp.TaskManagement.Domain.Shared.csproj new file mode 100644 index 000000000..a268776f7 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN.Abp.TaskManagement.Domain.Shared.csproj @@ -0,0 +1,27 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs new file mode 100644 index 000000000..bc316921f --- /dev/null +++ b/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; +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobLogConsts.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobLogConsts.cs new file mode 100644 index 000000000..618438e20 --- /dev/null +++ b/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; +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json new file mode 100644 index 000000000..10f2dda00 --- /dev/null +++ b/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" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..5b3611e36 --- /dev/null +++ b/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": "任务管理" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/TaskManagementResource.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/TaskManagementResource.cs new file mode 100644 index 000000000..9e8624631 --- /dev/null +++ b/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 +{ +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementDomainSharedModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementDomainSharedModule.cs new file mode 100644 index 000000000..02c1252b3 --- /dev/null +++ b/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(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add() + .AddVirtualJson("/LINGYUN/Abp/TaskManagement/Localization/Resources"); + }); + + Configure(options => + { + options.MapCodeNamespace(TaskManagementErrorCodes.Namespace, typeof(TaskManagementResource)); + }); + } +} \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementErrorCodes.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/TaskManagementErrorCodes.cs new file mode 100644 index 000000000..faa4bb6af --- /dev/null +++ b/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"; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN.Abp.TaskManagement.Domain.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN.Abp.TaskManagement.Domain.csproj new file mode 100644 index 000000000..7b045dcbe --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN.Abp.TaskManagement.Domain.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs new file mode 100644 index 000000000..4704f65b9 --- /dev/null +++ b/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 +{ + /// + /// 任务名称 + /// + public virtual string Name { get; protected set; } + /// + /// 任务分组 + /// + public virtual string Group { get; protected set; } + /// + /// 任务类型 + /// + public virtual string Type { get; protected set; } + /// + /// 任务参数 + /// + public virtual ExtraPropertyDictionary Args { get; protected set; } + /// + /// 任务状态 + /// + public virtual JobStatus Status { get; protected set; } + /// + /// 是否启用 + /// + public virtual bool IsEnabled { get; set; } + /// + /// 描述 + /// + public virtual string Description { get; set; } + /// + /// 任务独占超时时长(秒) + /// 0或更小不生效 + /// + public virtual int LockTimeOut { get; set; } + /// + /// 开始时间 + /// + public virtual DateTime BeginTime { get; protected set; } + /// + /// 结束时间 + /// + public virtual DateTime? EndTime { get; protected set; } + /// + /// 上次执行时间 + /// + public virtual DateTime? LastRunTime { get; protected set; } + /// + /// 下次执行时间 + /// + public virtual DateTime? NextRunTime { get; protected set; } + /// + /// 任务类别 + /// + public virtual JobType JobType { get; protected set; } + /// + /// Cron表达式,如果是持续任务需要指定 + /// + public virtual string Cron { get; protected set; } + /// + /// 任务优先级 + /// + public virtual JobPriority Priority { get; protected set; } + /// + /// 触发次数 + /// + public virtual int TriggerCount { get; set; } + /// + /// 失败重试次数 + /// + public virtual int TryCount { get; set; } + /// + /// 失败重试上限 + /// 默认:50 + /// + public virtual int MaxTryCount { get; set; } + /// + /// 最大执行次数 + /// 默认:0, 无限制 + /// + public virtual int MaxCount { get; set; } + /// + /// 间隔时间,单位秒,与Cron表达式冲突 + /// 默认: 300 + /// + public virtual int Interval { get; protected set; } + /// + /// 连续失败且不会再次执行 + /// + public virtual bool IsAbandoned { get; set; } + + protected BackgroundJobInfo() { } + + public BackgroundJobInfo( + Guid id, + string name, + string group, + string type, + IDictionary 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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoFilter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoFilter.cs new file mode 100644 index 000000000..53573fba8 --- /dev/null +++ b/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; + +/// +/// 后台任务过滤 +/// +public class BackgroundJobInfoFilter +{ + /// + /// 其他过滤条件 + /// + public string Filter { get; set; } + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } + /// + /// 任务状态 + /// + public JobStatus? Status { get; set; } + /// + /// 开始时间 + /// + public DateTime? BeginTime { get; set; } + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + /// + /// 上次起始触发时间 + /// + public DateTime? BeginLastRunTime { get; set; } + /// + /// 上次截止触发时间 + /// + public DateTime? EndLastRunTime { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 截止创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 是否已放弃任务 + /// + public bool? IsAbandoned { get; set; } + /// + /// 是否持续性任务 + /// + public bool? IsPeriod { get; set; } + /// + /// 优先级 + /// + public JobPriority? Priority { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs new file mode 100644 index 000000000..9ae5ca1d0 --- /dev/null +++ b/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 +{ + 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; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLogFilter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLogFilter.cs new file mode 100644 index 000000000..99bbdd984 --- /dev/null +++ b/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 +{ + /// + /// 其他过滤条件 + /// + public string Filter { get; set; } + /// + /// 存在异常 + /// + public bool? HasExceptions { get; set; } + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务分组 + /// + public string Group { get; set; } + /// + /// 任务类型 + /// + public string Type { get; set; } + /// + /// 开始触发时间 + /// + public DateTime? BeginRunTime { get; set; } + /// + /// 结束触发时间 + /// + public DateTime? EndRunTime { get; set; } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs new file mode 100644 index 000000000..06b39aaea --- /dev/null +++ b/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 CreateAsync(BackgroundJobInfo jobInfo) + { + await BackgroundJobInfoRepository.InsertAsync(jobInfo); + + if (jobInfo.IsEnabled && jobInfo.JobType == JobType.Period) + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.QueueAsync(job); + } + + return jobInfo; + } + + public virtual async Task UpdateAsync(BackgroundJobInfo jobInfo, bool resetJob = false) + { + await BackgroundJobInfoRepository.UpdateAsync(jobInfo); + + if (!jobInfo.IsEnabled || resetJob) + { + var job = ObjectMapper.Map(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(jobInfo); + await JobScheduler.RemoveAsync(job); + + await BackgroundJobInfoRepository.DeleteAsync(jobInfo); + } + + public virtual async Task QueueAsync(BackgroundJobInfo jobInfo) + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.QueueAsync(job); + } + + public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo) + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.TriggerAsync(job); + } + + public virtual async Task PauseAsync(BackgroundJobInfo jobInfo) + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.PauseAsync(job); + } + + public virtual async Task ResumeAsync(BackgroundJobInfo jobInfo) + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.ResumeAsync(job); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs new file mode 100644 index 000000000..e5498ab6f --- /dev/null +++ b/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> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default) + { + var jobInfos = await JobInfoRepository.GetAllPeriodTasksAsync(cancellationToken); + + return ObjectMapper.Map, List>(jobInfos); + } + + public async virtual Task> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default) + { + var jobInfos = await JobInfoRepository.GetWaitingListAsync(maxResultCount, cancellationToken); + + return ObjectMapper.Map, List>(jobInfos); + } + + public async virtual Task FindAsync(Guid jobId) + { + var jobInfo = await JobInfoRepository.FindAsync(jobId); + + return ObjectMapper.Map(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); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs new file mode 100644 index 000000000..bffca21b9 --- /dev/null +++ b/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 +{ + Task CheckNameAsync( + string group, + string name, + CancellationToken cancellationToken = default); + /// + /// 获取过期任务列表 + /// + /// + /// + /// + /// + Task> GetExpiredJobsAsync( + int maxResultCount, + TimeSpan jobExpiratime, + CancellationToken cancellationToken = default); + /// + /// 获取所有周期性任务 + /// 指定了Cron表达式的任务需要作为持续性任务交给任务引擎 + /// + /// + Task> GetAllPeriodTasksAsync( + CancellationToken cancellationToken = default); + /// + /// 获取等待入队的任务列表 + /// + /// + /// + /// + Task> GetWaitingListAsync( + int maxResultCount, + CancellationToken cancellationToken = default); + /// + /// 获取过滤后的任务数量 + /// + /// + /// + /// + Task GetCountAsync( + BackgroundJobInfoFilter filter, + CancellationToken cancellationToken = default); + /// + /// 获取过滤后的任务列表 + /// + /// + /// + /// + /// + /// + /// + Task> GetListAsync( + BackgroundJobInfoFilter filter, + string sorting = nameof(BackgroundJobInfo.Name), + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobLogRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobLogRepository.cs new file mode 100644 index 000000000..e9036a51a --- /dev/null +++ b/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 +{ + /// + /// 获取过滤后的任务日志数量 + /// + /// + /// + /// + /// + Task GetCountAsync( + BackgroundJobLogFilter filter, + Guid? jobId = null, + CancellationToken cancellationToken = default); + /// + /// 获取过滤后的任务日志列表 + /// + /// + /// + /// + /// + /// + /// + /// + Task> GetListAsync( + BackgroundJobLogFilter filter, + Guid? jobId = null, + string sorting = nameof(BackgroundJobLog.RunTime), + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDbProperties.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDbProperties.cs new file mode 100644 index 000000000..a396d4036 --- /dev/null +++ b/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"; +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainMapperProfile.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainMapperProfile.cs new file mode 100644 index 000000000..e5e133e55 --- /dev/null +++ b/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(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/TaskManagementDomainModule.cs new file mode 100644 index 000000000..2d75c167c --- /dev/null +++ b/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(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + } +} \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj new file mode 100644 index 000000000..60375de9f --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs new file mode 100644 index 000000000..8cc7bc601 --- /dev/null +++ b/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, + IBackgroundJobInfoRepository +{ + protected IClock Clock { get; } + + public EfCoreBackgroundJobInfoRepository( + IClock clock, + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + Clock = clock; + } + + public virtual async Task 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> 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> 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 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> 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> 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)); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs new file mode 100644 index 000000000..4508de1a0 --- /dev/null +++ b/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, + IBackgroundJobLogRepository +{ + public EfCoreBackgroundJobLogRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task 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> 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)); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/ITaskManagementDbContext.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/ITaskManagementDbContext.cs new file mode 100644 index 000000000..c6284c0ab --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/ITaskManagementDbContext.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; + +[ConnectionStringName(TaskManagementDbProperties.ConnectionStringName)] +public interface ITaskManagementDbContext :IEfCoreDbContext +{ +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContext.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContext.cs new file mode 100644 index 000000000..a6d1f99ac --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContext.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; + +public class TaskManagementDbContext : AbpDbContext, ITaskManagementDbContext +{ + public TaskManagementDbContext( + DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureTaskManagement(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs new file mode 100644 index 000000000..c32fabef7 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore; +using System; +using Volo.Abp; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.EntityFrameworkCore.ValueComparers; +using Volo.Abp.EntityFrameworkCore.ValueConverters; + +namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; + +public static class TaskManagementDbContextModelCreatingExtensions +{ + public static void ConfigureTaskManagement( + this ModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new TaskManagementModelBuilderConfigurationOptions( + TaskManagementDbProperties.DbTablePrefix, + TaskManagementDbProperties.DbSchema + ); + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "BackgroundJobs", options.Schema); + + b.Property(p => p.Name) + .HasColumnName(nameof(BackgroundJobInfo.Name)) + .HasMaxLength(BackgroundJobInfoConsts.MaxNameLength) + .IsRequired(); + b.Property(p => p.Group) + .HasColumnName(nameof(BackgroundJobInfo.Group)) + .HasMaxLength(BackgroundJobInfoConsts.MaxGroupLength) + .IsRequired(); + b.Property(p => p.Type) + .HasColumnName(nameof(BackgroundJobInfo.Type)) + .HasMaxLength(BackgroundJobInfoConsts.MaxTypeLength) + .IsRequired(); + b.Property(p => p.Cron) + .HasColumnName(nameof(BackgroundJobInfo.Cron)) + .HasMaxLength(BackgroundJobInfoConsts.MaxCronLength); + b.Property(p => p.Description) + .HasColumnName(nameof(BackgroundJobInfo.Description)) + .HasMaxLength(BackgroundJobInfoConsts.MaxDescriptionLength); + b.Property(p => p.Args) + .HasColumnName(nameof(BackgroundJobInfo.Args)) + .HasConversion(new ExtraPropertiesValueConverter(b.Metadata.ClrType)) + .Metadata.SetValueComparer(new ExtraPropertyDictionaryValueComparer()); + + b.ConfigureByConvention(); + + b.HasIndex(p => new { p.Name, p.Group }); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "BackgroundJobLogs", options.Schema); + + b.Property(p => p.JobName) + .HasColumnName(nameof(BackgroundJobLog.JobName)) + .HasMaxLength(BackgroundJobInfoConsts.MaxNameLength); + b.Property(p => p.JobGroup) + .HasColumnName(nameof(BackgroundJobLog.JobGroup)) + .HasMaxLength(BackgroundJobInfoConsts.MaxGroupLength); + b.Property(p => p.JobType) + .HasColumnName(nameof(BackgroundJobLog.JobType)) + .HasMaxLength(BackgroundJobInfoConsts.MaxTypeLength); + b.Property(p => p.Message) + .HasColumnName(nameof(BackgroundJobLog.Message)) + .HasMaxLength(BackgroundJobLogConsts.MaxMessageLength); + b.Property(p => p.Exception) + .HasColumnName(nameof(BackgroundJobLog.Exception)) + .HasMaxLength(BackgroundJobLogConsts.MaxExceptionLength); + + b.ConfigureByConvention(); + + b.HasIndex(p => new { p.JobGroup, p.JobName }); + }); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementEntityFrameworkCoreModule.cs new file mode 100644 index 000000000..f2edc4178 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementEntityFrameworkCoreModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; + +[DependsOn(typeof(TaskManagementDomainModule))] +[DependsOn(typeof(AbpEntityFrameworkCoreModule))] +public class TaskManagementEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementModelBuilderConfigurationOptions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementModelBuilderConfigurationOptions.cs new file mode 100644 index 000000000..03b205c7c --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementModelBuilderConfigurationOptions.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; + +public class TaskManagementModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions +{ + public TaskManagementModelBuilderConfigurationOptions( + [NotNull] string tablePrefix = "", + [CanBeNull] string schema = null) + : base( + tablePrefix, + schema) + { + + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN.Abp.TaskManagement.HttpApi.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN.Abp.TaskManagement.HttpApi.csproj new file mode 100644 index 000000000..128b311a8 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN.Abp.TaskManagement.HttpApi.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementHttpApiModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementHttpApiModule.cs new file mode 100644 index 000000000..b6d43f111 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementHttpApiModule.cs @@ -0,0 +1,39 @@ +using LINGYUN.Abp.TaskManagement.Localization; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.Validation.Localization; + +namespace LINGYUN.Abp.TaskManagement; + +[DependsOn(typeof(TaskManagementApplicationContractsModule))] +[DependsOn(typeof(AbpAspNetCoreMvcModule))] +public class TaskManagementHttpApiModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(TaskManagementHttpApiModule).Assembly); + }); + + PreConfigure(options => + { + options.AddAssemblyResource( + typeof(TaskManagementResource), + typeof(TaskManagementApplicationContractsModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpValidationResource)); + }); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN.Abp.TaskManagement.Quartz.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN.Abp.TaskManagement.Quartz.csproj new file mode 100644 index 000000000..a079141f7 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN.Abp.TaskManagement.Quartz.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/IQuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/IQuartzJobExecutorProvider.cs new file mode 100644 index 000000000..23f8d4b4f --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/IQuartzJobExecutorProvider.cs @@ -0,0 +1,10 @@ +using Quartz; + +namespace LINGYUN.Abp.TaskManagement.Quartz; + +public interface IQuartzJobExecutorProvider +{ + IJobDetail CreateJob(JobInfo job); + + ITrigger CreateTrigger(JobInfo job); +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobExecutorProvider.cs new file mode 100644 index 000000000..97082fe02 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobExecutorProvider.cs @@ -0,0 +1,20 @@ +using Quartz; +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.TaskManagement.Quartz; + +public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonDependency +{ + public IJobDetail CreateJob(JobInfo job) + { + throw new NotImplementedException(); + } + + public ITrigger CreateTrigger(JobInfo job) + { + throw new NotImplementedException(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobScheduler.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobScheduler.cs new file mode 100644 index 000000000..0e8a636b9 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobScheduler.cs @@ -0,0 +1,63 @@ +using Quartz; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.TaskManagement.Quartz; +public class QuartzJobScheduler : IJobScheduler +{ + protected IScheduler Scheduler { get; } + + public QuartzJobScheduler(IScheduler scheduler) + { + Scheduler = scheduler; + } + + public virtual async Task ExistsAsync(string group, string name) + { + throw new System.NotImplementedException(); + } + + public Task PauseAsync(string group, string name) + { + throw new System.NotImplementedException(); + } + + public Task QueueAsync(JobInfo job) + { + throw new System.NotImplementedException(); + } + + public Task RefreshAsync(JobInfo job) + { + throw new System.NotImplementedException(); + } + + public Task RemoveAsync(string group, string name) + { + throw new System.NotImplementedException(); + } + + public Task ResumeAsync(string group, string name) + { + throw new System.NotImplementedException(); + } + + public Task ShutdownAsync() + { + throw new System.NotImplementedException(); + } + + public Task StartAsync() + { + throw new System.NotImplementedException(); + } + + public Task StopAsync() + { + throw new System.NotImplementedException(); + } + + public Task TriggerAsync(JobInfo job) + { + throw new System.NotImplementedException(); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobSchedulerOptions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobSchedulerOptions.cs new file mode 100644 index 000000000..dbd0c774e --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Quartz/LINGYUN/Abp/TaskManagement/Quartz/QuartzJobSchedulerOptions.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LINGYUN.Abp.TaskManagement.Quartz; + +internal class QuartzJobSchedulerOptions +{ +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Controllers/HomeController.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Controllers/HomeController.cs new file mode 100644 index 000000000..f98d6c1ca --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Controllers/HomeController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace LY.MicroService.TaskManagement.Controllers; + +public class HomeController : AbpController +{ + public IActionResult Index() + { + return Redirect("/swagger/index.html"); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile new file mode 100644 index 000000000..a38f2b0c8 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +LABEL maintainer="colin.in@foxmail.com" +WORKDIR /app + +COPY . /app + +#东8区 +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone + +EXPOSE 80/tcp +VOLUME [ "./app/Logs" ] +VOLUME [ "./app/Modules" ] + +ENTRYPOINT ["dotnet", "LY.MicroService.TaskManagement.HttpApi.Host.dll"] diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContext.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContext.cs new file mode 100644 index 000000000..229b8fdcc --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContext.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.TaskManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LY.MicroService.TaskManagement.EntityFrameworkCore; + +public class TaskManagementMigrationsDbContext : AbpDbContext +{ + public TaskManagementMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureTaskManagement(); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContextFactory.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContextFactory.cs new file mode 100644 index 000000000..7ef7887cc --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/EntityFrameworkCore/TaskManagementMigrationsDbContextFactory.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using System.IO; + +namespace LY.MicroService.TaskManagement.EntityFrameworkCore; + +public class TaskManagementMigrationsDbContextFactory : IDesignTimeDbContextFactory +{ + public TaskManagementMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + var connectionString = configuration.GetConnectionString("TaskManagement"); + + var builder = new DbContextOptionsBuilder() + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); + + return new TaskManagementMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false) + .AddJsonFile("appsettings.Development.json", optional: true); + + return builder.Build(); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj new file mode 100644 index 000000000..ccec8e8b2 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj @@ -0,0 +1,65 @@ + + + + False + False + False + + + + False + False + False + + + + net6.0 + LY.MicroService.TaskManagement + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Program.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Program.cs new file mode 100644 index 000000000..013d47509 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Program.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace LY.MicroService.TaskManagement; + +public class Program +{ + public static int Main(string[] args) + { + try + { + var host = CreateHostBuilder(args).Build(); + Log.Information("Starting web host."); + host.Run(); + return 0; + } + finally + { + Log.CloseAndFlush(); + } + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }) + .ConfigureAppConfiguration((context, config) => + { + var configuration = config.Build(); + if (configuration.GetSection("AgileConfig").Exists()) + { + config.AddAgileConfig(new AgileConfig.Client.ConfigClient(configuration)); + } + }) + .UseSerilog((context, config) => + { + config.ReadFrom.Configuration(context.Configuration); + }) + .UseAutofac(); +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Properties/launchSettings.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Properties/launchSettings.json new file mode 100644 index 000000000..38de94ebd --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Properties/launchSettings.json @@ -0,0 +1,21 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:43766", + "sslPort": 0 + } + }, + "profiles": { + "LY.MicroService.TaskManagement.HttpApi.Host": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://127.0.0.1:30040", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Startup.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Startup.cs new file mode 100644 index 000000000..35612530f --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Startup.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using System.IO; +using Volo.Abp.IO; +using Volo.Abp.Modularity.PlugIns; + +namespace LY.MicroService.TaskManagement; + +public class Startup +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + // 搜索 Modules 目录下所有文件作为插件 + // 取消显示引用所有其他项目的模块,改为通过插件的形式引用 + var pluginFolder = Path.Combine( + Directory.GetCurrentDirectory(), "Modules"); + DirectoryHelper.CreateIfNotExists(pluginFolder); + options.PlugInSources.AddFolder( + pluginFolder, + SearchOption.AllDirectories); + }); + } + + public void Configure(IApplicationBuilder app) + { + app.InitializeApplication(); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs new file mode 100644 index 000000000..6eafb7798 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs @@ -0,0 +1,215 @@ +using LINGYUN.Abp.ExceptionHandling; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using Medallion.Threading; +using Medallion.Threading.Redis; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using StackExchange.Redis; +using System; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Volo.Abp; +using Volo.Abp.Auditing; +using Volo.Abp.Caching; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Json; +using Volo.Abp.Json.SystemTextJson; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; +using Volo.Abp.VirtualFileSystem; + +namespace LY.MicroService.TaskManagement; + +public partial class TaskManagementHttpApiHostModule +{ + private void PreConfigureApp() + { + AbpSerilogEnrichersConsts.ApplicationName = "TaskManagement"; + } + + private void ConfigureDistributedLock(IServiceCollection services, IConfiguration configuration) + { + var redis = ConnectionMultiplexer.Connect(configuration["DistributedLock:Redis:Configuration"]); + services.AddSingleton(_ => new RedisDistributedSynchronizationProvider(redis.GetDatabase())); + } + + private void ConfigureDbContext() + { + // 配置Ef + Configure(options => + { + options.UseMySQL(); + }); + } + + private void ConfigureJsonSerializer() + { + // 解决某些不支持类型的序列化 + Configure(options => + { + options.DefaultDateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + }); + // 中文序列化的编码问题 + Configure(options => + { + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + }); + } + + private void ConfigureExceptionHandling() + { + // 自定义需要处理的异常 + Configure(options => + { + // 加入需要处理的异常类型 + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + }); + // 自定义需要发送邮件通知的异常类型 + Configure(options => + { + // 是否发送堆栈信息 + options.SendStackTrace = true; + // 未指定异常接收者的默认接收邮件 + // 指定自己的邮件地址 + }); + } + + private void ConfigureAuditing(IConfiguration configuration) + { + Configure(options => + { + options.ApplicationName = "TaskManagement"; + // 是否启用实体变更记录 + var entitiesChangedConfig = configuration.GetSection("App:TrackingEntitiesChanged"); + if (entitiesChangedConfig.Exists() && entitiesChangedConfig.Get()) + { + options + .EntityHistorySelectors + .AddAllEntities(); + } + }); + } + + private void ConfigureCaching(IConfiguration configuration) + { + Configure(options => + { + // 最好统一命名,不然某个缓存变动其他应用服务有例外发生 + options.KeyPrefix = "LINGYUN.Abp.Application"; + // 滑动过期30天 + options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30d); + // 绝对过期60天 + options.GlobalCacheEntryOptions.AbsoluteExpiration = DateTimeOffset.Now.AddDays(60d); + }); + + Configure(options => + { + var redisConfig = ConfigurationOptions.Parse(options.Configuration); + options.ConfigurationOptions = redisConfig; + options.InstanceName = configuration["Redis:InstanceName"]; + }); + } + + private void ConfigureVirtualFileSystem() + { + Configure(options => + { + options.FileSets.AddEmbedded("LINGYUN.Abp.TaskManagement"); + }); + } + + private void ConfigureMultiTenancy(IConfiguration configuration) + { + // 多租户 + Configure(options => + { + options.IsEnabled = true; + }); + + var tenantResolveCfg = configuration.GetSection("App:Domains"); + if (tenantResolveCfg.Exists()) + { + Configure(options => + { + var domains = tenantResolveCfg.Get(); + foreach (var domain in domains) + { + options.AddDomainTenantResolver(domain); + } + }); + } + } + + private void ConfigureSwagger(IServiceCollection services) + { + // Swagger + services.AddSwaggerGen( + options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "WorkflowManagement API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); + }); + } + + private void ConfigureLocalization() + { + // 支持本地化语言类型 + Configure(options => + { + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + // 动态语言支持 + options.Resources.AddDynamic(); + }); + } + + private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false) + { + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.Audience = configuration["AuthServer:ApiName"]; + }); + + if (!isDevelopment) + { + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); + services + .AddDataProtection() + .SetApplicationName("LINGYUN.Abp.Application") + .PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys"); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs new file mode 100644 index 000000000..453210150 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs @@ -0,0 +1,114 @@ +using LINGYUN.Abp.AuditLogging.Elasticsearch; +using LINGYUN.Abp.BackgroundTasks.Quartz; +using LINGYUN.Abp.Data.DbMigrator; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; +using LINGYUN.Abp.MultiTenancy.DbFinder; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using LINGYUN.Abp.Serilog.Enrichers.UniqueId; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Globalization; +using Volo.Abp; +using Volo.Abp.AspNetCore.Authentication.JwtBearer; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Serilog; +using Volo.Abp.Autofac; +using Volo.Abp.Caching.StackExchangeRedis; +using Volo.Abp.EntityFrameworkCore.MySQL; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Http.Client.IdentityModel.Web; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.Swashbuckle; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace LY.MicroService.TaskManagement; + +[DependsOn( + typeof(AbpSerilogEnrichersApplicationModule), + typeof(AbpSerilogEnrichersUniqueIdModule), + typeof(AbpAuditLoggingElasticsearchModule), + typeof(AbpAspNetCoreSerilogModule), + typeof(AbpEntityFrameworkCoreMySQLModule), + typeof(AbpAspNetCoreAuthenticationJwtBearerModule), + typeof(AbpEmailingExceptionHandlingModule), + typeof(AbpHttpClientIdentityModelWebModule), + typeof(AbpAspNetCoreMultiTenancyModule), + typeof(AbpDbFinderMultiTenancyModule), + typeof(AbpBackgroundTasksQuartzModule), + //typeof(TaskManagementApplicationModule), + //typeof(TaskManagementHttpApiModule), + //typeof(TaskManagementEntityFrameworkCoreModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpLocalizationManagementEntityFrameworkCoreModule), + typeof(AbpDataDbMigratorModule), + typeof(AbpCachingStackExchangeRedisModule), + typeof(AbpAspNetCoreMvcModule), + typeof(AbpSwashbuckleModule), + typeof(AbpAutofacModule) + )] +public partial class TaskManagementHttpApiHostModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigureApp(); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + ConfigureDbContext(); + ConfigureLocalization(); + ConfigureJsonSerializer(); + ConfigureExceptionHandling(); + ConfigureVirtualFileSystem(); + ConfigureCaching(configuration); + ConfigureAuditing(configuration); + ConfigureMultiTenancy(configuration); + ConfigureSwagger(context.Services); + ConfigureDistributedLock(context.Services, configuration); + ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment()); + + // 开发取消权限检查 + // context.Services.AddAlwaysAllowAuthorization(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + app.UseStaticFiles(); + app.UseCorrelationId(); + app.UseRouting(); + app.UseCors(); + app.UseAuthentication(); + app.UseJwtTokenMiddleware(); + app.UseMultiTenancy(); + app.UseAbpRequestLocalization(options => options.SetDefaultCulture(CultureInfo.CurrentCulture.Name)); + app.UseAuthorization(); + app.UseSwagger(); + app.UseAbpSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support Task Management API"); + + var configuration = context.GetConfiguration(); + options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); + options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]); + options.OAuthScopes("TaskManagement"); + }); + app.UseAuditing(); + app.UseAbpSerilogEnrichers(); + app.UseConfiguredEndpoints(); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json new file mode 100644 index 000000000..3315406f2 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json @@ -0,0 +1,118 @@ +{ + "AgileConfig": { + "env": "DEV", + "appId": "LINGYUN.Abp.TaskManagement", + "secret": "1q2w3E*", + "nodes": "http://127.0.0.1:15000", + "name": "LINGYUN.Abp.TaskManagement", + "tag": "LINGYUN.Abp.TaskManagement" + }, + "App": { + "TrackingEntitiesChanged": true + }, + "CAP": { + "EventBus": { + "DefaultGroupName": "TaskManagement", + "Version": "v1", + "FailedRetryInterval": 300, + "FailedRetryCount": 10, + "CollectorCleaningInterval": 3600000 + }, + "MySql": { + "TableNamePrefix": "tsk", + "ConnectionString": "Server=localhost;Database=Platform;User Id=root;Password=123456" + }, + "RabbitMQ": { + "HostName": "localhost", + "Port": 5672, + "UserName": "guest", + "Password": "guest", + "ExchangeName": "LINGYUN.Abp.Application", + "VirtualHost": "/" + } + }, + "ConnectionStrings": { + "Default": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "TaskManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpFeatureManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" + }, + "RemoteServices": { + "AbpOssManagement": { + "BaseUrl": "http://127.0.0.1:30025", + "IdentityClient": "InternalServiceClient", + "UseCurrentAccessToken": false + } + }, + "IdentityClients": { + "InternalServiceClient": { + "Authority": "http://127.0.0.1:44385", + "RequireHttps": false, + "GrantType": "client_credentials", + "Scope": "lingyun-abp-application", + "ClientId": "InternalServiceClient", + "ClientSecret": "1q2w3E*" + } + }, + "DistributedLock": { + "Redis": { + "Configuration": "127.0.0.1,defaultDatabase=15" + } + }, + "Redis": { + "Configuration": "127.0.0.1,defaultDatabase=10", + "InstanceName": "LINGYUN.Abp.Application" + }, + "AuthServer": { + "Authority": "http://127.0.0.1:44385/", + "ApiName": "lingyun-abp-application", + "SwaggerClientId": "InternalServiceClient", + "SwaggerClientSecret": "1q2w3E*" + }, + "Logging": { + "Serilog": { + "Elasticsearch": { + "IndexFormat": "abp.dev.logging-{0:yyyy.MM.dd}" + } + } + }, + "AuditLogging": { + "Elasticsearch": { + "IndexPrefix": "abp.dev.auditing" + } + }, + "Elasticsearch": { + "NodeUris": "http://127.0.0.1:9200" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "System": "Warning", + "Microsoft": "Warning", + "DotNetCore": "Debug" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "restrictedToMinimumLevel": "Debug", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Elasticsearch", + "Args": { + "nodeUris": "http://127.0.0.1:9200", + "indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}", + "autoRegisterTemplate": true, + "autoRegisterTemplateVersion": "ESv7" + } + } + ] + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json new file mode 100644 index 000000000..67e1bc4bd --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json @@ -0,0 +1,80 @@ +{ + "StringEncryption": { + "DefaultPassPhrase": "s46c5q55nxpeS8Ra", + "InitVectorBytes": "s83ng0abvd02js84", + "DefaultSalt": "sf&5)s3#" + }, + "AllowedHosts": "*", + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft.EntityFrameworkCore": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName", "WithUniqueId" ], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "initialMinimumLevel": "Verbose", + "standardErrorFromLevel": "Verbose", + "restrictedToMinimumLevel": "Verbose", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Debug-.log", + "restrictedToMinimumLevel": "Debug", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Info-.log", + "restrictedToMinimumLevel": "Information", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Warn-.log", + "restrictedToMinimumLevel": "Warning", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Error-.log", + "restrictedToMinimumLevel": "Error", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Fatal-.log", + "restrictedToMinimumLevel": "Fatal", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + } + ] + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/dapr.sh b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/dapr.sh new file mode 100644 index 000000000..fa1450cf4 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/dapr.sh @@ -0,0 +1 @@ +dapr run --app-id workflow --app-port 30040 -H 34178 -- dotnet run --no-build \ No newline at end of file From 44220eb2cc22b80e62c9c2fd7526bcbc7f9d3caa Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sat, 8 Jan 2022 23:04:04 +0800 Subject: [PATCH 02/10] feat(tasks): if the task fails, recalculate the priority --- .../LINGYUN/Abp/BackgroundTasks/JobInfo.cs | 2 +- .../LINGYUN.Abp.BackgroundTasks.Quartz/README.md | 2 +- .../Abp/BackgroundTasks/IJobRunnableExecuter.cs | 0 .../BackgroundTasks/Internal/JobExecutedEvent.cs | 14 +++++++++++++- .../LINGYUN.Abp.BackgroundTasks/README.md | 2 ++ 5 files changed, 17 insertions(+), 3 deletions(-) rename aspnet-core/modules/task-management/{LINGYUN.Abp.BackgroundTasks.Abstractions => LINGYUN.Abp.BackgroundTasks}/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs (100%) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs index ce724e44e..85246741a 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs @@ -62,7 +62,7 @@ public class JobInfo /// public JobType JobType { get; set; } = JobType.Once; /// - /// Cron表达式,如果是持续任务需要指定 + /// Cron表达式,如果是周期性任务需要指定 /// public string Cron { get; set; } /// diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md index b1dd81e5b..84110905b 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/README.md @@ -1,6 +1,6 @@ # LINGYUN.Abp.BackgroundTasks.Quartz -后台任务(队列)模块的Quartz实现, 使用任务适配器来做到任务的幂等性控制. +后台任务(队列)模块的Quartz实现. 并添加一个监听器用于通知管理者任务状态 ## 配置使用 diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs similarity index 100% rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobRunnableExecuter.cs diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs index dc64301b2..4cbbc0fd9 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs @@ -28,9 +28,21 @@ internal class JobExecutedEvent : JobEventBase, ITransientDepe if (context.EventData.Exception != null) { job.TryCount += 1; + // 将任务标记为运行中, 会被轮询重新进入队列 job.Status = JobStatus.Running; job.Result = context.EventData.Exception.Message; - + + // 多次异常后需要重新计算优先级 + if (job.TryCount <= (job.MaxTryCount / 2) && + job.TryCount > (job.MaxTryCount / 3)) + { + job.Priority = JobPriority.BelowNormal; + } + else if (job.TryCount > (job.MaxTryCount / 1.5)) + { + job.Priority = JobPriority.Low; + } + if (job.TryCount > job.MaxTryCount) { job.Status = JobStatus.Stopped; diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md index a636695dc..b3a9bee08 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/README.md @@ -50,6 +50,8 @@ public class DemoClass Cron = "0/5 * * * * ? ", TryCount = 10, Status = JobStatus.Running, + // 定义此字段处理并发 + LockTimeOut = 120, }); // 将一次性任务添加到队列, 将在10(Interval)秒后被执行 From 869abc14fdedd8d2963d3e3da454d0d191828b25 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sat, 8 Jan 2022 23:08:44 +0800 Subject: [PATCH 03/10] fix(tasks): the execution type is job configuration --- .../LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs index 9df770d40..471a7c41a 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs @@ -44,7 +44,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency { { nameof(TArgs), args }, { "ArgsType", jobConfiguration.ArgsType.AssemblyQualifiedName }, - { "JobType", typeof(BackgroundJobAdapter).AssemblyQualifiedName }, + { "JobType", jobConfiguration.JobType.AssemblyQualifiedName }, }; var jobInfo = new JobInfo { From f307fe51ee27f5663c849a3000135fe55fc2dd64 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sun, 9 Jan 2022 16:20:37 +0800 Subject: [PATCH 04/10] feat(tasks): perfect management api. --- .../LINGYUN.MicroService.TaskManagement.sln | 104 ++++++++++ .../AbpBackgroundTaskConcurrentException.cs | 45 ++++ .../Quartz/QuartzJobConcurrentAdapter.cs | 14 ++ .../Quartz/QuartzJobExecutorProvider.cs | 5 + .../Quartz/QuartzJobListener.cs | 11 +- .../LINGYUN.Abp.BackgroundTasks.csproj | 2 +- .../AbpBackgroundTasksModule.cs | 2 +- .../Internal/BackgroundCleaningJob.cs | 2 + .../Internal/DefaultBackgroundWorker.cs | 1 + .../Internal/JobExecutedEvent.cs | 8 +- .../BackgroundTasks/Internal/JobLogEvent.cs | 3 +- .../BackgroundTasks/JobRunnableExecuter.cs | 15 +- .../BackgroundTasks/Primitives/SleepJob.cs | 16 ++ ...askManagement.Application.Contracts.csproj | 1 + .../BackgroundJobLogGetListInput.cs | 1 + .../IBackgroundJobLogAppService.cs | 3 +- ...kManagementPermissionDefinitionProvider.cs | 60 ++++++ .../Permissions/TaskManagementPermissions.cs | 6 + .../TaskManagementRemoteServiceConsts.cs | 8 + .../BackgroundJobLogAppService.cs | 53 +++++ .../TaskManagementApplicationMapperProfile.cs | 1 + .../TaskManagement/BackgroundJobInfoConsts.cs | 1 + .../Localization/Resources/en.json | 12 +- .../Localization/Resources/zh-Hans.json | 12 +- .../Abp/TaskManagement/BackgroundJobInfo.cs | 29 ++- .../Abp/TaskManagement/BackgroundJobLog.cs | 15 +- .../TaskManagement/BackgroundJobManager.cs | 37 +++- .../Abp/TaskManagement/BackgroundJobStore.cs | 5 +- .../EfCoreBackgroundJobInfoRepository.cs | 2 +- ...agementDbContextModelCreatingExtensions.cs | 3 + .../BackgroundJobInfoController.cs | 84 ++++++++ .../BackgroundJobLogController.cs | 44 ++++ .../TaskManagementController.cs | 12 ++ ...916_Add-Module-Task-Management.Designer.cs | 188 +++++++++++++++++ ...220109030916_Add-Module-Task-Management.cs | 108 ++++++++++ ...esult-With-Background-Job-Info.Designer.cs | 193 ++++++++++++++++++ ...d-Field-Result-With-Background-Job-Info.cs | 27 +++ ...agementMigrationsDbContextModelSnapshot.cs | 191 +++++++++++++++++ .../TaskManagementHttpApiHostModule.cs | 8 +- .../appsettings.Development.json | 2 +- .../appsettings.json | 2 +- 41 files changed, 1297 insertions(+), 39 deletions(-) create mode 100644 aspnet-core/LINGYUN.MicroService.TaskManagement.sln create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobConcurrentAdapter.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissionDefinitionProvider.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementRemoteServiceConsts.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobLogAppService.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobInfoController.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobLogController.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementController.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.Designer.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.Designer.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/TaskManagementMigrationsDbContextModelSnapshot.cs diff --git a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln new file mode 100644 index 000000000..0ae43b0bb --- /dev/null +++ b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln @@ -0,0 +1,104 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{8DA8A2EE-0B26-487E-A6C4-518906E92B1B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C38EB7EF-BAE9-4129-862A-71C652B81775}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{77341F31-F54C-436A-AF8D-F78D91303C45}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TaskManagement.Domain.Shared", "modules\task-management\LINGYUN.Abp.TaskManagement.Domain.Shared\LINGYUN.Abp.TaskManagement.Domain.Shared.csproj", "{691CD138-9FFA-4988-BAC4-A32F0DAE1090}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TaskManagement.Domain", "modules\task-management\LINGYUN.Abp.TaskManagement.Domain\LINGYUN.Abp.TaskManagement.Domain.csproj", "{8873A651-4F83-43B3-A34E-7AAA8A3ED4CF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TaskManagement.Application.Contracts", "modules\task-management\LINGYUN.Abp.TaskManagement.Application.Contracts\LINGYUN.Abp.TaskManagement.Application.Contracts.csproj", "{D5EC4CA0-7E16-4D17-B08E-E162EF332C77}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TaskManagement.Application", "modules\task-management\LINGYUN.Abp.TaskManagement.Application\LINGYUN.Abp.TaskManagement.Application.csproj", "{09039DDF-5B0E-4670-8055-CC0BE82D4D2C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TaskManagement.HttpApi", "modules\task-management\LINGYUN.Abp.TaskManagement.HttpApi\LINGYUN.Abp.TaskManagement.HttpApi.csproj", "{283B0039-F67C-41F7-B554-DF5EE9178C4A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TaskManagement.EntityFrameworkCore", "modules\task-management\LINGYUN.Abp.TaskManagement.EntityFrameworkCore\LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj", "{B8AB5E9B-9711-470E-8072-7444579EC5F6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C99728F6-FB3C-4D26-8917-1D30725209B9}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "provider", "provider", "{385578CC-C0F1-4377-A7A2-682B8F416234}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.BackgroundTasks", "modules\task-management\LINGYUN.Abp.BackgroundTasks\LINGYUN.Abp.BackgroundTasks.csproj", "{AC0B4342-9C9B-41E6-9646-E505C763EE77}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.BackgroundTasks.Quartz", "modules\task-management\LINGYUN.Abp.BackgroundTasks.Quartz\LINGYUN.Abp.BackgroundTasks.Quartz.csproj", "{7051C251-11D0-4971-B13E-F6929AE6DE89}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.TaskManagement.HttpApi.Host", "services\LY.MicroService.TaskManagement.HttpApi.Host\LY.MicroService.TaskManagement.HttpApi.Host.csproj", "{E8022994-A19F-4540-B9D1-7EF4AA85D18A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BackgroundTasks.Abstractions", "modules\task-management\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj", "{4A049C32-55F2-4A5F-954A-C8A977C2D87F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {691CD138-9FFA-4988-BAC4-A32F0DAE1090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {691CD138-9FFA-4988-BAC4-A32F0DAE1090}.Debug|Any CPU.Build.0 = Debug|Any CPU + {691CD138-9FFA-4988-BAC4-A32F0DAE1090}.Release|Any CPU.ActiveCfg = Release|Any CPU + {691CD138-9FFA-4988-BAC4-A32F0DAE1090}.Release|Any CPU.Build.0 = Release|Any CPU + {8873A651-4F83-43B3-A34E-7AAA8A3ED4CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8873A651-4F83-43B3-A34E-7AAA8A3ED4CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8873A651-4F83-43B3-A34E-7AAA8A3ED4CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8873A651-4F83-43B3-A34E-7AAA8A3ED4CF}.Release|Any CPU.Build.0 = Release|Any CPU + {D5EC4CA0-7E16-4D17-B08E-E162EF332C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5EC4CA0-7E16-4D17-B08E-E162EF332C77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5EC4CA0-7E16-4D17-B08E-E162EF332C77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5EC4CA0-7E16-4D17-B08E-E162EF332C77}.Release|Any CPU.Build.0 = Release|Any CPU + {09039DDF-5B0E-4670-8055-CC0BE82D4D2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09039DDF-5B0E-4670-8055-CC0BE82D4D2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09039DDF-5B0E-4670-8055-CC0BE82D4D2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09039DDF-5B0E-4670-8055-CC0BE82D4D2C}.Release|Any CPU.Build.0 = Release|Any CPU + {283B0039-F67C-41F7-B554-DF5EE9178C4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {283B0039-F67C-41F7-B554-DF5EE9178C4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {283B0039-F67C-41F7-B554-DF5EE9178C4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {283B0039-F67C-41F7-B554-DF5EE9178C4A}.Release|Any CPU.Build.0 = Release|Any CPU + {B8AB5E9B-9711-470E-8072-7444579EC5F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8AB5E9B-9711-470E-8072-7444579EC5F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8AB5E9B-9711-470E-8072-7444579EC5F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8AB5E9B-9711-470E-8072-7444579EC5F6}.Release|Any CPU.Build.0 = Release|Any CPU + {AC0B4342-9C9B-41E6-9646-E505C763EE77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC0B4342-9C9B-41E6-9646-E505C763EE77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC0B4342-9C9B-41E6-9646-E505C763EE77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC0B4342-9C9B-41E6-9646-E505C763EE77}.Release|Any CPU.Build.0 = Release|Any CPU + {7051C251-11D0-4971-B13E-F6929AE6DE89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7051C251-11D0-4971-B13E-F6929AE6DE89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7051C251-11D0-4971-B13E-F6929AE6DE89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7051C251-11D0-4971-B13E-F6929AE6DE89}.Release|Any CPU.Build.0 = Release|Any CPU + {E8022994-A19F-4540-B9D1-7EF4AA85D18A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8022994-A19F-4540-B9D1-7EF4AA85D18A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8022994-A19F-4540-B9D1-7EF4AA85D18A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8022994-A19F-4540-B9D1-7EF4AA85D18A}.Release|Any CPU.Build.0 = Release|Any CPU + {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {691CD138-9FFA-4988-BAC4-A32F0DAE1090} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {8873A651-4F83-43B3-A34E-7AAA8A3ED4CF} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {D5EC4CA0-7E16-4D17-B08E-E162EF332C77} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {09039DDF-5B0E-4670-8055-CC0BE82D4D2C} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {283B0039-F67C-41F7-B554-DF5EE9178C4A} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {B8AB5E9B-9711-470E-8072-7444579EC5F6} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {AC0B4342-9C9B-41E6-9646-E505C763EE77} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {7051C251-11D0-4971-B13E-F6929AE6DE89} = {385578CC-C0F1-4377-A7A2-682B8F416234} + {E8022994-A19F-4540-B9D1-7EF4AA85D18A} = {8DA8A2EE-0B26-487E-A6C4-518906E92B1B} + {4A049C32-55F2-4A5F-954A-C8A977C2D87F} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E1FD1F4C-D344-408B-97CF-B6F1F6D7D293} + EndGlobalSection +EndGlobal diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs new file mode 100644 index 000000000..a4f65d170 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs @@ -0,0 +1,45 @@ +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.BackgroundTasks; + +public class AbpBackgroundTaskConcurrentException : AbpException +{ + public Type JobType { get; } + /// + /// Creates a new object. + /// + /// Inner exception + public AbpBackgroundTaskConcurrentException(Type jobType) + : this( + jobType, + $"This job {jobType.Name} cannot be performed because it has been locked by another performer", + null) + { + } + + /// + /// Creates a new object. + /// + /// Execute job type + /// Inner exception + public AbpBackgroundTaskConcurrentException(Type jobType, Exception innerException) + : this( + jobType, + $"This job {jobType.Name} cannot be performed because it has been locked by another performer", + innerException) + { + } + + /// + /// Creates a new object. + /// + /// Execute job type + /// Exception message + /// Inner exception + public AbpBackgroundTaskConcurrentException(Type jobType, string message, Exception innerException) + : base(message, innerException) + { + JobType = jobType; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobConcurrentAdapter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobConcurrentAdapter.cs new file mode 100644 index 000000000..1fde4e3c0 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobConcurrentAdapter.cs @@ -0,0 +1,14 @@ +using System; +using Quartz; + +namespace LINGYUN.Abp.BackgroundTasks.Quartz; + +[DisallowConcurrentExecution] +public class QuartzJobConcurrentAdapter : QuartzJobSimpleAdapter + where TJobRunnable : IJobRunnable +{ + public QuartzJobConcurrentAdapter(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs index ec3ab3501..45f35155e 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs @@ -35,6 +35,10 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD } var adapterType = typeof(QuartzJobSimpleAdapter<>); + if (job.LockTimeOut > 0) + { + adapterType = typeof(QuartzJobConcurrentAdapter<>); + } if (!typeof(IJob).IsAssignableFrom(jobType)) { @@ -88,6 +92,7 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD triggerBuilder .WithIdentity(job.Name, job.Group) .WithDescription(job.Description) + .StartAt(Clock.Now.AddSeconds(job.Interval)) .EndAt(job.EndTime) .ForJob(job.Name, job.Group) .WithPriority((int)job.Priority) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs index 7408ffcf2..b90cb54b6 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs @@ -82,7 +82,6 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency } } - [UnitOfWork] public override async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default) { try @@ -91,9 +90,15 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency var jobId = context.GetString(nameof(JobInfo.Id)); if (Guid.TryParse(jobId, out var jobUUId)) { + var jobType = context.JobDetail.JobType; + if (jobType.IsGenericType) + { + jobType = jobType.GetGenericArguments()[0]; + } + var jobEventData = new JobEventData( jobUUId, - context.JobDetail.JobType, + jobType, context.JobDetail.Key.Group, context.JobDetail.Key.Name, jobException) @@ -112,7 +117,7 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency jobEventData.NextRunTime = context.NextFireTimeUtc?.LocalDateTime; if (context.Result != null) { - jobEventData.Result = context.Result.ToString(); + jobEventData.Result = context.Result?.ToString(); } var tenantIdString = context.GetString(nameof(IMultiTenant.TenantId)); if (Guid.TryParse(tenantIdString, out var tenantId)) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj index 96b629b02..14206d3f2 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj @@ -11,7 +11,7 @@ - + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs index 205ccd0e9..2ee46ed95 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs @@ -12,9 +12,9 @@ using Volo.Abp.Reflection; namespace LINGYUN.Abp.BackgroundTasks; [DependsOn(typeof(AbpAuditingModule))] -[DependsOn(typeof(AbpDistributedLockingModule))] [DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))] [DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))] +[DependsOn(typeof(AbpDistributedLockingAbstractionsModule))] [DependsOn(typeof(AbpGuidsModule))] public class AbpBackgroundTasksModule : AbpModule { diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs index ee767808d..f21537b5d 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/BackgroundCleaningJob.cs @@ -1,9 +1,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System.Threading.Tasks; +using Volo.Abp.Auditing; namespace LINGYUN.Abp.BackgroundTasks.Internal; +[DisableAuditing] internal class BackgroundCleaningJob : IJobRunnable { public virtual async Task ExecuteAsync(JobRunnableContext context) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs index e86ae7879..adce084cb 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/DefaultBackgroundWorker.cs @@ -63,6 +63,7 @@ internal class DefaultBackgroundWorker : BackgroundService JobType = JobType.Once, Priority = JobPriority.High, MaxCount = 1, + Interval = 30, Type = typeof(BackgroundKeepAliveJob).AssemblyQualifiedName, }; } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs index 4cbbc0fd9..dd2275dde 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs @@ -4,7 +4,7 @@ using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.BackgroundTasks.Internal; -internal class JobExecutedEvent : JobEventBase, ITransientDependency +public class JobExecutedEvent : JobEventBase, ITransientDependency { protected override async Task OnJobAfterExecutedAsync(JobEventContext context) { @@ -15,8 +15,8 @@ internal class JobExecutedEvent : JobEventBase, ITransientDepe { job.TriggerCount += 1; job.NextRunTime = context.EventData.NextRunTime; - job.LastRunTime = context.EventData.LastRunTime; - job.Result = context.EventData.Result; + job.LastRunTime = context.EventData.RunTime; + job.Result = context.EventData.Result ?? "OK"; // 一次性任务执行一次后标记为已完成 if (job.JobType == JobType.Once) @@ -53,7 +53,7 @@ internal class JobExecutedEvent : JobEventBase, ITransientDepe } // 所有任务达到上限则标记已完成 - if (job.MaxCount > 0 && job.TriggerCount > job.MaxCount) + if (job.MaxCount > 0 && job.TriggerCount >= job.MaxCount) { job.Status = JobStatus.Completed; diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs index 6fb015762..8a239d806 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Volo.Abp.Auditing; using Volo.Abp.DependencyInjection; +using Volo.Abp.Uow; namespace LINGYUN.Abp.BackgroundTasks.Internal; @@ -11,7 +12,7 @@ namespace LINGYUN.Abp.BackgroundTasks.Internal; /// /// 任务类型标记了 特性则不会记录日志 /// -internal class JobLogEvent : JobEventBase, ITransientDependency +public class JobLogEvent : JobEventBase, ITransientDependency { protected async override Task OnJobAfterExecutedAsync(JobEventContext context) { diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs index 24ad6721f..042f681fa 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs @@ -10,7 +10,7 @@ namespace LINGYUN.Abp.BackgroundTasks; public class JobRunnableExecuter : IJobRunnableExecuter, ISingletonDependency { - protected const string LockKeyFormat = "job:{0},key:{1}"; + protected const string LockKeyFormat = "p:{0},job:{1},key:{2}"; public async virtual Task ExecuteAsync(JobRunnableContext context) { @@ -26,12 +26,21 @@ public class JobRunnableExecuter : IJobRunnableExecuter, ISingletonDependency { 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 jobLockKey = string.Format(LockKeyFormat, tenantId?.ToString() ?? "Default", context.JobType.Name, jobId); var distributedLock = context.ServiceProvider.GetRequiredService(); - await using (await distributedLock.TryAcquireAsync(jobLockKey, TimeSpan.FromSeconds(time))) + + var handle = await distributedLock.TryAcquireAsync(jobLockKey, TimeSpan.FromSeconds(time)); + if (handle == null) + { + // 抛出异常 通过监听器使其重试 + throw new AbpBackgroundTaskConcurrentException(context.JobType); + } + + await using (handle) { await InternalExecuteAsync(context); } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs new file mode 100644 index 000000000..d2f65f17e --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.BackgroundTasks.Primitives; + +public class SleepJob : IJobRunnable +{ + public async Task ExecuteAsync(JobRunnableContext context) + { + context.JobData.TryGetValue("Delay", out var sleep); + + Console.WriteLine($"Sleep {sleep ?? 20000} milliseconds."); + + await Task.Delay(sleep?.To() ?? 20000); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj index d7b51dbfa..19dfce08a 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN.Abp.TaskManagement.Application.Contracts.csproj @@ -9,6 +9,7 @@ + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs index 2967be0aa..2c8ee69c9 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobLogGetListInput.cs @@ -5,6 +5,7 @@ namespace LINGYUN.Abp.TaskManagement; public class BackgroundJobLogGetListInput : PagedAndSortedResultRequestDto { + public Guid? JobId { get; set; } /// /// 其他过滤条件 /// diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs index cc41aae14..6acde52ff 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobLogAppService.cs @@ -6,6 +6,7 @@ public interface IBackgroundJobLogAppService : IReadOnlyAppService< BackgroundJobLogDto, long, - BackgroundJobLogGetListInput> + BackgroundJobLogGetListInput>, + IDeleteAppService { } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissionDefinitionProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissionDefinitionProvider.cs new file mode 100644 index 000000000..34503780c --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissionDefinitionProvider.cs @@ -0,0 +1,60 @@ +using LINGYUN.Abp.TaskManagement.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.TaskManagement.Permissions; + +public class TaskManagementPermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + var group = context.AddGroup( + TaskManagementPermissions.GroupName, + L("Permissions:TaskManagement"), + MultiTenancySides.Host); + + var backgroundJobs = group.AddPermission( + TaskManagementPermissions.BackgroundJobs.Default, + L("Permissions:BackgroundJobs"), + MultiTenancySides.Host); + backgroundJobs.AddChild( + TaskManagementPermissions.BackgroundJobs.Create, + L("Permissions:CreateJob"), + MultiTenancySides.Host); + backgroundJobs.AddChild( + TaskManagementPermissions.BackgroundJobs.Update, + L("Permissions:UpdateJob"), + MultiTenancySides.Host); + backgroundJobs.AddChild( + TaskManagementPermissions.BackgroundJobs.Delete, + L("Permissions:DeleteJob"), + MultiTenancySides.Host); + backgroundJobs.AddChild( + TaskManagementPermissions.BackgroundJobs.Trigger, + L("Permissions:TriggerJob"), + MultiTenancySides.Host); + backgroundJobs.AddChild( + TaskManagementPermissions.BackgroundJobs.Pause, + L("Permissions:PauseJob"), + MultiTenancySides.Host); + backgroundJobs.AddChild( + TaskManagementPermissions.BackgroundJobs.Resume, + L("Permissions:ResumeJob"), + MultiTenancySides.Host); + + var backgroundJobLogs = group.AddPermission( + TaskManagementPermissions.BackgroundJobLogs.Default, + L("Permissions:BackgroundJobLogs"), + MultiTenancySides.Host); + backgroundJobLogs.AddChild( + TaskManagementPermissions.BackgroundJobLogs.Delete, + L("Permissions:DeleteJobLogs"), + MultiTenancySides.Host); + } + + private ILocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs index 8ca7655fe..b30f69d67 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs @@ -14,4 +14,10 @@ public static class TaskManagementPermissions public const string Pause = Default + ".Pause"; public const string Resume = Default + ".Resume"; } + + public static class BackgroundJobLogs + { + public const string Default = GroupName + ".BackgroundJobLogs"; + public const string Delete = Default + ".Delete"; + } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementRemoteServiceConsts.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementRemoteServiceConsts.cs new file mode 100644 index 000000000..c2d6dccd2 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/TaskManagementRemoteServiceConsts.cs @@ -0,0 +1,8 @@ +namespace LINGYUN.Abp.TaskManagement; + +public static class TaskManagementRemoteServiceConsts +{ + public const string RemoteServiceName = "TaskManagement"; + + public const string ModuleName = "task-management"; +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobLogAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobLogAppService.cs new file mode 100644 index 000000000..012a1db32 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobLogAppService.cs @@ -0,0 +1,53 @@ +using LINGYUN.Abp.TaskManagement.Permissions; +using Microsoft.AspNetCore.Authorization; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.TaskManagement; + +[Authorize(TaskManagementPermissions.BackgroundJobLogs.Default)] +public class BackgroundJobLogAppService : TaskManagementApplicationService, IBackgroundJobLogAppService +{ + protected IBackgroundJobLogRepository BackgroundJobLogRepository { get; } + + public BackgroundJobLogAppService( + IBackgroundJobLogRepository backgroundJobLogRepository) + { + BackgroundJobLogRepository = backgroundJobLogRepository; + } + + [Authorize(TaskManagementPermissions.BackgroundJobLogs.Delete)] + public virtual Task DeleteAsync(long id) + { + return BackgroundJobLogRepository.DeleteAsync(id); + } + + public virtual async Task GetAsync(long id) + { + var backgroundJobLog = await BackgroundJobLogRepository.GetAsync(id); + + return ObjectMapper.Map(backgroundJobLog); + } + + public virtual async Task> GetListAsync(BackgroundJobLogGetListInput input) + { + var filter = new BackgroundJobLogFilter + { + BeginRunTime = input.BeginRunTime, + EndRunTime = input.EndRunTime, + HasExceptions = input.HasExceptions, + Filter = input.Filter, + Group = input.Group, + Name = input.Name, + Type = input.Type + }; + + var totalCount = await BackgroundJobLogRepository.GetCountAsync(filter, input.JobId); + var backgroundJobLogs = await BackgroundJobLogRepository.GetListAsync( + filter, input.JobId, input.Sorting, input.MaxResultCount, input.SkipCount); + + return new PagedResultDto(totalCount, + ObjectMapper.Map, List>(backgroundJobLogs)); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs index 11468eb23..f250cfe5a 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/TaskManagementApplicationMapperProfile.cs @@ -7,5 +7,6 @@ public class TaskManagementApplicationMapperProfile : Profile public TaskManagementApplicationMapperProfile() { CreateMap(); + CreateMap(); } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs index bc316921f..25c26d24c 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/BackgroundJobInfoConsts.cs @@ -7,4 +7,5 @@ public static class BackgroundJobInfoConsts public static int MaxGroupLength { get; set; } = 50; public static int MaxTypeLength { get; set; } = 200; public static int MaxDescriptionLength { get; set; } = 255; + public static int MaxResultLength { get; set; } = 1000; } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json index 10f2dda00..6f45cd09e 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json @@ -1,6 +1,16 @@ { "culture": "en", "texts": { - "Permission:TaskManagement": "TaskManagement" + "Permissions:TaskManagement": "Task Management", + "Permissions:BackgroundJobs": "Background Jobs", + "Permissions:CreateJob": "Create Job", + "Permissions:UpdateJob": "Update Job", + "Permissions:DeleteJob": "Delete Job", + "Permissions:TriggerJob": "Trigger Job", + "Permissions:PauseJob": "Pause Job", + "Permissions:ResumeJob": "Resume Job", + "Permissions:BackgroundJobLogs": "BackgroundJobs Logs", + "Permissions:DeleteJobLogs": "Delete Job Logs", + "TaskManagement:01000": "A job named {Name} already exists in the Group {Group}!" } } \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json index 5b3611e36..02a7e286b 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json @@ -1,6 +1,16 @@ { "culture": "zh-Hans", "texts": { - "Permission:TaskManagement": "任务管理" + "Permissions:TaskManagement": "任务调度平台", + "Permissions:BackgroundJobs": "任务管理", + "Permissions:CreateJob": "新建作业", + "Permissions:UpdateJob": "编辑作业", + "Permissions:DeleteJob": "删除作业", + "Permissions:TriggerJob": "触发作业", + "Permissions:PauseJob": "暂停作业", + "Permissions:ResumeJob": "恢复作业", + "Permissions:BackgroundJobLogs": "日志管理", + "Permissions:DeleteJobLogs": "删除作业日志", + "TaskManagement:01000": "分组 {Group} 中已经存在一个名称为 {Name} 的作业!" } } \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs index 4704f65b9..bb3d5d1e7 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs @@ -1,6 +1,7 @@ using LINGYUN.Abp.BackgroundTasks; using System; using System.Collections.Generic; +using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Domain.Entities.Auditing; @@ -21,6 +22,10 @@ public class BackgroundJobInfo : AuditedAggregateRoot /// public virtual string Type { get; protected set; } /// + /// 上一次执行结果 + /// + public virtual string Result { get; protected set; } + /// /// 任务参数 /// public virtual ExtraPropertyDictionary Args { get; protected set; } @@ -111,9 +116,9 @@ public class BackgroundJobInfo : AuditedAggregateRoot int maxCount = 0, int maxTryCount = 50) : base(id) { - Name = name; - Group = group; - Type = type; + Name = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength); + Group = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength); + Type = Check.NotNullOrWhiteSpace(type, nameof(type), BackgroundJobInfoConsts.MaxTypeLength); Priority = priority; BeginTime = beginTime; EndTime = endTime; @@ -123,13 +128,15 @@ public class BackgroundJobInfo : AuditedAggregateRoot Status = JobStatus.Running; + // TODO: 是否需要将参数挪到另一个实体? + // 任务参数的建议是尽量最小化, 仅存储关键信息 Args = new ExtraPropertyDictionary(); Args.AddIfNotContains(args); } public void SetPeriodJob(string cron) { - Cron = cron; + Cron = Check.NotNullOrWhiteSpace(cron, nameof(cron), BackgroundJobInfoConsts.MaxCronLength); JobType = JobType.Period; } @@ -155,6 +162,20 @@ public class BackgroundJobInfo : AuditedAggregateRoot NextRunTime = nextRunTime; } + public void SetResult(string result) + { + if (result.IsNullOrWhiteSpace()) + { + return; + } + if (result.Length > BackgroundJobInfoConsts.MaxResultLength) + { + result = result.Substring(0, BackgroundJobInfoConsts.MaxResultLength - 1); + } + + Result = result; + } + public void SetStatus(JobStatus status) { Status = status; diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs index 9ae5ca1d0..192eaaacb 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs @@ -1,4 +1,5 @@ using System; +using Volo.Abp; using Volo.Abp.Domain.Entities; namespace LINGYUN.Abp.TaskManagement; @@ -13,12 +14,16 @@ public class BackgroundJobLog : Entity public virtual DateTime RunTime { get; protected set; } public virtual string Exception { get; protected set; } protected BackgroundJobLog() { } - public BackgroundJobLog(string type, string group, string name) + public BackgroundJobLog( + string type, + string group, + string name, + DateTime runTime) { - JobType = type; - JobGroup = group; - JobName = name; - RunTime = DateTime.Now; + JobType = Check.NotNullOrWhiteSpace(type, nameof(type), BackgroundJobInfoConsts.MaxTypeLength); + JobGroup = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength); + JobName = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength); + RunTime = runTime; } public BackgroundJobLog SetMessage(string message, Exception ex) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs index 06b39aaea..bf7d50bd1 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs @@ -1,7 +1,9 @@ using LINGYUN.Abp.BackgroundTasks; using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.Domain.Services; using Volo.Abp.ObjectMapping; +using Volo.Abp.Uow; namespace LINGYUN.Abp.TaskManagement; @@ -9,15 +11,18 @@ public class BackgroundJobManager : DomainService { protected IObjectMapper ObjectMapper { get; } protected IJobScheduler JobScheduler { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IBackgroundJobInfoRepository BackgroundJobInfoRepository { get; } public BackgroundJobManager( IObjectMapper objectMapper, IJobScheduler jobScheduler, + IUnitOfWorkManager unitOfWorkManager, IBackgroundJobInfoRepository backgroundJobInfoRepository) { ObjectMapper = objectMapper; JobScheduler = jobScheduler; + UnitOfWorkManager = unitOfWorkManager; BackgroundJobInfoRepository = backgroundJobInfoRepository; } @@ -28,7 +33,16 @@ public class BackgroundJobManager : DomainService if (jobInfo.IsEnabled && jobInfo.JobType == JobType.Period) { var job = ObjectMapper.Map(jobInfo); - await JobScheduler.QueueAsync(job); + if (await JobScheduler.ExistsAsync(job)) + { + throw new BusinessException(TaskManagementErrorCodes.JobNameAlreadyExists) + .WithData("Group", job.Group) + .WithData("Name", job.Name); + } + UnitOfWorkManager.Current.OnCompleted(async () => + { + await JobScheduler.QueueAsync(job); + }); } return jobInfo; @@ -40,13 +54,19 @@ public class BackgroundJobManager : DomainService if (!jobInfo.IsEnabled || resetJob) { - var job = ObjectMapper.Map(jobInfo); - await JobScheduler.RemoveAsync(job); + UnitOfWorkManager.Current.OnCompleted(async () => + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.RemoveAsync(job); + }); } if (resetJob && jobInfo.JobType == JobType.Period) { - await QueueAsync(jobInfo); + UnitOfWorkManager.Current.OnCompleted(async () => + { + await QueueAsync(jobInfo); + }); } return jobInfo; @@ -54,10 +74,13 @@ public class BackgroundJobManager : DomainService public virtual async Task DeleteAsync(BackgroundJobInfo jobInfo) { - var job = ObjectMapper.Map(jobInfo); - await JobScheduler.RemoveAsync(job); - await BackgroundJobInfoRepository.DeleteAsync(jobInfo); + + UnitOfWorkManager.Current.OnCompleted(async () => + { + var job = ObjectMapper.Map(jobInfo); + await JobScheduler.RemoveAsync(job); + }); } public virtual async Task QueueAsync(BackgroundJobInfo jobInfo) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs index e5498ab6f..189f0bf9c 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs @@ -56,6 +56,7 @@ public class BackgroundJobStore : IJobStore, ITransientDependency backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime); backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime); backgroundJobInfo.SetStatus(jobInfo.Status); + backgroundJobInfo.SetResult(jobInfo.Result); backgroundJobInfo.TriggerCount = jobInfo.TriggerCount; backgroundJobInfo.TryCount = jobInfo.TryCount; backgroundJobInfo.IsAbandoned = jobInfo.IsAbandoned; @@ -79,6 +80,7 @@ public class BackgroundJobStore : IJobStore, ITransientDependency backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime); backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime); backgroundJobInfo.SetStatus(jobInfo.Status); + backgroundJobInfo.SetResult(jobInfo.Result); backgroundJobInfo.TriggerCount = jobInfo.TriggerCount; backgroundJobInfo.IsAbandoned = jobInfo.IsAbandoned; backgroundJobInfo.TryCount = jobInfo.TryCount; @@ -107,7 +109,8 @@ public class BackgroundJobStore : IJobStore, ITransientDependency var jogLog = new BackgroundJobLog( eventData.Type.Name, eventData.Group, - eventData.Name) + eventData.Name, + eventData.RunTime) { JobId = eventData.Key }; diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs index 8cc7bc601..ff840e35c 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs @@ -32,7 +32,7 @@ public class EfCoreBackgroundJobInfoRepository : CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) - .AllAsync(x => x.Group.Equals(group) && x.Name.Equals(name), + .AnyAsync(x => x.Group.Equals(group) && x.Name.Equals(name), GetCancellationToken(cancellationToken)); } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs index c32fabef7..e85773fb2 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/TaskManagementDbContextModelCreatingExtensions.cs @@ -43,6 +43,9 @@ public static class TaskManagementDbContextModelCreatingExtensions b.Property(p => p.Description) .HasColumnName(nameof(BackgroundJobInfo.Description)) .HasMaxLength(BackgroundJobInfoConsts.MaxDescriptionLength); + b.Property(p => p.Result) + .HasColumnName(nameof(BackgroundJobInfo.Result)) + .HasMaxLength(BackgroundJobInfoConsts.MaxResultLength); b.Property(p => p.Args) .HasColumnName(nameof(BackgroundJobInfo.Args)) .HasConversion(new ExtraPropertiesValueConverter(b.Metadata.ClrType)) diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobInfoController.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobInfoController.cs new file mode 100644 index 000000000..dd755ebbd --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobInfoController.cs @@ -0,0 +1,84 @@ +using LINGYUN.Abp.TaskManagement.Permissions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.TaskManagement; + +[RemoteService(Name = TaskManagementRemoteServiceConsts.RemoteServiceName)] +[Area(TaskManagementRemoteServiceConsts.ModuleName)] +[Authorize(TaskManagementPermissions.BackgroundJobs.Default)] +[Route($"api/{TaskManagementRemoteServiceConsts.ModuleName}/background-jobs")] +public class BackgroundJobInfoController : TaskManagementController, IBackgroundJobInfoAppService +{ + protected IBackgroundJobInfoAppService BackgroundJobInfoAppService { get; } + + public BackgroundJobInfoController( + IBackgroundJobInfoAppService backgroundJobInfoAppService) + { + BackgroundJobInfoAppService = backgroundJobInfoAppService; + } + + [HttpPost] + [Authorize(TaskManagementPermissions.BackgroundJobs.Create)] + public Task CreateAsync(BackgroundJobInfoCreateDto input) + { + return BackgroundJobInfoAppService.CreateAsync(input); + } + + [HttpDelete] + [Route("{id}")] + [Authorize(TaskManagementPermissions.BackgroundJobs.Delete)] + public Task DeleteAsync(Guid id) + { + return BackgroundJobInfoAppService.DeleteAsync(id); + } + + [HttpGet] + [Route("{id}")] + public Task GetAsync(Guid id) + { + return BackgroundJobInfoAppService.GetAsync(id); + } + + [HttpGet] + public Task> GetListAsync(BackgroundJobInfoGetListInput input) + { + return BackgroundJobInfoAppService.GetListAsync(input); + } + + [HttpPut] + [Route("{id}/pause")] + [Authorize(TaskManagementPermissions.BackgroundJobs.Pause)] + public Task PauseAsync(Guid id) + { + return BackgroundJobInfoAppService.PauseAsync(id); + } + + [HttpPut] + [Route("{id}/resume")] + [Authorize(TaskManagementPermissions.BackgroundJobs.Resume)] + public Task ResumeAsync(Guid id) + { + return BackgroundJobInfoAppService.ResumeAsync(id); + } + + [HttpPut] + [Route("{id}/trigger")] + [Authorize(TaskManagementPermissions.BackgroundJobs.Trigger)] + public Task TriggerAsync(Guid id) + { + return BackgroundJobInfoAppService.TriggerAsync(id); + } + + [HttpPut] + [Route("{id}")] + [Authorize(TaskManagementPermissions.BackgroundJobs.Update)] + public Task UpdateAsync(Guid id, BackgroundJobInfoUpdateDto input) + { + return BackgroundJobInfoAppService.UpdateAsync(id, input); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobLogController.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobLogController.cs new file mode 100644 index 000000000..a84ddb52a --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobLogController.cs @@ -0,0 +1,44 @@ +using LINGYUN.Abp.TaskManagement.Permissions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.TaskManagement; + +[RemoteService(Name = TaskManagementRemoteServiceConsts.RemoteServiceName)] +[Area(TaskManagementRemoteServiceConsts.ModuleName)] +[Authorize(TaskManagementPermissions.BackgroundJobLogs.Default)] +[Route($"api/{TaskManagementRemoteServiceConsts.ModuleName}/background-jobs/logs")] +public class BackgroundJobLogController : TaskManagementController, IBackgroundJobLogAppService +{ + protected IBackgroundJobLogAppService BackgroundJobLogAppService { get; } + + public BackgroundJobLogController( + IBackgroundJobLogAppService backgroundJobLogAppService) + { + BackgroundJobLogAppService = backgroundJobLogAppService; + } + + [HttpDelete] + [Route("{id}")] + [Authorize(TaskManagementPermissions.BackgroundJobLogs.Delete)] + public Task DeleteAsync(long id) + { + return BackgroundJobLogAppService.DeleteAsync(id); + } + + [HttpGet] + [Route("{id}")] + public Task GetAsync(long id) + { + return BackgroundJobLogAppService.GetAsync(id); + } + + [HttpGet] + public Task> GetListAsync(BackgroundJobLogGetListInput input) + { + return BackgroundJobLogAppService.GetListAsync(input); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementController.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementController.cs new file mode 100644 index 000000000..0e011fd88 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/TaskManagementController.cs @@ -0,0 +1,12 @@ +using LINGYUN.Abp.TaskManagement.Localization; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.TaskManagement; + +public abstract class TaskManagementController : AbpControllerBase +{ + protected TaskManagementController() + { + LocalizationResource = typeof(TaskManagementResource); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.Designer.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.Designer.cs new file mode 100644 index 000000000..bb69b999d --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.Designer.cs @@ -0,0 +1,188 @@ +// +using System; +using LY.MicroService.TaskManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.TaskManagement.Migrations +{ + [DbContext(typeof(TaskManagementMigrationsDbContext))] + [Migration("20220109030916_Add-Module-Task-Management")] + partial class AddModuleTaskManagement + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Args") + .HasColumnType("longtext") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("datetime(6)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("int"); + + b.Property("IsAbandoned") + .HasColumnType("tinyint(1)"); + + b.Property("IsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("JobType") + .HasColumnType("int"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("datetime(6)"); + + b.Property("LockTimeOut") + .HasColumnType("int"); + + b.Property("MaxCount") + .HasColumnType("int"); + + b.Property("MaxTryCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("datetime(6)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TriggerCount") + .HasColumnType("int"); + + b.Property("TryCount") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasColumnType("char(36)"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.cs new file mode 100644 index 000000000..1c04c8779 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109030916_Add-Module-Task-Management.cs @@ -0,0 +1,108 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.TaskManagement.Migrations +{ + public partial class AddModuleTaskManagement : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "TK_BackgroundJobLogs", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + JobId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + JobName = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + JobGroup = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + JobType = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Message = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RunTime = table.Column(type: "datetime(6)", nullable: false), + Exception = table.Column(type: "varchar(2000)", maxLength: 2000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_TK_BackgroundJobLogs", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "TK_BackgroundJobs", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Group = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Type = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Args = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "int", nullable: false), + IsEnabled = table.Column(type: "tinyint(1)", nullable: false), + Description = table.Column(type: "varchar(255)", maxLength: 255, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + LockTimeOut = table.Column(type: "int", nullable: false), + BeginTime = table.Column(type: "datetime(6)", nullable: false), + EndTime = table.Column(type: "datetime(6)", nullable: true), + LastRunTime = table.Column(type: "datetime(6)", nullable: true), + NextRunTime = table.Column(type: "datetime(6)", nullable: true), + JobType = table.Column(type: "int", nullable: false), + Cron = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Priority = table.Column(type: "int", nullable: false), + TriggerCount = table.Column(type: "int", nullable: false), + TryCount = table.Column(type: "int", nullable: false), + MaxTryCount = table.Column(type: "int", nullable: false), + MaxCount = table.Column(type: "int", nullable: false), + Interval = table.Column(type: "int", nullable: false), + IsAbandoned = table.Column(type: "tinyint(1)", nullable: false), + ExtraProperties = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "varchar(40)", maxLength: 40, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationTime = table.Column(type: "datetime(6)", nullable: false), + CreatorId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + LastModificationTime = table.Column(type: "datetime(6)", nullable: true), + LastModifierId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_TK_BackgroundJobs", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_TK_BackgroundJobLogs_JobGroup_JobName", + table: "TK_BackgroundJobLogs", + columns: new[] { "JobGroup", "JobName" }); + + migrationBuilder.CreateIndex( + name: "IX_TK_BackgroundJobs_Name_Group", + table: "TK_BackgroundJobs", + columns: new[] { "Name", "Group" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TK_BackgroundJobLogs"); + + migrationBuilder.DropTable( + name: "TK_BackgroundJobs"); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.Designer.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.Designer.cs new file mode 100644 index 000000000..72dfca939 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.Designer.cs @@ -0,0 +1,193 @@ +// +using System; +using LY.MicroService.TaskManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.TaskManagement.Migrations +{ + [DbContext(typeof(TaskManagementMigrationsDbContext))] + [Migration("20220109033926_Add-Field-Result-With-Background-Job-Info")] + partial class AddFieldResultWithBackgroundJobInfo + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Args") + .HasColumnType("longtext") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("datetime(6)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("int"); + + b.Property("IsAbandoned") + .HasColumnType("tinyint(1)"); + + b.Property("IsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("JobType") + .HasColumnType("int"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("datetime(6)"); + + b.Property("LockTimeOut") + .HasColumnType("int"); + + b.Property("MaxCount") + .HasColumnType("int"); + + b.Property("MaxTryCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("datetime(6)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Result") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)") + .HasColumnName("Result"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TriggerCount") + .HasColumnType("int"); + + b.Property("TryCount") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasColumnType("char(36)"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.cs new file mode 100644 index 000000000..644c17adb --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220109033926_Add-Field-Result-With-Background-Job-Info.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.TaskManagement.Migrations +{ + public partial class AddFieldResultWithBackgroundJobInfo : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Result", + table: "TK_BackgroundJobs", + type: "varchar(1000)", + maxLength: 1000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Result", + table: "TK_BackgroundJobs"); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/TaskManagementMigrationsDbContextModelSnapshot.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/TaskManagementMigrationsDbContextModelSnapshot.cs new file mode 100644 index 000000000..233b57e47 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/TaskManagementMigrationsDbContextModelSnapshot.cs @@ -0,0 +1,191 @@ +// +using System; +using LY.MicroService.TaskManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.TaskManagement.Migrations +{ + [DbContext(typeof(TaskManagementMigrationsDbContext))] + partial class TaskManagementMigrationsDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Args") + .HasColumnType("longtext") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("datetime(6)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("int"); + + b.Property("IsAbandoned") + .HasColumnType("tinyint(1)"); + + b.Property("IsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("JobType") + .HasColumnType("int"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("datetime(6)"); + + b.Property("LockTimeOut") + .HasColumnType("int"); + + b.Property("MaxCount") + .HasColumnType("int"); + + b.Property("MaxTryCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("datetime(6)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Result") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)") + .HasColumnName("Result"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TriggerCount") + .HasColumnType("int"); + + b.Property("TryCount") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasColumnType("char(36)"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs index 453210150..ef8042545 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs @@ -6,6 +6,8 @@ using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; using LINGYUN.Abp.MultiTenancy.DbFinder; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; +using LINGYUN.Abp.TaskManagement; +using LINGYUN.Abp.TaskManagement.EntityFrameworkCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -41,9 +43,9 @@ namespace LY.MicroService.TaskManagement; typeof(AbpAspNetCoreMultiTenancyModule), typeof(AbpDbFinderMultiTenancyModule), typeof(AbpBackgroundTasksQuartzModule), - //typeof(TaskManagementApplicationModule), - //typeof(TaskManagementHttpApiModule), - //typeof(TaskManagementEntityFrameworkCoreModule), + typeof(TaskManagementApplicationModule), + typeof(TaskManagementHttpApiModule), + typeof(TaskManagementEntityFrameworkCoreModule), typeof(AbpFeatureManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json index 3315406f2..25b1a9c80 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.json @@ -93,7 +93,7 @@ "Override": { "System": "Warning", "Microsoft": "Warning", - "DotNetCore": "Debug" + "DotNetCore": "Warning" } }, "WriteTo": [ diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json index 67e1bc4bd..1e85dd7a6 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json @@ -9,7 +9,7 @@ "MinimumLevel": { "Default": "Debug", "Override": { - "Microsoft.EntityFrameworkCore": "Debug", + "Microsoft.EntityFrameworkCore": "Warning", "System": "Information", "Microsoft": "Information" } From d3a59d6c912e978672ee7bebd902e9b7d4ce0a30 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sun, 9 Jan 2022 16:23:37 +0800 Subject: [PATCH 05/10] feat(admin): add task management public modules to admin modules --- build/modules.dependencies.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/modules.dependencies.json b/build/modules.dependencies.json index 2c9b4e490..a6141ad42 100644 --- a/build/modules.dependencies.json +++ b/build/modules.dependencies.json @@ -57,7 +57,9 @@ "LINGYUN.Abp.Features.LimitValidation.dll", "LINGYUN.Abp.Features.LimitValidation.Redis.dll", "LINGYUN.Abp.WorkflowManagement.Application.Contracts.dll", - "LINGYUN.Abp.WorkflowManagement.Domain.Shared.dll" + "LINGYUN.Abp.WorkflowManagement.Domain.Shared.dll", + "LINGYUN.Abp.TaskManagement.Application.Contracts.dll", + "LINGYUN.Abp.TaskManagement.Domain.Shared.dll" ] } ] From b1fe9c8cb8ec610b10574ac739c6c42ff1d3e3b5 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sun, 9 Jan 2022 16:29:44 +0800 Subject: [PATCH 06/10] feat(admin): add task management public modules to docker-compose --- build/build-aspnetcore-common.ps1 | 13 +++++++------ docker-compose.override.yml | 12 ++++++++++++ docker-compose.yml | 7 +++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/build/build-aspnetcore-common.ps1 b/build/build-aspnetcore-common.ps1 index 492d6cff0..6299f5d25 100644 --- a/build/build-aspnetcore-common.ps1 +++ b/build/build-aspnetcore-common.ps1 @@ -5,12 +5,13 @@ $rootFolder = (Get-Item -Path "./" -Verbose).FullName # List of solutions used only in development mode [PsObject[]]$serviceArray = @() -$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/"; Service = "backend-admin-api" } -$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.IdentityServer/"; Service = "identity" } -$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/"; Service = "identity-api" } -$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/"; Service = "localization-api" } -$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/"; Service = "platform-api" } -$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/"; Service = "realtime-message-api" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/"; Service = "admin" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.IdentityServer/"; Service = "identityserver" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/"; Service = "identityserver4-admin" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/"; Service = "localization" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/"; Service = "platform" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/"; Service = "messages" } +$serviceArray += [PsObject]@{ Path = $rootFolder + "/../aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/"; Service = "task-management" } $serviceArray += [PsObject]@{ Path = $rootFolder + "/../gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/"; Service = "internal-apigateway" } Write-host "" diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 04a5d8a8c..7e136becf 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -72,6 +72,18 @@ services: networks: - framework + task-api: + build: + context: ./aspnet-core/services/Publish/task-management + volumes: + - /var/opt/abp/logs/task-management:/app/Logs + - /var/opt/abp/data/task-management/Modules:/app/Modules + depends_on: + - sts-server + restart: always + networks: + - framework + internal-apigateway: build: context: ./aspnet-core/services/Publish/internal-apigateway diff --git a/docker-compose.yml b/docker-compose.yml index d09c9c101..b11b5300b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,13 @@ services: ports: - "30020:80" + task-api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:80 + ports: + - "30040:80" + sts-server: environment: - ASPNETCORE_ENVIRONMENT=Production From 58428ab3d8c8b8cd256113f810652cc06cdd3540 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sun, 9 Jan 2022 18:36:03 +0800 Subject: [PATCH 07/10] feat(tasks): add commonly used jobs --- .../LINGYUN.MicroService.TaskManagement.sln | 7 ++ .../AbpBackgroundTaskConcurrentException.cs | 11 +- .../AbpBackgroundTasksAbstractionsModule.cs | 28 ++++- .../AbpBackgroundTasksOptions.cs | 15 +++ .../AbpJobExecutionException.cs | 44 +++++++ .../LINGYUN/Abp/BackgroundTasks/IJobEvent.cs | 0 .../Abp/BackgroundTasks/JobEventBase.cs | 0 .../Abp/BackgroundTasks/JobRunnableContext.cs | 6 + .../JobRunnableContextExtensions.cs | 85 +++++++++++++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++++ .../LINGYUN.Abp.BackgroundTasks.Jobs.csproj | 21 ++++ .../Jobs/AbpBackgroundTasksJobsModule.cs | 25 ++++ .../Abp/BackgroundTasks/Jobs}/ConsoleJob.cs | 2 +- .../BackgroundTasks/Jobs/DefaultJobNames.cs | 29 +++++ .../BackgroundTasks/Jobs/HttpRequestJob.cs | 78 ++++++++++++ .../Abp/BackgroundTasks/Jobs/SendEmailJob.cs | 26 ++++ .../Abp/BackgroundTasks/Jobs/SendSmsJob.cs | 29 +++++ .../Jobs/ServiceInvocationJob.cs | 117 ++++++++++++++++++ .../Abp/BackgroundTasks/Jobs}/SleepJob.cs | 2 +- .../Quartz/QuartzJobExecutorProvider.cs | 3 +- .../Quartz/QuartzJobSimpleAdapter.cs | 2 + .../AbpBackgroundTasksModule.cs | 26 ---- .../BackgroundTasks/Primitives/EmailingJob.cs | 14 --- ...Service.TaskManagement.HttpApi.Host.csproj | 1 + .../TaskManagementHttpApiHostModule.cs | 2 + 26 files changed, 555 insertions(+), 51 deletions(-) rename aspnet-core/modules/task-management/{LINGYUN.Abp.BackgroundTasks => LINGYUN.Abp.BackgroundTasks.Abstractions}/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs (78%) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs rename aspnet-core/modules/task-management/{LINGYUN.Abp.BackgroundTasks => LINGYUN.Abp.BackgroundTasks.Abstractions}/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs (100%) rename aspnet-core/modules/task-management/{LINGYUN.Abp.BackgroundTasks => LINGYUN.Abp.BackgroundTasks.Abstractions}/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs (100%) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs rename aspnet-core/modules/task-management/{LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives => LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs}/ConsoleJob.cs (81%) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs rename aspnet-core/modules/task-management/{LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives => LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs}/SleepJob.cs (84%) delete mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs diff --git a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln index 0ae43b0bb..1691e1e80 100644 --- a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln +++ b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln @@ -36,6 +36,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.TaskManagem EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BackgroundTasks.Abstractions", "modules\task-management\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj", "{4A049C32-55F2-4A5F-954A-C8A977C2D87F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BackgroundTasks.Jobs", "modules\task-management\LINGYUN.Abp.BackgroundTasks.Jobs\LINGYUN.Abp.BackgroundTasks.Jobs.csproj", "{4C59F590-AA6C-4C46-8060-DA65D8305980}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -82,6 +84,10 @@ Global {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A049C32-55F2-4A5F-954A-C8A977C2D87F}.Release|Any CPU.Build.0 = Release|Any CPU + {4C59F590-AA6C-4C46-8060-DA65D8305980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C59F590-AA6C-4C46-8060-DA65D8305980}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C59F590-AA6C-4C46-8060-DA65D8305980}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C59F590-AA6C-4C46-8060-DA65D8305980}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -97,6 +103,7 @@ Global {7051C251-11D0-4971-B13E-F6929AE6DE89} = {385578CC-C0F1-4377-A7A2-682B8F416234} {E8022994-A19F-4540-B9D1-7EF4AA85D18A} = {8DA8A2EE-0B26-487E-A6C4-518906E92B1B} {4A049C32-55F2-4A5F-954A-C8A977C2D87F} = {C38EB7EF-BAE9-4129-862A-71C652B81775} + {4C59F590-AA6C-4C46-8060-DA65D8305980} = {C38EB7EF-BAE9-4129-862A-71C652B81775} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E1FD1F4C-D344-408B-97CF-B6F1F6D7D293} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs index a4f65d170..02acfafd6 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTaskConcurrentException.cs @@ -1,17 +1,15 @@ using System; -using Volo.Abp; namespace LINGYUN.Abp.BackgroundTasks; -public class AbpBackgroundTaskConcurrentException : AbpException +public class AbpBackgroundTaskConcurrentException : AbpJobExecutionException { - public Type JobType { get; } /// /// Creates a new object. /// /// Inner exception public AbpBackgroundTaskConcurrentException(Type jobType) - : this( + : base( jobType, $"This job {jobType.Name} cannot be performed because it has been locked by another performer", null) @@ -24,7 +22,7 @@ public class AbpBackgroundTaskConcurrentException : AbpException /// Execute job type /// Inner exception public AbpBackgroundTaskConcurrentException(Type jobType, Exception innerException) - : this( + : base( jobType, $"This job {jobType.Name} cannot be performed because it has been locked by another performer", innerException) @@ -38,8 +36,7 @@ public class AbpBackgroundTaskConcurrentException : AbpException /// Exception message /// Inner exception public AbpBackgroundTaskConcurrentException(Type jobType, string message, Exception innerException) - : base(message, innerException) + : base(jobType, message, innerException) { - JobType = jobType; } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs index 3e349c78c..5ad3b3a60 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksAbstractionsModule.cs @@ -1,7 +1,33 @@ -using Volo.Abp.Modularity; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using Volo.Abp.Modularity; +using Volo.Abp.Reflection; namespace LINGYUN.Abp.BackgroundTasks; public class AbpBackgroundTasksAbstractionsModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AutoAddJobMonitors(context.Services); + } + + private static void AutoAddJobMonitors(IServiceCollection services) + { + var jobMonitors = new List(); + + services.OnRegistred(context => + { + if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(JobEventBase<>))) + { + jobMonitors.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + options.JobMonitors.AddIfNotContains(jobMonitors); + }); + } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs similarity index 78% rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs index 69457817b..788102a83 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Volo.Abp.Collections; namespace LINGYUN.Abp.BackgroundTasks; @@ -13,6 +14,13 @@ public class AbpBackgroundTasksOptions /// public ITypeList JobMonitors { get; } /// + /// 作业提供者列表 + /// + /// + /// 用户实现的作业可以添加在集合中 + /// + public IDictionary JobProviders { get; } + /// /// 任务过期时间 /// 默认: 15 days /// @@ -65,5 +73,12 @@ public class AbpBackgroundTasksOptions JobCleanCronExpression = "0 0/10 * * * ? *"; JobMonitors = new TypeList(); + JobProviders = new Dictionary(); + } + + public void AddProvider(string name) + where TJobRunnable : IJobRunnable + { + JobProviders[name] = typeof(TJobRunnable); } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs new file mode 100644 index 000000000..3de421a9b --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpJobExecutionException.cs @@ -0,0 +1,44 @@ +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.BackgroundTasks; + +public class AbpJobExecutionException : AbpException +{ + public Type JobType { get; } + /// + /// Creates a new object. + /// + /// Inner exception + public AbpJobExecutionException(Type jobType) + : this( + jobType, + $"Unable to execute job {jobType.Name}.", + null) + { + } + + /// + /// Creates a new object. + /// + /// Execute job type + /// Inner exception + public AbpJobExecutionException(Type jobType, Exception innerException) + : this( + jobType, + $"Unable to execute job {jobType.Name} because it: {innerException.Message}", + innerException) + { + } + /// + /// Creates a new object. + /// + /// Execute job type + /// Exception message + /// Inner exception + public AbpJobExecutionException(Type jobType, string message, Exception innerException) + : base(message, innerException) + { + JobType = jobType; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs similarity index 100% rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobEvent.cs diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs similarity index 100% rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobEventBase.cs diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs index 75af712ad..96f366af8 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContext.cs @@ -8,6 +8,7 @@ public class JobRunnableContext public Type JobType { get; } public IServiceProvider ServiceProvider { get; } public IReadOnlyDictionary JobData { get; } + public object Result { get; private set; } public JobRunnableContext( Type jobType, IServiceProvider serviceProvider, @@ -17,4 +18,9 @@ public class JobRunnableContext ServiceProvider = serviceProvider; JobData = jobData; } + + public void SetResult(object result) + { + Result = result; + } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs new file mode 100644 index 000000000..f2fe7510b --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs @@ -0,0 +1,85 @@ +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace LINGYUN.Abp.BackgroundTasks; + +public static class JobRunnableContextExtensions +{ + public static T GetService(this JobRunnableContext context) + { + return context.ServiceProvider.GetService(); + } + + public static object GetService(this JobRunnableContext context, Type serviceType) + { + return context.ServiceProvider.GetService(serviceType); + } + + public static T GetRequiredService(this JobRunnableContext context) + { + return context.ServiceProvider.GetRequiredService(); + } + + public static object GetRequiredService(this JobRunnableContext context, Type serviceType) + { + return context.ServiceProvider.GetRequiredService(serviceType); + } + + public static string GetString(this JobRunnableContext context, string key) + { + return context.GetJobData(key).ToString(); + } + + public static bool TryGetString(this JobRunnableContext context, string key, out string value) + { + if (context.TryGetJobData(key, out var data) && data != null) + { + value = data.ToString(); + return true; + } + value = default; + return false; + } + + public static T GetJobData(this JobRunnableContext context, string key) where T : struct + { + var value = context.GetJobData(key); + + return value.To(); + } + + public static bool TryGetJobData(this JobRunnableContext context, string key, out T value) where T : struct + { + if (context.TryGetJobData(key, out var data) && data != null) + { + try + { + value = data.To(); + return true; + } + catch + { + } + } + value = default; + return false; + } + + public static object GetJobData(this JobRunnableContext context, string key) + { + if (context.TryGetJobData(key, out var value) && value != null) + { + return value; + } + throw new ArgumentException(key + " not specified."); + } + + public static bool TryGetJobData(this JobRunnableContext context, string key, out object value) + { + if (context.JobData.TryGetValue(key, out value)) + { + return true; + } + return false; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml new file mode 100644 index 000000000..ac6b5b292 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj new file mode 100644 index 000000000..f7afc48e8 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN.Abp.BackgroundTasks.Jobs.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs new file mode 100644 index 000000000..b0ccd972a --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/AbpBackgroundTasksJobsModule.cs @@ -0,0 +1,25 @@ +using Volo.Abp.Emailing; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; +using Volo.Abp.Sms; + +namespace LINGYUN.Abp.BackgroundTasks.Jobs; + +[DependsOn(typeof(AbpEmailingModule))] +[DependsOn(typeof(AbpSmsModule))] +[DependsOn(typeof(AbpHttpClientModule))] +public class AbpBackgroundTasksJobsModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.AddProvider(DefaultJobNames.ConsoleJob); + options.AddProvider(DefaultJobNames.SendEmailJob); + options.AddProvider(DefaultJobNames.SendSmsJob); + options.AddProvider(DefaultJobNames.SleepJob); + options.AddProvider(DefaultJobNames.ServiceInvocationJob); + options.AddProvider(DefaultJobNames.HttpRequestJob); + }); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs similarity index 81% rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs index a108d492c..52377cb0e 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/ConsoleJob.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace LINGYUN.Abp.BackgroundTasks.Primitives; +namespace LINGYUN.Abp.BackgroundTasks.Jobs; public class ConsoleJob : IJobRunnable { diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs new file mode 100644 index 000000000..b8527766d --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/DefaultJobNames.cs @@ -0,0 +1,29 @@ +namespace LINGYUN.Abp.BackgroundTasks.Jobs; + +public static class DefaultJobNames +{ + /// + /// 发送邮件 + /// + public const string SendEmailJob = "SendEmail"; + /// + /// 发送短信 + /// + public const string SendSmsJob = "SendSms"; + /// + /// 控制台输出 + /// + public const string ConsoleJob = "Console"; + /// + /// 休眠 + /// + public const string SleepJob = "Sleep"; + /// + /// 服务间调用 + /// + public const string ServiceInvocationJob = "ServiceInvocation"; + /// + /// Http请求 + /// + public const string HttpRequestJob = "HttpRequest"; +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs new file mode 100644 index 000000000..c3849914a --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Http; +using Volo.Abp.Json; + +namespace LINGYUN.Abp.BackgroundTasks.Jobs; + +public class HttpRequestJob : IJobRunnable +{ + public const string PropertyUrl = "url"; + public const string PropertyMethod = "method"; + public const string PropertyData = "data"; + public const string PropertyContentType = "contentType"; + public const string PropertyHeaders = "headers"; + public const string PropertyToken = "token"; + + public virtual async Task ExecuteAsync(JobRunnableContext context) + { + var clientFactory = context.GetRequiredService(); + + var client = clientFactory.CreateClient(); + var requestMessage = BuildRequestMessage(context); + + var response = await client.SendAsync( + requestMessage, + HttpCompletionOption.ResponseHeadersRead); + + var stringContent = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode && stringContent.IsNullOrWhiteSpace()) + { + context.SetResult($"HttpStatusCode: {(int)response.StatusCode}, Reason: {response.ReasonPhrase}"); + return; + } + context.SetResult(stringContent); + } + + protected virtual HttpRequestMessage BuildRequestMessage(JobRunnableContext context) + { + var url = context.GetString(PropertyUrl); + var method = context.GetString(PropertyMethod); + context.TryGetJobData(PropertyData, out var data); + context.TryGetJobData(PropertyContentType, out var contentType); + + var jsonSerializer = context.GetRequiredService(); + + var httpRequestMesasge = new HttpRequestMessage(new HttpMethod(method), url); + if (data != null) + { + // TODO: 需要支持表单类型 + + // application/json 支持 + httpRequestMesasge.Content = new StringContent( + jsonSerializer.Serialize(data), + Encoding.UTF8, + contentType?.ToString() ?? MimeTypes.Application.Json); + } + if (context.TryGetJobData(PropertyHeaders, out var headers) && + headers is IDictionary headersDic) + { + foreach (var header in headersDic) + { + httpRequestMesasge.Headers.Add(header.Key, header.Value); + } + } + // TODO: 和 headers 一起? + if (context.TryGetString(PropertyToken, out var accessToken)) + { + httpRequestMesasge.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + } + + return httpRequestMesasge; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs new file mode 100644 index 000000000..765c35db6 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Volo.Abp.Emailing; + +namespace LINGYUN.Abp.BackgroundTasks.Jobs; + +public class SendEmailJob : IJobRunnable +{ + public const string PropertyFrom = "from"; + public const string PropertyTo = "to"; + public const string PropertySubject = "subject"; + public const string PropertyBody = "body"; + public const string PropertyIsBodyHtml = "isBodyHtml"; + + public virtual async Task ExecuteAsync(JobRunnableContext context) + { + context.TryGetString(PropertyFrom, out var from); + var to = context.GetString(PropertyTo); + var subject = context.GetString(PropertySubject); + var body = context.GetString(PropertyBody); + context.TryGetJobData(PropertyIsBodyHtml, out var isBodyHtml); + + var emailSender = context.GetRequiredService(); + + await emailSender.QueueAsync(from, to, subject, body, isBodyHtml); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs new file mode 100644 index 000000000..7d3cc2463 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendSmsJob.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Sms; + +namespace LINGYUN.Abp.BackgroundTasks.Jobs; + +public class SendSmsJob : IJobRunnable +{ + public const string PropertyPhoneNumber = "phoneNumber"; + public const string PropertyMessage = "message"; + public const string PropertyProperties = "properties"; + + public virtual async Task ExecuteAsync(JobRunnableContext context) + { + var phoneNumber = context.GetString(PropertyPhoneNumber); + var message = context.GetString(PropertyMessage); + + var smsMessage = new SmsMessage(phoneNumber, message); + if (context.TryGetJobData(PropertyProperties, out var data) && + data is IDictionary properties) + { + smsMessage.Properties.AddIfNotContains(properties); + } + + var smsSender = context.GetRequiredService(); + + await smsSender.SendAsync(smsMessage); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs new file mode 100644 index 000000000..19ba601c4 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ServiceInvocationJob.cs @@ -0,0 +1,117 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Http.Client; +using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.Http.Client.DynamicProxying; +using Volo.Abp.Http.Client.Proxying; +using Volo.Abp.Http.Modeling; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.BackgroundTasks.Jobs; + +public class ServiceInvocationJob : IJobRunnable +{ + public const string PropertyService = "service"; + public const string PropertyMethod = "method"; + public const string PropertyCulture = "culture"; + + public virtual async Task ExecuteAsync(JobRunnableContext context) + { + // 获取参数列表 + var type = context.GetString(PropertyService); + var method = context.GetString(PropertyMethod); + var serviceType = Type.GetType(type, true); + var serviceMethod = serviceType.GetMethod(method); + context.TryGetString(PropertyCulture, out var culture); + + using (CultureHelper.Use(culture ?? CultureInfo.CurrentCulture.Name)) + { + // 反射所必须的参数 + var callRequestMethod = nameof(DynamicHttpProxyInterceptorClientProxy.CallRequestAsync); + var clientProxyType = typeof(DynamicHttpProxyInterceptorClientProxy<>).MakeGenericType(serviceType); + var clientProxy = context.GetRequiredService(clientProxyType); + var clientProxyMethod = typeof(DynamicHttpProxyInterceptorClientProxy<>).GetMethod(callRequestMethod); + + // 调用远程服务发现端点 + var actionApiDescription = await GetActionApiDescriptionModel(context, serviceType, serviceMethod); + + // 拼接调用参数 + var invokeParameters = new Dictionary(); + var methodParameters = serviceMethod.GetParameters(); + foreach (var parameter in methodParameters) + { + if (context.TryGetJobData(parameter.Name, out var value)) + { + invokeParameters.Add(parameter.Name, value); + } + } + + // 构造服务代理上下文 + var clientProxyRequestContext = new ClientProxyRequestContext( + actionApiDescription, + invokeParameters, + serviceType); + + if (serviceMethod.ReturnType.GenericTypeArguments.IsNullOrEmpty()) + { + // 直接调用 + var taskProxy = (Task)clientProxyMethod.Invoke(clientProxy, new object[] { clientProxyRequestContext }); + await taskProxy; + } + else + { + // 有返回值的调用 + + var callRequestAsyncMethod = typeof(DynamicHttpProxyInterceptor) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .First(m => m.Name == callRequestMethod && m.IsGenericMethodDefinition); + + var returnType = serviceMethod.ReturnType.GenericTypeArguments[0]; + var result = (Task)callRequestAsyncMethod + .MakeGenericMethod(returnType) + .Invoke(this, new object[] { context }); + + context.SetResult(await GetResultAsync(result, returnType)); + } + } + } + + protected virtual async Task GetActionApiDescriptionModel( + JobRunnableContext context, + Type serviceType, + MethodInfo method) + { + var clientOptions = context.GetRequiredService>().Value; + var remoteServiceConfigurationProvider = context.GetRequiredService(); + var proxyHttpClientFactory = context.GetRequiredService(); + var apiDescriptionFinder = context.GetRequiredService(); + + var clientConfig = clientOptions.HttpClientProxies.GetOrDefault(serviceType) ?? + throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {serviceType.FullName}."); + var remoteServiceConfig = await remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); + var client = proxyHttpClientFactory.Create(clientConfig.RemoteServiceName); + + return await apiDescriptionFinder.FindActionAsync( + client, + remoteServiceConfig.BaseUrl, + serviceType, + method + ); + } + + protected virtual async Task GetResultAsync(Task task, Type resultType) + { + await task; + var resultProperty = typeof(Task<>) + .MakeGenericType(resultType) + .GetProperty(nameof(Task.Result), BindingFlags.Instance | BindingFlags.Public); + Check.NotNull(resultProperty, nameof(resultProperty)); + return resultProperty.GetValue(task); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs similarity index 84% rename from aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs rename to aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs index d2f65f17e..9ec6be494 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/SleepJob.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace LINGYUN.Abp.BackgroundTasks.Primitives; +namespace LINGYUN.Abp.BackgroundTasks.Jobs; public class SleepJob : IJobRunnable { diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs index 45f35155e..2c14fa422 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Quartz; using System; +using System.Collections.Generic; using Volo.Abp.DependencyInjection; using Volo.Abp.Timing; @@ -27,7 +28,7 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD public IJobDetail CreateJob(JobInfo job) { - var jobType = Type.GetType(job.Type); + var jobType = Type.GetType(job.Type) ?? Options.JobProviders.GetOrDefault(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."); diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs index 55b65a3af..7f411b16f 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs @@ -29,5 +29,7 @@ public class QuartzJobSimpleAdapter : IJob context.MergedJobDataMap.ToImmutableDictionary()); await jobExecuter.ExecuteAsync(jobContext); + + context.Result = jobContext.Result; } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs index 2ee46ed95..94c50a110 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs @@ -1,13 +1,10 @@ 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; @@ -18,32 +15,9 @@ namespace LINGYUN.Abp.BackgroundTasks; [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(); } - - private static void AutoAddJobMonitors(IServiceCollection services) - { - var jobMonitors = new List(); - - services.OnRegistred(context => - { - if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(JobEventBase<>))) - { - jobMonitors.Add(context.ImplementationType); - } - }); - - services.Configure(options => - { - options.JobMonitors.AddIfNotContains(jobMonitors); - }); - } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs deleted file mode 100644 index b8e3f95e8..000000000 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Primitives/EmailingJob.cs +++ /dev/null @@ -1,14 +0,0 @@ -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) - { - - } -} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj index ccec8e8b2..1610fecc9 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj @@ -50,6 +50,7 @@ + diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs index ef8042545..1626a8402 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AuditLogging.Elasticsearch; +using LINGYUN.Abp.BackgroundTasks.Jobs; using LINGYUN.Abp.BackgroundTasks.Quartz; using LINGYUN.Abp.Data.DbMigrator; using LINGYUN.Abp.ExceptionHandling.Emailing; @@ -42,6 +43,7 @@ namespace LY.MicroService.TaskManagement; typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpAspNetCoreMultiTenancyModule), typeof(AbpDbFinderMultiTenancyModule), + typeof(AbpBackgroundTasksJobsModule), typeof(AbpBackgroundTasksQuartzModule), typeof(TaskManagementApplicationModule), typeof(TaskManagementHttpApiModule), From 97071b9562dc4b5935a9b9129a42463777fb8571 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Mon, 10 Jan 2022 20:23:09 +0800 Subject: [PATCH 08/10] feat(tasks): perfect job management interface support --- .../api/task-management/backgroundJobInfo.ts | 78 +++++ .../api/task-management/backgroundJobLog.ts | 32 ++ .../model/backgroundJobInfoModel.ts | 88 +++++ .../model/backgroundJobLogModel.ts | 22 ++ .../components/BackgroundJobInfoModal.vue | 325 ++++++++++++++++++ .../components/BackgroundJobInfoTable.vue | 164 +++++++++ .../background-jobs/datas/ModalData.ts | 154 +++++++++ .../background-jobs/datas/TableData.ts | 125 +++++++ .../background-jobs/datas/typing.ts | 41 +++ .../task-management/background-jobs/index.vue | 15 + apps/vue/types/abp.d.ts | 2 + .../LINGYUN.MicroService.TaskManagement.sln | 4 +- .../LINGYUN.Abp.BackgroundTasks.Jobs.csproj | 5 +- .../Jobs/AbpBackgroundTasksJobsModule.cs | 4 +- .../Abp/BackgroundTasks/Jobs/ConsoleJob.cs | 4 +- .../BackgroundTasks/Jobs/HttpRequestJob.cs | 14 +- .../Abp/BackgroundTasks/Jobs/SendSmsJob.cs | 13 +- .../Jobs/ServiceInvocationJob.cs | 228 +++++++++--- .../Quartz/QuartzJobScheduler.cs | 11 +- .../BackgroundTasks/BackgroundJobAdapter.cs | 9 +- .../BackgroundTasks/BackgroundJobManager.cs | 22 +- .../Internal/BackgroundKeepAliveJob.cs | 1 + .../Internal/BackgroundPollingJob.cs | 3 + .../Internal/JobExecutedEvent.cs | 4 + .../BackgroundJobInfoCreateDto.cs | 21 +- .../BackgroundJobInfoCreateOrUpdateDto.cs | 11 +- .../TaskManagement/BackgroundJobInfoDto.cs | 9 +- .../BackgroundJobInfoGetListInput.cs | 4 +- .../IBackgroundJobInfoAppService.cs | 2 + ...kManagementPermissionDefinitionProvider.cs | 4 + .../Permissions/TaskManagementPermissions.cs | 1 + .../BackgroundJobInfoAppService.cs | 22 +- .../TaskManagement/BackgroundJobInfoConsts.cs | 2 +- .../Localization/Resources/en.json | 63 +++- .../Localization/Resources/zh-Hans.json | 63 +++- .../TaskManagementErrorCodes.cs | 2 + .../Abp/TaskManagement/BackgroundJobInfo.cs | 5 + .../TaskManagement/BackgroundJobInfoFilter.cs | 2 +- .../TaskManagement/BackgroundJobManager.cs | 27 ++ .../Abp/TaskManagement/BackgroundJobStore.cs | 6 +- .../EfCoreBackgroundJobInfoRepository.cs | 16 +- .../BackgroundJobInfoController.cs | 8 + ...ength-With-Background-Job-Info.Designer.cs | 193 +++++++++++ ...ld-Type-Length-With-Background-Job-Info.cs | 65 ++++ ...agementMigrationsDbContextModelSnapshot.cs | 8 +- .../Properties/launchSettings.json | 2 +- .../ocelot.Development.json | 35 ++ .../ocelot.platform.json | 35 ++ 48 files changed, 1875 insertions(+), 99 deletions(-) create mode 100644 apps/vue/src/api/task-management/backgroundJobInfo.ts create mode 100644 apps/vue/src/api/task-management/backgroundJobLog.ts create mode 100644 apps/vue/src/api/task-management/model/backgroundJobInfoModel.ts create mode 100644 apps/vue/src/api/task-management/model/backgroundJobLogModel.ts create mode 100644 apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoModal.vue create mode 100644 apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoTable.vue create mode 100644 apps/vue/src/views/task-management/background-jobs/datas/ModalData.ts create mode 100644 apps/vue/src/views/task-management/background-jobs/datas/TableData.ts create mode 100644 apps/vue/src/views/task-management/background-jobs/datas/typing.ts create mode 100644 apps/vue/src/views/task-management/background-jobs/index.vue create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220110104046_Reset-Field-Type-Length-With-Background-Job-Info.Designer.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220110104046_Reset-Field-Type-Length-With-Background-Job-Info.cs diff --git a/apps/vue/src/api/task-management/backgroundJobInfo.ts b/apps/vue/src/api/task-management/backgroundJobInfo.ts new file mode 100644 index 000000000..4dbb69242 --- /dev/null +++ b/apps/vue/src/api/task-management/backgroundJobInfo.ts @@ -0,0 +1,78 @@ +import { defAbpHttp } from '/@/utils/http/abp'; +import { + BackgroundJobInfo, + BackgroundJobInfoCreate, + BackgroundJobInfoUpdate, + BackgroundJobInfoGetListInput, +} from './model/backgroundJobInfoModel'; +import { format } from '/@/utils/strings'; +import { PagedResultDto } from '../model/baseModel'; + +enum Api { + GetById = '/api/task-management/background-jobs/{id}', + GetList = '/api/task-management/background-jobs', + Create = '/api/task-management/background-jobs', + Update = '/api/task-management/background-jobs/{id}', + Delete = '/api/task-management/background-jobs/{id}', + Pause = '/api/task-management/background-jobs/{id}/pause', + Resume = '/api/task-management/background-jobs/{id}/resume', + Trigger = '/api/task-management/background-jobs/{id}/trigger', + Stop = '/api/task-management/background-jobs/{id}/stop', +} + +export const getById = (id: string) => { + return defAbpHttp.get({ + url: format(Api.GetById, { id: id }), + }); +}; + +export const getList = (input: BackgroundJobInfoGetListInput) => { + return defAbpHttp.get>({ + url: Api.GetList, + params: input, + }); +}; + +export const create = (input: BackgroundJobInfoCreate) => { + return defAbpHttp.post({ + url: Api.Create, + data: input, + }); +}; + +export const update = (id: string, input: BackgroundJobInfoUpdate) => { + return defAbpHttp.put({ + url: format(Api.Update, { id: id }), + data: input, + }); +}; + +export const deleteById = (id: string) => { + return defAbpHttp.delete({ + url: format(Api.Delete, { id: id }), + }); +}; + +export const pause = (id: string) => { + return defAbpHttp.put({ + url: format(Api.Pause, { id: id }), + }); +}; + +export const resume = (id: string) => { + return defAbpHttp.put({ + url: format(Api.Resume, { id: id }), + }); +}; + +export const trigger = (id: string) => { + return defAbpHttp.put({ + url: format(Api.Trigger, { id: id }), + }); +}; + +export const stop = (id: string) => { + return defAbpHttp.put({ + url: format(Api.Stop, { id: id }), + }); +}; diff --git a/apps/vue/src/api/task-management/backgroundJobLog.ts b/apps/vue/src/api/task-management/backgroundJobLog.ts new file mode 100644 index 000000000..816530283 --- /dev/null +++ b/apps/vue/src/api/task-management/backgroundJobLog.ts @@ -0,0 +1,32 @@ +import { defAbpHttp } from '/@/utils/http/abp'; +import { + BackgroundJobLog, + BackgroundJobLogGetListInput, +} from './model/backgroundJobLogModel'; +import { format } from '/@/utils/strings'; +import { PagedResultDto } from '../model/baseModel'; + +enum Api { + GetById = '/api/task-management/background-jobs/logs/{id}', + GetList = '/api/task-management/background-jobs/logs', + Delete = '/api/task-management/background-jobs/logs/{id}', +} + +export const getById = (id: string) => { + return defAbpHttp.get({ + url: format(Api.GetById, { id: id }), + }); +}; + +export const getList = (input: BackgroundJobLogGetListInput) => { + return defAbpHttp.get>({ + url: Api.GetList, + params: input, + }); +}; + +export const deleteById = (id: string) => { + return defAbpHttp.delete({ + url: format(Api.Delete, { id: id }), + }); +}; diff --git a/apps/vue/src/api/task-management/model/backgroundJobInfoModel.ts b/apps/vue/src/api/task-management/model/backgroundJobInfoModel.ts new file mode 100644 index 000000000..8bf3a9d8d --- /dev/null +++ b/apps/vue/src/api/task-management/model/backgroundJobInfoModel.ts @@ -0,0 +1,88 @@ +import { ExtensibleAuditedEntity, IHasConcurrencyStamp, PagedAndSortedResultRequestDto } from "../../model/baseModel"; + +export enum JobStatus { + None = -1, + Completed = 0, + Running = 10, + Paused = 20, + Stopped = 30, +} + +export enum JobType { + Once, + Period, + Persistent, +} + +export enum JobPriority { + Low = 5, + BelowNormal = 10, + Normal = 0xF, + AboveNormal = 20, + High = 25 +} + +export interface BackgroundJobInfo extends ExtensibleAuditedEntity, IHasConcurrencyStamp { + isEnabled: boolean; + name: string; + group: string; + type: string; + result: string; + args: ExtraPropertyDictionary, + description?: string; + beginTime: Date; + endTime?: Date; + lastRunTime?: Date; + nextRunTime?: Date; + jobType: JobType; + cron: string; + triggerCount: number; + tryCount: number; + maxTryCount: number; + maxCount: number; + isAbandoned: boolean; + interval: number; + priority: JobPriority; + lockTimeOut: number; +} + +interface BackgroundJobInfoCreateOrUpdate { + isEnabled: boolean; + args: ExtraPropertyDictionary; + description?: string; + beginTime: Date; + endTime?: Date; + jobType: JobType; + cron: string; + maxCount: number; + interval: number; + priority: JobPriority; + lockTimeOut: number; +} + +export interface BackgroundJobInfoCreate extends BackgroundJobInfoCreateOrUpdate { + name: string; + group: string; + type: string; +} + +export interface BackgroundJobInfoUpdate extends BackgroundJobInfoCreateOrUpdate, IHasConcurrencyStamp { + +} + +export interface BackgroundJobInfoGetListInput extends PagedAndSortedResultRequestDto { + filter?: string; + name?: string; + group?: string; + type?: string; + status?: JobStatus; + beginTime?: Date; + endTime?: Date; + beginLastRunTime?: Date; + endLastRunTime?: Date; + beginCreationTime?: Date; + endCreationTime?: Date; + isAbandoned?: boolean; + jobType?: JobType; + priority?: JobPriority; +} diff --git a/apps/vue/src/api/task-management/model/backgroundJobLogModel.ts b/apps/vue/src/api/task-management/model/backgroundJobLogModel.ts new file mode 100644 index 000000000..d1e8377bd --- /dev/null +++ b/apps/vue/src/api/task-management/model/backgroundJobLogModel.ts @@ -0,0 +1,22 @@ +import { PagedAndSortedResultRequestDto } from '/@/api/model/baseModel'; + +export interface BackgroundJobLog { + id: number; + jobName: string; + jobGroup: string; + jobType: string; + message: string; + runTime: Date; + exception?: string; +} + +export interface BackgroundJobLogGetListInput extends PagedAndSortedResultRequestDto { + jobId?: string; + filter?: string; + hasExceptions?: boolean; + name?: string; + group?: string; + type?: string; + beginRunTime?: Date; + endRunTime?: Date; +} diff --git a/apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoModal.vue b/apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoModal.vue new file mode 100644 index 000000000..ea653d3ea --- /dev/null +++ b/apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoModal.vue @@ -0,0 +1,325 @@ +