´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_BasicInfoService/ApiInfoService.cs
@@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using WIDESEAWCS_Core; @@ -36,11 +37,15 @@ { WebResponseContent content = new WebResponseContent(); string response = string.Empty; string requestHash = string.Empty; string responseHash = string.Empty; Dt_ApiInfo? apiInfo = null; try { apiInfo = BaseDal.QueryFirst(x => x.ApiCode == apiCode) ?? throw new Exception($"æªæ¾å°{remark}æ¥å£é 置信æ¯ï¼è¯·æ£æ¥æ¥å£é ç½®"); response = HttpHelper.Post(apiInfo.ApiAddress, requestParameters); requestHash = ComputeHash(requestParameters); responseHash = ComputeHash(response); if (apiInfo.Remark.Contains("åå车")) { FOURBOTReturn fOURBOTReturn = response.DeserializeObject<FOURBOTReturn>(); @@ -70,12 +75,34 @@ { if (isAdd && !string.IsNullOrEmpty(response) && apiInfo != null) { #region æ¥è¯¢å½å¤©æ¯å¦åå¨ç¸åè®°å½ var today = DateTime.Now.Date; var existingLog = _interfaceLogService.Repository.QueryFirst(x => x.ApiCode == apiCode && x.RequestParametersHash == requestHash && x.ResponseParametersHash == responseHash && x.CreateDate.Date == today); if (existingLog != null) { existingLog.PushFrequency = existingLog.PushFrequency + 1; existingLog.PushState = content.Status ? 1 : 2; existingLog.ResponseParameters = response; existingLog.Remark = content.Status ? remark : content.Message; existingLog.Modifier = App.User?.UserName ?? "System"; existingLog.ModifyDate = DateTime.Now; _interfaceLogService.Repository.UpdateData(existingLog); } #endregion else { Dt_InterfaceLog interfaceLog = new Dt_InterfaceLog() { ApiCode = apiCode, RequestParameters = requestParameters, ApiAddress = apiInfo.ApiAddress, ApiName = apiInfo.ApiName, RequestParametersHash = requestHash, ResponseParametersHash = responseHash, PushFrequency = 1, PushState = content.Status ? 1 : 2, Requestor = "WCS", @@ -87,7 +114,24 @@ _interfaceLogService.Repository.AddData(interfaceLog); } } } return content; } /// <summary> /// 计ç®å符串çSHA256åå¸å¼ /// </summary> private static string ComputeHash(string input) { if (string.IsNullOrEmpty(input)) return string.Empty; using (SHA256 sha256 = SHA256.Create()) { byte[] bytes = Encoding.UTF8.GetBytes(input); byte[] hashBytes = sha256.ComputeHash(bytes); return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } } } } ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/BasicInfo/Dt_InterfaceLog.cs
@@ -51,6 +51,12 @@ public int PushState { get; set; } = 0; /// <summary> /// è¯·æ±æ¹ç¨æ·IP /// </summary> [SugarColumn(IsNullable = true, Length = 20, ColumnDescription = "è¯·æ±æ¹ç¨æ·IP")] public string RequestorUserIP { get; set; } /// <summary> /// è¯·æ±æ¹ /// </summary> [SugarColumn(IsNullable = true, Length = 50, ColumnDescription = "è¯·æ±æ¹")] @@ -75,6 +81,18 @@ public string ResponseParameters { get; set; } /// <summary> /// 请æ±åæ°åå¸å¼ï¼ç¨äºç´¢å¼åå»éï¼ /// </summary> [SugarColumn(IsNullable = true, Length = 64, ColumnDescription = "请æ±åæ°åå¸å¼")] public string RequestParametersHash { get; set; } /// <summary> /// ååºåæ°åå¸å¼ï¼ç¨äºç´¢å¼åå»éï¼ /// </summary> [SugarColumn(IsNullable = true, Length = 64, ColumnDescription = "ååºåæ°åå¸å¼")] public string ResponseParametersHash { get; set; } /// <summary> /// 夿³¨ /// </summary> [SugarColumn(IsNullable = true, Length = 100, ColumnDescription = "夿³¨")] ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/WMS/WMSController.cs
@@ -71,7 +71,7 @@ } /// <summary> /// è´¨æ£ç»æåé¦ /// 夿£ç»æåé¦ /// </summary> /// <param name="iQC"></param> /// <returns></returns> ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Filter/Log/ApiLogMiddlewareNew.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,144 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using WIDESEAWCS_Core.Helper; using WIDESEAWCS_Core.LogHelper; namespace WIDESEAWCS_Core.Middlewares { /// <summary> /// è®°å½è¯·æ±åååºæ°æ® /// </summary> public class ApiLogMiddlewareNew { /// <summary> /// /// </summary> private readonly RequestDelegate _next; public ApiLogMiddlewareNew(RequestDelegate next, ILogger<ApiLogMiddlewareNew> logger) { _next = next; } //todo public async Task InvokeAsync(HttpContext context) { if (App.ExpDateTime != null && (DateTime.Now - App.ExpDateTime.GetValueOrDefault()).TotalSeconds > 0) { context.Response.StatusCode = HttpStatusCode.InternalServerError.ObjToInt(); context.Response.ContentType = "application/json"; var json = new WebResponseContent(); json.Message = HttpStatusCode.InternalServerError.ToString();//éè¯¯ä¿¡æ¯ json.Code = 500;//500å¼å¸¸ StreamWriter streamWriter = new StreamWriter(context.Response.Body); await streamWriter.WriteAsync(json.Serialize()); return; } // è¿æ»¤ï¼åªææ¥å£ if (context.Request.Path.Value?.Contains("api") ?? false) { context.Request.EnableBuffering(); Stream originalBody = context.Response.Body; string requestParam = string.Empty; string responseParam = string.Empty; try { (context.RequestServices.GetService(typeof(RequestLogModel)) as RequestLogModel).RequestDate = DateTime.Now; try { // åå¨è¯·æ±æ°æ® requestParam = RequestDataLog(context); context.Request.Body.Position = 0; } catch { } using MemoryStream ms = new(); context.Response.Body = ms; await _next(context); try { // åå¨ååºæ°æ® responseParam = ResponseDataLog(context.Response); } catch { } ms.Position = 0; await ms.CopyToAsync(originalBody); #region è¿æ»¤æ¥å£ string path = context.Request.Path.Value; if (!string.IsNullOrEmpty(path)) { List<string> RequestPaths = new List<string>(); string RequestPath = AppSettings.Configuration[nameof(RequestPath)]; if (!string.IsNullOrEmpty(RequestPath)) RequestPaths = RequestPath.Split(',').ToList(); bool containsAny = RequestPaths.Any(k => path.Contains(k, StringComparison.OrdinalIgnoreCase)); if (!containsAny) Logger.Add(requestParam, responseParam);//è®°å½ç³»ç»æ¥å¿ //è®°å½æ¥å£æ¥å¿ var ignoreKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "get" }; containsAny = RequestPaths.Where(k => !ignoreKeywords.Contains(k)) .Any(k => path.Contains(k, StringComparison.OrdinalIgnoreCase)); if (containsAny && context != null) LoggerNew.Add(requestParam, responseParam, context); } #endregion //if (!(context.Request.Path.Value?.Contains("get") ?? true)) // Logger.Add(requestParam, responseParam); } catch (Exception ex) { // è®°å½å¼å¸¸ } finally { context.Response.Body = originalBody; } } else { await _next(context); } } private string RequestDataLog(HttpContext context) { var request = context.Request; var sr = new StreamReader(request.Body); object obj = new { QueryString = request.QueryString.ToString(), BodyData = sr.ReadToEndAsync().Result }; string data = JsonConvert.SerializeObject(obj); request.Body.Position = 0; return data; } private string ResponseDataLog(HttpResponse response) { response.Body.Position = 0; using StreamReader stream = new StreamReader(response.Body, leaveOpen: true); string body = stream.ReadToEnd(); response.Body.Position = 0; return body; } } } ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Filter/Log/LogCleanupService.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,426 @@ 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 { /// <summary> /// æ¥å¿æ°æ®æ¸ çæå¡ /// </summary> public class LogCleanupService : IHostedService, IDisposable { private Timer _cleanupTimer; private bool _isDisposed = false; private readonly object _lockObject = new object(); /// <summary> /// æ°æ®ä¿ç天æ°ï¼é»è®¤90å¤©ï¼ /// </summary> public int RetentionDays { get; set; } = 90; /// <summary> /// æ¸ çé´éï¼å°æ¶ï¼é»è®¤24å°æ¶ï¼ /// </summary> public int CleanupIntervalHours { get; set; } = 24; /// <summary> /// æ¸ çæ§è¡æ¶é´ï¼å°æ¶ï¼0-23ï¼é»è®¤åæ¨2ç¹ï¼ /// </summary> public int CleanupHour { get; set; } = 2; /// <summary> /// æ¯å¦å¯ç¨å½æ¡£ /// </summary> public bool EnableArchive { get; set; } = false; /// <summary> /// æ¯å¦å¯ç¨èªå¨æ¸ ç /// </summary> public bool EnableAutoCleanup { get; set; } = true; /// <summary> /// æ¥å¿æä»¶ä¿çå¤©æ° /// </summary> public int LogRetentionDays { get; set; } = 30; /// <summary> /// æ¥å¿æä»¶è·¯å¾ /// </summary> 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; } /// <summary> /// ç¡®ä¿æ¥å¿ç®å½åå¨ /// </summary> private void EnsureLogDirectoryExists() { try { string logDirectory = Path.GetDirectoryName(LogFilePath); if (!Directory.Exists(logDirectory)) { Directory.CreateDirectory(logDirectory); } } catch (Exception ex) { Console.WriteLine($"å建æ¥å¿ç®å½å¤±è´¥: {ex.Message}"); } } /// <summary> /// åå ¥æ¥å¿å°æä»¶ /// </summary> 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}"); } } /// <summary> /// èªå¨æ¸ çè¿æçæ¥å¿æä»¶ï¼ä¿ç30å¤©ï¼ /// </summary> 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()); } /// <summary> /// æ§è¡æ¸ ç /// </summary> 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<Dt_InterfaceLog>() .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); } } /// <summary> /// æå¨æ§è¡æ¸ ç /// </summary> public async Task<int> 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<Dt_InterfaceLog>() .Where(x => x.CreateDate < cutoffDate) .ExecuteCommandAsync(); WriteLog($"æå¨æ¸ ç宿ï¼å é¤ {deletedCount} æ¡è®°å½"); return deletedCount; } } catch (Exception ex) { WriteLog($"æå¨æ¸ ç失败: {ex.Message}", true); return 0; } } /// <summary> /// è·åæ¸ çç»è®¡ä¿¡æ¯ /// </summary> public async Task<CleanupStatistics> GetStatistics() { try { using (SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig() { ConnectionString = DBContext.GetMainConnectionDb().Connection, IsAutoCloseConnection = true, DbType = DBContext.DbType, })) { var totalCount = await sugarClient.Queryable<Dt_InterfaceLog>().CountAsync(); var oldestLog = await sugarClient.Queryable<Dt_InterfaceLog>() .OrderBy(x => x.CreateDate) .FirstAsync(); var newestLog = await sugarClient.Queryable<Dt_InterfaceLog>() .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; } } } /// <summary> /// æ¸ çç»è®¡ä¿¡æ¯ /// </summary> 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; } } } ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Filter/Log/LoggerNew.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,431 @@ using SqlSugar; using System.Collections.Concurrent; using System.Text; using WIDESEAWCS_Core.HttpContextUser; using WIDESEAWCS_Core.Seed; using WIDESEAWCS_Model.Models; using System.Security.Cryptography; using WIDESEAWCS_Core.Helper; using Newtonsoft.Json.Linq; using WIDESEAWCS_DTO.RGV.FOURBOT; using WIDESEAWCS_DTO.WMS; using WIDESEAWCS_DTO; namespace WIDESEAWCS_Core.LogHelper { public static class LoggerNew { // ç¨äºç¼åå½å¤©å·²åå¨çæ¥å¿ï¼key: æ¥æ+ApiCode+请æ±åå¸+ååºåå¸ï¼ private static ConcurrentDictionary<string, Dt_InterfaceLog> _cacheLogs = new ConcurrentDictionary<string, Dt_InterfaceLog>(); // éåç¨äºåå¨éè¦æ°å¢ææ´æ°çæ¥å¿ public static ConcurrentQueue<Dt_InterfaceLog> loggerQueueData = new ConcurrentQueue<Dt_InterfaceLog>(); // æ¯å¤©åæ¨æ¸ 空ç¼å private static DateTime _lastClearDate = DateTime.Now.Date; static LoggerNew() { Task.Run(() => { StartWriteLog(); }); // å¯å¨ä¸ä¸ªåå°ä»»å¡ï¼æ¯å¤©åæ¨æ¸ 空ç¼å Task.Run(() => { while (true) { Thread.Sleep(60000); // æ¯å鿣æ¥ä¸æ¬¡ DateTime today = DateTime.Now.Date; if (_lastClearDate < today) { _cacheLogs.Clear(); _lastClearDate = today; Console.WriteLine($"Loggerç¼åå·²æ¸ ç©ºï¼æ¥æï¼{today:yyyy-MM-dd}"); } } }); } static void StartWriteLog() { List<Dt_InterfaceLog> batchList = new List<Dt_InterfaceLog>(); while (true) { try { if (loggerQueueData.Count() > 0 && batchList.Count < 500) { DequeueToBatchList(batchList); continue; } Thread.Sleep(5000); if (batchList.Count == 0) { continue; } SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig() { ConnectionString = DBContext.GetMainConnectionDb().Connection, IsAutoCloseConnection = true, DbType = DBContext.DbType, }); try { sugarClient.BeginTran(); // å¼å¯äºå¡ var toInsert = new List<Dt_InterfaceLog>(); var toUpdate = new List<Dt_InterfaceLog>(); foreach (var log in batchList) { // å¦æå·²ç»æ Id æè IsExisting æ å¿ï¼ç´æ¥å¤ç if (log.Id > 0) { toUpdate.Add(log); continue; } // æ ¹æ®åå¸å¼æ¥è¯¢æ¯å¦å·²åå¨ var existing = sugarClient.Queryable<Dt_InterfaceLog>() .First(x => x.RequestParametersHash == log.RequestParametersHash && x.ResponseParametersHash == log.ResponseParametersHash && x.ApiCode == log.ApiCode && SqlFunc.DateIsSame(x.CreateDate, DateTime.Now)); // åä¸å¤© if (existing != null) { // å·²åå¨ï¼æ´æ° log.Id = existing.Id; log.PushFrequency = existing.PushFrequency + 1; // å å æ¬¡æ° log.ModifyDate = DateTime.Now; toUpdate.Add(log); // æ´æ°ç¼åä¸ç Id string cacheKey = GenerateCacheKey(log.ApiCode, log.RequestParametersHash, log.ResponseParametersHash); if (_cacheLogs.TryGetValue(cacheKey, out var cachedLog)) { cachedLog.Id = existing.Id; } } else { // ä¸åå¨ï¼æ°å¢ toInsert.Add(log); } } // æ¹éæ°å¢ if (toInsert.Any()) { int rows = sugarClient.Fastest<Dt_InterfaceLog>().BulkCopy(toInsert); // è·åæ°å¢æ¥å¿çèªå¢IDå¹¶æ´æ°ç¼å foreach (var insertedLog in toInsert) { var savedLog = sugarClient.Queryable<Dt_InterfaceLog>() .First(x => x.RequestParametersHash == insertedLog.RequestParametersHash && x.ResponseParametersHash == insertedLog.ResponseParametersHash); if (savedLog != null) { string cacheKey = GenerateCacheKey(insertedLog.ApiCode, insertedLog.RequestParametersHash, insertedLog.ResponseParametersHash); if (_cacheLogs.TryGetValue(cacheKey, out var cachedLog)) { cachedLog.Id = savedLog.Id; } } } } // æ¹éæ´æ°æ¨éæ¬¡æ° if (toUpdate.Any()) { foreach (var log in toUpdate) { sugarClient.Updateable<Dt_InterfaceLog>() .SetColumns(it => new Dt_InterfaceLog { PushFrequency = log.PushFrequency, PushState = log.PushState, Remark = log.Remark, //CreateDate = log.CreateDate ModifyDate = log.ModifyDate }) .Where(it => it.Id == log.Id) .ExecuteCommand(); } } sugarClient.CommitTran(); // æäº¤äºå¡ } catch (Exception ex) { sugarClient.RollbackTran(); // åæ»äºå¡ Console.WriteLine($"å¤çæ¥å¿æ¹æ¬¡å¤±è´¥: {ex.Message}"); } finally { batchList.Clear(); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } } private static void DequeueToBatchList(List<Dt_InterfaceLog> batchList) { if (loggerQueueData.TryDequeue(out Dt_InterfaceLog log) && log != null) { batchList.Add(log); } } /// <summary> /// 计ç®å符串çSHA256åå¸å¼ /// </summary> private static string ComputeHash(string input) { if (string.IsNullOrEmpty(input)) return string.Empty; using (SHA256 sha256 = SHA256.Create()) { byte[] bytes = Encoding.UTF8.GetBytes(input); byte[] hashBytes = sha256.ComputeHash(bytes); return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } } /// <summary> /// çæç¼åKey /// </summary> private static string GenerateCacheKey(string apiCode, string requestHash, string responseHash) { string today = DateTime.Now.ToString("yyyy-MM-dd"); return $"{today}_{apiCode}_{requestHash}_{responseHash}"; } /// <summary> /// è·åæå建æ¥å¿ï¼ç¨äºå å æ¨é次æ°ï¼ /// </summary> /// <param name="apiCode">ç¼å·</param> /// <param name="apiName">åç§°</param> /// <param name="apiAddress">å°å</param> /// <param name="requestParameters">请æ±å 容</param> /// <param name="responseParameters">ååºå 容</param> /// <param name="pushState">ç¶æ</param> /// <param name="requestorUserIP">请æ±ç¨æ·IP</param> /// <param name="requestor">è¯·æ±æ¹</param> /// <param name="recipient">æ¥æ¶æ¹</param> /// <param name="remark">夿³¨</param> /// <returns></returns> private static Dt_InterfaceLog GetOrCreateLog(string apiCode, string apiName, string apiAddress, string requestParameters, string responseParameters, int pushState, string requestorUserIP, string requestor, string recipient, string remark) { string requestHash = ComputeHash(requestParameters); string responseHash = ComputeHash(responseParameters); string cacheKey = GenerateCacheKey(apiCode, requestHash, responseHash); // å°è¯ä»ç¼åä¸è·å if (_cacheLogs.TryGetValue(cacheKey, out Dt_InterfaceLog existingLog)) { // åå¨ç¸ååæ°çæ¥å¿ï¼å¢å æ¨éæ¬¡æ° existingLog.PushFrequency++; existingLog.PushState = pushState; // æ´æ°æ¨éç¶æ existingLog.Remark = remark; // æ´æ°å¤æ³¨ //existingLog.CreateDate = DateTime.Now; // æ´æ°æ¶é´ existingLog.ModifyDate = DateTime.Now; // æ´æ°æ¶é´ // è¿åç°ææ¥å¿ï¼éè¦æ´æ°æ°æ®åºï¼ return existingLog; } else { // ä¸åå¨ï¼åå»ºæ°æ¥å¿ var newLog = new Dt_InterfaceLog { ApiCode = apiCode, ApiName = apiName, ApiAddress = apiAddress, PushFrequency = 1, PushState = pushState, RequestorUserIP = requestorUserIP, Requestor = requestor, RequestParameters = requestParameters, RequestParametersHash = requestHash, Recipient = recipient, ResponseParameters = responseParameters, ResponseParametersHash = responseHash, Remark = remark, Creater = "System", CreateDate = DateTime.Now }; // å å ¥ç¼å _cacheLogs.TryAdd(cacheKey, newLog); return newLog; } } /// <summary> /// æ·»å æ¥å£æ¥å¿ï¼å¸¦URLã请æ±åæ°ãååºåæ°ãå¼å§æ¶é´ï¼ /// </summary> public static void Add(string apiCode, string apiName, string apiAddress, string requestParameters, string responseParameters, int pushState = 0, string recipient = "", string remark = "") { Dt_InterfaceLog log = null; try { // è·åå½åç¨æ· IUser? user = App.User; // è·åæå建æ¥å¿ï¼èªå¨å¤çå å ï¼ log = GetOrCreateLog(apiCode, apiName, apiAddress, requestParameters, responseParameters, pushState, "", user?.UserName ?? "", recipient, remark); } catch (Exception exception) { // 妿åçå¼å¸¸ï¼å建åºç¡æ¥å¿å¯¹è±¡ log = log ?? new Dt_InterfaceLog { ApiCode = apiCode, ApiName = apiName, ApiAddress = apiAddress, PushFrequency = 1, PushState = pushState, RequestParameters = requestParameters, ResponseParameters = responseParameters, Creater = "System", Remark = $"è®°å½æ¥å¿æ¶åçå¼å¸¸ï¼{exception.Message}" }; } // æ·»å ç³»ç»æ¥å¿ loggerQueueData.Enqueue(log); } /// <summary> /// æ·»å æ¥å£æ¥å¿ï¼ä»HttpContextèªå¨è·åä¿¡æ¯ï¼ /// </summary> /// <param name="requestParameters">请æ±å 容</param> /// <param name="responseParameters">ååºå 容</param> /// <param name="apiCode">æ¥å£ç¼å·</param> /// <param name="apiName">æ¥å£åç§°</param> /// <param name="pushState">æ¨éç¶æ</param> /// <param name="recipient">æ¥æ¶æ¹</param> /// <param name="remark">夿³¨</param> public static void Add(string requestParameters, string responseParameters, HttpContext context, string apiCode = "", string apiName = "", int pushState = 0, string recipient = "WCS", string remark = "") { Dt_InterfaceLog log = null; try { // å¦æè¯·æ±æ¹æ³ä¸ºOPTIONSï¼åè¿å if (context.Request.Method == "OPTIONS") return; #region æ°æ®è½¬æ¢ // è§£æå¤å± JSON JObject outerObj = JObject.Parse(requestParameters); // è·å BodyData çå¼ï¼å®æ¯ä¸ä¸ªåç¬¦ä¸²ï¼ requestParameters = outerObj["BodyData"]?.ToString() ?? requestParameters; // æå»ºå®æ´çæ¥å£å°å string apiAddress = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Path; // è·å客æ·ç«¯IP string clientIP = GetClientIP(context)?.Replace("::ffff:", "") ?? ""; string requestor = "";//è¯·æ±æ¹ if (apiAddress.Contains("/api/WMS")) { requestor = "WMS";//ç§ä¼ var wMSReturn = responseParameters.DeserializeObject<WMSReturn>(); pushState = wMSReturn.code == 0 ? 1 : 2; } else if (apiAddress.Contains("/api/KHAGV")) { requestor = "海康AGV"; var hIKROBOTReturn = responseParameters.DeserializeObject<HIKROBOTReturn>(); pushState = hIKROBOTReturn.code == "SUCCESS" ? 1 : 2; } else if (apiAddress.Contains("/api/KLS")) { requestor = "å¯ä¹å£«AGV"; ReturnGALAXIS returnGALAXIS = responseParameters.DeserializeObject<ReturnGALAXIS>(); pushState = returnGALAXIS.returnStatus == 0 ? 1 : 2; } else if (apiAddress.Contains("/api/RGV")) { requestor = "ååç©¿æ¢è½¦";//æ·è§æ²³å¾ FOURBOTReturn fOURBOTReturn = responseParameters.DeserializeObject<FOURBOTReturn>(); pushState = fOURBOTReturn.returnCode == 0 ? 1 : 2; } apiCode = apiAddress.Split("/").Last(); apiName = AppSettings.Configuration.GetValue($"{requestor}:{apiCode}", apiCode); #endregion // è·åæå建æ¥å¿ï¼èªå¨å¤çå å ï¼ log = GetOrCreateLog(apiCode, apiName, apiAddress, requestParameters, responseParameters, pushState, clientIP, requestor, recipient, remark); } catch (Exception exception) { // 妿åçå¼å¸¸ï¼å建åºç¡æ¥å¿å¯¹è±¡ log = log ?? new Dt_InterfaceLog { ApiCode = apiCode, ApiName = apiName, PushFrequency = 1, PushState = pushState, RequestParameters = requestParameters, ResponseParameters = responseParameters, Creater = "System", Remark = $"è®°å½æ¥å¿æ¶åçå¼å¸¸ï¼{exception.Message}" }; } // æ·»å ç³»ç»æ¥å¿ loggerQueueData.Enqueue(log); } /// <summary> /// æå¨å¢å æ¨é次æ°ï¼ç¨äºéè¯åºæ¯ï¼ /// </summary> public static void IncrementPushCount(string apiCode, string requestParameters, string responseParameters) { string requestHash = ComputeHash(requestParameters); string responseHash = ComputeHash(responseParameters); string cacheKey = GenerateCacheKey(apiCode, requestHash, responseHash); if (_cacheLogs.TryGetValue(cacheKey, out Dt_InterfaceLog existingLog)) { existingLog.PushFrequency++; loggerQueueData.Enqueue(existingLog); } } public static string GetClientIP(HttpContext context) { // è·å请æ±å¤´ä¸çX-Forwarded-Foråæ®µï¼å¹¶å°å ¶è½¬æ¢ä¸ºå符串 var ip = context.Request.Headers["X-Forwarded-For"].ObjToString(); // 妿X-Forwarded-Foråæ®µä¸ºç©ºï¼åè·åè¿ç¨IPå°å if (string.IsNullOrEmpty(ip)) { ip = context.Connection.RemoteIpAddress.ObjToString(); } // è¿åIPå°å return ip; } } } ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Filter/Log/MiddlewareHelpersNew.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ using Microsoft.AspNetCore.Builder; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WIDESEAWCS_Core.Middlewares { public static class MiddlewareHelpersNew { /// <summary> /// 请æ±ååºä¸é´ä»¶ /// </summary> /// <param name="app"></param> /// <returns></returns> public static IApplicationBuilder UseApiLogMiddlewareNew(this IApplicationBuilder app) { return app.UseMiddleware<ApiLogMiddlewareNew>(); } } } ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs
@@ -18,6 +18,7 @@ using WIDESEAWCS_Core.Extensions; using WIDESEAWCS_Core.Filter; using WIDESEAWCS_Core.Helper; using WIDESEAWCS_Core.LogHelper; using WIDESEAWCS_Core.Middlewares; using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.QuartzExtensions; @@ -50,6 +51,25 @@ builder.Services.AddHostedService<SeedDataHostedService>();//åå§åæ°æ®åº builder.Services.AddDbSetup();//Db å¯å¨æå¡ #region æ·»å æ¥å¿æ¸ çæå¡ var cleanupRetentionDays = builder.Configuration.GetValue<int>("LogCleanup:RetentionDays", 90); var cleanupHour = builder.Configuration.GetValue<int>("LogCleanup:CleanupHour", 2); var enableAutoCleanup = builder.Configuration.GetValue<bool>("LogCleanup:EnableAutoCleanup", true); var enableArchive = builder.Configuration.GetValue<bool>("LogCleanup:EnableArchive", false); var logRetentionDays = builder.Configuration.GetValue<int>("LogCleanup:LogRetentionDays", 30); builder.Services.AddSingleton(sp => new LogCleanupService { RetentionDays = cleanupRetentionDays, CleanupHour = cleanupHour, EnableAutoCleanup = enableAutoCleanup, EnableArchive = enableArchive, LogRetentionDays= logRetentionDays, CleanupIntervalHours = 24 }); builder.Services.AddHostedService(sp => sp.GetRequiredService<LogCleanupService>()); #endregion //builder.Services.AddScoped<QuartzJobCreateDataTabel>();//ä»»å¡è°åº¦ æ³¨å ¥å建QuartzJobæ°æ®åºè¡¨ç±» //builder.Services.AddHostedService<QuartzJobDataTableHostedService>();//ä»»å¡è°åº¦ æ å°QuartzJobæ°æ®åºè¡¨ @@ -123,7 +143,8 @@ app.UseSwaggerMiddle(() => Assembly.GetExecutingAssembly().GetManifestResourceStream("WIDESEAWCS_Server.index.html") ?? throw new Exception("æªæ¾å°WIDESEAWCS_Server.index.htmlæä»¶")); app.UseIpLimitMiddle(); app.UseApiLogMiddleware(); //app.UseApiLogMiddleware(); app.UseApiLogMiddlewareNew(); app.UseCors(AppSettings.Get(new string[] { "Cors", "PolicyName" })); @@ -148,4 +169,14 @@ app.MapControllers(); #region åºç¨å¯å¨æ¶ç«å³æ¸ ç䏿¬¡ //Task.Run(async () => //{ // // çå¾ åºç¨å®å ¨å¯å¨ // await Task.Delay(30000); // var cleanupService = app.Services.GetRequiredService<LogCleanupService>(); // await cleanupService.ManualCleanup(90); //}); #endregion app.Run(); ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/WIDESEAWCS_Server.csproj
@@ -76,8 +76,4 @@ </Content> </ItemGroup> <ItemGroup> <Folder Include="Log\" /> </ItemGroup> </Project> ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -17,6 +17,7 @@ //5.PostgreSQL "DBType": "SqlServer", //è¿æ¥å符串 //"ConnectionString": "Data Source=172.16.161.20;Initial Catalog=WIDESEAWCS_GP;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_GP;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", //è·¨å "Cors": { @@ -26,15 +27,39 @@ // 注æï¼http://127.0.0.1:1818 å http://localhost:1818 æ¯ä¸ä¸æ ·ç "IPs": "http://127.0.0.1:8080,http://localhost:8080" }, "LogCleanup": { //æ¥å£æ¥å¿æ¸ çé ç½® "RetentionDays": 90, // æ°æ®ä¿çå¤©æ° "CleanupHour": 2, // æ¸ çæ§è¡æ¶é´ï¼å°æ¶ï¼0-23ï¼ "EnableAutoCleanup": true, // æ¯å¦å¯ç¨èªå¨æ¸ ç "EnableArchive": true, // æ¯å¦å¯ç¨å½æ¡£ï¼å½æ¡£å°åå²è¡¨ï¼ "LogRetentionDays": 30 // æ¥å¿æä»¶ä¿çå¤©æ° }, "ApiLogIgnore": "", //è®°å½æ¥å¿æ¶ï¼å¿½ç¥çAPIåç§°ï¼å¤ä¸ªç¨éå·åéï¼é ç½®çä¸è®°å½å°æ°æ®åºä¸ "ApiName": "WIDESEAWCS", "ExpMinutes": 120, "RequestPath": "GET,WMS,RGV,KHAGV,KLSAGV", //, ///æ¥å£è¯´æï¼æ¥å£æ¥å¿è®°å½ä½¿ç¨ "WMS": { "ReceiveTask": "WMSä¸åä»»å¡", "ContainerRequest": "容卿µå¨è¯·æ±", "UpOrDownContainer": "å¹³åºè´§ä½ä¸ä¸æ¶å®¹å¨", "CancelWMSTask": "WMSåæ¶ä»»å¡", "IQCResult": "夿£ç»æåé¦", "UpdateArea": "ä¿®æ¹åºä½ååº", "UpdateTaskGrade": "WMSä¿®æ¹ä»»å¡ä¼å 级", "QueryStation": "åºååºä½ä¿¡æ¯æ¥è¯¢", "UpLocationStatus": "æ´æ°ä»åºåºä½ç¶æ" }, "海康AGV": { "UpdateTaskStatus": "海康AGVä»»å¡ç¶ææ´æ°" }, "å¯ä¹å£«AGV": { "UpdateTaskStatus": "å¯ä¹å£«AGVä»»å¡ç¶ææ´æ°" }, "ååç©¿æ¢è½¦": { "UpdateTaskStatus": "ååç©¿æ¢è½¦ä»»å¡ç¶ææ´æ°" }, ///æ¥å£è¯´æ "QuartzJobAutoStart": true, "DBSeedEnable": false, "QuartzDBSeedEnable": false, "LogDeubgEnable": true, //æ¯å¦è®°å½è°è¯æ¥å¿ "PrintSql": true, //æå°SQLè¯å¥ "LogAOPEnable": true, //æ¯å¦è®°å½AOPæ¥å¿ "LogDeubgEnable": false, //æ¯å¦è®°å½è°è¯æ¥å¿ "PrintSql": false, //æå°SQLè¯å¥ "LogAOPEnable": false, //æ¯å¦è®°å½AOPæ¥å¿ "WebSocketEnable": true, //æ¯å¦å¼å¯WebSocketæå¡ "WebSocketPort": 9296 //WebSocketæå¡ç«¯å£ } ´úÂë¹ÜÀí/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/Task/RGVTaskExtend.cs
@@ -528,6 +528,13 @@ try { List<Dt_RGVLocationInfo> rGVLocationInfos = _rGVLocationInfoService.Repository.QueryData(x => blockPods.Contains(x.PalletCode)); var palletCodes = rGVLocationInfos.Select(x => x.PalletCode).ToList(); // è·å blockPods ä¸åå¨ä½ palletCodes ä¸ä¸åå¨çæçç var missingPalletCodes = blockPods.Except(palletCodes).ToList(); if (missingPalletCodes.Any()) { throw new Exception($"æªæ¾å°ä»¥ä¸é»ç¢æè´§ä½ä¿¡æ¯ï¼{string.Join(",", missingPalletCodes)}"); } var Items = _rGVLocationInfoService.GetMoveFreeLocationInfo(rGVLocationInfos); List<Dt_Task> tasks = Items.Item1;