Files
FATrace/FATrace.OEMApp/Services/TimeClearDataService.cs
2026-01-13 15:03:02 +08:00

197 lines
7.2 KiB
C#

using FATrace.Com;
using FATrace.Model;
using NLog;
namespace FATrace.OEMApp.Services
{
public sealed class TimeClearDataService
{
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
private CancellationTokenSource? _cts;
private Task? _loopTask;
private TimeSpan _runAt = new TimeSpan(2, 0, 0);
/// <summary>
/// 文件暂存保存的天数
/// </summary>
private int FileRetentionDays = 365;
private bool _enabled = true;
/// <summary>
/// 数据库保存的信息天数
/// </summary>
private int DbRetentionDays = 365;
public event Action<string>? Info;
public void Start()
{
FileRetentionDays=ConfigHelper.GetIntOrDefault("VideoFileSaveDay", 365);
DbRetentionDays = ConfigHelper.GetIntOrDefault("DbSaveDay", 365);
if (_cts != null) return;
try
{
_enabled = ConfigHelper.GetBoolOrDefault("DataCleanupEnabled", true);
var tod = ConfigHelper.GetStringOrDefault("DataCleanupTimeOfDay", "02:00:00");
if (!TimeSpan.TryParse(tod, out _runAt))
{
_runAt = new TimeSpan(2, 0, 0);
}
}
catch { }
if (!_enabled)
{
_logger.Warn("[TimeClear] 已禁用,未启动");
return;
}
_cts = new CancellationTokenSource();
_loopTask = Task.Run(() => RunAsync(_cts.Token));
_logger.Info("[TimeClear] 服务已启动,时间点={RunAt}, 文件保留天数={FileDays}, 数据库保留天数={DbDays}", _runAt, FileRetentionDays, DbRetentionDays);
SafeInfo($"定时清理服务启动,时间点={_runAt}, 文件保留天数={FileRetentionDays}, 数据库保留天数={DbRetentionDays}");
}
public void Stop()
{
try { _cts?.Cancel(); } catch { }
_cts = null;
_logger.Info("[TimeClear] 服务已停止");
}
private async Task RunAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
var now = DateTime.Now;
var next = GetNextRunTime(now);
var delay = next - now;
SafeInfo($"距离下一次清理还有 {delay:hh\\:mm\\:ss},计划时间 {next:yyyy-MM-dd HH:mm:ss}");
await Task.Delay(delay, token);
await CleanupOnceAsync(token);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
_logger.Error(ex, "[TimeClear] 后台循环异常");
try { await Task.Delay(TimeSpan.FromMinutes(1), token); } catch { }
}
}
}
private DateTime GetNextRunTime(DateTime now)
{
var next = now.Date + _runAt;
if (next <= now.AddSeconds(1)) next = next.AddDays(1);
return next;
}
private async Task CleanupOnceAsync(CancellationToken token)
{
var fileCutoff = DateTime.Now.AddDays(-FileRetentionDays);
var dbCutoff = DateTime.Now.AddDays(-DbRetentionDays);
_logger.Info("[TimeClear] 开始清理,文件截止: {FileCutoff}, 数据库截止: {DbCutoff}", fileCutoff, dbCutoff);
SafeInfo($"开始清理,文件截止: {fileCutoff:yyyy-MM-dd HH:mm:ss}, 数据库截止: {dbCutoff:yyyy-MM-dd HH:mm:ss}");
long delJf = 0, delDl = 0, delRaw = 0, delAct = 0, delDlFiles = 0;
try
{
var toDelFile = FSqlContext.FDb.Select<DownloadTask>()
.Where(a => a.CreateTime < fileCutoff && a.VideoFilePath != null && a.VideoFilePath != "")
.ToList();
foreach (var t in toDelFile)
{
try
{
if (!string.IsNullOrWhiteSpace(t.VideoFilePath) && File.Exists(t.VideoFilePath))
{
File.Delete(t.VideoFilePath);
delDlFiles++;
}
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 删除 DownloadTask 文件失败: {Path}", t.VideoFilePath);
}
}
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 扫描 DownloadTask 待删文件失败");
}
try
{
var dbOld = FSqlContext.FDb.Select<DownloadTask>()
.Where(a => a.CreateTime < dbCutoff)
.ToList();
foreach (var t in dbOld)
{
try
{
if (!string.IsNullOrWhiteSpace(t.VideoFilePath) && File.Exists(t.VideoFilePath))
{
File.Delete(t.VideoFilePath);
delDlFiles++;
}
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 删除 DownloadTask 文件失败(删库前): {Path}", t.VideoFilePath);
}
}
delDl = FSqlContext.FDb.Delete<DownloadTask>()
.Where(a => a.CreateTime < dbCutoff)
.ExecuteAffrows();
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 清理 DownloadTask 失败");
}
try
{
delJf = FSqlContext.FDb.Delete<JellyfinMonitorTask>().Where(a => a.CreateTime < dbCutoff).ExecuteAffrows();
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 清理 JellyfinMonitorTask 失败");
}
try
{
delRaw = FSqlContext.FDb.Delete<OEMRawUse>().Where(a => a.CreateTime < dbCutoff).ExecuteAffrows();
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 清理 OEMRawUse 失败");
}
try
{
delAct = FSqlContext.FDb.Delete<VideoAction>().Where(a => a.CreateTime < dbCutoff).ExecuteAffrows();
}
catch (Exception ex)
{
_logger.Warn(ex, "[TimeClear] 清理 VideoAction 失败");
}
_logger.Info("[TimeClear] 清理完成: JellyfinMonitorTask={Jf}, DownloadTask(删库)={Dl}, DownloadTask(删文件)={DlFiles}, OEMRawUse={Raw}, VideoAction={Act}", delJf, delDl, delDlFiles, delRaw, delAct);
SafeInfo($"清理完成: Jf={delJf}, Dl(库)={delDl}, Dl(文件)={delDlFiles}, Raw={delRaw}, Act={delAct}");
await Task.CompletedTask;
}
private void SafeInfo(string msg)
{
try { Info?.Invoke(msg); } catch { }
}
}
}