diff --git a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln
index 04fc3f6c9..819bd6a40 100644
--- a/aspnet-core/LINGYUN.MicroService.TaskManagement.sln
+++ b/aspnet-core/LINGYUN.MicroService.TaskManagement.sln
@@ -84,7 +84,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.TaskManagement.DbMigrator", "migrations\LY.MicroService.TaskManagement.DbMigrator\LY.MicroService.TaskManagement.DbMigrator.csproj", "{52FD8F39-0AB6-4C51-A584-187C219AC8B5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LY.MicroService.TaskManagement.EntityFrameworkCore", "migrations\LY.MicroService.TaskManagement.EntityFrameworkCore\LY.MicroService.TaskManagement.EntityFrameworkCore.csproj", "{58F51875-6D2C-4A65-9DD3-8BC004B39B72}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.TaskManagement.EntityFrameworkCore", "migrations\LY.MicroService.TaskManagement.EntityFrameworkCore\LY.MicroService.TaskManagement.EntityFrameworkCore.csproj", "{58F51875-6D2C-4A65-9DD3-8BC004B39B72}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "elasticsearch", "elasticsearch", "{DC7ACD8B-044B-435D-94C5-06C75A8BD026}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Elasticsearch", "modules\elasticsearch\LINGYUN.Abp.Elasticsearch\LINGYUN.Abp.Elasticsearch.csproj", "{167CE485-72D3-4E95-80B1-00430CBB8888}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Elasticsearch.Jobs", "modules\elasticsearch\LINGYUN.Abp.Elasticsearch.Jobs\LINGYUN.Abp.Elasticsearch.Jobs.csproj", "{798FEFB4-B65F-4B6C-A36D-611DD71A48AB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -212,6 +218,14 @@ Global
{58F51875-6D2C-4A65-9DD3-8BC004B39B72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58F51875-6D2C-4A65-9DD3-8BC004B39B72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58F51875-6D2C-4A65-9DD3-8BC004B39B72}.Release|Any CPU.Build.0 = Release|Any CPU
+ {167CE485-72D3-4E95-80B1-00430CBB8888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {167CE485-72D3-4E95-80B1-00430CBB8888}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {167CE485-72D3-4E95-80B1-00430CBB8888}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {167CE485-72D3-4E95-80B1-00430CBB8888}.Release|Any CPU.Build.0 = Release|Any CPU
+ {798FEFB4-B65F-4B6C-A36D-611DD71A48AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {798FEFB4-B65F-4B6C-A36D-611DD71A48AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {798FEFB4-B65F-4B6C-A36D-611DD71A48AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {798FEFB4-B65F-4B6C-A36D-611DD71A48AB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -247,6 +261,8 @@ Global
{DF963ABB-E713-49E9-A03C-DEB431E369DF} = {B38D2141-AC0F-4F4A-9315-4292E3856C15}
{52FD8F39-0AB6-4C51-A584-187C219AC8B5} = {820A8FA4-17C4-44DF-8C31-9F211D00F790}
{58F51875-6D2C-4A65-9DD3-8BC004B39B72} = {820A8FA4-17C4-44DF-8C31-9F211D00F790}
+ {167CE485-72D3-4E95-80B1-00430CBB8888} = {DC7ACD8B-044B-435D-94C5-06C75A8BD026}
+ {798FEFB4-B65F-4B6C-A36D-611DD71A48AB} = {A9536BD2-2573-44CA-B033-DC16B7E345E5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E1FD1F4C-D344-408B-97CF-B6F1F6D7D293}
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/FodyWeavers.xml b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/FodyWeavers.xsd b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.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/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN.Abp.Elasticsearch.Jobs.csproj b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN.Abp.Elasticsearch.Jobs.csproj
new file mode 100644
index 000000000..dee000c39
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN.Abp.Elasticsearch.Jobs.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/AbpElasticsearchJobsModule.cs b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/AbpElasticsearchJobsModule.cs
new file mode 100644
index 000000000..bb0949a9b
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/AbpElasticsearchJobsModule.cs
@@ -0,0 +1,29 @@
+using LINGYUN.Abp.BackgroundTasks;
+using LINGYUN.Abp.Elasticsearch.Jobs.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.Timing;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.Elasticsearch.Jobs;
+
+[DependsOn(typeof(AbpTimingModule))]
+[DependsOn(typeof(AbpElasticsearchModule))]
+[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))]
+public class AbpElasticsearchJobsModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add()
+ .AddVirtualJson("/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources");
+ });
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/ElasticsearchJobDefinitionProvider.cs b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/ElasticsearchJobDefinitionProvider.cs
new file mode 100644
index 000000000..d9d2a4a83
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/ElasticsearchJobDefinitionProvider.cs
@@ -0,0 +1,16 @@
+using LINGYUN.Abp.BackgroundTasks;
+
+namespace LINGYUN.Abp.Elasticsearch.Jobs;
+
+public class NotificationJobDefinitionProvider : JobDefinitionProvider
+{
+ public override void Define(IJobDefinitionContext context)
+ {
+ context.Add(
+ new JobDefinition(
+ ExpiredIndicesCleanupJob.Name,
+ typeof(ExpiredIndicesCleanupJob),
+ LocalizableStatic.Create("IndicesCleanupJob"),
+ ExpiredIndicesCleanupJob.Paramters));
+ }
+}
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/ExpiredIndicesCleanupJob.cs b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/ExpiredIndicesCleanupJob.cs
new file mode 100644
index 000000000..8817d765f
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/ExpiredIndicesCleanupJob.cs
@@ -0,0 +1,114 @@
+using LINGYUN.Abp.BackgroundTasks;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Timing;
+
+namespace LINGYUN.Abp.Elasticsearch.Jobs;
+
+///
+/// Elasticsearch 过期索引清理作业
+///
+public class ExpiredIndicesCleanupJob : IJobRunnable
+{
+ #region Definition Paramters
+
+ public readonly static IReadOnlyList Paramters =
+ new List
+ {
+ new JobDefinitionParamter(
+ PropertyIndexPrefix,
+ LocalizableStatic.Create("Indices:IndexPrefix"),
+ required: true),
+ new JobDefinitionParamter(
+ PropertyTimeZone,
+ LocalizableStatic.Create("Indices:TimeZone"),
+ LocalizableStatic.Create("Indices:TimeZoneDesc")),
+ new JobDefinitionParamter(
+ PropertyExpirationTime,
+ LocalizableStatic.Create("Indices:ExpirationTime")),
+ };
+
+ public const string Name = "ExpiredIndicesCleanupJob";
+ ///
+ /// 每次清除记录大小
+ ///
+ private const string PropertyIndexPrefix = "IndexPrefix";
+ ///
+ /// 计算时差的时区, 默认Utc
+ ///
+ private const string PropertyTimeZone = "TimeZone";
+ ///
+ /// 过期时间, 单位秒, 默认 5184000 (60天)
+ ///
+ private const string PropertyExpirationTime = "ExpirationTime";
+
+ #endregion
+
+ public async virtual Task ExecuteAsync(JobRunnableContext context)
+ {
+ #region Initializes Job Parameters
+
+ var timeZone = TimeZoneInfo.Utc;
+ var indexPrefix = context.GetString(PropertyIndexPrefix);
+ var timeZoneString = context.GetOrDefaultString(PropertyTimeZone, "utc");
+ var expirationSecond = context.GetOrDefaultJobData(PropertyExpirationTime, 5184000L);
+
+ if (!timeZoneString.IsNullOrWhiteSpace())
+ {
+ timeZone = timeZoneString.ToLowerInvariant() switch
+ {
+ "local" => TimeZoneInfo.Local,
+ _ => TimeZoneInfo.Utc,
+ };
+ }
+
+ var elasticClientFactory = context.GetRequiredService();
+ var elasticClient = elasticClientFactory.Create();
+
+ var clock = context.GetRequiredService();
+ var expirationTime = clock.Now.AddSeconds(-expirationSecond);
+ var startTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), timeZone);
+ var removeIndices = new List();
+
+ #endregion
+
+ #region ES indices.get_settings API
+
+ // GET demo*/_settings
+ var settingResponse = await elasticClient.Indices.GetSettingsAsync(indexPrefix);
+ if (!settingResponse.IsValid)
+ {
+ throw new AbpJobExecutionException(GetType(), settingResponse.ServerError.ToString(), settingResponse.OriginalException);
+ }
+
+ foreach (var indexSet in settingResponse.Indices)
+ {
+ // 索引创建日期
+ if (indexSet.Value.Settings.TryGetValue("index.creation_date", out var indexSetV) &&
+ long.TryParse(indexSetV.ToString(), out var timestamp))
+ {
+ var indexCreationDate = startTime.AddMilliseconds(timestamp);
+ if (indexCreationDate <= expirationTime)
+ {
+ removeIndices.Add(indexSet.Key.Name);
+ }
+ }
+ }
+
+ #endregion
+
+ #region ES indices.delete API
+
+ foreach (var index in removeIndices)
+ {
+ var delResponse = await elasticClient.Indices.DeleteAsync(index);
+ if (!delResponse.IsValid)
+ {
+ throw new AbpJobExecutionException(GetType(), delResponse.ServerError.ToString(), delResponse.OriginalException);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/LocalizableStatic.cs b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/LocalizableStatic.cs
new file mode 100644
index 000000000..4c1d3fcf5
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/LocalizableStatic.cs
@@ -0,0 +1,12 @@
+using LINGYUN.Abp.Elasticsearch.Jobs.Localization;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Elasticsearch.Jobs;
+
+internal static class LocalizableStatic
+{
+ public static ILocalizableString Create(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/ElasticsearchJobsResource.cs b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/ElasticsearchJobsResource.cs
new file mode 100644
index 000000000..d8752f225
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/ElasticsearchJobsResource.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Elasticsearch.Jobs.Localization;
+
+[LocalizationResourceName("ElasticsearchJobs")]
+public class ElasticsearchJobsResource
+{
+}
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources/en.json b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources/en.json
new file mode 100644
index 000000000..605aeb326
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources/en.json
@@ -0,0 +1,10 @@
+{
+ "culture": "en",
+ "texts": {
+ "IndicesCleanupJob": "Expired index cleanup job",
+ "Indices:IndexPrefix": "Index prefix",
+ "Indices:TimeZone": "Timezone",
+ "Indices:TimeZoneDesc": "Time zone for calculating the time difference, default Utc, optional (Utc, Local).",
+ "Indices:ExpirationTime": "Expiration time(seconds)"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources/zh-Hans.json b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..ecf73db34
--- /dev/null
+++ b/aspnet-core/modules/elasticsearch/LINGYUN.Abp.Elasticsearch.Jobs/LINGYUN/Abp/Elasticsearch/Jobs/Localization/Resources/zh-Hans.json
@@ -0,0 +1,10 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "IndicesCleanupJob": "过期索引清理作业",
+ "Indices:IndexPrefix": "索引前缀",
+ "Indices:TimeZone": "时区",
+ "Indices:TimeZoneDesc": "计算时差的时区, 默认Utc, 可选(Utc、Local)",
+ "Indices:ExpirationTime": "过期时间(秒)"
+ }
+}
\ No newline at end of file
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
index a9ac515da..f5da58d43 100644
--- 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
@@ -30,6 +30,16 @@ public static class JobRunnableContextExtensions
return context.GetJobData(key).ToString();
}
+ public static string GetOrDefaultString(this JobRunnableContext context, string key, string defaultValue = "")
+ {
+ if (context.TryGetString(key, out var value))
+ {
+ return value;
+ }
+
+ return defaultValue;
+ }
+
public static bool TryGetString(this JobRunnableContext context, string key, out string value)
{
if (context.TryGetJobData(key, out var data) && data != null)
@@ -65,6 +75,16 @@ public static class JobRunnableContextExtensions
return value.To();
}
+ public static T GetOrDefaultJobData(this JobRunnableContext context, string key, T defaultValue) where T : struct
+ {
+ if (context.TryGetJobData(key, out var value))
+ {
+ return value;
+ }
+
+ return defaultValue;
+ }
+
public static bool TryGetJobData(this JobRunnableContext context, string key, out T value) where T : struct
{
if (context.TryGetJobData(key, out var data) && data != null)