using Microsoft.Extensions.Hosting;
using SqlSugar;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_Core.DB;
using WIDESEAWCS_Core.Seed;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_Core.LogHelper
{
///
/// 日志数据清理服务
///
public class LogCleanupService : IHostedService, IDisposable
{
private Timer _cleanupTimer;
private bool _isDisposed = false;
private readonly object _lockObject = new object();
///
/// 数据保留天数(默认90天)
///
public int RetentionDays { get; set; } = 90;
///
/// 清理间隔(小时,默认24小时)
///
public int CleanupIntervalHours { get; set; } = 24;
///
/// 清理执行时间(小时,0-23,默认凌晨2点)
///
public int CleanupHour { get; set; } = 2;
///
/// 是否启用归档
///
public bool EnableArchive { get; set; } = false;
///
/// 是否启用自动清理
///
public bool EnableAutoCleanup { get; set; } = true;
///
/// 日志文件保留天数
///
public int LogRetentionDays { get; set; } = 30;
///
/// 日志文件路径
///
private string LogFilePath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs", "LogCleanup", $"Cleanup_{DateTime.Now:yyyyMMdd}.log");
public Task StartAsync(CancellationToken cancellationToken)
{
if (EnableAutoCleanup)
{
// 确保日志目录存在
EnsureLogDirectoryExists();
// 计算到下一个清理时间的间隔
var now = DateTime.Now;
var nextCleanupTime = DateTime.Today.AddHours(CleanupHour);
if (now > nextCleanupTime)
{
nextCleanupTime = nextCleanupTime.AddDays(1);
}
var timeToFirstCleanup = nextCleanupTime - now;
// 设置定时器,在指定时间执行清理
_cleanupTimer = new Timer(CleanupCallback, null,
timeToFirstCleanup,
TimeSpan.FromHours(CleanupIntervalHours));
WriteLog($"日志清理服务已启动,首次清理时间: {nextCleanupTime:yyyy-MM-dd HH:mm:ss}");
}
return Task.CompletedTask;
}
///
/// 确保日志目录存在
///
private void EnsureLogDirectoryExists()
{
try
{
string logDirectory = Path.GetDirectoryName(LogFilePath);
if (!Directory.Exists(logDirectory))
{
Directory.CreateDirectory(logDirectory);
}
}
catch (Exception ex)
{
Console.WriteLine($"创建日志目录失败: {ex.Message}");
}
}
///
/// 写入日志到文件
///
private void WriteLog(string message, bool isError = false)
{
try
{
lock (_lockObject)
{
EnsureLogDirectoryExists();
string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {(isError ? "[ERROR]" : "[INFO]")} {message}";
// 写入文件
File.AppendAllText(LogFilePath, logMessage + Environment.NewLine);
// 同时输出到控制台(可选,便于调试)
Console.WriteLine(logMessage);
// 日志文件过大时自动清理(保留最近30天的日志文件)
AutoCleanupLogFiles();
}
}
catch (Exception ex)
{
// 如果文件日志失败,至少输出到控制台
Console.WriteLine($"写入日志文件失败: {ex.Message}");
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
}
}
///
/// 自动清理过期的日志文件(保留30天)
///
private void AutoCleanupLogFiles()
{
try
{
string logDirectory = Path.GetDirectoryName(LogFilePath);
if (Directory.Exists(logDirectory))
{
var files = Directory.GetFiles(logDirectory, "Cleanup_*.log");
DateTime cutoffDate = DateTime.Now.AddDays(-30);
foreach (var file in files)
{
try
{
// 从文件名中提取日期
string fileName = Path.GetFileNameWithoutExtension(file);
if (fileName.StartsWith("Cleanup_"))
{
string dateStr = fileName.Replace("Cleanup_", "");
if (DateTime.TryParseExact(dateStr, "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out DateTime fileDate))
{
if (fileDate < cutoffDate)
{
File.Delete(file);
WriteLog($"删除过期日志文件: {Path.GetFileName(file)}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"删除过期日志文件失败 {file}: {ex.Message}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"清理日志文件失败: {ex.Message}");
}
}
private void CleanupCallback(object state)
{
Task.Run(() => CleanupAsync());
}
///
/// 执行清理
///
public async Task CleanupAsync()
{
try
{
WriteLog($"开始清理 {RetentionDays} 天前的日志数据");
using (SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = DBContext.GetMainConnectionDb().Connection,
IsAutoCloseConnection = true,
DbType = DBContext.DbType,
}))
{
DateTime cutoffDate = DateTime.Now.AddDays(-RetentionDays);
WriteLog($"截止日期: {cutoffDate:yyyy-MM-dd HH:mm:ss}");
if (EnableArchive)
{
// 先归档再删除
await ArchiveDataAsync(sugarClient, cutoffDate);
}
// 删除历史数据
int deletedCount = await sugarClient.Deleteable()
.Where(x => x.CreateDate < cutoffDate)
.ExecuteCommandAsync();
WriteLog($"清理完成,共删除 {deletedCount} 条记录");
// 可选:记录清理日志到数据库
await LogCleanupResult(sugarClient, deletedCount, cutoffDate);
}
}
catch (Exception ex)
{
WriteLog($"清理失败: {ex.Message}", true);
WriteLog($"详细错误: {ex.ToString()}", true);
}
}
private async Task ArchiveDataAsync(SqlSugarClient sugarClient, DateTime cutoffDate)
{
try
{
string archiveTableName = $"Dt_InterfaceLog_Archive_{DateTime.Now:yyyyMM}";
WriteLog($"开始归档数据到表: {archiveTableName}");
// 创建归档表
string createTableSql = $@"
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='{archiveTableName}' AND xtype='U')
BEGIN
SELECT * INTO {archiveTableName} FROM Dt_InterfaceLog WHERE 1=0
ALTER TABLE {archiveTableName} ADD ArchiveDate datetime DEFAULT GETDATE()
END";
await sugarClient.Ado.ExecuteCommandAsync(createTableSql);
// 归档数据
string archiveSql = $@"
INSERT INTO {archiveTableName} (Id, ApiCode, ApiName, ApiAddress, PushFrequency,
PushState, Requestor, RequestParameters, RequestParametersHash, Recipient,
ResponseParameters, ResponseParametersHash, Remark, CreateTime, ArchiveDate)
SELECT Id, ApiCode, ApiName, ApiAddress, PushFrequency, PushState,
Requestor, RequestParameters, RequestParametersHash, Recipient,
ResponseParameters, ResponseParametersHash, Remark, CreateTime, GETDATE()
FROM Dt_InterfaceLog
WHERE CreateTime < @CutoffDate";
int archivedCount = await sugarClient.Ado.ExecuteCommandAsync(archiveSql,
new { CutoffDate = cutoffDate });
WriteLog($"归档完成,共归档 {archivedCount} 条记录到 {archiveTableName}");
}
catch (Exception ex)
{
WriteLog($"归档失败: {ex.Message}", true);
}
}
private async Task LogCleanupResult(SqlSugarClient sugarClient, int deletedCount, DateTime cutoffDate)
{
try
{
// 创建清理日志表(如果不存在)
string createLogTableSql = @"
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='LogCleanupHistory' AND xtype='U')
BEGIN
CREATE TABLE LogCleanupHistory (
Id INT IDENTITY(1,1) PRIMARY KEY,
TableName NVARCHAR(100),
DeletedCount INT,
CutoffDate DATETIME,
CleanupTime DATETIME DEFAULT GETDATE()
)
END";
await sugarClient.Ado.ExecuteCommandAsync(createLogTableSql);
// 记录清理日志
string insertLogSql = @"
INSERT INTO LogCleanupHistory (TableName, DeletedCount, CutoffDate, CleanupTime)
VALUES ('Dt_InterfaceLog', @DeletedCount, @CutoffDate, GETDATE())";
await sugarClient.Ado.ExecuteCommandAsync(insertLogSql,
new { DeletedCount = deletedCount, CutoffDate = cutoffDate });
WriteLog($"清理记录已写入数据库日志表");
}
catch (Exception ex)
{
WriteLog($"写入数据库日志失败: {ex.Message}", true);
}
}
///
/// 手动执行清理
///
public async Task ManualCleanup(int days = 90)
{
try
{
WriteLog($"手动清理开始,删除超过 {days} 天的数据");
using (SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = DBContext.GetMainConnectionDb().Connection,
IsAutoCloseConnection = true,
DbType = DBContext.DbType,
}))
{
DateTime cutoffDate = DateTime.Now.AddDays(-days);
int deletedCount = await sugarClient.Deleteable()
.Where(x => x.CreateDate < cutoffDate)
.ExecuteCommandAsync();
WriteLog($"手动清理完成,删除 {deletedCount} 条记录");
return deletedCount;
}
}
catch (Exception ex)
{
WriteLog($"手动清理失败: {ex.Message}", true);
return 0;
}
}
///
/// 获取清理统计信息
///
public async Task GetStatistics()
{
try
{
using (SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = DBContext.GetMainConnectionDb().Connection,
IsAutoCloseConnection = true,
DbType = DBContext.DbType,
}))
{
var totalCount = await sugarClient.Queryable().CountAsync();
var oldestLog = await sugarClient.Queryable()
.OrderBy(x => x.CreateDate)
.FirstAsync();
var newestLog = await sugarClient.Queryable()
.OrderBy(x => x.CreateDate, OrderByType.Desc)
.FirstAsync();
return new CleanupStatistics
{
TotalCount = totalCount,
OldestLogTime = oldestLog?.CreateDate,
NewestLogTime = newestLog?.CreateDate,
RetentionDays = RetentionDays,
LastCleanupTime = GetLastCleanupTime(),
LogFilePath = LogFilePath
};
}
}
catch (Exception ex)
{
WriteLog($"获取统计信息失败: {ex.Message}", true);
return null;
}
}
private DateTime? GetLastCleanupTime()
{
try
{
string logDirectory = Path.GetDirectoryName(LogFilePath);
if (Directory.Exists(logDirectory))
{
var latestLogFile = Directory.GetFiles(logDirectory, "Cleanup_*.log")
.OrderByDescending(f => f)
.FirstOrDefault();
if (latestLogFile != null && File.Exists(latestLogFile))
{
return File.GetLastWriteTime(latestLogFile);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"获取上次清理时间失败: {ex.Message}");
}
return null;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_cleanupTimer?.Dispose();
WriteLog("日志清理服务已停止");
return Task.CompletedTask;
}
public void Dispose()
{
if (!_isDisposed)
{
_cleanupTimer?.Dispose();
_isDisposed = true;
}
}
}
///
/// 清理统计信息
///
public class CleanupStatistics
{
public int TotalCount { get; set; }
public DateTime? OldestLogTime { get; set; }
public DateTime? NewestLogTime { get; set; }
public int RetentionDays { get; set; }
public DateTime? LastCleanupTime { get; set; }
public string LogFilePath { get; set; }
}
}