using SWRIS.Core; using SWRIS.Models; using SWRIS.Repository; using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace SWRIS.Services { public class HistoryCleanupService : IDisposable { private Timer _cleanupTimer; private readonly IRecordRepository _recordRepository; private bool _isRunning = false; private bool _disposed = false; private int _consecutiveFailures = 0; public event EventHandler OnCleanupCompleted; public event EventHandler OnCleanupFailed; public bool IsRunning => _isRunning; public DateTime? NextScheduledTime { get; private set; } public DateTime? LastCleanupTime { get; private set; } public DeleteResultModel LastCleanupResult { get; private set; } public HistoryCleanupService() { _recordRepository = new RecordRepository(); ValidateConfiguration(); } public void StartScheduledCleanup() { if (_isRunning) { LogHelper.Warn("清理服务已在运行中"); return; } try { ValidateConfiguration(); var now = DateTime.Now; var scheduledTime = CalculateNextScheduledTime(now); var initialDelay = scheduledTime - now; _cleanupTimer = new Timer( callback: _ => ExecuteCleanupAsync().ConfigureAwait(false), state: null, dueTime: initialDelay, period: TimeSpan.FromHours(24) ); _isRunning = true; NextScheduledTime = scheduledTime; _consecutiveFailures = 0; LogHelper.Info($"计划清理已启动。下次执行: {scheduledTime:yyyy-MM-dd HH:mm:ss}"); } catch (Exception ex) { LogHelper.Error($"启动计划任务失败: {ex.Message}"); _isRunning = false; OnCleanupFailed?.Invoke(this, ex); // 延迟重试 Task.Delay(TimeSpan.FromMinutes(5)).ContinueWith(_ => RetryStartup()); } } private DateTime CalculateNextScheduledTime(DateTime now) { var timeSpan = App.Config.CleanScheduledTime; var scheduledTime = new DateTime(now.Year, now.Month, now.Day, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds); if (now > scheduledTime) { scheduledTime = scheduledTime.AddDays(1); } return scheduledTime; } private void RetryStartup(int retryCount = 0) { if (retryCount >= 3) { LogHelper.Error("启动重试次数超过限制,请检查系统配置"); return; } LogHelper.Info($"尝试重新启动清理服务 (重试 {retryCount + 1}/3)"); StartScheduledCleanup(); } private async Task ExecuteCleanupAsync() { try { LastCleanupTime = DateTime.Now; LogHelper.Info($"开始执行计划清理任务 ({LastCleanupTime:yyyy-MM-dd HH:mm:ss})"); var result = await Task.Run(() => CleanupHistoryOlderThanDays(App.Config.DataSaveDays)); // 更新下一次执行时间 NextScheduledTime = CalculateNextScheduledTime(DateTime.Now); // 重置连续失败计数 _consecutiveFailures = 0; // 在UI线程更新结果 await Application.Current.Dispatcher.InvokeAsync(() => { LastCleanupResult = result; if (result.IsSuccess) { LogHelper.Info($"{result.Message} (执行时间: {result.OperationTime:HH:mm:ss})"); LogHelper.Info($"删除记录: {result.DeletedRecords} 条, 删除文件: {result.DeletedFiles} 个"); } else { LogHelper.Warn($"清理任务完成但有警告: {result.Message}"); } OnCleanupCompleted?.Invoke(this, result); }); } catch (Exception ex) { _consecutiveFailures++; LogHelper.Error($"清理执行失败 (失败次数: {_consecutiveFailures}): {ex.Message}", ex); // 连续失败告警 if (_consecutiveFailures >= 3) { LogHelper.Warn("连续清理失败次数过多,请检查系统状态"); } OnCleanupFailed?.Invoke(this, ex); } } public DeleteResultModel CleanupHistoryOlderThanDays(int days) { var result = new DeleteResultModel { OperationTime = DateTime.Now, DataSaveDays = days }; try { if (days <= 0) { throw new ArgumentException("数据保存天数必须大于0", nameof(days)); } var endTime = DateTime.Today.AddDays(-days); LogHelper.Info($"开始清理 {endTime:yyyy-MM-dd} 之前的历史数据"); var (effect, filePaths) = _recordRepository.DeleteRecords(endTime); result.DeletedRecords = effect; result.DeletedFiles = DeleteAssociatedFiles(filePaths); result.IsSuccess = true; result.Message = $"清理完成,删除了 {result.DeletedRecords} 条记录和 {result.DeletedFiles} 个文件。"; LogHelper.Info($"数据库记录清理: {result.DeletedRecords} 条"); LogHelper.Info($"关联文件清理: {result.DeletedFiles} 个"); } catch (Exception ex) { result.IsSuccess = false; result.Message = $"清理过程中发生错误: {ex.Message}"; LogHelper.Error($"Cleanup Error: {ex}", ex); } return result; } private int DeleteAssociatedFiles(List filePaths) { int deletedCount = 0; int errorCount = 0; if (filePaths == null || filePaths.Count == 0) { return 0; } LogHelper.Info($"开始清理 {filePaths.Count} 个关联文件"); foreach (var filePath in filePaths) { try { if (File.Exists(filePath)) { File.Delete(filePath); deletedCount++; } else { LogHelper.Warn($"文件不存在: {filePath}"); } } catch (Exception ex) { errorCount++; LogHelper.Error($"删除文件失败 {filePath}: {ex.Message}"); } } if (errorCount > 0) { LogHelper.Warn($"{errorCount} 个文件删除失败"); } return deletedCount; } public void StopScheduledCleanup() { _cleanupTimer?.Change(Timeout.Infinite, Timeout.Infinite); _isRunning = false; NextScheduledTime = null; LogHelper.Info("计划清理已停止"); } public void TriggerManualCleanup() { LogHelper.Info("手动触发清理任务"); ExecuteCleanupAsync().ConfigureAwait(false); } private void ValidateConfiguration() { if (App.Config?.CleanScheduledTime == null) { throw new InvalidOperationException("清理计划时间未配置"); } if (App.Config.DataSaveDays <= 0) { throw new InvalidOperationException("数据保存天数必须大于0"); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { StopScheduledCleanup(); _cleanupTimer?.Dispose(); _cleanupTimer = null; } _disposed = true; } } ~HistoryCleanupService() { Dispose(false); } } }