From 3ed9cf5ebd6359107f994bfdeab78fada355a947 Mon Sep 17 00:00:00 2001 From: SunCheng Date: Wed, 28 Jan 2026 11:19:23 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84:=20=E5=B0=86=20LogCleanupSer?= =?UTF-8?q?vice=20=E8=BD=AC=E4=B8=BA=20Quartz=20Job=20=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 LogCleanupJob 替代 LogCleanupService (BackgroundService) - 在 Expand.cs 中注册 LogCleanupJob (每天凌晨2点执行, 保留30天日志) - 从 Program.cs 移除 LogCleanupService 的 HostedService 注册 - 删除 Service/LogCleanupService.cs - 删除 Service/PeriodicBillBackgroundService.cs (已无用的重复服务) 所有后台任务现在统一通过 Quartz.NET 管理, 支持运行时控制 --- Service/{ => AI}/OpenAiService.cs | 2 +- Service/{ => AI}/SmartHandleService.cs | 4 +- Service/{ => AI}/TextSegmentService.cs | 2 +- Service/Budget/BudgetSavingsService.cs | 2 + Service/Budget/BudgetService.cs | 4 + Service/Budget/BudgetStatsService.cs | 2 + Service/EmailServices/EmailHandleService.cs | 4 +- .../EmailParse/EmailParseForm95555.cs | 4 +- .../EmailParse/EmailParseFormCcsvc.cs | 1 + .../EmailParse/IEmailParseServices.cs | 4 +- Service/Jobs/LogCleanupJob.cs | 78 +++++++++++++ Service/Jobs/PeriodicBillJob.cs | 1 + Service/LogCleanupService.cs | 106 ------------------ Service/{ => Message}/MessageService.cs | 2 +- Service/{ => Message}/NotificationService.cs | 2 +- Service/PeriodicBillBackgroundService.cs | 61 ---------- .../TransactionPeriodicService.cs | 2 +- .../TransactionStatisticsService.cs | 2 +- WebApi.Test/Budget/BudgetSavingsTest.cs | 2 + WebApi.Test/Budget/BudgetStatsTest.cs | 3 + WebApi/Controllers/MessageRecordController.cs | 1 + WebApi/Controllers/NotificationController.cs | 4 +- .../TransactionPeriodicController.cs | 4 +- .../TransactionRecordController.cs | 3 + WebApi/Expand.cs | 13 ++- WebApi/Program.cs | 3 - 26 files changed, 133 insertions(+), 183 deletions(-) rename Service/{ => AI}/OpenAiService.cs (99%) rename Service/{ => AI}/SmartHandleService.cs (99%) rename Service/{ => AI}/TextSegmentService.cs (99%) create mode 100644 Service/Jobs/LogCleanupJob.cs delete mode 100644 Service/LogCleanupService.cs rename Service/{ => Message}/MessageService.cs (98%) rename Service/{ => Message}/NotificationService.cs (99%) delete mode 100644 Service/PeriodicBillBackgroundService.cs rename Service/{ => Transaction}/TransactionPeriodicService.cs (99%) rename Service/{ => Transaction}/TransactionStatisticsService.cs (99%) diff --git a/Service/OpenAiService.cs b/Service/AI/OpenAiService.cs similarity index 99% rename from Service/OpenAiService.cs rename to Service/AI/OpenAiService.cs index 7de4f2b..151a2f6 100644 --- a/Service/OpenAiService.cs +++ b/Service/AI/OpenAiService.cs @@ -1,6 +1,6 @@ using System.Net.Http.Headers; -namespace Service; +namespace Service.AI; public interface IOpenAiService { diff --git a/Service/SmartHandleService.cs b/Service/AI/SmartHandleService.cs similarity index 99% rename from Service/SmartHandleService.cs rename to Service/AI/SmartHandleService.cs index b5d42e2..277cf42 100644 --- a/Service/SmartHandleService.cs +++ b/Service/AI/SmartHandleService.cs @@ -1,4 +1,6 @@ -namespace Service; +using Service.Transaction; + +namespace Service.AI; public interface ISmartHandleService { diff --git a/Service/TextSegmentService.cs b/Service/AI/TextSegmentService.cs similarity index 99% rename from Service/TextSegmentService.cs rename to Service/AI/TextSegmentService.cs index 8e6aa5e..8a06163 100644 --- a/Service/TextSegmentService.cs +++ b/Service/AI/TextSegmentService.cs @@ -1,7 +1,7 @@ using JiebaNet.Analyser; using JiebaNet.Segmenter; -namespace Service; +namespace Service.AI; /// /// 文本分词服务接口 diff --git a/Service/Budget/BudgetSavingsService.cs b/Service/Budget/BudgetSavingsService.cs index 9922ff6..cb2aef4 100644 --- a/Service/Budget/BudgetSavingsService.cs +++ b/Service/Budget/BudgetSavingsService.cs @@ -1,3 +1,5 @@ +using Service.Transaction; + namespace Service.Budget; public interface IBudgetSavingsService diff --git a/Service/Budget/BudgetService.cs b/Service/Budget/BudgetService.cs index c57f433..b858662 100644 --- a/Service/Budget/BudgetService.cs +++ b/Service/Budget/BudgetService.cs @@ -1,3 +1,7 @@ +using Service.AI; +using Service.Message; +using Service.Transaction; + namespace Service.Budget; public interface IBudgetService diff --git a/Service/Budget/BudgetStatsService.cs b/Service/Budget/BudgetStatsService.cs index 64a30e6..c1a1d96 100644 --- a/Service/Budget/BudgetStatsService.cs +++ b/Service/Budget/BudgetStatsService.cs @@ -1,3 +1,5 @@ +using Service.Transaction; + namespace Service.Budget; public interface IBudgetStatsService diff --git a/Service/EmailServices/EmailHandleService.cs b/Service/EmailServices/EmailHandleService.cs index bf0d2ab..03dfeaa 100644 --- a/Service/EmailServices/EmailHandleService.cs +++ b/Service/EmailServices/EmailHandleService.cs @@ -1,4 +1,6 @@ -using Service.EmailServices.EmailParse; +using Service.AI; +using Service.EmailServices.EmailParse; +using Service.Message; namespace Service.EmailServices; diff --git a/Service/EmailServices/EmailParse/EmailParseForm95555.cs b/Service/EmailServices/EmailParse/EmailParseForm95555.cs index eb87910..09daef0 100644 --- a/Service/EmailServices/EmailParse/EmailParseForm95555.cs +++ b/Service/EmailServices/EmailParse/EmailParseForm95555.cs @@ -1,4 +1,6 @@ -namespace Service.EmailServices.EmailParse; +using Service.AI; + +namespace Service.EmailServices.EmailParse; public class EmailParseForm95555( ILogger logger, diff --git a/Service/EmailServices/EmailParse/EmailParseFormCcsvc.cs b/Service/EmailServices/EmailParse/EmailParseFormCcsvc.cs index 345f7cc..9f3d1e9 100644 --- a/Service/EmailServices/EmailParse/EmailParseFormCcsvc.cs +++ b/Service/EmailServices/EmailParse/EmailParseFormCcsvc.cs @@ -1,4 +1,5 @@ using HtmlAgilityPack; +using Service.AI; // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract // ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract diff --git a/Service/EmailServices/EmailParse/IEmailParseServices.cs b/Service/EmailServices/EmailParse/IEmailParseServices.cs index 915696a..c72f7c1 100644 --- a/Service/EmailServices/EmailParse/IEmailParseServices.cs +++ b/Service/EmailServices/EmailParse/IEmailParseServices.cs @@ -1,4 +1,6 @@ -namespace Service.EmailServices.EmailParse; +using Service.AI; + +namespace Service.EmailServices.EmailParse; public interface IEmailParseServices { diff --git a/Service/Jobs/LogCleanupJob.cs b/Service/Jobs/LogCleanupJob.cs new file mode 100644 index 0000000..694f71c --- /dev/null +++ b/Service/Jobs/LogCleanupJob.cs @@ -0,0 +1,78 @@ +using Quartz; + +namespace Service.Jobs; + +/// +/// 日志清理定时任务 +/// +[DisallowConcurrentExecution] +public class LogCleanupJob(ILogger logger) : IJob +{ + private const int RetentionDays = 30; // 保留30天的日志 + + public Task Execute(IJobExecutionContext context) + { + try + { + logger.LogInformation("开始执行日志清理任务"); + + var logDirectory = Path.Combine(Directory.GetCurrentDirectory(), "logs"); + if (!Directory.Exists(logDirectory)) + { + logger.LogWarning("日志目录不存在: {LogDirectory}", logDirectory); + return Task.CompletedTask; + } + + var cutoffDate = DateTime.Now.AddDays(-RetentionDays); + var logFiles = Directory.GetFiles(logDirectory, "log-*.txt"); + var deletedCount = 0; + + foreach (var logFile in logFiles) + { + try + { + var fileName = Path.GetFileNameWithoutExtension(logFile); + var dateStr = fileName.Replace("log-", ""); + + // 尝试解析日期 (格式: yyyyMMdd) + if (DateTime.TryParseExact(dateStr, "yyyyMMdd", + CultureInfo.InvariantCulture, + DateTimeStyles.None, + out var logDate)) + { + if (logDate < cutoffDate) + { + File.Delete(logFile); + deletedCount++; + logger.LogInformation("已删除过期日志文件: {LogFile} (日期: {LogDate})", + Path.GetFileName(logFile), logDate.ToString("yyyy-MM-dd")); + } + } + } + catch (Exception ex) + { + logger.LogError(ex, "删除日志文件失败: {LogFile}", logFile); + } + } + + if (deletedCount > 0) + { + logger.LogInformation("日志清理完成,共删除 {DeletedCount} 个过期日志文件(保留 {RetentionDays} 天)", + deletedCount, RetentionDays); + } + else + { + logger.LogDebug("没有需要清理的过期日志文件"); + } + + logger.LogInformation("日志清理任务执行完成"); + } + catch (Exception ex) + { + logger.LogError(ex, "日志清理任务执行出错"); + throw; // 让 Quartz 知道任务失败 + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Service/Jobs/PeriodicBillJob.cs b/Service/Jobs/PeriodicBillJob.cs index a0c243c..94d3c2f 100644 --- a/Service/Jobs/PeriodicBillJob.cs +++ b/Service/Jobs/PeriodicBillJob.cs @@ -1,4 +1,5 @@ using Quartz; +using Service.Transaction; namespace Service.Jobs; diff --git a/Service/LogCleanupService.cs b/Service/LogCleanupService.cs deleted file mode 100644 index 8ab230f..0000000 --- a/Service/LogCleanupService.cs +++ /dev/null @@ -1,106 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace Service; - -/// -/// 日志清理后台服务 -/// -public class LogCleanupService(ILogger logger) : BackgroundService -{ - private readonly TimeSpan _checkInterval = TimeSpan.FromHours(24); // 每24小时检查一次 - private const int RetentionDays = 30; // 保留30天的日志 - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - logger.LogInformation("日志清理服务已启动"); - - // 启动时立即执行一次清理 - await CleanupOldLogsAsync(); - - // 定期清理 - while (!stoppingToken.IsCancellationRequested) - { - try - { - await Task.Delay(_checkInterval, stoppingToken); - await CleanupOldLogsAsync(); - } - catch (OperationCanceledException) - { - // 服务正在停止 - break; - } - catch (Exception ex) - { - logger.LogError(ex, "清理日志时发生错误"); - } - } - - logger.LogInformation("日志清理服务已停止"); - } - - /// - /// 清理过期的日志文件 - /// - private async Task CleanupOldLogsAsync() - { - await Task.Run(() => - { - try - { - var logDirectory = Path.Combine(Directory.GetCurrentDirectory(), "logs"); - if (!Directory.Exists(logDirectory)) - { - logger.LogWarning("日志目录不存在: {LogDirectory}", logDirectory); - return; - } - - var cutoffDate = DateTime.Now.AddDays(-RetentionDays); - var logFiles = Directory.GetFiles(logDirectory, "log-*.txt"); - var deletedCount = 0; - - foreach (var logFile in logFiles) - { - try - { - var fileName = Path.GetFileNameWithoutExtension(logFile); - var dateStr = fileName.Replace("log-", ""); - - // 尝试解析日期 (格式: yyyyMMdd) - if (DateTime.TryParseExact(dateStr, "yyyyMMdd", - CultureInfo.InvariantCulture, - DateTimeStyles.None, - out var logDate)) - { - if (logDate < cutoffDate) - { - File.Delete(logFile); - deletedCount++; - logger.LogInformation("已删除过期日志文件: {LogFile} (日期: {LogDate})", - Path.GetFileName(logFile), logDate.ToString("yyyy-MM-dd")); - } - } - } - catch (Exception ex) - { - logger.LogError(ex, "删除日志文件失败: {LogFile}", logFile); - } - } - - if (deletedCount > 0) - { - logger.LogInformation("日志清理完成,共删除 {DeletedCount} 个过期日志文件(保留 {RetentionDays} 天)", - deletedCount, RetentionDays); - } - else - { - logger.LogDebug("没有需要清理的过期日志文件"); - } - } - catch (Exception ex) - { - logger.LogError(ex, "清理日志过程中发生错误"); - } - }); - } -} diff --git a/Service/MessageService.cs b/Service/Message/MessageService.cs similarity index 98% rename from Service/MessageService.cs rename to Service/Message/MessageService.cs index e7c4114..f3570bf 100644 --- a/Service/MessageService.cs +++ b/Service/Message/MessageService.cs @@ -1,4 +1,4 @@ -namespace Service; +namespace Service.Message; public interface IMessageService { diff --git a/Service/NotificationService.cs b/Service/Message/NotificationService.cs similarity index 99% rename from Service/NotificationService.cs rename to Service/Message/NotificationService.cs index e5a0cf8..5dc5baa 100644 --- a/Service/NotificationService.cs +++ b/Service/Message/NotificationService.cs @@ -1,7 +1,7 @@ using WebPush; using PushSubscription = Entity.PushSubscription; -namespace Service; +namespace Service.Message; public interface INotificationService { diff --git a/Service/PeriodicBillBackgroundService.cs b/Service/PeriodicBillBackgroundService.cs deleted file mode 100644 index 8b9311a..0000000 --- a/Service/PeriodicBillBackgroundService.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace Service; - -/// -/// 周期性账单后台服务 -/// -public class PeriodicBillBackgroundService( - IServiceProvider serviceProvider, - ILogger logger -) : BackgroundService -{ - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - logger.LogInformation("周期性账单后台服务已启动"); - - while (!stoppingToken.IsCancellationRequested) - { - try - { - var now = DateTime.Now; - - // 计算下次执行时间(每天早上6点) - var nextRun = now.Date.AddHours(6); - if (now >= nextRun) - { - nextRun = nextRun.AddDays(1); - } - - var delay = nextRun - now; - logger.LogInformation("下次执行周期性账单检查时间: {NextRun}, 延迟: {Delay}", - nextRun.ToString("yyyy-MM-dd HH:mm:ss"), delay); - - await Task.Delay(delay, stoppingToken); - - if (stoppingToken.IsCancellationRequested) - break; - - // 执行周期性账单检查 - using (var scope = serviceProvider.CreateScope()) - { - var periodicService = scope.ServiceProvider.GetRequiredService(); - await periodicService.ExecutePeriodicBillsAsync(); - } - } - catch (OperationCanceledException) - { - logger.LogInformation("周期性账单后台服务已取消"); - break; - } - catch (Exception ex) - { - logger.LogError(ex, "周期性账单后台服务执行出错"); - // 出错后等待1小时再重试 - await Task.Delay(TimeSpan.FromHours(1), stoppingToken); - } - } - - logger.LogInformation("周期性账单后台服务已停止"); - } -} diff --git a/Service/TransactionPeriodicService.cs b/Service/Transaction/TransactionPeriodicService.cs similarity index 99% rename from Service/TransactionPeriodicService.cs rename to Service/Transaction/TransactionPeriodicService.cs index 1334644..93c0569 100644 --- a/Service/TransactionPeriodicService.cs +++ b/Service/Transaction/TransactionPeriodicService.cs @@ -1,4 +1,4 @@ -namespace Service; +namespace Service.Transaction; /// /// 周期性账单服务接口 diff --git a/Service/TransactionStatisticsService.cs b/Service/Transaction/TransactionStatisticsService.cs similarity index 99% rename from Service/TransactionStatisticsService.cs rename to Service/Transaction/TransactionStatisticsService.cs index f6c6d11..7554af3 100644 --- a/Service/TransactionStatisticsService.cs +++ b/Service/Transaction/TransactionStatisticsService.cs @@ -1,4 +1,4 @@ -namespace Service; +namespace Service.Transaction; public interface ITransactionStatisticsService { diff --git a/WebApi.Test/Budget/BudgetSavingsTest.cs b/WebApi.Test/Budget/BudgetSavingsTest.cs index 58e79c1..8971439 100644 --- a/WebApi.Test/Budget/BudgetSavingsTest.cs +++ b/WebApi.Test/Budget/BudgetSavingsTest.cs @@ -1,3 +1,5 @@ +using Service.Transaction; + namespace WebApi.Test.Budget; public class BudgetSavingsTest : BaseTest diff --git a/WebApi.Test/Budget/BudgetStatsTest.cs b/WebApi.Test/Budget/BudgetStatsTest.cs index 9226730..5592a05 100644 --- a/WebApi.Test/Budget/BudgetStatsTest.cs +++ b/WebApi.Test/Budget/BudgetStatsTest.cs @@ -1,4 +1,7 @@ using Microsoft.Extensions.Logging; +using Service.AI; +using Service.Message; +using Service.Transaction; namespace WebApi.Test.Budget; diff --git a/WebApi/Controllers/MessageRecordController.cs b/WebApi/Controllers/MessageRecordController.cs index 02b1da5..d9e98f4 100644 --- a/WebApi/Controllers/MessageRecordController.cs +++ b/WebApi/Controllers/MessageRecordController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Authorization; +using Service.Message; namespace WebApi.Controllers; diff --git a/WebApi/Controllers/NotificationController.cs b/WebApi/Controllers/NotificationController.cs index e2ba7e0..ae137fb 100644 --- a/WebApi/Controllers/NotificationController.cs +++ b/WebApi/Controllers/NotificationController.cs @@ -1,4 +1,6 @@ -namespace WebApi.Controllers; +using Service.Message; + +namespace WebApi.Controllers; [ApiController] [Route("api/[controller]/[action]")] diff --git a/WebApi/Controllers/TransactionPeriodicController.cs b/WebApi/Controllers/TransactionPeriodicController.cs index a447504..34bcdf1 100644 --- a/WebApi/Controllers/TransactionPeriodicController.cs +++ b/WebApi/Controllers/TransactionPeriodicController.cs @@ -1,4 +1,6 @@ -namespace WebApi.Controllers; +using Service.Transaction; + +namespace WebApi.Controllers; /// /// 周期性账单控制器 diff --git a/WebApi/Controllers/TransactionRecordController.cs b/WebApi/Controllers/TransactionRecordController.cs index 73fc6a6..2a4cddd 100644 --- a/WebApi/Controllers/TransactionRecordController.cs +++ b/WebApi/Controllers/TransactionRecordController.cs @@ -1,3 +1,6 @@ +using Service.AI; +using Service.Transaction; + namespace WebApi.Controllers; [ApiController] diff --git a/WebApi/Expand.cs b/WebApi/Expand.cs index 52a9270..472649d 100644 --- a/WebApi/Expand.cs +++ b/WebApi/Expand.cs @@ -1,4 +1,4 @@ -using Quartz; +using Quartz; using Service.Jobs; namespace WebApi; @@ -55,6 +55,17 @@ public static class Expand .WithIdentity("DbBackupTrigger") .WithCronSchedule("0 0 6 * * ?") // 每天早上6点执行 .WithDescription("每天早上6点执行数据库备份")); + + // 配置日志清理任务 - 每天凌晨2点执行 + var logCleanupJobKey = new JobKey("LogCleanupJob"); + q.AddJob(opts => opts + .WithIdentity(logCleanupJobKey) + .WithDescription("日志清理任务")); + q.AddTrigger(opts => opts + .ForJob(logCleanupJobKey) + .WithIdentity("LogCleanupTrigger") + .WithCronSchedule("0 0 2 * * ?") // 每天凌晨2点执行 + .WithDescription("每天凌晨2点执行日志清理(保留30天)")); }); // 添加 Quartz Hosted Service diff --git a/WebApi/Program.cs b/WebApi/Program.cs index 11817ef..0dc3548 100644 --- a/WebApi/Program.cs +++ b/WebApi/Program.cs @@ -124,9 +124,6 @@ builder.Services.AddSingleton(fsql); // 自动扫描注册服务和仓储 builder.Services.AddServices(); -// 注册日志清理后台服务 -builder.Services.AddHostedService(); - // 配置 Quartz.NET 定时任务 builder.AddScheduler();