diff --git a/backend/Admin.NET.Web.Core/Startup.cs b/backend/Admin.NET.Web.Core/Startup.cs index 4f4ae1dadbdd95d7a1793ac4f7cb6285a67d7747..7f5a908cb0f1d33ab8b29460cc73c31d4e84da4c 100644 --- a/backend/Admin.NET.Web.Core/Startup.cs +++ b/backend/Admin.NET.Web.Core/Startup.cs @@ -1,7 +1,7 @@ -using Furion.Extras.Admin.NET; -using Furion.Extras.Admin.NET.Service; -using Furion; +using Furion; +using Furion.Extras.Admin.NET; using Furion.Extras.Admin.NET.Options; +using Furion.Extras.Admin.NET.Service; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; @@ -23,23 +23,26 @@ namespace Admin.NET.Web.Core services.AddJwt(enableGlobalAuthorize: true); services.AddCorsAccessor(); services.AddRemoteRequest(); - services.AddControllersWithViews() - .AddMvcFilter() - .AddNewtonsoftJson(options => - { - // 首字母小写(驼峰样式) - options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - // 时间格式化 - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - // 忽略循环引用 - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - // 忽略空值 - // options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - }) - .AddInjectWithUnifyResult(); + services.AddControllersWithViews().AddMvcFilter().AddNewtonsoftJson(options => + { + // 首字母小写(驼峰样式) + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + // 时间格式化 + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + // 忽略循环引用 + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + // 忽略空值 + // options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + }).AddInjectWithUnifyResult(); services.AddViewEngine(); services.AddSignalR(); - services.AddEventBridge(); + + // 注册EventBus服务 + services.AddEventBus(builder => + { + // 注册 Log 日志订阅者 + builder.AddSubscriber(); + }); if (App.Configuration["Cache:CacheType"] == "RedisCache") { @@ -118,14 +121,12 @@ namespace Admin.NET.Web.Core { endpoints.MapHub("/hubs/chathub"); - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); + endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); // 设置雪花Id的workerId,确保每个实例workerId都应不同 var workerId = ushort.Parse(App.Configuration["SnowId:WorkerId"] ?? "1"); - YitIdHelper.SetIdGenerator(new IdGeneratorOptions { WorkerId = workerId }); + YitIdHelper.SetIdGenerator(new IdGeneratorOptions {WorkerId = workerId}); // 开启自启动定时任务 App.GetService().StartTimerJob(); diff --git a/backend/Furion.Extras.Admin.NET/EventHandler/LogEventHandler.cs b/backend/Furion.Extras.Admin.NET/EventHandler/LogEventHandler.cs deleted file mode 100644 index fcf457cdb9f2f71f22e1f251d1a7da8f00554dab..0000000000000000000000000000000000000000 --- a/backend/Furion.Extras.Admin.NET/EventHandler/LogEventHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Furion; -using Furion.DatabaseAccessor; -using Furion.DependencyInjection; -using Furion.EventBridge; - -namespace Furion.Extras.Admin.NET -{ - /// - /// 日志订阅处理 - /// - [EventHandler] - public class LogEventHandler : IEventHandler - { - private readonly IRepository _sysLogOpRep; - private readonly IRepository _sysLogExRep; - private readonly IRepository _sysLogVisRep; - public LogEventHandler(IRepository sysLogVisRep,IRepository sysLogOpRep,IRepository sysLogExRep) - { - _sysLogVisRep = sysLogVisRep; - _sysLogOpRep=sysLogOpRep; - _sysLogExRep=sysLogExRep; - } - - [EventMessage] - public void CreateOpLog(EventMessage eventMessage) - { - SysLogOp log = (SysLogOp)eventMessage.Payload; - _sysLogOpRep.InsertNow(log); - } - - [EventMessage] - public void CreateExLog(EventMessage eventMessage) - { - SysLogEx log = (SysLogEx)eventMessage.Payload; - _sysLogExRep.InsertNow(log); - } - - [EventMessage] - public void CreateVisLog(EventMessage eventMessage) - { - SysLogVis log = (SysLogVis)eventMessage.Payload; - _sysLogVisRep.InsertNow(log); - - } - } -} \ No newline at end of file diff --git a/backend/Furion.Extras.Admin.NET/EventSubscriber/LogEventSubscriber.cs b/backend/Furion.Extras.Admin.NET/EventSubscriber/LogEventSubscriber.cs new file mode 100644 index 0000000000000000000000000000000000000000..48e8d12944efe1f93fb341ee7ab8fc85691abe00 --- /dev/null +++ b/backend/Furion.Extras.Admin.NET/EventSubscriber/LogEventSubscriber.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading.Tasks; +using Furion.DatabaseAccessor; +using Furion.EventBus; +using Microsoft.Extensions.DependencyInjection; + +namespace Furion.Extras.Admin.NET +{ + public class LogEventSubscriber : IEventSubscriber + { + public IServiceProvider Services { get; } + + public LogEventSubscriber(IServiceProvider services) + { + Services = services; + } + + [EventSubscribe("Create:OpLog")] + public async Task CreateOpLog(EventHandlerExecutingContext context) + { + using var scope = Services.CreateScope(); + var _repository = scope.ServiceProvider.GetRequiredService>(); + var log = (SysLogOp)context.Source.Payload; + await _repository.InsertAsync(log); + } + + [EventSubscribe("Create:ExLog")] + public async Task CreateExLog(EventHandlerExecutingContext context) + { + using var scope = Services.CreateScope(); + var _repository = scope.ServiceProvider.GetRequiredService>(); + var log = (SysLogEx)context.Source.Payload; + await _repository.InsertAsync(log); + } + + [EventSubscribe("Create:VisLog")] + public async Task CreateVisLog(EventHandlerExecutingContext context) + { + using var scope = Services.CreateScope(); + var _repository = scope.ServiceProvider.GetRequiredService>(); + var log = (SysLogVis)context.Source.Payload; + await _repository.InsertAsync(log); + } + } +} \ No newline at end of file diff --git a/backend/Furion.Extras.Admin.NET/Filter/LogExceptionHandler.cs b/backend/Furion.Extras.Admin.NET/Filter/LogExceptionHandler.cs index 94146a15f8a494e3fbfd5fe13031ec0ce5b69c09..8a7e5f7250b23e7951d314f6ad55650d98344efd 100644 --- a/backend/Furion.Extras.Admin.NET/Filter/LogExceptionHandler.cs +++ b/backend/Furion.Extras.Admin.NET/Filter/LogExceptionHandler.cs @@ -1,11 +1,11 @@ -using Furion.DependencyInjection; +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Furion.DependencyInjection; +using Furion.EventBus; using Furion.FriendlyException; -using Furion.EventBridge; using Microsoft.AspNetCore.Mvc.Filters; using Serilog; -using System; -using System.Security.Claims; -using System.Threading.Tasks; namespace Furion.Extras.Admin.NET { @@ -14,27 +14,33 @@ namespace Furion.Extras.Admin.NET /// public class LogExceptionHandler : IGlobalExceptionHandler, ISingleton { - public Task OnExceptionAsync(ExceptionContext context) + private readonly IEventPublisher _eventPublisher; + + public LogExceptionHandler(IEventPublisher eventPublisher) + { + _eventPublisher = eventPublisher; + } + + public async Task OnExceptionAsync(ExceptionContext context) { var userContext = App.User; - Event.Emit("Log:CreateExLog", new SysLogEx - { - Account = userContext?.FindFirstValue(ClaimConst.CLAINM_ACCOUNT), - Name = userContext?.FindFirstValue(ClaimConst.CLAINM_NAME), - ClassName = context.Exception.TargetSite.DeclaringType?.FullName, - MethodName = context.Exception.TargetSite.Name, - ExceptionName = context.Exception.Message, - ExceptionMsg = context.Exception.Message, - ExceptionSource = context.Exception.Source, - StackTrace = context.Exception.StackTrace, - ParamsObj = context.Exception.TargetSite.GetParameters().ToString(), - ExceptionTime = DateTimeOffset.Now - }); + await _eventPublisher.PublishAsync(new ChannelEventSource("Create:ExLog", + new SysLogEx + { + Account = userContext?.FindFirstValue(ClaimConst.CLAINM_ACCOUNT), + Name = userContext?.FindFirstValue(ClaimConst.CLAINM_NAME), + ClassName = context.Exception.TargetSite.DeclaringType?.FullName, + MethodName = context.Exception.TargetSite.Name, + ExceptionName = context.Exception.Message, + ExceptionMsg = context.Exception.Message, + ExceptionSource = context.Exception.Source, + StackTrace = context.Exception.StackTrace, + ParamsObj = context.Exception.TargetSite.GetParameters().ToString(), + ExceptionTime = DateTimeOffset.Now + })); // 写日志文件 Log.Error(context.Exception.ToString()); - - return Task.CompletedTask; } } } \ No newline at end of file diff --git a/backend/Furion.Extras.Admin.NET/Filter/RequestActionFilter.cs b/backend/Furion.Extras.Admin.NET/Filter/RequestActionFilter.cs index a9fc6dd6f1bc8f6fbba51725be4b6438acf11193..4611c848612fd116480e7cbc93a641dc9aff3d49 100644 --- a/backend/Furion.Extras.Admin.NET/Filter/RequestActionFilter.cs +++ b/backend/Furion.Extras.Admin.NET/Filter/RequestActionFilter.cs @@ -1,13 +1,13 @@ -using Furion.EventBridge; +using System; +using System.Diagnostics; +using System.Security.Claims; +using System.Threading.Tasks; +using Furion.EventBus; using Furion.JsonSerialization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; -using System; -using System.Diagnostics; -using System.Security.Claims; -using System.Threading.Tasks; using UAParser; namespace Furion.Extras.Admin.NET @@ -17,6 +17,13 @@ namespace Furion.Extras.Admin.NET /// public class RequestActionFilter : IAsyncActionFilter { + private readonly IEventPublisher _eventPublisher; + + public RequestActionFilter(IEventPublisher eventPublisher) + { + _eventPublisher = eventPublisher; + } + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var httpContext = context.HttpContext; @@ -30,29 +37,29 @@ namespace Furion.Extras.Admin.NET // 判断是否请求成功(没有异常就是请求成功) var isRequestSucceed = actionContext.Exception == null; var headers = httpRequest.Headers; - var clientInfo = headers.ContainsKey("User-Agent") - ? Parser.GetDefault().Parse(headers["User-Agent"]) - : null; + var clientInfo = headers.ContainsKey("User-Agent") ? Parser.GetDefault().Parse(headers["User-Agent"]) : null; var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; - await Event.EmitAsync("Log:CreateOpLog", new SysLogOp - { - Name = httpContext.User?.FindFirstValue(ClaimConst.CLAINM_NAME), - Success = isRequestSucceed ? YesOrNot.Y : YesOrNot.N, - Ip = httpContext.GetRemoteIpAddressToIPv4(), - Location = httpRequest.GetRequestUrlAddress(), - Browser = clientInfo?.UA.Family + clientInfo?.UA.Major, - Os = clientInfo?.OS.Family + clientInfo?.OS.Major, - Url = httpRequest.Path, - ClassName = context.Controller.ToString(), - MethodName = actionDescriptor?.ActionName, - ReqMethod = httpRequest.Method, - Param = JSON.Serialize(context.ActionArguments.Count < 1 ? "" : context.ActionArguments), - Result = actionContext.Result?.GetType() == typeof(JsonResult) ? JSON.Serialize(actionContext.Result) : "", - ElapsedTime = sw.ElapsedMilliseconds, - OpTime = DateTimeOffset.Now, - Account = httpContext.User?.FindFirstValue(ClaimConst.CLAINM_ACCOUNT) - }); + await _eventPublisher.PublishAsync(new ChannelEventSource("Create:OpLog", + new SysLogOp + { + Name = httpContext.User?.FindFirstValue(ClaimConst.CLAINM_NAME), + Success = isRequestSucceed ? YesOrNot.Y : YesOrNot.N, + Ip = httpContext.GetRemoteIpAddressToIPv4(), + Location = httpRequest.GetRequestUrlAddress(), + Browser = clientInfo?.UA.Family + clientInfo?.UA.Major, + Os = clientInfo?.OS.Family + clientInfo?.OS.Major, + Url = httpRequest.Path, + ClassName = context.Controller.ToString(), + MethodName = actionDescriptor?.ActionName, + ReqMethod = httpRequest.Method, + Param = JSON.Serialize(context.ActionArguments.Count < 1 ? "" : context.ActionArguments), + Result = + actionContext.Result?.GetType() == typeof(JsonResult) ? JSON.Serialize(actionContext.Result) : "", + ElapsedTime = sw.ElapsedMilliseconds, + OpTime = DateTimeOffset.Now, + Account = httpContext.User?.FindFirstValue(ClaimConst.CLAINM_ACCOUNT) + })); } } } \ No newline at end of file diff --git a/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.csproj b/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.csproj index 8b91e1a16fcc74186e7b35c74e32ceeccaec45aa..c629b997978a0cfb3e9f3c8ceacd8d3cf24947f5 100644 --- a/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.csproj +++ b/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.csproj @@ -45,10 +45,10 @@ - - - - + + + + diff --git a/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.xml b/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.xml index dbc5539660bd9c3585ba13d80d4a3ba009f19335..3747cb8ed6ed351e02cc95ea25dff9c5389b522e 100644 --- a/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.xml +++ b/backend/Furion.Extras.Admin.NET/Furion.Extras.Admin.NET.xml @@ -2851,11 +2851,6 @@ 否 - - - 日志订阅处理 - - 字典扩展 @@ -9518,6 +9513,12 @@ 顶级节点的父节点Id(默认0) + + + 设置根节点方法 + 查询数据可以设置其他节点为根节点,避免父节点永远是0,查询不到数据的问题 + + 构造树节点 diff --git a/backend/Furion.Extras.Admin.NET/Service/Auth/AuthService.cs b/backend/Furion.Extras.Admin.NET/Service/Auth/AuthService.cs index e1f32974d2a7d48ff53bce3a332fa1d9cc8c8f14..1b7387cf78e98418e5cf7ff56ef399ff0216a805 100644 --- a/backend/Furion.Extras.Admin.NET/Service/Auth/AuthService.cs +++ b/backend/Furion.Extras.Admin.NET/Service/Auth/AuthService.cs @@ -1,19 +1,19 @@ -using Furion.DatabaseAccessor; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Furion.DatabaseAccessor; using Furion.DataEncryption; using Furion.DependencyInjection; using Furion.DynamicApiController; -using Furion.EventBridge; +using Furion.EventBus; using Furion.Extras.Admin.NET.Options; using Furion.FriendlyException; using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; using UAParser; namespace Furion.Extras.Admin.NET.Service @@ -26,27 +26,22 @@ namespace Furion.Extras.Admin.NET.Service { private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IRepository _sysUserRep; // 用户表仓储 + private readonly IRepository _sysUserRep; // 用户表仓储 private readonly IUserManager _userManager; // 用户管理 private readonly ISysUserService _sysUserService; // 系统用户服务 - private readonly ISysEmpService _sysEmpService; // 系统员工服务 + private readonly ISysEmpService _sysEmpService; // 系统员工服务 private readonly ISysRoleService _sysRoleService; // 系统角色服务 private readonly ISysMenuService _sysMenuService; // 系统菜单服务 - private readonly ISysAppService _sysAppService; // 系统应用服务 - private readonly IClickWordCaptcha _captchaHandle;// 验证码服务 + private readonly ISysAppService _sysAppService; // 系统应用服务 + private readonly IClickWordCaptcha _captchaHandle; // 验证码服务 private readonly ISysConfigService _sysConfigService; // 验证码服务 + private readonly IEventPublisher _eventPublisher; - public AuthService(IRepository sysUserRep, - IHttpContextAccessor httpContextAccessor, - IUserManager userManager, - ISysUserService sysUserService, - ISysEmpService sysEmpService, - ISysRoleService sysRoleService, - ISysMenuService sysMenuService, - ISysAppService sysAppService, - IClickWordCaptcha captchaHandle, - ISysConfigService sysConfigService) + public AuthService(IRepository sysUserRep, IHttpContextAccessor httpContextAccessor, IUserManager userManager, + ISysUserService sysUserService, ISysEmpService sysEmpService, ISysRoleService sysRoleService, + ISysMenuService sysMenuService, ISysAppService sysAppService, IClickWordCaptcha captchaHandle, + ISysConfigService sysConfigService, IEventPublisher eventPublisher) { _sysUserRep = sysUserRep; _httpContextAccessor = httpContextAccessor; @@ -58,6 +53,7 @@ namespace Furion.Extras.Admin.NET.Service _sysAppService = sysAppService; _captchaHandle = captchaHandle; _sysConfigService = sysConfigService; + _eventPublisher = eventPublisher; } /// @@ -75,7 +71,9 @@ namespace Furion.Extras.Admin.NET.Service var encryptPasswod = MD5Encryption.Encrypt(input.Password); // 判断用户名和密码是否正确 忽略全局过滤器 - var user = _sysUserRep.Where(u => u.Account.Equals(input.Account) && u.Password.Equals(encryptPasswod) && !u.IsDeleted, false, true).FirstOrDefault(); + var user = _sysUserRep + .Where(u => u.Account.Equals(input.Account) && u.Password.Equals(encryptPasswod) && !u.IsDeleted, false, true) + .FirstOrDefault(); _ = user ?? throw Oops.Oh(ErrorCode.D1000); // 验证账号是否被冻结 @@ -86,18 +84,19 @@ namespace Furion.Extras.Admin.NET.Service //var accessToken = await _jwtBearerManager.CreateTokenAdmin(user); var accessToken = JWTEncryption.Encrypt(new Dictionary { - { ClaimConst.CLAINM_USERID, user.Id }, - { ClaimConst.TENANT_ID, user.TenantId }, - { ClaimConst.CLAINM_ACCOUNT, user.Account }, - { ClaimConst.CLAINM_NAME, user.Name }, - { ClaimConst.CLAINM_SUPERADMIN, user.AdminType }, + {ClaimConst.CLAINM_USERID, user.Id}, + {ClaimConst.TENANT_ID, user.TenantId}, + {ClaimConst.CLAINM_ACCOUNT, user.Account}, + {ClaimConst.CLAINM_NAME, user.Name}, + {ClaimConst.CLAINM_SUPERADMIN, user.AdminType}, }); // 设置Swagger自动登录 _httpContextAccessor.HttpContext.SigninToSwagger(accessToken); // 生成刷新Token令牌 - var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, App.GetOptions().ExpiredTime); + var refreshToken = + JWTEncryption.GenerateRefreshToken(accessToken, App.GetOptions().ExpiredTime); // 设置刷新Token令牌 _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken; @@ -157,20 +156,22 @@ namespace Furion.Extras.Admin.NET.Service } // 更新用户最后登录Ip和时间 - await _sysUserRep.UpdateIncludeAsync(user, new[] { nameof(SysUser.LastLoginIp), nameof(SysUser.LastLoginTime) }); - - await Event.EmitAsync("Log:CreateVisLog", new SysLogVis - { - Name = loginOutput.Name, - Success = YesOrNot.Y, - Message = "登录成功", - Ip = loginOutput.LastLoginIp, - Browser = loginOutput.LastLoginBrowser, - Os = loginOutput.LastLoginOs, - VisType = LoginType.LOGIN, - VisTime = loginOutput.LastLoginTime, - Account = loginOutput.Account - }); + await _sysUserRep.UpdateIncludeAsync(user, new[] {nameof(SysUser.LastLoginIp), nameof(SysUser.LastLoginTime)}); + + // 增加登录日志 + await _eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog", + new SysLogVis + { + Name = loginOutput.Name, + Success = YesOrNot.Y, + Message = "登录成功", + Ip = loginOutput.LastLoginIp, + Browser = loginOutput.LastLoginBrowser, + Os = loginOutput.LastLoginOs, + VisType = LoginType.LOGIN, + VisTime = loginOutput.LastLoginTime, + Account = loginOutput.Account + })); return loginOutput; } @@ -185,17 +186,17 @@ namespace Furion.Extras.Admin.NET.Service _httpContextAccessor.HttpContext.SignoutToSwagger(); //_httpContextAccessor.HttpContext.Response.Headers["access-token"] = "invalid token"; - await Event.EmitAsync("Log:CreateVisLog", new SysLogVis - { - Name = _userManager.Name, - Success = YesOrNot.Y, - Message = "退出成功", - VisType = LoginType.LOGOUT, - VisTime = DateTimeOffset.Now, - Account = _userManager.Account - }); - - await Task.CompletedTask; + // 增加退出日志 + await _eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog", + new SysLogVis + { + Name = _userManager.Name, + Success = YesOrNot.Y, + Message = "退出成功", + VisType = LoginType.LOGOUT, + VisTime = DateTimeOffset.Now, + Account = _userManager.Account + })); } ///