From adf32518950ffe503c3dd04fe0e9d6dafd7eccc0 Mon Sep 17 00:00:00 2001 From: zuohuaijun Date: Sat, 11 Oct 2025 00:39:42 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=8E1=E3=80=81=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=B8=8B=E6=8B=89=E7=BB=84=E4=BB=B6=20=20=20?= =?UTF-8?q?2=E3=80=81=E4=BC=98=E5=8C=96=E8=AE=BF=E9=97=AE=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Logging/DatabaseLoggingWriter.cs | 200 ++++++++---------- .../Logging/LoggingMonitorDto.cs | 2 +- .../components/selector/pulldownSelecter.vue | 38 +++- 3 files changed, 115 insertions(+), 125 deletions(-) diff --git a/Admin.NET/Admin.NET.Core/Logging/DatabaseLoggingWriter.cs b/Admin.NET/Admin.NET.Core/Logging/DatabaseLoggingWriter.cs index 52cac66e..882dea4c 100644 --- a/Admin.NET/Admin.NET.Core/Logging/DatabaseLoggingWriter.cs +++ b/Admin.NET/Admin.NET.Core/Logging/DatabaseLoggingWriter.cs @@ -4,8 +4,6 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! -using Furion.Shapeless; - namespace Admin.NET.Core; /// @@ -13,13 +11,16 @@ namespace Admin.NET.Core; /// public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable { - private readonly IServiceScope _serviceScope; - private readonly IEventPublisher _eventPublisher; private readonly ILogger _logger; private readonly SysConfigService _sysConfigService; + private readonly IEventPublisher _eventPublisher; + private readonly IServiceScope _serviceScope; private readonly SqlSugarScopeProvider _db; - public DatabaseLoggingWriter(IServiceScopeFactory serviceScopeFactory, IEventPublisher eventPublisher, ILogger logger) + public DatabaseLoggingWriter( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IEventPublisher eventPublisher) { _serviceScope = serviceScopeFactory.CreateScope(); _sysConfigService = _serviceScope.ServiceProvider.GetRequiredService(); @@ -94,33 +95,24 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable return; } - var loggingMonitor = JSON.Deserialize(jsonStr); // 获取当前操作者 - string account = "", realName = "", userId = "", tenantId = ""; - if (loggingMonitor.authorizationClaims != null) - { - var authDict = (loggingMonitor.authorizationClaims as IEnumerable)!.ToDictionary(u => u.type.ToString(), u => u.value.ToString()); - account = authDict?.GetValueOrDefault(ClaimConst.Account); - realName = authDict?.GetValueOrDefault(ClaimConst.RealName); - tenantId = authDict?.GetValueOrDefault(ClaimConst.TenantId); - userId = authDict?.GetValueOrDefault(ClaimConst.UserId); - } + var loggingMonitor = JSON.Deserialize(jsonStr); + var userInfo = GetUserInfo(loggingMonitor); // 优先获取 X-Forwarded-For 头部信息携带的IP地址(如nginx代理配置转发) - var remoteIPv4 = ((JArray)loggingMonitor.requestHeaders).OfType() - .FirstOrDefault(header => (string)header["key"] == "X-Forwarded-For")?["value"]?.ToString(); + var reqHeaders = loggingMonitor.RequestHeaders.ToDictionary(u => u.Key, u => u.Value); + var remoteIPv4 = reqHeaders.GetValueOrDefault("X-Forwarded-For")?.ToString(); // 获取IP地理位置 - if (string.IsNullOrEmpty(remoteIPv4)) - remoteIPv4 = loggingMonitor.remoteIPv4; + if (string.IsNullOrEmpty(remoteIPv4)) remoteIPv4 = loggingMonitor.RemoteIPv4; (string ipLocation, double? longitude, double? latitude) = CommonHelper.GetIpAddress(remoteIPv4); // 获取设备信息 - var browser = ""; var os = ""; - if (loggingMonitor.userAgent != null) + var browser = ""; + if (loggingMonitor.UserAgent != null) { - var client = Parser.GetDefault().Parse(loggingMonitor.userAgent.ToString()); + var client = Parser.GetDefault().Parse(loggingMonitor.UserAgent); browser = $"{client.UA.Family} {client.UA.Major}.{client.UA.Minor} / {client.Device.Family}"; os = $"{client.OS.Family} {client.OS.Major} {client.OS.Minor}"; } @@ -128,120 +120,100 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable // 捕捉异常,否则会由于 unhandled exception 导致程序崩溃 try { - // 记录异常日志-发送邮件 - if (logMsg.Exception != null || loggingMonitor.exception != null) + var logEntity = new SysLogOp { - await _db.Insertable(new SysLogEx - { - ControllerName = loggingMonitor.controllerName, - ActionName = loggingMonitor.actionTypeName, - DisplayTitle = loggingMonitor.displayTitle, - Status = loggingMonitor.returnInformation?.httpStatusCode, - RemoteIp = remoteIPv4, - Location = ipLocation, - Longitude = longitude, - Latitude = latitude, - Browser = browser, // loggingMonitor.userAgent, - Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture, - Elapsed = loggingMonitor.timeOperationElapsedMilliseconds, - LogDateTime = logMsg.LogDateTime, - Account = account, - RealName = realName, - HttpMethod = loggingMonitor.httpMethod, - RequestUrl = loggingMonitor.requestUrl, - RequestParam = (loggingMonitor.parameters == null || loggingMonitor.parameters.Count == 0) ? null : JSON.Serialize(loggingMonitor.parameters[0].value), - ReturnResult = loggingMonitor.returnInformation == null ? null : JSON.Serialize(loggingMonitor.returnInformation), - EventId = logMsg.EventId.Id, - ThreadId = logMsg.ThreadId, - TraceId = logMsg.TraceId, - Exception = JSON.Serialize(loggingMonitor.exception), - Message = logMsg.Message, - CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId), - TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId), - LogLevel = logMsg.LogLevel - }).ExecuteCommandAsync(); + ControllerName = loggingMonitor.DisplayName, + ActionName = loggingMonitor.ActionTypeName, + DisplayTitle = loggingMonitor.DisplayTitle, + Status = loggingMonitor.ReturnInformation?.HttpStatusCode?.ToString(), + RemoteIp = remoteIPv4, + Location = ipLocation, + Longitude = longitude, + Latitude = latitude, + Browser = browser, + Os = os, + Elapsed = loggingMonitor.TimeOperationElapsedMilliseconds, + Message = logMsg.Message, + HttpMethod = loggingMonitor.HttpMethod, + RequestUrl = loggingMonitor.RequestUrl, + RequestParam = loggingMonitor.Parameters is { Count: > 0 } ? JSON.Serialize(loggingMonitor.Parameters[0].Value) : null, + ReturnResult = loggingMonitor.ReturnInformation?.Value != null ? JSON.Serialize(loggingMonitor.ReturnInformation?.Value) : null, + Exception = loggingMonitor.Exception == null ? JSON.Serialize(logMsg.Exception) : JSON.Serialize(loggingMonitor.Exception), + LogDateTime = logMsg.LogDateTime, + EventId = logMsg.EventId.Id, + ThreadId = logMsg.ThreadId, + TraceId = logMsg.TraceId, + Account = userInfo.Account, + RealName = userInfo.RealName, + CreateUserId = userInfo.UserId, + CreateUserName = userInfo.RealName, + TenantId = userInfo.TenantId, + LogLevel = logMsg.LogLevel, + }; + // 记录异常日志-发送邮件 + if (logMsg.Exception != null || loggingMonitor.Exception != null) + { + await _db.Insertable(logEntity.Adapt()).ExecuteCommandAsync(); // 将异常日志发送到邮件 - await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, logMsg.Exception ?? loggingMonitor.exception); - + await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, logMsg.Exception ?? loggingMonitor.Exception); return; } // 记录访问日志-登录退出 - if (loggingMonitor.actionName == "login" || loggingMonitor.actionName == "loginPhone" || loggingMonitor.actionName == "logout") + if (loggingMonitor.ActionName == "login" || loggingMonitor.ActionName == "loginPhone" || loggingMonitor.ActionName == "logout") { - if (loggingMonitor.actionName != "logout") - { - dynamic para = Clay.Parse((loggingMonitor.parameters == null) ? null : JSON.Serialize(loggingMonitor.parameters[0].value)); - if (loggingMonitor.actionName == "login") - account = para.account; - else if (loggingMonitor.actionName == "loginPhone") - account = para.phone; - } - - await _db.Insertable(new SysLogVis - { - ControllerName = loggingMonitor.displayName, - ActionName = loggingMonitor.actionTypeName, - DisplayTitle = loggingMonitor.displayTitle, - Status = loggingMonitor.returnInformation?.httpStatusCode, - RemoteIp = remoteIPv4, - Location = ipLocation, - Longitude = longitude, - Latitude = latitude, - Browser = browser, // loggingMonitor.userAgent, - Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture, - Elapsed = loggingMonitor.timeOperationElapsedMilliseconds, - LogDateTime = logMsg.LogDateTime, - Account = account, - RealName = realName, - CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId), - TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId), - LogLevel = logMsg.LogLevel - }).ExecuteCommandAsync(); + await _db.Insertable(logEntity.Adapt()).ExecuteCommandAsync(); return; } // 记录操作日志 if (!await _sysConfigService.GetConfigValueByCode(ConfigConst.SysOpLog)) return; - await _db.Insertable(new SysLogOp - { - ControllerName = loggingMonitor.controllerName, - ActionName = loggingMonitor.actionTypeName, - DisplayTitle = loggingMonitor.displayTitle, - Status = loggingMonitor.returnInformation?.httpStatusCode, - RemoteIp = remoteIPv4, - Location = ipLocation, - Longitude = longitude, - Latitude = latitude, - Browser = browser, // loggingMonitor.userAgent, - Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture, - Elapsed = loggingMonitor.timeOperationElapsedMilliseconds, - LogDateTime = logMsg.LogDateTime, - Account = account, - RealName = realName, - HttpMethod = loggingMonitor.httpMethod, - RequestUrl = loggingMonitor.requestUrl, - RequestParam = (loggingMonitor.parameters == null || loggingMonitor.parameters.Count == 0) ? null : JSON.Serialize(loggingMonitor.parameters[0].value), - ReturnResult = loggingMonitor.returnInformation == null ? null : JSON.Serialize(loggingMonitor.returnInformation), - EventId = logMsg.EventId.Id, - ThreadId = logMsg.ThreadId, - TraceId = logMsg.TraceId, - Exception = loggingMonitor.exception == null ? null : JSON.Serialize(loggingMonitor.exception), - Message = logMsg.Message, - CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId), - TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId), - LogLevel = logMsg.LogLevel - }).ExecuteCommandAsync(); + await _db.Insertable(logEntity.Adapt()).ExecuteCommandAsync(); await Task.Delay(50); // 延迟 0.05 秒写入数据库,有效减少高频写入数据库导致死锁问题 } catch (Exception ex) { _logger.LogError(ex, "操作日志入库"); + // 将异常日志发送到邮件 + await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, ex); } } + /// + /// 从日志消息中获取用户信息 + /// + /// + /// + private LoggingUserInfo GetUserInfo(LoggingMonitorDto loggingMonitor) + { + LoggingUserInfo result = new(); + if (loggingMonitor.AuthorizationClaims != null) + { + var authDict = loggingMonitor.AuthorizationClaims.ToDictionary(u => u.Type, u => u.Value); + result.UserId = long.TryParse(authDict?.GetValueOrDefault(ClaimConst.UserId) ?? "", out var userId) ? userId : null; + result.Account = authDict?.GetValueOrDefault(ClaimConst.Account); + result.RealName = authDict?.GetValueOrDefault(ClaimConst.RealName); + result.TenantId = long.TryParse(authDict?.GetValueOrDefault(ClaimConst.TenantId) ?? "", out var tenantId) ? tenantId : null; + } + + // 登陆时没有用户Id,需要根据入参获取 + if (result.UserId == null && loggingMonitor.ActionName == "login" && loggingMonitor.Parameters is { Count: > 0 }) + { + result.Account = (loggingMonitor.Parameters[0].Value as JObject)!.GetValue("account")?.ToString(); + if (!string.IsNullOrEmpty(result.Account)) + { + var db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId); + var user = db.Queryable().First(u => u.Account == result.Account); + result.TenantId = user?.TenantId; + result.RealName = user?.RealName; + result.UserId = user?.Id; + } + } + return result; + } + /// /// 释放服务作用域 /// diff --git a/Admin.NET/Admin.NET.Core/Logging/LoggingMonitorDto.cs b/Admin.NET/Admin.NET.Core/Logging/LoggingMonitorDto.cs index 87ee8c52..33534b1a 100644 --- a/Admin.NET/Admin.NET.Core/Logging/LoggingMonitorDto.cs +++ b/Admin.NET/Admin.NET.Core/Logging/LoggingMonitorDto.cs @@ -1,4 +1,4 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 // // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 // diff --git a/Web/src/components/selector/pulldownSelecter.vue b/Web/src/components/selector/pulldownSelecter.vue index c3515a6d..fae808d0 100644 --- a/Web/src/components/selector/pulldownSelecter.vue +++ b/Web/src/components/selector/pulldownSelecter.vue @@ -1,6 +1,6 @@