重构: 将 LogCleanupService 转为 Quartz Job 服务
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 22s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 22s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
- 创建 LogCleanupJob 替代 LogCleanupService (BackgroundService) - 在 Expand.cs 中注册 LogCleanupJob (每天凌晨2点执行, 保留30天日志) - 从 Program.cs 移除 LogCleanupService 的 HostedService 注册 - 删除 Service/LogCleanupService.cs - 删除 Service/PeriodicBillBackgroundService.cs (已无用的重复服务) 所有后台任务现在统一通过 Quartz.NET 管理, 支持运行时控制
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Service;
|
namespace Service.AI;
|
||||||
|
|
||||||
public interface IOpenAiService
|
public interface IOpenAiService
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Service;
|
using Service.Transaction;
|
||||||
|
|
||||||
|
namespace Service.AI;
|
||||||
|
|
||||||
public interface ISmartHandleService
|
public interface ISmartHandleService
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using JiebaNet.Analyser;
|
using JiebaNet.Analyser;
|
||||||
using JiebaNet.Segmenter;
|
using JiebaNet.Segmenter;
|
||||||
|
|
||||||
namespace Service;
|
namespace Service.AI;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文本分词服务接口
|
/// 文本分词服务接口
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace Service.Budget;
|
namespace Service.Budget;
|
||||||
|
|
||||||
public interface IBudgetSavingsService
|
public interface IBudgetSavingsService
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
using Service.AI;
|
||||||
|
using Service.Message;
|
||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace Service.Budget;
|
namespace Service.Budget;
|
||||||
|
|
||||||
public interface IBudgetService
|
public interface IBudgetService
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace Service.Budget;
|
namespace Service.Budget;
|
||||||
|
|
||||||
public interface IBudgetStatsService
|
public interface IBudgetStatsService
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Service.EmailServices.EmailParse;
|
using Service.AI;
|
||||||
|
using Service.EmailServices.EmailParse;
|
||||||
|
using Service.Message;
|
||||||
|
|
||||||
namespace Service.EmailServices;
|
namespace Service.EmailServices;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Service.EmailServices.EmailParse;
|
using Service.AI;
|
||||||
|
|
||||||
|
namespace Service.EmailServices.EmailParse;
|
||||||
|
|
||||||
public class EmailParseForm95555(
|
public class EmailParseForm95555(
|
||||||
ILogger<EmailParseForm95555> logger,
|
ILogger<EmailParseForm95555> logger,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using HtmlAgilityPack;
|
using HtmlAgilityPack;
|
||||||
|
using Service.AI;
|
||||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
// ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
// ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Service.EmailServices.EmailParse;
|
using Service.AI;
|
||||||
|
|
||||||
|
namespace Service.EmailServices.EmailParse;
|
||||||
|
|
||||||
public interface IEmailParseServices
|
public interface IEmailParseServices
|
||||||
{
|
{
|
||||||
|
|||||||
78
Service/Jobs/LogCleanupJob.cs
Normal file
78
Service/Jobs/LogCleanupJob.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
using Quartz;
|
||||||
|
|
||||||
|
namespace Service.Jobs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日志清理定时任务
|
||||||
|
/// </summary>
|
||||||
|
[DisallowConcurrentExecution]
|
||||||
|
public class LogCleanupJob(ILogger<LogCleanupJob> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Quartz;
|
using Quartz;
|
||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace Service.Jobs;
|
namespace Service.Jobs;
|
||||||
|
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace Service;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 日志清理后台服务
|
|
||||||
/// </summary>
|
|
||||||
public class LogCleanupService(ILogger<LogCleanupService> 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("日志清理服务已停止");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清理过期的日志文件
|
|
||||||
/// </summary>
|
|
||||||
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, "清理日志过程中发生错误");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Service;
|
namespace Service.Message;
|
||||||
|
|
||||||
public interface IMessageService
|
public interface IMessageService
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using WebPush;
|
using WebPush;
|
||||||
using PushSubscription = Entity.PushSubscription;
|
using PushSubscription = Entity.PushSubscription;
|
||||||
|
|
||||||
namespace Service;
|
namespace Service.Message;
|
||||||
|
|
||||||
public interface INotificationService
|
public interface INotificationService
|
||||||
{
|
{
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace Service;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 周期性账单后台服务
|
|
||||||
/// </summary>
|
|
||||||
public class PeriodicBillBackgroundService(
|
|
||||||
IServiceProvider serviceProvider,
|
|
||||||
ILogger<PeriodicBillBackgroundService> 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<ITransactionPeriodicService>();
|
|
||||||
await periodicService.ExecutePeriodicBillsAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
logger.LogInformation("周期性账单后台服务已取消");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError(ex, "周期性账单后台服务执行出错");
|
|
||||||
// 出错后等待1小时再重试
|
|
||||||
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.LogInformation("周期性账单后台服务已停止");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Service;
|
namespace Service.Transaction;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 周期性账单服务接口
|
/// 周期性账单服务接口
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Service;
|
namespace Service.Transaction;
|
||||||
|
|
||||||
public interface ITransactionStatisticsService
|
public interface ITransactionStatisticsService
|
||||||
{
|
{
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace WebApi.Test.Budget;
|
namespace WebApi.Test.Budget;
|
||||||
|
|
||||||
public class BudgetSavingsTest : BaseTest
|
public class BudgetSavingsTest : BaseTest
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Service.AI;
|
||||||
|
using Service.Message;
|
||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace WebApi.Test.Budget;
|
namespace WebApi.Test.Budget;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Service.Message;
|
||||||
|
|
||||||
namespace WebApi.Controllers;
|
namespace WebApi.Controllers;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace WebApi.Controllers;
|
using Service.Message;
|
||||||
|
|
||||||
|
namespace WebApi.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]/[action]")]
|
[Route("api/[controller]/[action]")]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace WebApi.Controllers;
|
using Service.Transaction;
|
||||||
|
|
||||||
|
namespace WebApi.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 周期性账单控制器
|
/// 周期性账单控制器
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using Service.AI;
|
||||||
|
using Service.Transaction;
|
||||||
|
|
||||||
namespace WebApi.Controllers;
|
namespace WebApi.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Quartz;
|
using Quartz;
|
||||||
using Service.Jobs;
|
using Service.Jobs;
|
||||||
|
|
||||||
namespace WebApi;
|
namespace WebApi;
|
||||||
@@ -55,6 +55,17 @@ public static class Expand
|
|||||||
.WithIdentity("DbBackupTrigger")
|
.WithIdentity("DbBackupTrigger")
|
||||||
.WithCronSchedule("0 0 6 * * ?") // 每天早上6点执行
|
.WithCronSchedule("0 0 6 * * ?") // 每天早上6点执行
|
||||||
.WithDescription("每天早上6点执行数据库备份"));
|
.WithDescription("每天早上6点执行数据库备份"));
|
||||||
|
|
||||||
|
// 配置日志清理任务 - 每天凌晨2点执行
|
||||||
|
var logCleanupJobKey = new JobKey("LogCleanupJob");
|
||||||
|
q.AddJob<LogCleanupJob>(opts => opts
|
||||||
|
.WithIdentity(logCleanupJobKey)
|
||||||
|
.WithDescription("日志清理任务"));
|
||||||
|
q.AddTrigger(opts => opts
|
||||||
|
.ForJob(logCleanupJobKey)
|
||||||
|
.WithIdentity("LogCleanupTrigger")
|
||||||
|
.WithCronSchedule("0 0 2 * * ?") // 每天凌晨2点执行
|
||||||
|
.WithDescription("每天凌晨2点执行日志清理(保留30天)"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加 Quartz Hosted Service
|
// 添加 Quartz Hosted Service
|
||||||
|
|||||||
@@ -124,9 +124,6 @@ builder.Services.AddSingleton(fsql);
|
|||||||
// 自动扫描注册服务和仓储
|
// 自动扫描注册服务和仓储
|
||||||
builder.Services.AddServices();
|
builder.Services.AddServices();
|
||||||
|
|
||||||
// 注册日志清理后台服务
|
|
||||||
builder.Services.AddHostedService<LogCleanupService>();
|
|
||||||
|
|
||||||
// 配置 Quartz.NET 定时任务
|
// 配置 Quartz.NET 定时任务
|
||||||
builder.AddScheduler();
|
builder.AddScheduler();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user