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