| | |
| | | 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; |
| | | using WIDESEAWCS_Core.BaseServices; |
| | | using WIDESEAWCS_ISystemRepository; |
| | | using WIDESEAWCS_ISystemServices; |
| | |
| | | public class Sys_LogService : ServiceBase<Sys_Log, ISys_LogRepository>, ISys_LogService |
| | | { |
| | | private static DateTime? _lastExecutionDate = null; |
| | | private static DateTime? _lastFileSizeCheckDate = null; |
| | | private readonly object _lock = new object(); |
| | | private readonly object _fileSizeLock = 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 = 7L * 1024 * 1024 * 1024; // 7GB |
| | | private const int TargetShrinkSizeMB = 4500; // 4.5GB |
| | | private const double FragmentationThreshold = 30.0; // 30%ç¢ççéå¼ |
| | | |
| | | public Sys_LogService(ISys_LogRepository BaseDal) : base(BaseDal) |
| | | { |
| | | |
| | | } |
| | | |
| | | public void DeleteOldLogs() |
| | |
| | | } |
| | | 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个æåçæ¥å¿ æ (7天åä¸URL为ç¹å®å°åçæ¥å¿) |
| | | // å é¤5天åçæ¥å¿ |
| | | var result = BaseDal.Db.Deleteable<Sys_Log>() |
| | | .Where(log => log.EndDate < threeMonthsAgo || |
| | | (log.EndDate < sevenDaysAgo && log.Url == specificUrl)) |
| | | .Where(log => log.EndDate < DateTime.Now.AddDays(-4)) |
| | | .ExecuteCommand(); |
| | | // æ´æ°æåæ§è¡æ¥æä¸ºä»å¤© |
| | | _lastExecutionDate = today; |
| | |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ§è¡æ¥å¿æ¸
çæ¶åçé误ï¼{ex.Message}"); |
| | | throw new Exception($"æ¥å¿æ¸
çæ¶åçé误: {ex.Message}", ex); |
| | | // åçé误æ¶ä¸æ´æ°æåæ§è¡æ¥æï¼ä»¥ä¾¿éè¯ |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æ¯å¨ææå¤©2ç¹æ§è¡ä¸æ¬¡ |
| | | public void CheckAndManageFileSize() |
| | | { |
| | | try |
| | | { |
| | | using (var connection = new SqlConnection(ConnectionString)) |
| | | { |
| | | connection.Open(); |
| | | |
| | | // 1. æ£æ¥æ°æ®åºæä»¶å¤§å° |
| | | CheckDatabaseFileSize(connection); |
| | | |
| | | // 2. æ£æ¥å¹¶å¤çç´¢å¼ç¢ç |
| | | CheckAndHandleIndexFragmentation(connection); |
| | | |
| | | // 3. æ´æ°ç»è®¡ä¿¡æ¯ |
| | | UpdateStatistics(connection); |
| | | |
| | | // æ´æ°æåæ£æ¥æ¥æ |
| | | lock (_fileSizeLock) |
| | | { |
| | | _lastFileSizeCheckDate = DateTime.Now; |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æä»¶çæ§å¤±è´¥: {ex.Message}"); |
| | | throw new Exception($"æä»¶çæ§å¤±è´¥: {ex.Message}", ex); |
| | | } |
| | | } |
| | | |
| | | private bool ShouldExecuteMaintenance() |
| | | { |
| | | lock (_fileSizeLock) |
| | | { |
| | | var now = DateTime.Now; |
| | | |
| | | // æ£æ¥ä»å¤©æ¯å¦æ¯ææå¤© |
| | | if (now.DayOfWeek != DayOfWeek.Sunday) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // æ£æ¥ä»å¤©æ¯å¦å·²ç»æ§è¡è¿ |
| | | if (_lastFileSizeCheckDate.HasValue) |
| | | { |
| | | // æ£æ¥æ¯å¦å¨ä»å¤©çåä¸å°æ¶å
æ§è¡è¿ï¼é¿å
é夿§è¡ï¼ |
| | | if (_lastFileSizeCheckDate.Value.Date == now.Date) |
| | | { |
| | | // å¦æå·²ç»æ§è¡è¿ï¼è·³è¿ |
| | | 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("æ°æ®åºæä»¶è¶
è¿7GBï¼å¼å§æ¶ç¼©..."); |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éå½è·åæä»¶ä¿¡æ¯ |
| | | /// </summary> |
| | | /// <param name="dirPath"></param> |
| | | /// <returns></returns> |
| | | private DirInfo GetDirInfo(string dirPath) |
| | | { |
| | | //å½åæä»¶å¤¹ |
| | | var dirInfo = new DirInfo(); |
| | | dirInfo.dirName = Path.GetFileName(dirPath); |
| | | |
| | | //åæä»¶ |
| | | List<FileDataInfo> files = new List<FileDataInfo>(); |
| | | foreach (var file in Directory.GetFiles(dirPath)) |
| | | { |
| | | files.Add(new FileDataInfo() |
| | | { |
| | | filePath = file, |
| | | fileName = Path.GetFileName(file) |
| | | }); |
| | | } |
| | | |
| | | //åæä»¶å¤¹ |
| | | var dirs = Directory.GetDirectories(dirPath); |
| | | dirInfo.dirs = new List<DirInfo>(); |
| | | foreach (var dir in dirs) |
| | | { |
| | | dirInfo.dirs.Add(GetDirInfo(dir)); |
| | | } |
| | | |
| | | //åæä»¶å¤¹ï¼ä¸åç®å½åå¹¶ |
| | | foreach (var file in files) |
| | | { |
| | | dirInfo.dirs.Add(new DirInfo() { dirPath = file.filePath, dirName = file.fileName }); |
| | | } |
| | | return dirInfo; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæ¥å¿æä»¶å表 |
| | | /// </summary> |
| | | /// <returns></returns> |
| | | public WebResponseContent GetLogList() |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | string path = Path.Combine(AppContext.BaseDirectory, "log"); |
| | | if (!Directory.Exists(path)) |
| | | { |
| | | return content.Error("ææ æ¥å¿æä»¶"); |
| | | } |
| | | |
| | | content.Data = GetDirInfo(path); |
| | | content.OK(); |
| | | return content; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæä»¶å
容 |
| | | /// </summary> |
| | | /// <param name="parm"></param> |
| | | /// <returns></returns> |
| | | public WebResponseContent GetLogData(GetLogParm parm) |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | string res = ""; |
| | | //æ¯å¦è¯»åå°æå |
| | | bool isEnd = false; |
| | | long startIndex = 0; |
| | | //æä»¶å¤§å° |
| | | long len = 0; |
| | | try |
| | | { |
| | | if (!File.Exists(parm.path)) |
| | | { |
| | | throw new Exception($"æä»¶{parm.path}ä¸åå¨ï¼"); |
| | | } |
| | | using (FileStream fs = new FileStream(parm.path, FileMode.Open, FileAccess.Read, FileShare.Read)) |
| | | { |
| | | //æå¤§è¯»åå¤§å° |
| | | int maxsize = (int)(1024 * parm.maxsize_KB); |
| | | len = fs.Length; |
| | | long startPos = (long)(len * (parm.percent / 100));//èµ·å§ä½ç½® |
| | | long readLen = len - startPos;//读åé¿åº¦ |
| | | |
| | | //ååå è½½ |
| | | if (parm.topStartPos != 0) |
| | | { |
| | | startPos = parm.topStartPos - maxsize; |
| | | if (startPos < 0) |
| | | { |
| | | //已读å°èµ·å§ä½ |
| | | startPos = 0; |
| | | readLen = parm.topStartPos; |
| | | } |
| | | else |
| | | { |
| | | readLen = maxsize; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | //读å大尿¯å¦è¶
åºæå¤§é¿åº¦ |
| | | if (readLen > maxsize) |
| | | { |
| | | readLen = maxsize; |
| | | } |
| | | else |
| | | { |
| | | isEnd = true; |
| | | } |
| | | } |
| | | |
| | | //å è½½100%ï¼ææå¤§å
容读å |
| | | if (parm.percent == 100) |
| | | { |
| | | if (len < maxsize) |
| | | { |
| | | startPos = 0; |
| | | readLen = len; |
| | | } |
| | | else |
| | | { |
| | | startPos = len - maxsize; |
| | | readLen = maxsize; |
| | | } |
| | | } |
| | | |
| | | fs.Seek(startPos, SeekOrigin.Begin); |
| | | var buffer = new byte[readLen]; |
| | | fs.Read(buffer, 0, (int)readLen); |
| | | |
| | | startIndex = startPos; |
| | | if (startPos != 0 && (parm.percent != 0 || parm.topStartPos != 0)) |
| | | { |
| | | //䏿¯ä»å¤´å è½½ï¼å é¤å¯è½ä¸å®æ´ç第ä¸è¡ |
| | | int skipCount = 0; |
| | | for (int i = 0; i < buffer.Length; i++) |
| | | { |
| | | if (buffer[i] == 10) |
| | | { |
| | | skipCount = i; |
| | | break; |
| | | } |
| | | } |
| | | if (skipCount != 0) |
| | | { |
| | | //廿æ¢è¡ |
| | | skipCount++; |
| | | //䏿¬¡è¯»ååå»¶ |
| | | startIndex += skipCount; |
| | | } |
| | | res = Encoding.UTF8.GetString(buffer.Skip(skipCount).ToArray()); |
| | | } |
| | | else |
| | | { |
| | | res = Encoding.UTF8.GetString(buffer); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return content.Error(ex.Message); |
| | | } |
| | | return content.OK(data: new { content = res, isEnd, startIndex, len }); |
| | | } |
| | | } |
| | | } |