feat: 登录日志独立存储 (#161)

请求日志自动分表
[skip ci]

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
2024-07-26 17:46:56 +08:00
committed by GitHub
parent e1bea2ec31
commit faaf5aa0fc
74 changed files with 1501 additions and 470 deletions

View File

@ -823,11 +823,14 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
public Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{
var rsp = await PostAsync("/api/sys/cache/get.all.entries"
, JsonContent.Create(new PagedQueryReq<GetAllEntriesReq>()))
.ConfigureAwait(true);
var rsp = PostAsync("/api/sys/cache/get.all.entries", JsonContent.Create(new GetAllEntriesReq()))
.ConfigureAwait(true)
.GetAwaiter()
#pragma warning disable xUnit1031
.GetResult();
#pragma warning restore xUnit1031
Assert.Equal(HttpStatusCode.OK, rsp.StatusCode);
return default;
}
@ -1029,6 +1032,12 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
return default;
}
/// <inheritdoc />
public Task<GetEntryRsp> GetEntryAsync(GetEntriesReq req)
{
return default;
}
/// <inheritdoc />
[Fact]
public IDictionary<string, Dictionary<string, string[]>> GetEnums()

View File

@ -12,19 +12,39 @@ public abstract class RedLockerService<TEntity, TPrimary, TLogger>(
where TEntity : EntityBase<TPrimary> //
where TPrimary : IEquatable<TPrimary>
{
/// <summary>
/// 获取锁
/// </summary>
protected Task<IRedLock> GetLockerAsync(string lockName)
{
return GetLockerAsync(lockName, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_WAIT)
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_RETRY_INTERVAL));
}
/// <summary>
/// 获取锁
/// </summary>
/// <exception cref="NetAdminGetLockerException">NetAdminGetLockerException</exception>
protected async Task<IRedLock> GetLockerAsync(string lockName)
protected async Task<IRedLock> GetLockerAsync(string lockName, TimeSpan waitTime, TimeSpan retryInterval)
{
// 加锁
var redLock = await redLocker.RedLockFactory.CreateLockAsync( //
lockName, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_EXPIRY)
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_WAIT)
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_RETRY))
.ConfigureAwait(false);
var lockTask = waitTime == default || retryInterval == default
? redLocker.RedLockFactory.CreateLockAsync(lockName, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_EXPIRY))
: redLocker.RedLockFactory.CreateLockAsync( //
lockName, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_EXPIRY), waitTime, retryInterval);
var redLock = await lockTask.ConfigureAwait(false);
return redLock.IsAcquired ? redLock : throw new NetAdminGetLockerException();
}
/// <summary>
/// 获取锁
/// </summary>
/// <remarks>
/// 不重试,失败直接抛出异常
/// </remarks>
protected Task<IRedLock> GetLockerOnceAsync(string lockName)
{
return GetLockerAsync(lockName, default, default);
}
}

View File

@ -49,4 +49,14 @@ public sealed record ContextUserToken : DataAbstraction
Id = user.Id, Token = user.Token, UserName = user.UserName, DeptId = user.DeptId
};
}
/// <summary>
/// 从 Json Web Token 创建上下文用户
/// </summary>
public static ContextUserToken Create(string jwt)
{
var claim = JWTEncryption.ReadJwtToken(jwt.TrimPrefix($"{Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA} "))
?.Claims.FirstOrDefault(x => x.Type == nameof(ContextUserToken));
return claim?.Value.ToObject<ContextUserToken>();
}
}

View File

@ -61,7 +61,7 @@ public record Sys_Api : ImmutableEntity<string>, IFieldSummary
[Column]
[CsvIgnore]
[JsonIgnore]
public int PathCrc32 { get; init; }
public virtual int PathCrc32 { get; init; }
/// <summary>
/// 角色集合

View File

@ -0,0 +1,150 @@
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 登录日志表
/// </summary>
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), $"{nameof(CreatedTime)} DESC", false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(OwnerId), nameof(OwnerId), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_LoginLog))]
public record Sys_LoginLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFieldCreatedClientIp
, IFieldCreatedClientUserAgent
{
/// <inheritdoc />
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual int? CreatedClientIp { get; init; }
/// <inheritdoc />
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)]
[CsvIgnore]
[JsonIgnore]
public virtual DateTime CreatedTime { get; init; }
/// <inheritdoc />
#if DBTYPE_SQLSERVER
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)]
#else
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[CsvIgnore]
[JsonIgnore]
public virtual string CreatedUserAgent { get; init; }
/// <summary>
/// 执行耗时(毫秒)
/// </summary>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual int Duration { get; init; }
/// <summary>
/// 程序响应码
/// </summary>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual ErrorCodes ErrorCode { get; init; }
/// <summary>
/// HTTP状态码
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_SMALL_INT)]
[CsvIgnore]
[JsonIgnore]
public virtual int HttpStatusCode { get; init; }
/// <summary>
/// 登录用户名
/// </summary>
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[CsvIgnore]
[JsonIgnore]
public virtual string LoginUserName { get; init; }
/// <summary>
/// 拥有者
/// </summary>
[CsvIgnore]
[JsonIgnore]
[Navigate(nameof(OwnerId))]
public Sys_User Owner { get; init; }
/// <inheritdoc />
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long? OwnerDeptId { get; init; }
/// <inheritdoc />
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual long? OwnerId { get; init; }
/// <summary>
/// 请求内容
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[CsvIgnore]
[JsonIgnore]
public virtual string RequestBody { get; init; }
/// <summary>
/// 请求头信息
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[CsvIgnore]
[JsonIgnore]
public virtual string RequestHeaders { get; init; }
/// <summary>
/// 请求地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[CsvIgnore]
[JsonIgnore]
public virtual string RequestUrl { get; init; }
/// <summary>
/// 响应内容
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[CsvIgnore]
[JsonIgnore]
public virtual string ResponseBody { get; init; }
/// <summary>
/// 响应头
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[CsvIgnore]
[JsonIgnore]
public virtual string ResponseHeaders { get; init; }
/// <summary>
/// 服务器IP
/// </summary>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual int? ServerIp { get; init; }
}

View File

@ -5,11 +5,13 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 请求日志表
/// </summary>
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiPathCrc32), nameof(ApiPathCrc32), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), $"{nameof(CreatedTime)} DESC", false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(OwnerId), nameof(OwnerId), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiPathCrc32), nameof(ApiPathCrc32), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), $"{nameof(CreatedTime)} DESC", false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(OwnerId), nameof(OwnerId), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table( //
Name = $"{Chars.FLG_DB_TABLE_NAME_PREFIX}{nameof(Sys_RequestLog)}_{{yyyyMMdd}}"
, AsTable = $"{nameof(CreatedTime)}=2024-1-1(1 day)")]
public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFieldCreatedClientIp
{
/// <summary>
@ -91,4 +93,12 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFi
[CsvIgnore]
[JsonIgnore]
public virtual long? OwnerId { get; init; }
/// <summary>
/// 请求跟踪标识
/// </summary>
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual Guid TraceId { get; init; }
}

View File

@ -4,6 +4,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 请求日志明细表
/// </summary>
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLogDetail))]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), $"{nameof(CreatedTime)} DESC", false)]
public record Sys_RequestLogDetail : SimpleEntity, IFieldCreatedTime, IFieldCreatedClientUserAgent
{
/// <inheritdoc />
@ -121,12 +122,4 @@ public record Sys_RequestLogDetail : SimpleEntity, IFieldCreatedTime, IFieldCrea
[CsvIgnore]
[JsonIgnore]
public virtual int? ServerIp { get; init; }
/// <summary>
/// 请求跟踪标识
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
[CsvIgnore]
[JsonIgnore]
public virtual string TraceId { get; init; }
}

View File

@ -50,29 +50,34 @@ public sealed record DynamicFilterInfo : DataAbstraction
}
}
if (d?.Operator != DynamicFilterOperator.DateRange) {
return;
if (new[] { nameof(IFieldCreatedClientIp.CreatedClientIp), nameof(IFieldModifiedClientIp.ModifiedClientIp) }
.Contains(d?.Field, StringComparer.OrdinalIgnoreCase)) {
var val = d!.Value?.ToString();
if (val?.IsIpV4() == true) {
d.Value = val.IpV4ToInt32();
}
}
else if (d?.Operator == DynamicFilterOperator.DateRange) {
var values = ((JsonElement)d.Value).Deserialize<string[]>();
if (!DateTime.TryParse(values[0], CultureInfo.InvariantCulture, out _)) {
var result = values[0]
.ExecuteCSharpCodeAsync<DateTime>([typeof(DateTime).Assembly], nameof(System))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
values[0] = $"{result:yyyy-MM-dd HH:mm:ss}";
}
var values = ((JsonElement)d.Value).Deserialize<string[]>();
if (!DateTime.TryParse(values[0], CultureInfo.InvariantCulture, out _)) {
var result = values[0]
.ExecuteCSharpCodeAsync<DateTime>([typeof(DateTime).Assembly], nameof(System))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
values[0] = $"{result:yyyy-MM-dd HH:mm:ss}";
if (!DateTime.TryParse(values[1], CultureInfo.InvariantCulture, out _)) {
var result = values[1]
.ExecuteCSharpCodeAsync<DateTime>([typeof(DateTime).Assembly], nameof(System))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
values[1] = $"{result:yyyy-MM-dd HH:mm:ss}";
}
d.Value = values;
}
if (!DateTime.TryParse(values[1], CultureInfo.InvariantCulture, out _)) {
var result = values[1]
.ExecuteCSharpCodeAsync<DateTime>([typeof(DateTime).Assembly], nameof(System))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
values[1] = $"{result:yyyy-MM-dd HH:mm:ss}";
}
d.Value = values;
}
}

View File

@ -10,26 +10,26 @@ public record ExportApiRsp : QueryApiRsp
public override IEnumerable<QueryApiRsp> Children { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.接口路径))]
public override string Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.请求方式))]
public override string Method { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.接口名称))]
public override string Name { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.接口描述))]
public override string Summary { get; init; }
}

View File

@ -27,6 +27,10 @@ public record QueryApiRsp : Sys_Api
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ParentId { get; init; }
/// <inheritdoc cref="Sys_Api.PathCrc32" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int PathCrc32 { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }

View File

@ -6,7 +6,7 @@ namespace NetAdmin.Domain.Dto.Sys.Cache;
public sealed record GetAllEntriesReq : DataAbstraction
{
/// <summary>
/// 数据库索引号
/// 关键词
/// </summary>
public int DbIndex { get; init; }
public string Keywords { get; init; }
}

View File

@ -1,55 +0,0 @@
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 响应:获取所有缓存项
/// </summary>
public sealed record GetAllEntriesRsp : DataAbstraction
{
/// <summary>
/// Initializes a new instance of the <see cref="GetAllEntriesRsp" /> class.
/// </summary>
public GetAllEntriesRsp() { }
/// <summary>
/// Initializes a new instance of the <see cref="GetAllEntriesRsp" /> class.
/// </summary>
public GetAllEntriesRsp(long absExp, string key, long sldExp, string data)
{
AbsExp = absExp;
Key = key;
SldExp = sldExp;
Data = data;
}
/// <summary>
/// 绝对过期时间
/// </summary>
public DateTime? AbsExpTime => AbsExp == -1 ? null : DateTime.FromBinary(AbsExp).ToLocalTime();
/// <summary>
/// 滑动过期时间
/// </summary>
public DateTime? SldExpTime => SldExp == -1 ? null : DateTime.FromBinary(SldExp).ToLocalTime();
/// <summary>
/// 绝对过期时间
/// </summary>
[JsonInclude]
public long AbsExp { get; init; }
/// <summary>
/// 缓存值
/// </summary>
public string Data { get; init; }
/// <summary>
/// 缓存键
/// </summary>
public string Key { get; init; }
/// <summary>
/// 滑动过期时间
/// </summary>
[JsonInclude]
public long SldExp { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 请求:获取缓存项
/// </summary>
public sealed record GetEntriesReq : DataAbstraction
{
/// <summary>
/// 缓存键
/// </summary>
public string Key { get; init; }
}

View File

@ -0,0 +1,34 @@
using StackExchange.Redis;
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 响应:获取所有缓存项
/// </summary>
public sealed record GetEntryRsp : DataAbstraction
{
/// <summary>
/// Initializes a new instance of the <see cref="GetEntryRsp" /> class.
/// </summary>
public GetEntryRsp() { }
/// <summary>
/// 缓存值
/// </summary>
public string Data { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public DateTime? ExpireTime { get; init; }
/// <summary>
/// 缓存键
/// </summary>
public string Key { get; init; }
/// <summary>
/// 数据类型
/// </summary>
public RedisType Type { get; init; }
}

View File

@ -9,20 +9,20 @@ namespace NetAdmin.Domain.Dto.Sys.Config;
public record ExportConfigRsp : QueryConfigRsp, IRegister
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.是否启用))]
public override bool Enabled { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.人工审核))]
public override bool UserRegisterConfirm { get; init; }
@ -33,8 +33,8 @@ public record ExportConfigRsp : QueryConfigRsp, IRegister
/// <summary>
/// 默认部门
/// </summary>
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.默认部门))]
public string UserRegisterDeptName { get; init; }
@ -45,8 +45,8 @@ public record ExportConfigRsp : QueryConfigRsp, IRegister
/// <summary>
/// 默认角色
/// </summary>
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.默认角色))]
public string UserRegisterRoleName { get; init; }

View File

@ -10,38 +10,38 @@ public record ExportDeptRsp : QueryDeptRsp
public override IEnumerable<QueryDeptRsp> Children { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(5)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.是否启用))]
public override bool Enabled { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.部门名称))]
public override string Name { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.排序))]
public override long Sort { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.备注))]
public override string Summary { get; init; }
}

View File

@ -6,20 +6,20 @@ namespace NetAdmin.Domain.Dto.Sys.Dic.Content;
public record ExportDicContentRsp : QueryDicContentRsp
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.项名))]
public override string Key { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.项值))]
public override string Value { get; init; }
}

View File

@ -10,68 +10,68 @@ namespace NetAdmin.Domain.Dto.Sys.Job;
public record ExportJobRsp : QueryJobRsp
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(5)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.上次执行状态))]
public override string LastStatusCode => base.LastStatusCode;
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(10)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(9)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.是否启用))]
public override bool Enabled { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.执行计划))]
public override string ExecutionCron { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.请求方式))]
public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.作业名称))]
public override string JobName { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(7)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.上次执行耗时))]
public override long? LastDuration { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.上次执行时间))]
public override DateTime? LastExecTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(8)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.下次执行时间))]
public override DateTime? NextExecTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.作业状态))]
public override JobStatues Status { get; init; }

View File

@ -8,26 +8,26 @@ namespace NetAdmin.Domain.Dto.Sys.JobRecord;
public record ExportJobRecordRsp : QueryJobRecordRsp, IRegister
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.响应状态码))]
public override string HttpStatusCode => base.HttpStatusCode;
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.执行耗时))]
public override long Duration { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
@ -38,14 +38,14 @@ public record ExportJobRecordRsp : QueryJobRecordRsp, IRegister
/// <summary>
/// 作业名称
/// </summary>
[CsvIgnore(false)]
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.作业名称))]
public string JobName { get; set; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(5)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.响应体))]
public override string ResponseBody { get; init; }

View File

@ -0,0 +1,50 @@
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.Dto.Sys.RequestLog;
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.LoginLog;
/// <summary>
/// 请求:创建登录日志
/// </summary>
public record CreateLoginLogReq : Sys_LoginLog, IRegister
{
/// <inheritdoc />
public void Register(TypeAdapterConfig config)
{
_ = config.ForType<CreateRequestLogReq, CreateLoginLogReq>().MapWith(x => Convert(x));
}
private static CreateLoginLogReq Convert(CreateRequestLogReq s)
{
var body = s.Detail.ResponseBody.ToObject<RestfulInfo<LoginRsp>>();
ContextUserToken userToken = null;
if (body.Data?.AccessToken != null) {
try {
userToken = ContextUserToken.Create(body.Data.AccessToken);
}
catch {
// ignored
}
}
return new CreateLoginLogReq {
Id = s.Id
, CreatedClientIp = s.CreatedClientIp
, CreatedTime = s.CreatedTime
, Duration = s.Duration
, HttpStatusCode = s.HttpStatusCode
, ErrorCode = s.Detail.ErrorCode
, RequestBody = s.Detail.RequestBody
, RequestHeaders = s.Detail.RequestHeaders
, RequestUrl = s.Detail.RequestUrl
, ResponseBody = s.Detail.ResponseBody
, ResponseHeaders = s.Detail.ResponseHeaders
, ServerIp = s.Detail.ServerIp
, CreatedUserAgent = s.Detail.CreatedUserAgent
, OwnerId = userToken?.Id
, OwnerDeptId = userToken?.DeptId
, LoginUserName = s.Detail.RequestBody?.ToObject<LoginByPwdReq>()?.Account
};
}
}

View File

@ -0,0 +1,55 @@
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.LoginLog;
/// <summary>
/// 响应:导出登录日志
/// </summary>
public sealed record ExportLoginLogRsp : QueryLoginLogRsp
{
/// <inheritdoc />
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.客户端IP))]
public override string CreatedClientIp => base.CreatedClientIp;
/// <inheritdoc />
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.操作系统))]
public override string Os => base.Os;
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIndex(5)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.用户代理))]
public override string CreatedUserAgent { get; init; }
/// <inheritdoc />
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.响应状态码))]
public override int HttpStatusCode { get; init; }
/// <inheritdoc />
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvName(nameof(Ln.登录名))]
public override string LoginUserName { get; init; }
/// <inheritdoc />
[CsvIgnore]
public override QueryUserRsp Owner { get; init; }
}

View File

@ -0,0 +1,11 @@
namespace NetAdmin.Domain.Dto.Sys.LoginLog;
/// <summary>
/// 请求:查询登录日志
/// </summary>
public sealed record QueryLoginLogReq : Sys_LoginLog
{
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -0,0 +1,77 @@
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.LoginLog;
/// <summary>
/// 响应:查询登录日志
/// </summary>
public record QueryLoginLogRsp : Sys_LoginLog
{
/// <summary>
/// 创建者客户端IP
/// </summary>
[JsonInclude]
public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4();
/// <summary>
/// 操作系统
/// </summary>
[JsonInclude]
public virtual string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform;
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="IFieldCreatedClientUserAgent.CreatedUserAgent" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserAgent { get; init; }
/// <inheritdoc cref="Sys_LoginLog.Duration" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int Duration { get; init; }
/// <inheritdoc cref="Sys_LoginLog.ErrorCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override ErrorCodes ErrorCode { get; init; }
/// <inheritdoc cref="Sys_LoginLog.HttpStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int HttpStatusCode { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_LoginLog.LoginUserName" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string LoginUserName { get; init; }
/// <inheritdoc cref="Sys_LoginLog.Owner" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public new virtual QueryUserRsp Owner { get; init; }
/// <inheritdoc cref="Sys_LoginLog.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }
/// <inheritdoc cref="Sys_LoginLog.RequestHeaders" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestHeaders { get; init; }
/// <inheritdoc cref="Sys_LoginLog.RequestUrl" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestUrl { get; init; }
/// <inheritdoc cref="Sys_LoginLog.ResponseBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseBody { get; init; }
/// <inheritdoc cref="Sys_LoginLog.ResponseHeaders" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseHeaders { get; init; }
/// <inheritdoc cref="Sys_LoginLog.ServerIp" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? ServerIp { get; init; }
}

View File

@ -14,13 +14,13 @@ public record ExportRequestLogRsp : QueryRequestLogRsp
/// 接口路径
/// </summary>
[CsvIndex(2)]
[CsvName(nameof(Ln.接口路径))]
[JsonInclude]
[CsvName(nameof(Ln.接口路径))]
public string ApiId => Api.Id;
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.客户端IP))]
public override string CreatedClientIp => base.CreatedClientIp;
@ -28,8 +28,8 @@ public record ExportRequestLogRsp : QueryRequestLogRsp
/// 用户名
/// </summary>
[CsvIndex(5)]
[CsvName(nameof(Ln.用户名))]
[JsonInclude]
[CsvName(nameof(Ln.用户名))]
public string UserName => Owner?.UserName;
/// <inheritdoc />
@ -37,7 +37,9 @@ public record ExportRequestLogRsp : QueryRequestLogRsp
public override QueryApiRsp Api { get; init; }
/// <inheritdoc />
[CsvIgnore]
[CsvIgnore(false)]
[CsvIndex(8)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
@ -45,38 +47,36 @@ public record ExportRequestLogRsp : QueryRequestLogRsp
public override QueryRequestLogDetailRsp Detail { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.执行耗时))]
public override int Duration { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.请求方式))]
public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.响应状态码))]
public override int HttpStatusCode { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore]
public override QueryUserLiteRsp Owner { get; init; }
public override QueryUserRsp Owner { get; init; }
/// <inheritdoc />
[CsvIgnore]
public override long? OwnerDeptId { get; init; }
/// <inheritdoc />
[CsvIgnore]
public override long? OwnerId { get; init; }
[CsvIndex(7)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.跟踪标识))]
public override Guid TraceId { get; init; }
}

View File

@ -3,4 +3,9 @@ namespace NetAdmin.Domain.Dto.Sys.RequestLog;
/// <summary>
/// 请求:查询请求日志
/// </summary>
public sealed record QueryRequestLogReq : Sys_RequestLog;
public sealed record QueryRequestLogReq : Sys_RequestLog
{
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
}

View File

@ -46,9 +46,13 @@ public record QueryRequestLogRsp : Sys_RequestLog
/// <inheritdoc cref="Sys_RequestLog.Owner" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public new virtual QueryUserLiteRsp Owner { get; init; }
public new virtual QueryUserRsp Owner { get; init; }
/// <inheritdoc cref="IFieldOwner.OwnerId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? OwnerId { get; init; }
/// <inheritdoc cref="Sys_RequestLog.TraceId" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override Guid TraceId { get; init; }
}

View File

@ -66,8 +66,4 @@ public sealed record QueryRequestLogDetailRsp : Sys_RequestLogDetail
/// <inheritdoc cref="Sys_RequestLogDetail.ServerIp" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? ServerIp { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.TraceId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string TraceId { get; init; }
}

View File

@ -8,50 +8,50 @@ namespace NetAdmin.Domain.Dto.Sys.Role;
public sealed record ExportRoleRsp : QueryRoleRsp
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(7)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.数据范围))]
public override DataScopes DataScope { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(5)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.显示仪表板))]
public override bool DisplayDashboard { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.是否启用))]
public override bool Enabled { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.无限权限))]
public override bool IgnorePermissionControl { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.角色名称))]
public override string Name { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.排序))]
public override long Sort { get; init; }
}

View File

@ -11,14 +11,14 @@ namespace NetAdmin.Domain.Dto.Sys.SiteMsg;
public record ExportSiteMsgRsp : QuerySiteMsgRsp
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(5)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.用户名))]
public override string CreatedUserName { get; init; }
@ -27,14 +27,14 @@ public record ExportSiteMsgRsp : QuerySiteMsgRsp
public override IEnumerable<QueryDeptRsp> Depts { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.消息类型))]
public override SiteMsgTypes MsgType { get; init; }
@ -43,14 +43,14 @@ public record ExportSiteMsgRsp : QuerySiteMsgRsp
public override IEnumerable<QueryRoleRsp> Roles { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.消息摘要))]
public override string Summary { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.消息主题))]
public override string Title { get; init; }

View File

@ -9,8 +9,8 @@ namespace NetAdmin.Domain.Dto.Sys.User;
public record ExportUserRsp : QueryUserRsp
{
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(7)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
@ -26,26 +26,26 @@ public record ExportUserRsp : QueryUserRsp
public string DeptName { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.邮箱号))]
public override string Email { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(6)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.是否启用))]
public override bool Enabled { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.唯一编码))]
public override long Id { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(2)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.手机号))]
public override string Mobile { get; init; }
@ -61,8 +61,8 @@ public record ExportUserRsp : QueryUserRsp
public override IEnumerable<QueryRoleRsp> Roles { get; init; }
/// <inheritdoc />
[CsvIgnore(false)]
[CsvIndex(1)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.用户名))]
public override string UserName { get; init; }

View File

@ -1,15 +0,0 @@
namespace NetAdmin.Domain.Dto.Sys.User;
/// <summary>
/// 响应:查询用户精简版
/// </summary>
public record QueryUserLiteRsp : Sys_User
{
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_User.UserName" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string UserName { get; init; }
}

View File

@ -72,6 +72,6 @@ public abstract class WorkBase<TLogger>
{
return _redLocker.RedLockFactory.CreateLockAsync(lockId, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_EXPIRY)
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_WAIT)
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_RETRY));
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_RETRY_INTERVAL));
}
}

View File

@ -44,7 +44,6 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
, ResponseContentType = context.Response.ContentType
, ResponseHeaders = context.Response.Headers.Json()
, ServerIp = context.GetLocalIpAddressToIPv4()?.IpV4ToInt32()
, TraceId = context.TraceIdentifier
}
, Duration = (int)duration
, HttpMethod = Enum.Parse<HttpMethods>(context.Request.Method, true)
@ -54,6 +53,7 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
, OwnerId = associatedUser?.UserId
, OwnerDeptId = associatedUser?.DeptId
, Id = id
, TraceId = context.GetTraceId()
};
// 打印日志
@ -74,13 +74,10 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
ContextUserToken userToken = null;
try {
var jsonWebToken
= JWTEncryption.ReadJwtToken(token.TrimPrefix($"{Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA} "));
var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken));
userToken = claim?.Value.ToObject<ContextUserToken>();
userToken = ContextUserToken.Create(token);
}
catch (Exception ex) {
logger.Warn($"{Ln.读取用户令牌出错}: {ex}");
logger.Warn($"{Ln.读取用户令牌出错} {ex}");
}
return userToken == null ? null : (userToken.Id, userToken.DeptId, userToken.UserName);

View File

@ -24,12 +24,12 @@ public static class Numbers
public const int MAX_LIMIT_QUERY_PAGE_NO = 10000; // 最大限制:分页查询页码
public const int MAX_LIMIT_QUERY_PAGE_SIZE = 100; // 最大限制:分页查询页容量
public const int SECS_CACHE_CHART = 300; // 秒:缓存时间-仪表
public const int SECS_CACHE_DEFAULT = 60; // 秒:缓存时间-默认
public const int SECS_CACHE_DIC_CATALOG_CODE = 300; // 秒:缓存时间-字典配置-目录代码
public const int SECS_RED_LOCK_EXPIRY = 30; // 秒RedLock-锁过期时间,假如持有锁的进程挂掉,最多在此时间内锁将被释放(如持有锁的进程正常,此值不会生效)
public const int SECS_RED_LOCK_RETRY = 1; // 秒RedLock-锁等待时间内,多久尝试获取一次
public const int SECS_RED_LOCK_WAIT = 10; // 秒RedLock-锁等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
public const int SECS_TIMEOUT_HTTP_CLIENT = 15; // 秒:超时时间-默认HTTP客户端
public const int SECS_TIMEOUT_JOB = 600; // 秒:超时时间-作业
public const int SECS_CACHE_CHART = 300; // 秒:缓存时间-仪表
public const int SECS_CACHE_DEFAULT = 60; // 秒:缓存时间-默认
public const int SECS_CACHE_DIC_CATALOG_CODE = 300; // 秒:缓存时间-字典配置-目录代码
public const int SECS_RED_LOCK_EXPIRY = 30; // 秒RedLock-锁过期时间,假如持有锁的进程挂掉,最多在此时间内锁将被释放(如持有锁的进程正常,此值不会生效)
public const int SECS_RED_LOCK_RETRY_INTERVAL = 1; // 秒RedLock-锁等待时间内,多久尝试获取一次
public const int SECS_RED_LOCK_WAIT = 10; // 秒RedLock-锁等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
public const int SECS_TIMEOUT_HTTP_CLIENT = 15; // 秒:超时时间-默认HTTP客户端
public const int SECS_TIMEOUT_JOB = 600; // 秒:超时时间-作业
}

View File

@ -22,4 +22,12 @@ public static class HttpContextExtensions
? ip2
: me.Connection.RemoteIpAddress;
}
/// <summary>
/// 获取跟踪标识
/// </summary>
public static Guid GetTraceId(this HttpContext me)
{
return me.TraceIdentifier.Md5(Encoding.UTF8).Guid();
}
}

View File

@ -8,6 +8,8 @@ namespace NetAdmin.Infrastructure.Extensions;
/// </summary>
public static class StringExtensions
{
private static readonly Regex _regexIpV4 = new(Chars.RGXL_IP_V4);
/// <summary>
/// 计算Crc32
/// </summary>
@ -27,6 +29,14 @@ public static class StringExtensions
me, ScriptOptions.Default.WithReferences(assemblies).WithImports(importNamespaces));
}
/// <summary>
/// 是否IPV4地址
/// </summary>
public static bool IsIpV4(this string me)
{
return _regexIpV4.IsMatch(me);
}
/// <summary>
/// object -> json
/// </summary>
@ -43,24 +53,6 @@ public static class StringExtensions
return me.Object(toType, GlobalStatic.JsonSerializerOptions);
}
/// <summary>
/// 去掉尾部字符串“Async”
/// </summary>
#pragma warning disable RCS1047, ASA002, VSTHRD200
public static string TrimEndAsync(this string me)
#pragma warning restore VSTHRD200, ASA002, RCS1047
{
return TrimSuffix(me, "Async");
}
/// <summary>
/// 去掉尾部字符串“Options”
/// </summary>
public static string TrimEndOptions(this string me)
{
return TrimSuffix(me, "Options");
}
/// <summary>
/// 去掉前部字符串
/// </summary>
@ -69,6 +61,16 @@ public static class StringExtensions
return Regex.Replace(me, $"^{clearStr}", string.Empty);
}
/// <summary>
/// 去掉尾部字符串“Async”
/// </summary>
#pragma warning disable RCS1047, ASA002, VSTHRD200
public static string TrimPrefixAsync(this string me)
#pragma warning restore VSTHRD200, ASA002, RCS1047
{
return TrimPrefix(me, "Async");
}
/// <summary>
/// 去掉尾部字符串
/// </summary>
@ -76,4 +78,12 @@ public static class StringExtensions
{
return Regex.Replace(me, $"{clearStr}$", string.Empty);
}
/// <summary>
/// 去掉尾部字符串“Options”
/// </summary>
public static string TrimSuffixOptions(this string me)
{
return TrimPrefix(me, "Options");
}
}

View File

@ -15,7 +15,7 @@
<PackageReference Include="Minio" Version="6.0.3"/>
<PackageReference Include="NSExt" Version="2.2.0"/>
<PackageReference Include="RedLock.net" Version="2.3.2"/>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.3"/>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.4"/>
</ItemGroup>
<ItemGroup>
<None Update="*.json">

View File

@ -15,7 +15,8 @@ public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
{
var freeSql = new FreeSql.FreeSqlBuilder().UseConnectionString(databaseOptions.DbType, databaseOptions.ConnStr)
.UseGenerateCommandParameterWithLambda(true)
.UseAutoSyncStructure(false)
.UseAutoSyncStructure(
initMethods.HasFlag(FreeSqlInitMethods.SyncStructure))
.Build();
_ = InitDbAsync(freeSql, initMethods); // 初始化数据库 ,异步

View File

@ -18,7 +18,7 @@ public sealed class XmlCommentReader : ISingleton
{
var xmlComments = specificationDocumentSettings.Value.XmlComments //
?? App.GetConfig<SpecificationDocumentSettingsOptions>(
nameof(SpecificationDocumentSettingsOptions).TrimEndOptions())
nameof(SpecificationDocumentSettingsOptions).TrimSuffixOptions())
.XmlComments;
foreach (var commentFile in xmlComments.Where(x => x.Contains(nameof(NetAdmin)))) {
var xmlDoc = new XmlDocument();

View File

@ -1,4 +1,3 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
@ -16,5 +15,10 @@ public interface ICacheModule
/// <summary>
/// 获取所有缓存项
/// </summary>
Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req);
Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req);
/// <summary>
/// 获取缓存项
/// </summary>
Task<GetEntryRsp> GetEntryAsync(GetEntriesReq req);
}

View File

@ -0,0 +1,13 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.LoginLog;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 登录日志模块
/// </summary>
public interface ILoginLogModule : ICrudModule<CreateLoginLogReq, QueryLoginLogRsp // 创建类型
, QueryLoginLogReq, QueryLoginLogRsp // 查询类型
, DelReq // 删除类型
>;

View File

@ -8,10 +8,11 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IApiService" />
public sealed class ApiService(
BasicRepository<Sys_Api, string> rpo //
, XmlCommentReader xmlCommentReader //
, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) //
: RepositoryService<Sys_Api, string, IApiService>(rpo), IApiService
BasicRepository<Sys_Api, string> rpo //
, XmlCommentReader xmlCommentReader //
, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider
, RedLocker redLocker) //
: RedLockerService<Sys_Api, string, IApiService>(rpo, redLocker), IApiService
{
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
@ -125,6 +126,7 @@ public sealed class ApiService(
/// <inheritdoc />
public async Task SyncAsync()
{
await using var locker = await GetLockerOnceAsync(nameof(SyncAsync)).ConfigureAwait(false);
_ = await Rpo.DeleteAsync(_ => true).ConfigureAwait(false);
var list = ReflectionList(false);

View File

@ -1,5 +1,5 @@
using System.Collections.Concurrent;
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using StackExchange.Redis;
@ -31,23 +31,58 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
}
/// <inheritdoc />
public async Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
public async Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{
req.ThrowIfInvalid();
#pragma warning disable VSTHRD103
var server = connectionMultiplexer.GetServers()[0];
var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
var redisResults = (RedisResult[])await database
.ExecuteAsync("scan", (req.Page - 1) * req.PageSize, "count"
, req.PageSize)
.ConfigureAwait(false);
var keys = server.Keys(_redisInstance.Database, $"*{req.Keywords}*", Numbers.MAX_LIMIT_BULK_REQ)
.Take(Numbers.MAX_LIMIT_BULK_REQ)
.ToList();
#pragma warning restore VSTHRD103
var list = ((string[])redisResults![1])!.Where(x => database.KeyType(x) == RedisType.Hash)
.Select(x => database.HashGetAll(x)
.Append(new HashEntry("key", x))
.ToArray()
.ToStringDictionary())
.ToList()
.ConvertAll(x => x.Adapt<GetAllEntriesRsp>());
var dic = new ConcurrentDictionary<string, (DateTime?, RedisType)>();
return new PagedQueryRsp<GetAllEntriesRsp>(req.Page, req.PageSize, 10000, list);
await Parallel
.ForEachAsync(
keys
, async (key, _) =>
dic.TryAdd(
key
, (DateTime.Now + await database.KeyTimeToLiveAsync(key).ConfigureAwait(false)
, await database.KeyTypeAsync(key).ConfigureAwait(false))))
.ConfigureAwait(false);
return dic.Select(x => new GetEntryRsp { Key = x.Key, ExpireTime = x.Value.Item1, Type = x.Value.Item2 });
}
/// <inheritdoc />
public async Task<GetEntryRsp> GetEntryAsync(GetEntriesReq req)
{
var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
var ret = new GetEntryRsp {
Type = await database.KeyTypeAsync(req.Key).ConfigureAwait(false)
, Key = req.Key
, ExpireTime = DateTime.Now +
await database.KeyTimeToLiveAsync(req.Key).ConfigureAwait(false)
};
#pragma warning disable IDE0072
ret.Data = ret.Type switch
#pragma warning restore IDE0072
{
RedisType.String => await database.StringGetAsync(req.Key).ConfigureAwait(false)
, RedisType.List => string.Join(", ", await database.ListRangeAsync(req.Key).ConfigureAwait(false))
, RedisType.Set => string.Join(", ", await database.SetMembersAsync(req.Key).ConfigureAwait(false))
, RedisType.SortedSet =>
string.Join(", ", await database.SortedSetRangeByRankAsync(req.Key).ConfigureAwait(false))
, RedisType.Hash => string.Join(
", ", await database.HashGetAllAsync(req.Key).ConfigureAwait(false))
, _ => "Unsupported key type"
};
return ret;
}
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 登录日志服务
/// </summary>
public interface ILoginLogService : IService, ILoginLogModule;

View File

@ -0,0 +1,150 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.LoginLog;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="ILoginLogService" />
public sealed class LoginLogService(BasicRepository<Sys_LoginLog, long> rpo) //
: RepositoryService<Sys_LoginLog, long, ILoginLogService>(rpo), ILoginLogService
{
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var ret = 0;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var item in req.Items) {
ret += await DeleteAsync(item).ConfigureAwait(false);
}
return ret;
}
/// <inheritdoc />
public Task<long> CountAsync(QueryReq<QueryLoginLogReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.CountAsync();
}
/// <inheritdoc />
public async Task<QueryLoginLogRsp> CreateAsync(CreateLoginLogReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryLoginLogRsp>();
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryLoginLogReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.AnyAsync();
}
/// <inheritdoc />
public Task<IActionResult> ExportAsync(QueryReq<QueryLoginLogReq> req)
{
req.ThrowIfInvalid();
return ExportAsync<QueryLoginLogReq, ExportLoginLogRsp>(QueryInternal, req, Ln.);
}
/// <inheritdoc />
public async Task<QueryLoginLogRsp> GetAsync(QueryLoginLogReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryLoginLogReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryLoginLogRsp>();
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryLoginLogRsp>> PagedQueryAsync(PagedQueryReq<QueryLoginLogReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Include(a => a.Owner)
.Page(req.Page, req.PageSize)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Count(out var total)
.ToListAsync(a => new {
a.CreatedClientIp
, a.CreatedTime
, a.CreatedUserAgent
, a.Duration
, a.ErrorCode
, a.HttpStatusCode
, a.Id
, a.LoginUserName
, Owner = new { a.Owner.Id, a.Owner.UserName }
, a.RequestUrl
, a.ServerIp
})
.ConfigureAwait(false);
return new PagedQueryRsp<QueryLoginLogRsp>(req.Page, req.PageSize, total, list.Adapt<List<QueryLoginLogRsp>>());
}
/// <inheritdoc />
public async Task<IEnumerable<QueryLoginLogRsp>> QueryAsync(QueryReq<QueryLoginLogReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Take(req.Count)
.ToListAsync()
.ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryLoginLogRsp>>();
}
private ISelect<Sys_LoginLog> QueryInternal(QueryReq<QueryLoginLogReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
if (req.Keywords?.Length > 0) {
ret = req.Keywords.IsIpV4()
? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32())
: ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.OwnerId == req.Keywords.Int64Try(0) ||
a.LoginUserName == req.Keywords);
}
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;
case Orders.Random:
return ret.OrderByRandom();
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
}
}

View File

@ -2,19 +2,16 @@ using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys;
using NetAdmin.Domain.Dto.Sys.LoginLog;
using NetAdmin.Domain.Dto.Sys.RequestLog;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IRequestLogService" />
public sealed class RequestLogService(
BasicRepository<Sys_RequestLog, long> rpo
, RequestLogDetailService requestLogDetailService) //
public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo, LoginLogService loginLogService) //
: RepositoryService<Sys_RequestLog, long, IRequestLogService>(rpo), IRequestLogService
{
private static readonly Regex _regex = new(Chars.RGXL_IP_V4);
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
@ -45,7 +42,12 @@ public sealed class RequestLogService(
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
_ = await requestLogDetailService.CreateAsync(req.Detail).ConfigureAwait(false);
// 插入登录日志
if (req.ApiPathCrc32 == Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Crc32()) {
_ = await loginLogService.CreateAsync(req.Adapt<CreateLoginLogReq>()).ConfigureAwait(false);
}
return ret.Adapt<QueryRequestLogRsp>();
}
@ -88,7 +90,16 @@ public sealed class RequestLogService(
public async Task<QueryRequestLogRsp> GetAsync(QueryRequestLogReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req })
var df = new DynamicFilterInfo {
Field = nameof(QueryRequestLogReq.CreatedTime)
, Operator = DynamicFilterOperators.DateRange
, Value = new[] {
req.CreatedTime.AddHours(-1).yyyy_MM_dd_HH_mm_ss()
, req.CreatedTime.AddHours(1).yyyy_MM_dd_HH_mm_ss()
}.Json()
.Object<JsonElement>()
};
var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req, DynamicFilter = df })
.Include(a => a.Detail)
.ToOneAsync()
.ConfigureAwait(false);
@ -155,43 +166,27 @@ public sealed class RequestLogService(
public async Task<PagedQueryRsp<QueryRequestLogRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
var select = QueryInternal(req)
.Page(req.Page, req.PageSize)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Count(out var total);
var select = QueryInternal(req with { Order = Orders.None }, false);
var selectTemp = select.WithTempQuery(a => new { temp = a });
object ret
= req.DynamicFilter?.Filters?.Exists(
x => nameof(QueryRequestLogReq.ApiPathCrc32).Equals(x.Field, StringComparison.OrdinalIgnoreCase) &&
x.Value.ToString().Int32() == Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Crc32()) ?? false
? await select.Include(a => a.Detail)
.ToListAsync(a => new {
Api = new { a.Api.Summary, a.Api.Id }
, Owner = new { a.Owner.Id, a.Owner.UserName }
, a.CreatedClientIp
, a.CreatedTime
, a.Duration
, a.HttpMethod
, a.HttpStatusCode
, a.Id
, a.ApiPathCrc32
, Detail = new { a.Detail.RequestBody, a.Detail.CreatedUserAgent }
})
.ConfigureAwait(false)
: await select.ToListAsync(a => new {
Api = new { a.Api.Summary, a.Api.Id }
, Owner = new { a.Owner.Id, a.Owner.UserName }
, a.CreatedClientIp
, a.CreatedTime
, a.Duration
, a.HttpMethod
, a.HttpStatusCode
, a.Id
, a.ApiPathCrc32
})
.ConfigureAwait(false);
if (req.Order == Orders.Random) {
selectTemp = selectTemp.OrderByRandom();
}
else {
selectTemp = selectTemp.OrderBy( //
req.Prop?.Length > 0, $"{req.Prop} {(req.Order == Orders.Ascending ? "ASC" : "DESC")}");
if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
selectTemp = selectTemp.OrderByDescending(a => a.temp.CreatedTime);
}
}
var ret = await selectTemp.Page(req.Page, req.PageSize)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Count(out var total)
.ToListAsync(a => a.temp)
.ConfigureAwait(false);
return new PagedQueryRsp<QueryRequestLogRsp>(req.Page, req.PageSize, total
, ret.Adapt<IEnumerable<QueryRequestLogRsp>>());
@ -229,10 +224,9 @@ public sealed class RequestLogService(
}
if (req.Keywords?.Length > 0) {
ret = _regex.IsMatch(req.Keywords)
ret = req.Keywords.IsIpV4()
? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32())
: ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.OwnerId == req.Keywords.Int64Try(0) ||
a.Owner.UserName == req.Keywords);
: ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.OwnerId == req.Keywords.Int64Try(0));
}
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault

View File

@ -18,14 +18,15 @@ public sealed class ToolsService : ServiceBase<IToolsService>, IToolsService
/// <inheritdoc />
public Task<IEnumerable<GetModulesRsp>> GetModulesAsync()
{
return Task.FromResult<IEnumerable<GetModulesRsp>>( //
AppDomain.CurrentDomain.GetAssemblies()
.Where(x => !x.FullName?.Contains('#') ?? false)
.Select(x => {
var asm = x.GetName();
return new GetModulesRsp { Name = asm.Name, Version = asm.Version?.ToString() };
})
.OrderBy(x => x.Name));
return Task.FromResult<IEnumerable<GetModulesRsp>>(AppDomain.CurrentDomain.GetAssemblies()
.Select(x => {
var asm = x.GetName();
return new GetModulesRsp {
Name = asm.Name
, Version = asm.Version?.ToString()
};
})
.OrderBy(x => x.Name));
}
/// <inheritdoc />

View File

@ -1,5 +1,4 @@
using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
@ -22,8 +21,14 @@ public sealed class CacheCache(IDistributedCache cache, ICacheService service) /
}
/// <inheritdoc />
public Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
public Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{
return Service.GetAllEntriesAsync(req);
}
/// <inheritdoc />
public Task<GetEntryRsp> GetEntryAsync(GetEntriesReq req)
{
return Service.GetEntryAsync(req);
}
}

View File

@ -0,0 +1,10 @@
using NetAdmin.Cache;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// <summary>
/// 登录日志缓存
/// </summary>
public interface ILoginLogCache : ICache<IDistributedCache, ILoginLogService>, ILoginLogModule;

View File

@ -0,0 +1,66 @@
using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.LoginLog;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="ILoginLogCache" />
public sealed class LoginLogCache(IDistributedCache cache, ILoginLogService service)
: DistributedCache<ILoginLogService>(cache, service), IScoped, ILoginLogCache
{
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Service.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<long> CountAsync(QueryReq<QueryLoginLogReq> req)
{
return Service.CountAsync(req);
}
/// <inheritdoc />
public Task<QueryLoginLogRsp> CreateAsync(CreateLoginLogReq req)
{
return Service.CreateAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
return Service.DeleteAsync(req);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryLoginLogReq> req)
{
return Service.ExistAsync(req);
}
/// <inheritdoc />
public Task<IActionResult> ExportAsync(QueryReq<QueryLoginLogReq> req)
{
return Service.ExportAsync(req);
}
/// <inheritdoc />
public Task<QueryLoginLogRsp> GetAsync(QueryLoginLogReq req)
{
return Service.GetAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryLoginLogRsp>> PagedQueryAsync(PagedQueryReq<QueryLoginLogReq> req)
{
return Service.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryLoginLogRsp>> QueryAsync(QueryReq<QueryLoginLogReq> req)
{
return Service.QueryAsync(req);
}
}

View File

@ -1,4 +1,3 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.Host.Controllers;
using NetAdmin.SysComponent.Application.Modules.Sys;
@ -24,8 +23,16 @@ public sealed class CacheController(ICacheCache cache) : ControllerBase<ICacheCa
/// <summary>
/// 获取所有缓存项
/// </summary>
public Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
public Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{
return Cache.GetAllEntriesAsync(req);
}
/// <summary>
/// 获取缓存项
/// </summary>
public Task<GetEntryRsp> GetEntryAsync(GetEntriesReq req)
{
return Cache.GetEntryAsync(req);
}
}

View File

@ -0,0 +1,92 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.LoginLog;
using NetAdmin.Host.Attributes;
using NetAdmin.Host.Controllers;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// <summary>
/// 登录日志服务
/// </summary>
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class LoginLogController(ILoginLogCache cache) : ControllerBase<ILoginLogCache, ILoginLogService>(cache)
, ILoginLogModule
{
/// <summary>
/// 批量删除登录日志
/// </summary>
[Transaction]
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Cache.BulkDeleteAsync(req);
}
/// <summary>
/// 登录日志计数
/// </summary>
public Task<long> CountAsync(QueryReq<QueryLoginLogReq> req)
{
return Cache.CountAsync(req);
}
/// <summary>
/// 创建登录日志
/// </summary>
[Transaction]
public Task<QueryLoginLogRsp> CreateAsync(CreateLoginLogReq req)
{
return Cache.CreateAsync(req);
}
/// <summary>
/// 删除登录日志
/// </summary>
[Transaction]
public Task<int> DeleteAsync(DelReq req)
{
return Cache.DeleteAsync(req);
}
/// <summary>
/// 登录日志是否存在
/// </summary>
public Task<bool> ExistAsync(QueryReq<QueryLoginLogReq> req)
{
return Cache.ExistAsync(req);
}
/// <summary>
/// 导出登录日志
/// </summary>
public Task<IActionResult> ExportAsync(QueryReq<QueryLoginLogReq> req)
{
return Cache.ExportAsync(req);
}
/// <summary>
/// 获取单个登录日志
/// </summary>
public Task<QueryLoginLogRsp> GetAsync(QueryLoginLogReq req)
{
return Cache.GetAsync(req);
}
/// <summary>
/// 分页查询登录日志
/// </summary>
public Task<PagedQueryRsp<QueryLoginLogRsp>> PagedQueryAsync(PagedQueryReq<QueryLoginLogReq> req)
{
return Cache.PagedQueryAsync(req);
}
/// <summary>
/// 查询登录日志
/// </summary>
public Task<IEnumerable<QueryLoginLogRsp>> QueryAsync(QueryReq<QueryLoginLogReq> req)
{
return Cache.QueryAsync(req);
}
}

View File

@ -13,8 +13,8 @@ namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// 请求日志服务
/// </summary>
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class LogController(IRequestLogCache cache) : ControllerBase<IRequestLogCache, IRequestLogService>(cache)
, IRequestLogModule
public sealed class RequestLogController(IRequestLogCache cache)
: ControllerBase<IRequestLogCache, IRequestLogService>(cache), IRequestLogModule
{
/// <summary>
/// 批量删除请求日志