16 changed files with 1036 additions and 0 deletions
@ -0,0 +1,17 @@ |
|||||||
|
using ATS.NonCustodial.Domain.Shared.AggRootEntities; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
using System.ComponentModel.DataAnnotations.Schema; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Domain.Entities.Logs |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 登录日志 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-12 18:30 PM |
||||||
|
[Table("app_login_log")] |
||||||
|
[Index(nameof(CreatedUserId), nameof(CreatedUserName))] |
||||||
|
public class AppLoginLog : LogAbstract |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 操作日志 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-12 18:30 PM |
||||||
|
[Table("app_operation_log")] |
||||||
|
[Index(nameof(CreatedUserId), nameof(CreatedUserName))] |
||||||
|
public class AppOperationLog : LogAbstract |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 接口名称 |
||||||
|
/// </summary> |
||||||
|
[MaxLength(StringLengthConstants.StringLength255)] |
||||||
|
public string? ApiLabel { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口地址 |
||||||
|
/// </summary> |
||||||
|
[MaxLength(StringLengthConstants.StringLength128)] |
||||||
|
public string? ApiPath { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口提交方法 |
||||||
|
/// </summary> |
||||||
|
[MaxLength(StringLengthConstants.StringLength10)] |
||||||
|
public string? ApiMethod { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作参数 |
||||||
|
/// </summary> |
||||||
|
[MaxLength(StringLengthConstants.StringLength2048)] |
||||||
|
public string? Params { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 审计日志服务 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-04-25 16:31 |
||||||
|
[DynamicApi(Area = "admin")] |
||||||
|
public class AuditLogService : AdminAppServiceBase, IAuditLogService, IDynamicApi |
||||||
|
{ |
||||||
|
#region Identity |
||||||
|
|
||||||
|
protected readonly IAuditLogRepository<AuditLog> AuditLogRepository; |
||||||
|
|
||||||
|
public AuditLogService(IAuditLogRepository<AuditLog> auditLogRepository) |
||||||
|
{ |
||||||
|
AuditLogRepository = auditLogRepository; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Identity |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 查询 |
||||||
|
/// </summary> |
||||||
|
/// <param name="filters"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpPost] |
||||||
|
public async Task<AuditLogsDto> 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; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 删除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="deleteOlderThan"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpDelete] |
||||||
|
public virtual async Task DeleteLogsOlderThanAsync(DateTime deleteOlderThan) => await AuditLogRepository.DeleteLogsOlderThanAsync(deleteOlderThan); |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 登录日志服务 |
||||||
|
/// </summary> |
||||||
|
/// 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<AppLoginLog?, long> _loginLogRepository; |
||||||
|
|
||||||
|
public LoginLogService( |
||||||
|
IHttpContextAccessor context, |
||||||
|
IEfRepository<AppLoginLog?, long> loginLogRepository |
||||||
|
) |
||||||
|
{ |
||||||
|
_context = context; |
||||||
|
_loginLogRepository = loginLogRepository; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Identity |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 查询登录日志列表 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpPost] |
||||||
|
public async Task<IResultOutput> GetPageAsync(LogGetPageDto input) |
||||||
|
{ |
||||||
|
var express = GetExpression(input, _loginLogRepository.AsQueryable(false, true)); |
||||||
|
var rtn = await base.GetPageAsync<AppLoginLog, LogGetPageDto, LoginLogListOutput>(input, express); |
||||||
|
return ResultOutput.Ok(rtn); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 新增 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<IResultOutput<long>> AddAsync(LoginLogAddInput input) |
||||||
|
{ |
||||||
|
var res = new ResultOutput<long>(); |
||||||
|
|
||||||
|
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<AppLoginLog>(input); |
||||||
|
var id = (await _loginLogRepository.InsertAsync(entity)).Id; |
||||||
|
|
||||||
|
return id > 0 ? res.Ok(id) : res; |
||||||
|
} |
||||||
|
|
||||||
|
#region Private |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 查询条件 |
||||||
|
/// </summary> |
||||||
|
/// <param name="pageInput"></param> |
||||||
|
/// <param name="query"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private IQueryable<AppLoginLog> GetExpression(LogGetPageDto pageInput, IQueryable<AppLoginLog?> query) |
||||||
|
{ |
||||||
|
query = query |
||||||
|
.WhereIf(pageInput.OperatorName.NotNull(), w => pageInput.OperatorName.Contains(w.CreatedUserName)); |
||||||
|
|
||||||
|
var express = base.GetEntityAddExpression<AppLoginLog, LogGetPageDto, long>(pageInput, query); |
||||||
|
|
||||||
|
return express; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Private |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 操作日志服务 |
||||||
|
/// </summary> |
||||||
|
/// 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<AppOperationLog?, long> _oprationLogRepository; |
||||||
|
private readonly IAppCaseManagementService _appCaseManagementService; |
||||||
|
|
||||||
|
public OperationLogService( |
||||||
|
IHttpContextAccessor context, |
||||||
|
IEfRepository<AppOperationLog?, long> oprationLogRepository, |
||||||
|
IAppCaseManagementService appCaseManagementService |
||||||
|
) |
||||||
|
{ |
||||||
|
_context = context; |
||||||
|
_oprationLogRepository = oprationLogRepository; |
||||||
|
_appCaseManagementService = appCaseManagementService; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Identity |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 查询操作日志列表 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpPost] |
||||||
|
public async Task<IResultOutput> GetPageAsync(LogGetPageDto input) |
||||||
|
{ |
||||||
|
var express = await GetExpression(input, _oprationLogRepository.AsQueryable(false, true).Take(3000)); |
||||||
|
var rtn = await base.GetPageAsync<AppOperationLog, LogGetPageDto, OprationLogListOutput>(input, express); |
||||||
|
|
||||||
|
return ResultOutput.Ok(rtn); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 查询操作日志明细 |
||||||
|
/// </summary> |
||||||
|
/// <param name="id"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<IResultOutput> Get(long id) |
||||||
|
{ |
||||||
|
var rtn = await base.GetAsync<AppOperationLog, OprationLogListOutput, long>(_oprationLogRepository, id); |
||||||
|
return ResultOutput.Ok(rtn); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 新增 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<IResultOutput> 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<AppOperationLog>(input); |
||||||
|
var id = ((await _oprationLogRepository.InsertAsync(entity))!).Id; |
||||||
|
|
||||||
|
return ResultOutput.Result(id > 0); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 业务工作台==>最新5条操作日志 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<IResultOutput> 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<OprationLogListOutput>(Mapper.ConfigurationProvider) |
||||||
|
.ToListAsync(); |
||||||
|
return ResultOutput.Ok(dataList); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 批量删除操作日志 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<IResultOutput> BatchDeleteAsync(BatchIdsInput input) |
||||||
|
{ |
||||||
|
var rtn = await _oprationLogRepository.DeleteAsync(w => input.Ids.Contains(w.Id)); |
||||||
|
|
||||||
|
return ResultOutput.Ok(rtn > 0); |
||||||
|
} |
||||||
|
|
||||||
|
#region Private |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 查询条件 |
||||||
|
/// </summary> |
||||||
|
/// <param name="pageInput"></param> |
||||||
|
/// <param name="query"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private async Task<IQueryable<AppOperationLog>> GetExpression(LogGetPageDto pageInput, IQueryable<AppOperationLog?> 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<AppOperationLog, LogGetPageDto, long>(pageInput, query); |
||||||
|
|
||||||
|
return express; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Private |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
using ATS.NonCustodial.AuditLogging.Dtos.Input; |
||||||
|
using ATS.NonCustodial.AuditLogging.Dtos.Output; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.AuditLog |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-04-25 16:23 |
||||||
|
public interface IAuditLogService |
||||||
|
{ |
||||||
|
Task<AuditLogsDto> GetAsync(AuditLogFilterDto filters); |
||||||
|
|
||||||
|
Task DeleteLogsOlderThanAsync(DateTime deleteOlderThan); |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// µÇ¼ÈÕÖ¾½Ó¿Ú |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public interface ILoginLogService |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<IResultOutput> GetPageAsync(LogGetPageDto input); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<IResultOutput<long>> AddAsync(LoginLogAddInput input); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
using ATS.NonCustodial.Shared.Common.Dtos; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public class LogGetPageDto : PageRequestBaseInput |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 操作者名称 |
||||||
|
/// </summary> |
||||||
|
public string? OperatorName { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// 手机型号 |
||||||
|
/// </summary> |
||||||
|
public string? Device { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// app |
||||||
|
/// </summary> |
||||||
|
public string? NickName { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,75 @@ |
|||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 添加 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public class LoginLogAddInput |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 租户Id |
||||||
|
/// </summary> |
||||||
|
public long? TenantId { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 昵称 |
||||||
|
/// </summary> |
||||||
|
public string? NickName { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// IP |
||||||
|
/// </summary> |
||||||
|
public string? IP { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 浏览器 |
||||||
|
/// </summary> |
||||||
|
public string? Browser { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作系统 |
||||||
|
/// </summary> |
||||||
|
public string? Os { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设备 |
||||||
|
/// </summary> |
||||||
|
public string? Device { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 浏览器信息 |
||||||
|
/// </summary> |
||||||
|
public string? BrowserInfo { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 耗时(毫秒) |
||||||
|
/// </summary> |
||||||
|
public long ElapsedMilliseconds { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作状态 |
||||||
|
/// </summary> |
||||||
|
public bool? Status { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作消息 |
||||||
|
/// </summary> |
||||||
|
public string? Msg { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作结果 |
||||||
|
/// </summary> |
||||||
|
public string? Result { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 创建者Id |
||||||
|
/// </summary> |
||||||
|
public long? CreatedUserId { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 创建者 |
||||||
|
/// </summary> |
||||||
|
public string? CreatedUserName { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,47 @@ |
|||||||
|
using ATS.NonCustodial.Domain.Shared.AggRootEntities; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Output |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 登录日志输出Dto |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public class LoginLogListOutput : EntityFull |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 昵称 |
||||||
|
/// </summary> |
||||||
|
public string? NickName { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// IP |
||||||
|
/// </summary> |
||||||
|
public string? IP { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 浏览器 |
||||||
|
/// </summary> |
||||||
|
public string? Browser { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作系统 |
||||||
|
/// </summary> |
||||||
|
public string? Os { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设备 |
||||||
|
/// </summary> |
||||||
|
public string? Device { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 耗时(毫秒) |
||||||
|
/// </summary> |
||||||
|
public long ElapsedMilliseconds { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作消息 |
||||||
|
/// </summary> |
||||||
|
public string? Msg { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 操作日志接口 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public interface IOperationLogService |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<IResultOutput> GetPageAsync(LogGetPageDto input); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<IResultOutput> AddAsync(OprationLogAddInput input); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 业务工作台==>最新5条操作日志 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
Task<IResultOutput> OperationBusinessWorkbench(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Input |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 添加 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public class OprationLogAddInput |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 昵称 |
||||||
|
/// </summary> |
||||||
|
public string? NickName { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口名称 |
||||||
|
/// </summary> |
||||||
|
public string? ApiLabel { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口地址 |
||||||
|
/// </summary> |
||||||
|
public string? ApiPath { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口提交方法 |
||||||
|
/// </summary> |
||||||
|
public string? ApiMethod { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// IP |
||||||
|
/// </summary> |
||||||
|
public string? IpAddress { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 浏览器 |
||||||
|
/// </summary> |
||||||
|
public string? Browser { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作系统 |
||||||
|
/// </summary> |
||||||
|
public string? Os { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设备 |
||||||
|
/// </summary> |
||||||
|
public string? Device { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 浏览器信息 |
||||||
|
/// </summary> |
||||||
|
public string? BrowserInfo { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 耗时(毫秒) |
||||||
|
/// </summary> |
||||||
|
public long ElapsedMilliseconds { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作状态 |
||||||
|
/// </summary> |
||||||
|
public bool? Status { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作消息 |
||||||
|
/// </summary> |
||||||
|
public string? Msg { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作参数 |
||||||
|
/// </summary> |
||||||
|
public string? Params { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作结果 |
||||||
|
/// </summary> |
||||||
|
public string? Result { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 异常信息 |
||||||
|
/// </summary> |
||||||
|
public string? Exception { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
using ATS.NonCustodial.Domain.Shared.AggRootEntities; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Logs.OprationLog.Output |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// OprationLogListOutput |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-14 09:32 PM |
||||||
|
public class OprationLogListOutput : EntityFull |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 昵称 |
||||||
|
/// </summary> |
||||||
|
public string? NickName { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口名称 |
||||||
|
/// </summary> |
||||||
|
public string? ApiLabel { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口地址 |
||||||
|
/// </summary> |
||||||
|
public string? ApiPath { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口提交方法 |
||||||
|
/// </summary> |
||||||
|
public string? ApiMethod { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// IpAddress |
||||||
|
/// </summary> |
||||||
|
public string? IpAddress { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 浏览器 |
||||||
|
/// </summary> |
||||||
|
public string? Browser { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作系统 |
||||||
|
/// </summary> |
||||||
|
public string? Os { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设备 |
||||||
|
/// </summary> |
||||||
|
public string? Device { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 耗时(毫秒) |
||||||
|
/// </summary> |
||||||
|
public long ElapsedMilliseconds { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作消息 |
||||||
|
/// </summary> |
||||||
|
public string? Msg { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作参数 |
||||||
|
/// </summary> |
||||||
|
public string? Params { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作结果 |
||||||
|
/// </summary> |
||||||
|
public string? Result { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 异常信息 |
||||||
|
/// </summary> |
||||||
|
public string? Exception { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Api帮助类 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-18 09:46 AM |
||||||
|
[SingleInstance] |
||||||
|
public class ApiHelper |
||||||
|
{ |
||||||
|
#region Identity |
||||||
|
|
||||||
|
private List<ApiHelperDto> _apis; |
||||||
|
private static readonly object LockObject = new(); |
||||||
|
private readonly IApiService _apiService; |
||||||
|
|
||||||
|
public ApiHelper(IApiService apiService) |
||||||
|
{ |
||||||
|
_apiService = apiService; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Identity |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 主要是在Action 操作的时候添加操作日志时候用 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public List<ApiHelperDto> GetApis() |
||||||
|
{ |
||||||
|
if (_apis != null && _apis.Any()) return _apis; |
||||||
|
|
||||||
|
lock (LockObject) |
||||||
|
{ |
||||||
|
if (_apis != null && _apis.Any()) return _apis; |
||||||
|
|
||||||
|
_apis = new List<ApiHelperDto>(); |
||||||
|
|
||||||
|
var apis = ((ResultOutput<List<ApiListOutput>>)_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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
public class ApiHelperDto |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 接口名称 |
||||||
|
/// </summary> |
||||||
|
public string Label { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 接口地址 |
||||||
|
/// </summary> |
||||||
|
public string Path { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.AdminUi.Helpers.Logs |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 操作日志处理接口 |
||||||
|
/// </summary> |
||||||
|
/// Author:mxg |
||||||
|
/// CreatedTimed:2022-05-18 09:46 AM |
||||||
|
public interface ILogHandler |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 写操作日志 |
||||||
|
/// </summary> |
||||||
|
/// <param name="context"></param> |
||||||
|
/// <param name="next"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next); |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 控制器操作日志记录 ==> 操作日志处理 |
||||||
|
/// </summary> |
||||||
|
/// 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<LogHandler> logger, |
||||||
|
ApiHelper apiHelper, |
||||||
|
IOperationLogService operationLogService, |
||||||
|
IAuditEventLogger auditEventLogger |
||||||
|
) |
||||||
|
{ |
||||||
|
_logger = logger; |
||||||
|
_apiHelper = apiHelper; |
||||||
|
_operationLogService = operationLogService; |
||||||
|
_auditEventLogger = auditEventLogger; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion Identity |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// <param name="context"></param> |
||||||
|
/// <param name="next"></param> |
||||||
|
/// <returns></returns> |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue