diff --git a/src/1.datas/ATS.NonCustodial.Domain/Entities/Log/AppLoginLog.cs b/src/1.datas/ATS.NonCustodial.Domain/Entities/Log/AppLoginLog.cs new file mode 100644 index 0000000..e492d5b --- /dev/null +++ b/src/1.datas/ATS.NonCustodial.Domain/Entities/Log/AppLoginLog.cs @@ -0,0 +1,17 @@ +using ATS.NonCustodial.Domain.Shared.AggRootEntities; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ATS.NonCustodial.Domain.Entities.Logs +{ + /// + /// 登录日志 + /// + /// Author:mxg + /// CreatedTimed:2022-05-12 18:30 PM + [Table("app_login_log")] + [Index(nameof(CreatedUserId), nameof(CreatedUserName))] + public class AppLoginLog : LogAbstract + { + } +} \ No newline at end of file diff --git a/src/1.datas/ATS.NonCustodial.Domain/Entities/Log/AppOperationLog.cs b/src/1.datas/ATS.NonCustodial.Domain/Entities/Log/AppOperationLog.cs new file mode 100644 index 0000000..dd9786a --- /dev/null +++ b/src/1.datas/ATS.NonCustodial.Domain/Entities/Log/AppOperationLog.cs @@ -0,0 +1,42 @@ +using ATS.NonCustodial.Domain.Shared.AggRootEntities; +using ATS.NonCustodial.Domain.Shared.Constants; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ATS.NonCustodial.Domain.Entities.Logs +{ + /// + /// 操作日志 + /// + /// Author:mxg + /// CreatedTimed:2022-05-12 18:30 PM + [Table("app_operation_log")] + [Index(nameof(CreatedUserId), nameof(CreatedUserName))] + public class AppOperationLog : LogAbstract + { + /// + /// 接口名称 + /// + [MaxLength(StringLengthConstants.StringLength255)] + public string? ApiLabel { get; set; } + + /// + /// 接口地址 + /// + [MaxLength(StringLengthConstants.StringLength128)] + public string? ApiPath { get; set; } + + /// + /// 接口提交方法 + /// + [MaxLength(StringLengthConstants.StringLength10)] + public string? ApiMethod { get; set; } + + /// + /// 操作参数 + /// + [MaxLength(StringLengthConstants.StringLength2048)] + public string? Params { get; set; } + } +} \ No newline at end of file diff --git a/src/2.services/ATS.NonCustodial.Application/Impl/Logs/AuditLogService.cs b/src/2.services/ATS.NonCustodial.Application/Impl/Logs/AuditLogService.cs new file mode 100644 index 0000000..ef9d6b7 --- /dev/null +++ b/src/2.services/ATS.NonCustodial.Application/Impl/Logs/AuditLogService.cs @@ -0,0 +1,55 @@ +using ATS.NonCustodial.Application.Base; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.AuditLog; +using ATS.NonCustodial.AuditLogging.AuditLoggings.Services; +using ATS.NonCustodial.AuditLogging.Dtos.Input; +using ATS.NonCustodial.AuditLogging.Dtos.Output; +using ATS.NonCustodial.AuditLogging.EntityFrameworkCore.Entities; +using ATS.NonCustodial.AuditLogging.Mappers; +using ATS.NonCustodial.DynamicApi; +using ATS.NonCustodial.DynamicApi.Attributes; +using Microsoft.AspNetCore.Mvc; + +namespace ATS.NonCustodial.Application.Impl.Logs +{ + /// + /// 审计日志服务 + /// + /// Author:mxg + /// CreatedTimed:2022-04-25 16:31 + [DynamicApi(Area = "admin")] + public class AuditLogService : AdminAppServiceBase, IAuditLogService, IDynamicApi + { + #region Identity + + protected readonly IAuditLogRepository AuditLogRepository; + + public AuditLogService(IAuditLogRepository auditLogRepository) + { + AuditLogRepository = auditLogRepository; + } + + #endregion Identity + + /// + /// 查询 + /// + /// + /// + [HttpPost] + public async Task GetAsync(AuditLogFilterDto filters) + { + var pagedList = await AuditLogRepository.GetAsync(filters.Event, filters.Source, filters.Category, filters.Created, filters.SubjectIdentifier, filters.SubjectName, filters.Page, filters.PageSize); + var auditLogsDto = pagedList.ToAuditLogModel(); + + return auditLogsDto; + } + + /// + /// 删除 + /// + /// + /// + [HttpDelete] + public virtual async Task DeleteLogsOlderThanAsync(DateTime deleteOlderThan) => await AuditLogRepository.DeleteLogsOlderThanAsync(deleteOlderThan); + } +} \ No newline at end of file diff --git a/src/2.services/ATS.NonCustodial.Application/Impl/Logs/LoginLogService.cs b/src/2.services/ATS.NonCustodial.Application/Impl/Logs/LoginLogService.cs new file mode 100644 index 0000000..75edc96 --- /dev/null +++ b/src/2.services/ATS.NonCustodial.Application/Impl/Logs/LoginLogService.cs @@ -0,0 +1,102 @@ +using ATS.NonCustodial.Application.Base; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Output; +using ATS.NonCustodial.Domain.Entities.Logs; +using ATS.NonCustodial.Domain.Shared.OrmRepositories.Basic.EfCore; +using ATS.NonCustodial.DynamicApi; +using ATS.NonCustodial.DynamicApi.Attributes; +using ATS.NonCustodial.Shared.Common.UnifiedResults; +using ATS.NonCustodial.Shared.Extensions; +using ATS.NonCustodial.Shared.Helpers; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace ATS.NonCustodial.Application.Impl.Logs +{ + /// + /// ¼־ + /// + /// Author:mxg + /// CreatedTimed:2022-05-17 10:15 PM + [DynamicApi(Area = "admin")] + public class LoginLogService : AdminAppServiceBase, ILoginLogService, IDynamicApi + { + #region Identity + + private readonly IHttpContextAccessor _context; + private readonly IEfRepository _loginLogRepository; + + public LoginLogService( + IHttpContextAccessor context, + IEfRepository loginLogRepository + ) + { + _context = context; + _loginLogRepository = loginLogRepository; + } + + #endregion Identity + + /// + /// ѯ¼־б + /// + /// + /// + [HttpPost] + public async Task GetPageAsync(LogGetPageDto input) + { + var express = GetExpression(input, _loginLogRepository.AsQueryable(false, true)); + var rtn = await base.GetPageAsync(input, express); + return ResultOutput.Ok(rtn); + } + + /// + /// + /// + /// + /// + public async Task> AddAsync(LoginLogAddInput input) + { + var res = new ResultOutput(); + + input.IP = IPHelper.GetIP(_context?.HttpContext?.Request); + + string ua = _context.HttpContext.Request.Headers["User-Agent"]; + if (ua.NotNull()) + { + var client = UAParser.Parser.GetDefault().Parse(ua); + var device = client.Device.Family; + device = device.ToLower() == "other" ? "" : device; + input.Browser = client.UA.Family; + input.Os = client.OS.Family; + input.Device = device; + input.BrowserInfo = ua; + } + var entity = Mapper.Map(input); + var id = (await _loginLogRepository.InsertAsync(entity)).Id; + + return id > 0 ? res.Ok(id) : res; + } + + #region Private + + /// + /// ѯ + /// + /// + /// + /// + private IQueryable GetExpression(LogGetPageDto pageInput, IQueryable query) + { + query = query + .WhereIf(pageInput.OperatorName.NotNull(), w => pageInput.OperatorName.Contains(w.CreatedUserName)); + + var express = base.GetEntityAddExpression(pageInput, query); + + return express; + } + + #endregion Private + } +} \ No newline at end of file diff --git a/src/2.services/ATS.NonCustodial.Application/Impl/Logs/OperationLogService.cs b/src/2.services/ATS.NonCustodial.Application/Impl/Logs/OperationLogService.cs new file mode 100644 index 0000000..17e8b14 --- /dev/null +++ b/src/2.services/ATS.NonCustodial.Application/Impl/Logs/OperationLogService.cs @@ -0,0 +1,153 @@ +using ATS.NonCustodial.Application.Base; +using ATS.NonCustodial.Application.Contracts.Interfaces.Business.AppCaseManagements.AppCaseManagement; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Input; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Output; +using ATS.NonCustodial.Domain.Entities.Logs; +using ATS.NonCustodial.Domain.Shared.AggRootEntities.Dtos; +using ATS.NonCustodial.Domain.Shared.OrmRepositories.Basic.EfCore; +using ATS.NonCustodial.DynamicApi; +using ATS.NonCustodial.DynamicApi.Attributes; +using ATS.NonCustodial.Shared.Common.UnifiedResults; +using ATS.NonCustodial.Shared.Extensions; +using AutoMapper.QueryableExtensions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace ATS.NonCustodial.Application.Impl.Logs +{ + /// + /// ־ + /// + /// Author:mxg + /// CreatedTimed:2022-05-17 10:11 PM + [DynamicApi(Area = "admin")] + public class OperationLogService : AdminAppServiceBase, IOperationLogService, IDynamicApi + { + #region Identity + + private readonly IHttpContextAccessor _context; + private readonly IEfRepository _oprationLogRepository; + private readonly IAppCaseManagementService _appCaseManagementService; + + public OperationLogService( + IHttpContextAccessor context, + IEfRepository oprationLogRepository, + IAppCaseManagementService appCaseManagementService + ) + { + _context = context; + _oprationLogRepository = oprationLogRepository; + _appCaseManagementService = appCaseManagementService; + } + + #endregion Identity + + /// + /// ѯ־б + /// + /// + /// + [HttpPost] + public async Task GetPageAsync(LogGetPageDto input) + { + var express = await GetExpression(input, _oprationLogRepository.AsQueryable(false, true).Take(3000)); + var rtn = await base.GetPageAsync(input, express); + + return ResultOutput.Ok(rtn); + } + + /// + /// ѯ־ϸ + /// + /// + /// + public async Task Get(long id) + { + var rtn = await base.GetAsync(_oprationLogRepository, id); + return ResultOutput.Ok(rtn); + } + + /// + /// + /// + /// + /// + public async Task AddAsync(OprationLogAddInput input) + { + string ua = _context.HttpContext!.Request.Headers["User-Agent"]; + var client = UAParser.Parser.GetDefault().Parse(ua); + var device = client.Device.Family; + device = device.ToLower() == "other" ? "" : device; + input.Browser = client.UA.Family; + input.Os = client.OS.Family; + input.Device = device; + input.BrowserInfo = ua; + + input.NickName = User.NickName; + input.IpAddress = _context.HttpContext.Connection.RemoteIpAddress?.ToString().Replace("::ffff:", "")/*IPHelper.GetIP(_context?.HttpContext?.Request)*/; + + var entity = Mapper.Map(input); + var id = ((await _oprationLogRepository.InsertAsync(entity))!).Id; + + return ResultOutput.Result(id > 0); + } + + /// + /// ҵ̨==>5־ + /// + /// + public async Task OperationBusinessWorkbench() + { + var userIds = await _appCaseManagementService.GetUserIdListByCurrentUser(); + + var dataList = await _oprationLogRepository.AsQueryable(false, true) + .Where(w => w.CreatedUserId != null && userIds.Contains(w.CreatedUserId.Value)) + .OrderByDescending(w => w.CreatedTime) + .Skip(0) + .Take(5) + .ProjectTo(Mapper.ConfigurationProvider) + .ToListAsync(); + return ResultOutput.Ok(dataList); + } + + /// + /// ɾ־ + /// + /// + /// + public async Task BatchDeleteAsync(BatchIdsInput input) + { + var rtn = await _oprationLogRepository.DeleteAsync(w => input.Ids.Contains(w.Id)); + + return ResultOutput.Ok(rtn > 0); + } + + #region Private + + /// + /// ѯ + /// + /// + /// + /// + private async Task> GetExpression(LogGetPageDto pageInput, IQueryable query) + { + var userIds = await _appCaseManagementService.GetUserIdListByCurrentUser(); + + query = query + .Where(w => w.CreatedUserId != null && userIds.Contains(w.CreatedUserId.Value)) + .WhereIf(pageInput.OperatorName.NotNull(), w => w.CreatedUserName.Contains(pageInput.OperatorName)) + .WhereIf(pageInput.Device.NotNull(), w => w.Device==pageInput.Device) + .WhereIf(pageInput.NickName.NotNull(), w => w.NickName==pageInput.NickName); + + var express = base.GetEntityAddExpression(pageInput, query); + + return express; + } + + #endregion Private + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/AuditLog/IAuditLogService.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/AuditLog/IAuditLogService.cs new file mode 100644 index 0000000..00df510 --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/AuditLog/IAuditLogService.cs @@ -0,0 +1,17 @@ +using ATS.NonCustodial.AuditLogging.Dtos.Input; +using ATS.NonCustodial.AuditLogging.Dtos.Output; + +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.AuditLog +{ + /// + /// + /// + /// Author:mxg + /// CreatedTimed:2022-04-25 16:23 + public interface IAuditLogService + { + Task GetAsync(AuditLogFilterDto filters); + + Task DeleteLogsOlderThanAsync(DateTime deleteOlderThan); + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/ILoginLogService.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/ILoginLogService.cs new file mode 100644 index 0000000..fe2d3ae --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/ILoginLogService.cs @@ -0,0 +1,27 @@ +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input; +using ATS.NonCustodial.Shared.Common.UnifiedResults; + +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog +{ + /// + /// ¼־ӿ + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public interface ILoginLogService + { + /// + /// + /// + /// + /// + Task GetPageAsync(LogGetPageDto input); + + /// + /// + /// + /// + /// + Task> AddAsync(LoginLogAddInput input); + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Input/LogGetPageDto.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Input/LogGetPageDto.cs new file mode 100644 index 0000000..e3eb6a6 --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Input/LogGetPageDto.cs @@ -0,0 +1,25 @@ +using ATS.NonCustodial.Shared.Common.Dtos; + +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input +{ + /// + /// + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public class LogGetPageDto : PageRequestBaseInput + { + /// + /// 操作者名称 + /// + public string? OperatorName { get; set; } + /// + /// 手机型号 + /// + public string? Device { get; set; } + /// + /// app + /// + public string? NickName { get; set; } + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Input/LoginLogAddInput.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Input/LoginLogAddInput.cs new file mode 100644 index 0000000..a97bcae --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Input/LoginLogAddInput.cs @@ -0,0 +1,75 @@ +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input +{ + /// + /// 添加 + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public class LoginLogAddInput + { + /// + /// 租户Id + /// + public long? TenantId { get; set; } + + /// + /// 昵称 + /// + public string? NickName { get; set; } + + /// + /// IP + /// + public string? IP { get; set; } + + /// + /// 浏览器 + /// + public string? Browser { get; set; } + + /// + /// 操作系统 + /// + public string? Os { get; set; } + + /// + /// 设备 + /// + public string? Device { get; set; } + + /// + /// 浏览器信息 + /// + public string? BrowserInfo { get; set; } + + /// + /// 耗时(毫秒) + /// + public long ElapsedMilliseconds { get; set; } + + /// + /// 操作状态 + /// + public bool? Status { get; set; } + + /// + /// 操作消息 + /// + public string? Msg { get; set; } + + /// + /// 操作结果 + /// + public string? Result { get; set; } + + /// + /// 创建者Id + /// + public long? CreatedUserId { get; set; } + + /// + /// 创建者 + /// + public string? CreatedUserName { get; set; } + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Output/LoginLogListOutput.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Output/LoginLogListOutput.cs new file mode 100644 index 0000000..53b28e5 --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/LoginLog/Output/LoginLogListOutput.cs @@ -0,0 +1,47 @@ +using ATS.NonCustodial.Domain.Shared.AggRootEntities; + +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Output +{ + /// + /// 登录日志输出Dto + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public class LoginLogListOutput : EntityFull + { + /// + /// 昵称 + /// + public string? NickName { get; set; } + + /// + /// IP + /// + public string? IP { get; set; } + + /// + /// 浏览器 + /// + public string? Browser { get; set; } + + /// + /// 操作系统 + /// + public string? Os { get; set; } + + /// + /// 设备 + /// + public string? Device { get; set; } + + /// + /// 耗时(毫秒) + /// + public long ElapsedMilliseconds { get; set; } + + /// + /// 操作消息 + /// + public string? Msg { get; set; } + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/IOperationLogService.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/IOperationLogService.cs new file mode 100644 index 0000000..b184156 --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/IOperationLogService.cs @@ -0,0 +1,34 @@ +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Input; +using ATS.NonCustodial.Shared.Common.UnifiedResults; + +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog +{ + /// + /// ־ӿ + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public interface IOperationLogService + { + /// + /// + /// + /// + /// + Task GetPageAsync(LogGetPageDto input); + + /// + /// + /// + /// + /// + Task AddAsync(OprationLogAddInput input); + + /// + /// ҵ̨==>5־ + /// + /// + Task OperationBusinessWorkbench(); + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/Input/OprationLogAddInput.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/Input/OprationLogAddInput.cs new file mode 100644 index 0000000..5957205 --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/Input/OprationLogAddInput.cs @@ -0,0 +1,85 @@ +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Input +{ + /// + /// 添加 + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public class OprationLogAddInput + { + /// + /// 昵称 + /// + public string? NickName { get; set; } + + /// + /// 接口名称 + /// + public string? ApiLabel { get; set; } + + /// + /// 接口地址 + /// + public string? ApiPath { get; set; } + + /// + /// 接口提交方法 + /// + public string? ApiMethod { get; set; } + + /// + /// IP + /// + public string? IpAddress { get; set; } + + /// + /// 浏览器 + /// + public string? Browser { get; set; } + + /// + /// 操作系统 + /// + public string? Os { get; set; } + + /// + /// 设备 + /// + public string? Device { get; set; } + + /// + /// 浏览器信息 + /// + public string? BrowserInfo { get; set; } + + /// + /// 耗时(毫秒) + /// + public long ElapsedMilliseconds { get; set; } + + /// + /// 操作状态 + /// + public bool? Status { get; set; } + + /// + /// 操作消息 + /// + public string? Msg { get; set; } + + /// + /// 操作参数 + /// + public string? Params { get; set; } + + /// + /// 操作结果 + /// + public string? Result { get; set; } + + /// + /// 异常信息 + /// + public string? Exception { get; set; } + } +} \ No newline at end of file diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/Output/OprationLogListOutput.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/Output/OprationLogListOutput.cs new file mode 100644 index 0000000..39a80fb --- /dev/null +++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Logs/OprationLog/Output/OprationLogListOutput.cs @@ -0,0 +1,77 @@ +using ATS.NonCustodial.Domain.Shared.AggRootEntities; + +namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Output +{ + /// + /// OprationLogListOutput + /// + /// Author:mxg + /// CreatedTimed:2022-05-14 09:32 PM + public class OprationLogListOutput : EntityFull + { + /// + /// 昵称 + /// + public string? NickName { get; set; } + + /// + /// 接口名称 + /// + public string? ApiLabel { get; set; } + + /// + /// 接口地址 + /// + public string? ApiPath { get; set; } + + /// + /// 接口提交方法 + /// + public string? ApiMethod { get; set; } + + /// + /// IpAddress + /// + public string? IpAddress { get; set; } + + /// + /// 浏览器 + /// + public string? Browser { get; set; } + + /// + /// 操作系统 + /// + public string? Os { get; set; } + + /// + /// 设备 + /// + public string? Device { get; set; } + + /// + /// 耗时(毫秒) + /// + public long ElapsedMilliseconds { get; set; } + + /// + /// 操作消息 + /// + public string? Msg { get; set; } + + /// + /// 操作参数 + /// + public string? Params { get; set; } + + /// + /// 操作结果 + /// + public string? Result { get; set; } + + /// + /// 异常信息 + /// + public string? Exception { get; set; } + } +} \ No newline at end of file diff --git a/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/ApiHelper.cs b/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/ApiHelper.cs new file mode 100644 index 0000000..4ecfe89 --- /dev/null +++ b/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/ApiHelper.cs @@ -0,0 +1,85 @@ +using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Api; +using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Api.Output; +using ATS.NonCustodial.Shared.Common.Attributes; +using ATS.NonCustodial.Shared.Common.UnifiedResults; +using ATS.NonCustodial.Shared.Extensions; + +namespace ATS.NonCustodial.AdminUi.Helpers.Logs +{ + /// + /// Api帮助类 + /// + /// Author:mxg + /// CreatedTimed:2022-05-18 09:46 AM + [SingleInstance] + public class ApiHelper + { + #region Identity + + private List _apis; + private static readonly object LockObject = new(); + private readonly IApiService _apiService; + + public ApiHelper(IApiService apiService) + { + _apiService = apiService; + } + + #endregion Identity + + /// + /// 主要是在Action 操作的时候添加操作日志时候用 + /// + /// + public List GetApis() + { + if (_apis != null && _apis.Any()) return _apis; + + lock (LockObject) + { + if (_apis != null && _apis.Any()) return _apis; + + _apis = new List(); + + var apis = ((ResultOutput>)_apiService.GetListAsync("").Result) + .Data + .Select(a => new + { + a.Id, + a.ParentId, + a.Label, + a.Path + }); + + foreach (var api in apis) + { + var parentLabel = apis.FirstOrDefault(a => a.Id == api.ParentId)?.Label; + + _apis.Add(new ApiHelperDto + { + Label = parentLabel.NotNull() ? $"{parentLabel} / {api.Label}" : api.Label, + Path = api.Path?.ToLower().Trim('/') + }); + } + + return _apis; + } + } + } + + /// + /// + /// + public class ApiHelperDto + { + /// + /// 接口名称 + /// + public string Label { get; set; } + + /// + /// 接口地址 + /// + public string Path { get; set; } + } +} \ No newline at end of file diff --git a/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/ILogHandler.cs b/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/ILogHandler.cs new file mode 100644 index 0000000..3eacae8 --- /dev/null +++ b/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/ILogHandler.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Mvc.Filters; + +namespace ATS.NonCustodial.AdminUi.Helpers.Logs +{ + /// + /// 操作日志处理接口 + /// + /// Author:mxg + /// CreatedTimed:2022-05-18 09:46 AM + public interface ILogHandler + { + /// + /// 写操作日志 + /// + /// + /// + /// + Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next); + } +} \ No newline at end of file diff --git a/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/LogHandler.cs b/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/LogHandler.cs new file mode 100644 index 0000000..267a1e7 --- /dev/null +++ b/src/5.shared/ATS.NonCustodial.AdminUi/Helpers/Logs/LogHandler.cs @@ -0,0 +1,175 @@ +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog; +using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Input; +using ATS.NonCustodial.AuditLogging.AuditLoggings.Services; +using ATS.NonCustodial.Shared.Common.UnifiedResults; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System.Diagnostics; +using System.Reflection; + +namespace ATS.NonCustodial.AdminUi.Helpers.Logs +{ + /// + /// 控制器操作日志记录 ==> 操作日志处理 + /// + /// Author:mxg + /// CreatedTimed:2022-05-18 09:46 AM + public class LogHandler : ILogHandler + { + #region Identity + + private readonly ILogger _logger; + private readonly ApiHelper _apiHelper; + private readonly IOperationLogService _operationLogService; + private readonly IAuditEventLogger _auditEventLogger; + + public LogHandler( + ILogger logger, + ApiHelper apiHelper, + IOperationLogService operationLogService, + IAuditEventLogger auditEventLogger + ) + { + _logger = logger; + _apiHelper = apiHelper; + _operationLogService = operationLogService; + _auditEventLogger = auditEventLogger; + } + + #endregion Identity + + /// + /// + /// + /// + /// + /// + public async Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var sw = new Stopwatch(); + sw.Start(); + //调用接口 + var actionExecutedContext = await next(); + sw.Stop(); + + try + { + //接口Type + var controllerType = (context.ActionDescriptor as ControllerActionDescriptor)?.ControllerTypeInfo.AsType().GetSummary(); + + //方法信息 + var controllerAction = (context.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo.GetSummary(); + + var input = new OprationLogAddInput + { + ApiLabel = $"{controllerType}/{controllerAction}".Trim('/'), + ApiMethod = context.HttpContext.Request.Method.ToLower(), + ApiPath = context.ActionDescriptor.AttributeRouteInfo?.Template?.ToLower(), + ElapsedMilliseconds = sw.ElapsedMilliseconds, + Params = JsonConvert.SerializeObject(context.ActionArguments) + }; + + if (actionExecutedContext.Result is ObjectResult { Value: IResultOutput res }) + { + input.Status = res.Success; + input.Msg = res.Msg; + } + + #region 设置参数 + + //switch (context.HttpContext.Request.Method) + //{ + // case "GET": + // case "DELETE": + // input.Params = context.HttpContext.Request.Path; + // break; + // case "PUT": + // case "POST": + // context.HttpContext.Request.EnableBuffering(); + // context.HttpContext.Request.Body.Position = 0; + // StreamReader reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8); + // input.Params = reader.ReadToEndAsync().GetAwaiter().GetResult(); + // context.HttpContext.Request.Body.Position = 0; + // break; + // default: break; + //} + //if (input.Params is { Length: > 1024 }) + //{ + // input.Params = input.Params.Substring(0, 1021) + "..."; + //} + + #endregion 设置参数 + + #region 设置返回值 + + try + { + if (actionExecutedContext.Exception != null && !actionExecutedContext.ExceptionHandled) + { + input.Exception = actionExecutedContext.Exception.StackTrace; + } + } + catch (Exception ex) + { + input.Exception = ex.StackTrace; + throw; + } + finally + { + if (input.Exception is { Length: > 2048 }) + { + input.Exception = input.Exception.Substring(0, 2045) + "..."; + } + + if (actionExecutedContext != null) + { + input.Result = actionExecutedContext.Result switch + { + ObjectResult objectResult => JsonConvert.SerializeObject(objectResult.Value), + JsonResult jsonResult => JsonConvert.SerializeObject(jsonResult.Value), + ContentResult contentResult => contentResult.Content, + _ => input.Result + }; + if (input.Result is { Length: > 2048 }) + { + input.Result = input.Result.Substring(0, 2045) + "..."; + } + } + } + + #endregion 设置返回值 + + //接口名称 + //input.ApiLabel = _apiHelper.GetApis().FirstOrDefault(a => a.Path == input.ApiPath)?.Label; + + //添加操作日志 + await _operationLogService.AddAsync(input); + + #region 添加审计日志 + + //var apiAuditLog = new CustomizedLogEvent() + //{ + // Category = nameof(CustomizedLogEvent), + // SubjectType = AuditSubjectTypes.machine, + // SubjectName = Environment.MachineName, + // SubjectIdentifier = Environment.MachineName, + // Action = new { Method = input.ApiPath, Class = context.Controller.ToString() } + //}; + //await _auditEventLogger.LogEventAsync(apiAuditLog, options => + //{ + // options.UseDefaultSubject = false; + // options.UseDefaultAction = false; + //}); + + #endregion 添加审计日志 + } + catch (Exception ex) + { + _logger.LogError("操作日志插入异常:{@ex}", ex); + } + } + } +} \ No newline at end of file