xiazhengtongxue
2026-01-23 f65bfbd6e84fdffc71353acfe7c9ccca5b117c45
ÏîÄ¿´úÂë/WCSServices/WIDESEAWCS_SystemServices/Sys_LogService.cs
@@ -1,4 +1,130 @@
using NPOI.HSSF.Record;
//using Microsoft.Data.SqlClient;
//using NPOI.HSSF.Record;
//using SkiaSharp;
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Linq.Expressions;
//using System.Text;
//using System.Threading.Tasks;
//using WIDESEAWCS_Core.BaseServices;
//using WIDESEAWCS_ISystemRepository;
//using WIDESEAWCS_ISystemServices;
//using WIDESEAWCS_Model.Models;
//namespace WIDESEAWCS_SystemServices
//{
//    public class Sys_LogService : ServiceBase<Sys_Log, ISys_LogRepository>, ISys_LogService
//    {
//        private static DateTime? _lastExecutionDate = null;
//        private readonly object _lock = new object();
//        private const string DbName = "WIDESEA_HF";
//        private const long MaxMdfSizeBytes = 8L * 1024 * 1024 * 1024; // 8GB
//        private const int TargetShrinkSizeMB = 4500; // 4.5GB
//        public Sys_LogService(ISys_LogRepository BaseDal) : base(BaseDal)
//        {
//        }
//        public void DeleteOldLogs()
//        {
//            // ä½¿ç”¨é”ç¡®ä¿çº¿ç¨‹å®‰å…¨
//            lock (_lock)
//            {
//                var today = DateTime.Today;
//                // æ£€æŸ¥ä»Šå¤©æ˜¯å¦å·²ç»æ‰§è¡Œè¿‡
//                if (_lastExecutionDate.HasValue && _lastExecutionDate.Value.Date == today)
//                {
//                    return;
//                }
//                try
//                {
//                    // è®¡ç®—3个月前的日期
//                    var threeMonthsAgo = DateTime.Now.AddMonths(-3);
//                    // è®¡ç®—3天前的日期
//                    var sevenDaysAgo = DateTime.Now.AddDays(-3);
//                    // ç‰¹å®šURL
//                    var specificUrl = "http://11.2.30.141:10870/interfaces/api/amr/robotQuery";
//                    // æž„建删除条件:3个月前的日志 æˆ– (3天前且URL为特定地址的日志)
//                    var result = BaseDal.Db.Deleteable<Sys_Log>()
//                        .Where(log => log.EndDate < threeMonthsAgo ||
//                                     (log.EndDate < sevenDaysAgo && log.Url == specificUrl))
//                        .ExecuteCommand();
//                    // æ›´æ–°æœ€åŽæ‰§è¡Œæ—¥æœŸä¸ºä»Šå¤©
//                    _lastExecutionDate = today;
//                }
//                catch (Exception ex)
//                {
//                    Console.WriteLine($"执行日志清理时发生错误:{ex.Message}");
//                    // å‘生错误时不更新最后执行日期,以便重试
//                }
//            }
//        }
//        // 15天执行一次星期天2点
//        public void CheckAndManageFileSize()
//        {
//            try
//            {
//                //                --检查数据库文件空间使用情况
//                //SELECT
//                //    name AS[文件逻辑名],
//                //    physical_name AS[物理路径],
//                //    size/ 128.0 AS[当前大小MB],
//                //    FILEPROPERTY(name, 'SpaceUsed') / 128.0 AS[已用空间MB],
//                //    size / 128.0 - FILEPROPERTY(name, 'SpaceUsed') / 128.0 AS[可用空间MB],
//                //    growth / 128 AS[增长量MB],
//                //    CASE is_percent_growth
//                //        WHEN 1 THEN CAST(growth AS VARCHAR) +'%'
//                //        ELSE CAST(growth/ 128 AS VARCHAR) +'MB'
//                //    END AS[增长方式]
//                //FROM sys.database_files;
//                using (var connection = new SqlConnection("Data Source=.;Initial Catalog=WIDESEA_HF;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"))
//                {
//                    connection.Open();
//                    // æ£€æŸ¥æ•°æ®åº“文件大小
//                    var sizeCheckSql = @"SELECT name AS [文件逻辑名], size/128.0 AS [当前大小MB], FILEPROPERTY(name, 'SpaceUsed')/128.0 AS [已用空间MB] FROM sys.database_files WHERE name = @dbName";
//                    using (var command = new SqlCommand(sizeCheckSql, connection))
//                    {
//                        command.Parameters.AddWithValue("@dbName", DbName);
//                        using (var reader = command.ExecuteReader())
//                        {
//                            if (reader.Read())
//                            {
//                                double currentSizeMB = Convert.ToDouble(reader["当前大小MB"]);
//                                double usedSpaceMB = Convert.ToDouble(reader["已用空间MB"]);
//                                if (currentSizeMB > MaxMdfSizeBytes / (1024 * 1024))
//                                {
//                                    // æ‰§è¡Œæ”¶ç¼©æ•°æ®åº“
//                                }else
//                                {
//                                    // æ•´ç†ç¢Žç‰‡>30%
//                                }
//                            }
//                        }
//                    }
//                }
//            }
//            catch (Exception ex)
//            {
//                throw new Exception($"文件监控失败: {ex.Message}");
//            }
//        }
//    }
//}
using Microsoft.Data.SqlClient;
using NPOI.HSSF.Record;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,11 +141,45 @@
    public class Sys_LogService : ServiceBase<Sys_Log, ISys_LogRepository>, ISys_LogService
    {
        private static DateTime? _lastExecutionDate = null;
        private static DateTime? _lastFileSizeCheckDate = null;
        private static DateTime? _lastBackupDate = null;
        private readonly object _lock = new object();
        private readonly object _fileSizeLock = new object();
        private readonly object _backupLock = new object();
        private const string DbName = "WIDESEA_HF";
        private const string ConnectionString = "Data Source=11.2.30.112;Initial Catalog=WIDESEA_HF;User ID=kuka;Password=kuka;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
        private const long MaxMdfSizeBytes = 8L * 1024 * 1024 * 1024; // 8GB
        private const int TargetShrinkSizeMB = 4500; // 4.5GB
        private const double FragmentationThreshold = 30.0; // 30%碎片率阈值
        private const int MaintenanceIntervalDays = 15; // 15天执行一次
        private const int BackupRetentionDays = 30; // å¤‡ä»½ä¿ç•™30天
        private const string BackupBasePath = @"D:\DatabaseBackups"; // å¤‡ä»½æ–‡ä»¶å­˜å‚¨ç›®å½•
        private const bool UseBackupCompression = true; // æ˜¯å¦ä½¿ç”¨å¤‡ä»½åŽ‹ç¼©
        private const bool VerifyBackup = true; // æ˜¯å¦éªŒè¯å¤‡ä»½
        private const int MaxBackupRetryCount = 3; // æœ€å¤§é‡è¯•次数
        private const int BackupTimeoutSeconds = 7200; // å¤‡ä»½è¶…时时间(秒)
        public Sys_LogService(ISys_LogRepository BaseDal) : base(BaseDal)
        {
            // ç¡®ä¿å¤‡ä»½ç›®å½•存在
            EnsureBackupDirectory();
        }
        private void EnsureBackupDirectory()
        {
            try
            {
                if (!Directory.Exists(BackupBasePath))
                {
                    Directory.CreateDirectory(BackupBasePath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"创建备份目录失败: {ex.Message}");
                throw;
            }
        }
        public void DeleteOldLogs()
@@ -39,14 +199,15 @@
                    // è®¡ç®—3个月前的日期
                    var threeMonthsAgo = DateTime.Now.AddMonths(-3);
                    // è®¡ç®—3天前的日期
                    var sevenDaysAgo = DateTime.Now.AddDays(-3);
                    var threeDaysAgo = DateTime.Now.AddDays(-3);
                    // ç‰¹å®šURL
                    var specificUrl = "http://11.2.30.141:10870/interfaces/api/amr/robotQuery";
                    var anonUrl = "http://11.2.30.111/anon/api/kuka/drivestatus";
                    // æž„建删除条件:3个月前的日志 æˆ– (7天前且URL为特定地址的日志)
                    // æž„建删除条件:3个月前的日志 æˆ– (3天前且URL为特定地址的日志)
                    var result = BaseDal.Db.Deleteable<Sys_Log>()
                        .Where(log => log.EndDate < threeMonthsAgo ||
                                     (log.EndDate < sevenDaysAgo && log.Url == specificUrl))
                                     (log.EndDate < threeDaysAgo && (log.Url == specificUrl || log.Url == anonUrl)))
                        .ExecuteCommand();
                    // æ›´æ–°æœ€åŽæ‰§è¡Œæ—¥æœŸä¸ºä»Šå¤©
                    _lastExecutionDate = today;
@@ -54,9 +215,557 @@
                catch (Exception ex)
                {
                    Console.WriteLine($"执行日志清理时发生错误:{ex.Message}");
                    throw new Exception($"日志清理时发生错误: {ex.Message}", ex);
                    // å‘生错误时不更新最后执行日期,以便重试
                }
            }
        }
        // 15天执行一次星期天2点
        public void CheckAndManageFileSize()
        {
            // æ£€æŸ¥æ˜¯å¦ç¬¦åˆæ‰§è¡Œæ¡ä»¶ï¼šæ˜ŸæœŸå¤©ã€2点左右、距离上次执行至少15天
            //if (!ShouldExecuteMaintenance())
            //{
            //    return;
            //}
            try
            {
                using (var connection = new SqlConnection(ConnectionString))
                {
                    connection.Open();
                    // 1. å¤‡ä»½æ•°æ®åº“
                    BackupDatabase();
                    // 2. æ£€æŸ¥æ•°æ®åº“文件大小
                    CheckDatabaseFileSize(connection);
                    // 3. æ£€æŸ¥å¹¶å¤„理索引碎片
                    CheckAndHandleIndexFragmentation(connection);
                    // 4. æ›´æ–°ç»Ÿè®¡ä¿¡æ¯
                    UpdateStatistics(connection);
                    // 5. æ¸…理旧备份
                    CleanupOldBackups();
                    // æ›´æ–°æœ€åŽæ£€æŸ¥æ—¥æœŸ
                    lock (_fileSizeLock)
                    {
                        _lastFileSizeCheckDate = DateTime.Now;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"文件监控失败: {ex.Message}");
                throw new Exception($"文件监控失败: {ex.Message}", ex);
            }
        }
        public void BackupDatabase()
        {
            lock (_backupLock)
            {
                try
                {
                    var today = DateTime.Today;
                    // æ£€æŸ¥ä»Šå¤©æ˜¯å¦å·²ç»å¤‡ä»½è¿‡
                    if (_lastBackupDate.HasValue && _lastBackupDate.Value.Date == today)
                    {
                        return;
                    }
                    var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                    var backupFileName = $"{DbName}_Full_{timestamp}.bak";
                    var backupFilePath = Path.Combine(BackupBasePath, backupFileName);
                    using (var connection = new SqlConnection(ConnectionString))
                    {
                        connection.Open();
                        // èŽ·å–æ•°æ®åº“æ–‡ä»¶ä¿¡æ¯ï¼Œè®¡ç®—å¤‡ä»½æ–‡ä»¶å¤§å°
                        var dbSizeQuery = @"
                            SELECT
                                SUM(size/128.0) AS TotalSizeMB,
                                SUM(FILEPROPERTY(name, 'SpaceUsed')/128.0) AS UsedSizeMB,
                                COUNT(*) AS FileCount
                            FROM sys.database_files
                            WHERE type IN (0, 1)"; // æ•°æ®æ–‡ä»¶å’Œæ—¥å¿—文件
                        double totalSizeMB = 0;
                        double usedSizeMB = 0;
                        int fileCount = 0;
                        using (var cmd = new SqlCommand(dbSizeQuery, connection))
                        using (var reader = cmd.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                totalSizeMB = Convert.ToDouble(reader["TotalSizeMB"]);
                                usedSizeMB = Convert.ToDouble(reader["UsedSizeMB"]);
                                fileCount = Convert.ToInt32(reader["FileCount"]);
                            }
                        }
                        // æ£€æŸ¥ç£ç›˜ç©ºé—´æ˜¯å¦è¶³å¤Ÿ
                        if (!CheckDiskSpace(backupFilePath, totalSizeMB))
                        {
                            throw new Exception("磁盘空间不足,无法执行备份");
                        }
                        // æž„建备份SQL语句
                        var backupSql = BuildBackupSql(backupFilePath);
                        // æ‰§è¡Œå¤‡ä»½ï¼ˆå¸¦é‡è¯•机制)
                        ExecuteBackupWithRetry(connection, backupSql, backupFilePath);
                        // éªŒè¯å¤‡ä»½æ–‡ä»¶
                        if (VerifyBackup)
                        {
                            VerifyBackupFile(connection, backupFilePath);
                        }
                        // æ›´æ–°æœ€åŽå¤‡ä»½æ—¥æœŸ
                        _lastBackupDate = DateTime.Now;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"数据库备份失败: {ex.Message}");
                    throw new Exception($"数据库备份失败: {ex.Message}", ex);
                }
            }
        }
        private bool CheckDiskSpace(string backupFilePath, double estimatedBackupSizeMB)
        {
            try
            {
                var driveInfo = new DriveInfo(Path.GetPathRoot(backupFilePath));
                var freeSpaceMB = driveInfo.AvailableFreeSpace / (1024 * 1024);
                // éœ€è¦è‡³å°‘1.5倍的空间来确保备份顺利进行
                var requiredSpaceMB = estimatedBackupSizeMB * 1.5;
                if (freeSpaceMB < requiredSpaceMB)
                {
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查磁盘空间时出错: {ex.Message}");
                return false;
            }
        }
        private string BuildBackupSql(string backupFilePath)
        {
            var compressionOption = UseBackupCompression ? "COMPRESSION" : "NO_COMPRESSION";
            var verifyOption = VerifyBackup ? ", CHECKSUM" : "";
            return $@"
                BACKUP DATABASE [{DbName}]
                TO DISK = '{backupFilePath.Replace("'", "''")}'
                WITH {compressionOption}
                     {verifyOption}
                     , STATS = 5
                     , DESCRIPTION = 'Full database backup created by automated maintenance on {DateTime.Now:yyyy-MM-dd HH:mm:ss}'
                     , MAXTRANSFERSIZE = 4194304
                     , BLOCKSIZE = 65536
                     , BUFFERCOUNT = 10";
        }
        private void ExecuteBackupWithRetry(SqlConnection connection, string backupSql, string backupFilePath)
        {
            int retryCount = 0;
            while (retryCount < MaxBackupRetryCount)
            {
                try
                {
                    using (var cmd = new SqlCommand(backupSql, connection))
                    {
                        cmd.CommandTimeout = BackupTimeoutSeconds;
                        var rowsAffected = cmd.ExecuteNonQuery();
                        return; // æˆåŠŸåˆ™é€€å‡º
                    }
                }
                catch (Exception ex)
                {
                    retryCount++;
                    if (retryCount >= MaxBackupRetryCount)
                    {
                        // åˆ é™¤å¯èƒ½æŸåçš„备份文件
                        try
                        {
                            if (File.Exists(backupFilePath))
                            {
                                File.Delete(backupFilePath);
                            }
                        }
                        catch { }
                        throw new Exception($"备份失败,已达到最大重试次数({MaxBackupRetryCount}次): {ex.Message}", ex);
                    }
                    Console.WriteLine($"备份失败,{MaxBackupRetryCount - retryCount}次重试机会: {ex.Message}");
                    Thread.Sleep(5000); // ç­‰å¾…5秒后重试
                }
            }
        }
        private void VerifyBackupFile(SqlConnection connection, string backupFilePath)
        {
            try
            {
                var verifySql = $@"
                    RESTORE VERIFYONLY
                    FROM DISK = '{backupFilePath.Replace("'", "''")}'
                    WITH CHECKSUM";
                using (var cmd = new SqlCommand(verifySql, connection))
                {
                    cmd.CommandTimeout = 300; // 5分钟超时
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                // éªŒè¯å¤±è´¥æ—¶åˆ é™¤å¤‡ä»½æ–‡ä»¶
                try
                {
                    if (File.Exists(backupFilePath))
                    {
                        File.Delete(backupFilePath);
                    }
                }
                catch { }
                throw new Exception($"备份文件验证失败: {ex.Message}", ex);
            }
        }
        private void CleanupOldBackups()
        {
            try
            {
                if (!Directory.Exists(BackupBasePath))
                {
                    Console.WriteLine("备份目录不存在,跳过清理");
                    return;
                }
                var cutoffDate = DateTime.Now.AddDays(-BackupRetentionDays);
                var backupFiles = Directory.GetFiles(BackupBasePath, $"{DbName}_Full_*.bak");
                int deletedCount = 0;
                foreach (var file in backupFiles)
                {
                    try
                    {
                        var fileInfo = new FileInfo(file);
                        if (fileInfo.CreationTime < cutoffDate)
                        {
                            fileInfo.Delete();
                            deletedCount++;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"删除备份文件 {file} æ—¶å‡ºé”™: {ex.Message}");
                        // ç»§ç»­å¤„理其他文件
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"清理旧备份时出错: {ex.Message}");
                // ä¸æŠ›å‡ºå¼‚常,避免影响主流程
            }
        }
        private bool ShouldExecuteMaintenance()
        {
            lock (_fileSizeLock)
            {
                var now = DateTime.Now;
                // æ£€æŸ¥ä»Šå¤©æ˜¯å¦æ˜¯æ˜ŸæœŸå¤©
                if (now.DayOfWeek != DayOfWeek.Sunday)
                {
                    return false;
                }
                // æ£€æŸ¥æ—¶é—´æ˜¯å¦æ˜¯1点左右(允许1:00-1:59)
                if (now.Hour != 1)
                {
                    return false;
                }
                // æ£€æŸ¥æ˜¯å¦å·²ç»æ‰§è¡Œè¿‡
                if (_lastFileSizeCheckDate.HasValue)
                {
                    // æ£€æŸ¥è·ç¦»ä¸Šæ¬¡æ‰§è¡Œæ˜¯å¦æ»¡è¶³15天间隔
                    var daysSinceLastCheck = (now - _lastFileSizeCheckDate.Value).TotalDays;
                    if (daysSinceLastCheck < MaintenanceIntervalDays)
                    {
                        return false;
                    }
                }
                return true;
            }
        }
        private void CheckDatabaseFileSize(SqlConnection connection)
        {
            try
            {
                var sizeCheckSql = @"
                    SELECT
                        name AS [文件逻辑名],
                        physical_name AS [物理路径],
                        size/128.0 AS [当前大小MB],
                        FILEPROPERTY(name, 'SpaceUsed')/128.0 AS [已用空间MB],
                        size/128.0 - FILEPROPERTY(name, 'SpaceUsed')/128.0 AS [可用空间MB],
                        max_size/128.0 AS [最大大小MB],
                        growth/128.0 AS [增长量MB],
                        CASE is_percent_growth
                            WHEN 1 THEN '百分比增长'
                            ELSE '固定大小增长'
                        END AS [增长方式]
                    FROM sys.database_files
                    WHERE type = 0"; // 0表示数据文件,1表示日志文件
                using (var command = new SqlCommand(sizeCheckSql, connection))
                {
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            string logicalName = reader["文件逻辑名"].ToString();
                            double currentSizeMB = Convert.ToDouble(reader["当前大小MB"]);
                            double usedSpaceMB = Convert.ToDouble(reader["已用空间MB"]);
                            double freeSpaceMB = Convert.ToDouble(reader["可用空间MB"]);
                            // æ£€æŸ¥æ˜¯å¦éœ€è¦æ”¶ç¼©
                            if (currentSizeMB * 1024 * 1024 > MaxMdfSizeBytes)
                            {
                                Console.WriteLine("数据库文件超过8GB,开始收缩...");
                                ShrinkDatabaseFile(connection, logicalName);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查数据库文件大小时出错: {ex.Message}");
                throw;
            }
        }
        private void ShrinkDatabaseFile(SqlConnection connection, string logicalName)
        {
            try
            {
                // æ”¶ç¼©æ•°æ®åº“文件
                string shrinkSql = $"DBCC SHRINKFILE (N'{logicalName}', {TargetShrinkSizeMB})";
                using (var command = new SqlCommand(shrinkSql, connection))
                {
                    int rowsAffected = command.ExecuteNonQuery();
                    Console.WriteLine($"数据库文件收缩完成,目标大小: {TargetShrinkSizeMB} MB");
                }
                // é‡æ–°ç»„织索引以避免收缩导致的碎片
                ReorganizeAllIndexes(connection);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"收缩数据库文件时出错: {ex.Message}");
                throw;
            }
        }
        private void CheckAndHandleIndexFragmentation(SqlConnection connection)
        {
            try
            {
                // æŸ¥è¯¢æ‰€æœ‰è¡¨çš„索引碎片率
                string fragmentationSql = @"
                    SELECT
                        OBJECT_NAME(ips.object_id) AS TableName,
                        i.name AS IndexName,
                        ips.index_type_desc AS IndexType,
                        ips.avg_fragmentation_in_percent AS FragmentationPercent,
                        ips.page_count AS PageCount
                    FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
                    INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
                    WHERE ips.avg_fragmentation_in_percent > 0
                      AND ips.index_id > 0 -- æŽ’除堆
                      AND ips.page_count > 100 -- åªå¤„理超过100页的索引
                    ORDER BY ips.avg_fragmentation_in_percent DESC";
                using (var command = new SqlCommand(fragmentationSql, connection))
                using (var reader = command.ExecuteReader())
                {
                    var indexesToReorganize = new List<(string TableName, string IndexName, double Fragmentation)>();
                    while (reader.Read())
                    {
                        string tableName = reader["TableName"].ToString();
                        string indexName = reader["IndexName"].ToString();
                        double fragmentation = Convert.ToDouble(reader["FragmentationPercent"]);
                        long pageCount = Convert.ToInt64(reader["PageCount"]);
                        Console.WriteLine($"表: {tableName}, ç´¢å¼•: {indexName}, ç¢Žç‰‡çއ: {fragmentation:F2}%, é¡µæ•°: {pageCount}");
                        if (fragmentation > FragmentationThreshold)
                        {
                            indexesToReorganize.Add((tableName, indexName, fragmentation));
                        }
                    }
                    reader.Close(); // å…³é—­reader以执行后续命令
                    // å¤„理需要重新组织的索引
                    if (indexesToReorganize.Any())
                    {
                        foreach (var index in indexesToReorganize)
                        {
                            ReorganizeIndex(connection, index.TableName, index.IndexName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查索引碎片时出错: {ex.Message}");
                throw;
            }
        }
        private void ReorganizeIndex(SqlConnection connection, string tableName, string indexName)
        {
            try
            {
                string reorganizeSql = $"ALTER INDEX [{indexName}] ON [{tableName}] REORGANIZE";
                using (var command = new SqlCommand(reorganizeSql, connection))
                {
                    command.CommandTimeout = 600; // è®¾ç½®10分钟超时
                    command.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"重新组织索引 {tableName}.{indexName} æ—¶å‡ºé”™: {ex.Message}");
                // ç»§ç»­å¤„理其他索引
            }
        }
        private void ReorganizeAllIndexes(SqlConnection connection)
        {
            try
            {
                string reorganizeAllSql = @"
                    DECLARE @TableName NVARCHAR(128)
                    DECLARE @IndexName NVARCHAR(128)
                    DECLARE @Sql NVARCHAR(MAX)
                    DECLARE IndexCursor CURSOR FOR
                    SELECT
                        OBJECT_NAME(i.object_id) AS TableName,
                        i.name AS IndexName
                    FROM sys.indexes i
                    WHERE i.index_id > 0 -- æŽ’除堆
                      AND OBJECT_NAME(i.object_id) IS NOT NULL
                    OPEN IndexCursor
                    FETCH NEXT FROM IndexCursor INTO @TableName, @IndexName
                    WHILE @@FETCH_STATUS = 0
                    BEGIN
                        SET @Sql = 'ALTER INDEX [' + @IndexName + '] ON [' + @TableName + '] REORGANIZE'
                        BEGIN TRY
                            EXEC sp_executesql @Sql
                            PRINT '已重新组织索引: ' + @TableName + '.' + @IndexName
                        END TRY
                        BEGIN CATCH
                            PRINT '重新组织索引 ' + @TableName + '.' + @IndexName + ' æ—¶å‡ºé”™: ' + ERROR_MESSAGE()
                        END CATCH
                        FETCH NEXT FROM IndexCursor INTO @TableName, @IndexName
                    END
                    CLOSE IndexCursor
                    DEALLOCATE IndexCursor";
                using (var command = new SqlCommand(reorganizeAllSql, connection))
                {
                    command.CommandTimeout = 1800; // è®¾ç½®30分钟超时
                    command.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"重新组织所有索引时出错: {ex.Message}");
                throw;
            }
        }
        private void UpdateStatistics(SqlConnection connection)
        {
            try
            {
                // æ›´æ–°æ‰€æœ‰è¡¨çš„统计信息
                string updateStatsSql = "EXEC sp_updatestats";
                using (var command = new SqlCommand(updateStatsSql, connection))
                {
                    command.CommandTimeout = 600; // è®¾ç½®10分钟超时
                    command.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"更新统计信息时出错: {ex.Message}");
                throw;
            }
        }
        // æä¾›ä¸€ä¸ªæ–¹æ³•供任务调度器调用
        public void ScheduleDatabaseMaintenance()
        {
            try
            {
                // 1. åˆ é™¤æ—§æ—¥å¿—
                DeleteOldLogs();
                // æ£€æŸ¥æ˜¯å¦ç¬¦åˆæ‰§è¡Œæ¡ä»¶
                if (!ShouldExecuteMaintenance())
                {
                    return;
                }
                // 2. æ£€æŸ¥å’Œç®¡ç†æ–‡ä»¶å¤§å°
                CheckAndManageFileSize();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"执行数据库维护任务时出错: {ex.Message}");
                throw;
            }
        }
    }
}