diff --git a/assets/res/Fields.ln b/assets/res/Fields.ln index b1a1b845..4e342191 100644 --- a/assets/res/Fields.ln +++ b/assets/res/Fields.ln @@ -134,6 +134,8 @@ 重设密码 链接 错误 +随机延时结束时间不正确 +随机延时起始时间不正确 随机排序 项值 项名 diff --git a/build/code.quality.props b/build/code.quality.props index dae8147d..e54f22a2 100644 --- a/build/code.quality.props +++ b/build/code.quality.props @@ -23,7 +23,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/package.json b/package.json index 38b42dd4..5adb111b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "devDependencies": { "cz-git": "^1.9.3", "commitizen": "^4.3.0", - "prettier": "^3.3.2", + "prettier": "^3.3.3", "standard-version": "^9.5.0" }, "config": { diff --git a/scripts/switcher.furion.json b/scripts/switcher.furion.json index f149da53..8aaa8b08 100644 --- a/scripts/switcher.furion.json +++ b/scripts/switcher.furion.json @@ -9,7 +9,7 @@ "packages": [ { "packageName": "Furion.Pure.NS", - "version": "4.9.4-ns1" + "version": "4.9.4.6-ns4" } ] } diff --git a/src/backend/NetAdmin.AdmServer.Host/Startup.cs b/src/backend/NetAdmin.AdmServer.Host/Startup.cs index 67c16c86..96275b89 100644 --- a/src/backend/NetAdmin.AdmServer.Host/Startup.cs +++ b/src/backend/NetAdmin.AdmServer.Host/Startup.cs @@ -1,3 +1,4 @@ +using Microsoft.IdentityModel.Logging; using NetAdmin.AdmServer.Host; using NetAdmin.AdmServer.Host.Extensions; using NetAdmin.Host.Extensions; @@ -78,8 +79,9 @@ namespace NetAdmin.AdmServer.Host public Task Execute(CommandContext context, CommandLineArgs settings) #pragma warning restore ASA001 { - Args = settings; - _ = Serve.Run(RunOptions.Default.WithArgs(context.Remaining.Raw.ToArray())); + Args = settings; + IdentityModelEventSource.ShowPII = true; + _ = Serve.Run(RunOptions.Default.WithArgs(context.Remaining.Raw.ToArray())); return Task.FromResult(0); } diff --git a/src/backend/NetAdmin.AdmServer.Tests/AllTests.cs b/src/backend/NetAdmin.AdmServer.Tests/AllTests.cs index 88944ab3..eb60988c 100644 --- a/src/backend/NetAdmin.AdmServer.Tests/AllTests.cs +++ b/src/backend/NetAdmin.AdmServer.Tests/AllTests.cs @@ -979,7 +979,7 @@ public class AllTests(WebApplicationFactory factory, ITestOutputHelper /// [InlineData(default)] [Theory] - public Task> GetBarChartAsync(QueryReq req) + public Task> GetBarChartAsync(QueryReq req) { return default; } @@ -1075,7 +1075,7 @@ public class AllTests(WebApplicationFactory factory, ITestOutputHelper /// [InlineData(default)] [Theory] - public Task> GetPieChartByApiSummaryAsync(QueryReq req) + public Task> GetPieChartByApiSummaryAsync(QueryReq req) { return default; } @@ -1083,7 +1083,7 @@ public class AllTests(WebApplicationFactory factory, ITestOutputHelper /// [InlineData(default)] [Theory] - public Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) + public Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) { return default; } @@ -1099,7 +1099,7 @@ public class AllTests(WebApplicationFactory factory, ITestOutputHelper /// [InlineData(default)] [Theory] - public Task> GetRecordBarChartAsync(QueryReq req) + public Task> GetRecordBarChartAsync(QueryReq req) { return default; } @@ -1107,8 +1107,7 @@ public class AllTests(WebApplicationFactory factory, ITestOutputHelper /// [InlineData(default)] [Theory] - public Task> GetRecordPieChartByHttpStatusCodeAsync( - QueryReq req) + public Task> GetRecordPieChartByHttpStatusCodeAsync(QueryReq req) { return default; } @@ -1116,7 +1115,7 @@ public class AllTests(WebApplicationFactory factory, ITestOutputHelper /// [InlineData(default)] [Theory] - public Task> GetRecordPieChartByNameAsync(QueryReq req) + public Task> GetRecordPieChartByNameAsync(QueryReq req) { return default; } diff --git a/src/backend/NetAdmin.Application/Services/RepositoryService.cs b/src/backend/NetAdmin.Application/Services/RepositoryService.cs index a5f35eb5..62763bae 100644 --- a/src/backend/NetAdmin.Application/Services/RepositoryService.cs +++ b/src/backend/NetAdmin.Application/Services/RepositoryService.cs @@ -30,6 +30,46 @@ public abstract class RepositoryService(BasicReposit set => Rpo.DbContextOptions.EnableCascadeSave = value; } + /// + /// 导出实体 + /// + protected async Task ExportAsync( // + Func, ISelect> selector, QueryReq query, string fileName + , Expression> listExp = null) + where TQuery : DataAbstraction, new() + { + var select = selector(query) + #if DBTYPE_SQLSERVER + .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) + #endif + .Take(Numbers.MAX_LIMIT_EXPORT); + + object list = listExp == null + ? await select.ToListAsync().ConfigureAwait(false) + : await select.ToListAsync(listExp).ConfigureAwait(false); + + var listTyped = list.Adapt>(); + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); + csv.WriteHeader(); + await csv.NextRecordAsync().ConfigureAwait(false); + + foreach (var item in listTyped) { + csv.WriteRecord(item); + await csv.NextRecordAsync().ConfigureAwait(false); + } + + await csv.FlushAsync().ConfigureAwait(false); + _ = stream.Seek(0, SeekOrigin.Begin); + + App.HttpContext.Response.Headers.ContentDisposition + = new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) { + FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv" + }.ToString(); + return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM); + } + /// /// 更新实体 /// @@ -77,43 +117,6 @@ public abstract class RepositoryService(BasicReposit } #endif - /// - /// 导出实体 - /// - protected async Task ExportAsync( // - Func, ISelect> selector, QueryReq query, string fileName) - where TQuery : DataAbstraction, new() - { - var data = await selector(query) - #if DBTYPE_SQLSERVER - .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) - #endif - .Take(Numbers.MAX_LIMIT_EXPORT) - .ToListAsync() - .ConfigureAwait(false); - - var list = data.Adapt>(); - var stream = new MemoryStream(); - var writer = new StreamWriter(stream); - var csv = new CsvWriter(writer, CultureInfo.InvariantCulture); - csv.WriteHeader(); - await csv.NextRecordAsync().ConfigureAwait(false); - - foreach (var item in list) { - csv.WriteRecord(item); - await csv.NextRecordAsync().ConfigureAwait(false); - } - - await csv.FlushAsync().ConfigureAwait(false); - _ = stream.Seek(0, SeekOrigin.Begin); - - App.HttpContext.Response.Headers.ContentDisposition - = new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) { - FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv" - }.ToString(); - return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM); - } - private IUpdate BuildUpdate( // TEntity entity // , IEnumerable includeFields // diff --git a/src/backend/NetAdmin.Cache/DistributedCache.cs b/src/backend/NetAdmin.Cache/DistributedCache.cs index 14bbb27d..e4d7c510 100644 --- a/src/backend/NetAdmin.Cache/DistributedCache.cs +++ b/src/backend/NetAdmin.Cache/DistributedCache.cs @@ -70,7 +70,8 @@ public abstract class DistributedCache(IDistributedCache cache, TServi , TimeSpan? slideLifeTime = null) { var cacheRead = await GetAsync(key).ConfigureAwait(false); - if (cacheRead is not null) { + if (cacheRead is not null && App.HttpContext?.Request.Headers.CacheControl.FirstOrDefault() != + Chars.FLG_HTTP_HEADER_VALUE_NO_CACHE) { return cacheRead; } diff --git a/src/backend/NetAdmin.Domain/Contexts/ContextUserToken.cs b/src/backend/NetAdmin.Domain/Contexts/ContextUserToken.cs index a8415d56..c7dc47ad 100644 --- a/src/backend/NetAdmin.Domain/Contexts/ContextUserToken.cs +++ b/src/backend/NetAdmin.Domain/Contexts/ContextUserToken.cs @@ -7,6 +7,12 @@ namespace NetAdmin.Domain.Contexts; /// public sealed record ContextUserToken : DataAbstraction { + /// + /// 部门编号 + /// + /// ReSharper disable once MemberCanBePrivate.Global + public long DeptId { get; init; } + /// /// 用户编号 /// @@ -39,6 +45,8 @@ public sealed record ContextUserToken : DataAbstraction /// public static ContextUserToken Create(QueryUserRsp user) { - return new ContextUserToken { Id = user.Id, Token = user.Token, UserName = user.UserName }; + return new ContextUserToken { + Id = user.Id, Token = user.Token, UserName = user.UserName, DeptId = user.DeptId + }; } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClientIp.cs b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClientIp.cs new file mode 100644 index 00000000..26739390 --- /dev/null +++ b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClientIp.cs @@ -0,0 +1,12 @@ +namespace NetAdmin.Domain.DbMaps.Dependency.Fields; + +/// +/// 创建者客户端IP字段接口 +/// +public interface IFieldCreatedClientIp +{ + /// + /// 创建者客户端IP + /// + int? CreatedClientIp { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClientUserAgent.cs similarity index 52% rename from src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs rename to src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClientUserAgent.cs index 1a8cb3c4..8c1b2f40 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClientUserAgent.cs @@ -1,15 +1,10 @@ namespace NetAdmin.Domain.DbMaps.Dependency.Fields; /// -/// 创建者客户端字段接口 +/// 创建者客户端用户代理字段接口 /// -public interface IFieldCreatedClient +public interface IFieldCreatedClientUserAgent { - /// - /// 创建者客户端IP - /// - int? CreatedClientIp { get; init; } - /// /// 创建者客户端用户代理 /// diff --git a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClientIp.cs b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClientIp.cs new file mode 100644 index 00000000..a301488d --- /dev/null +++ b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClientIp.cs @@ -0,0 +1,12 @@ +namespace NetAdmin.Domain.DbMaps.Dependency.Fields; + +/// +/// 修改客户端IP字段接口 +/// +public interface IFieldModifiedClientIp +{ + /// + /// 客户端IP + /// + int ModifiedClientIp { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClient.cs b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClientUserAgent.cs similarity index 53% rename from src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClient.cs rename to src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClientUserAgent.cs index f95d2c25..74fb59dc 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClient.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldModifiedClientUserAgent.cs @@ -1,15 +1,10 @@ namespace NetAdmin.Domain.DbMaps.Dependency.Fields; /// -/// 修改客户端字段接口 +/// 修改客户端用户代理字段接口 /// -public interface IFieldModifiedClient +public interface IFieldModifiedClientUserAgent { - /// - /// 客户端IP - /// - int ModifiedClientIp { get; init; } - /// /// 客户端用户代理 /// diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Api.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Api.cs index e538c007..e499e2af 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Api.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Api.cs @@ -4,6 +4,7 @@ namespace NetAdmin.Domain.DbMaps.Sys; /// Api接口表 /// [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Api))] +[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(PathCrc32), nameof(PathCrc32), true)] public record Sys_Api : ImmutableEntity, IFieldSummary { /// @@ -54,6 +55,14 @@ public record Sys_Api : ImmutableEntity, IFieldSummary [JsonIgnore] public virtual string ParentId { get; init; } + /// + /// 路径CRC32 + /// + [Column] + [Ignore] + [JsonIgnore] + public int PathCrc32 { get; init; } + /// /// 角色集合 /// diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicCatalog.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicCatalog.cs index 64bdba5a..3524a734 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicCatalog.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicCatalog.cs @@ -18,7 +18,7 @@ public record Sys_DicCatalog : VersionEntity /// /// 字典编码 /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)] + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)] [Ignore] [JsonIgnore] public virtual string Code { get; init; } @@ -34,7 +34,7 @@ public record Sys_DicCatalog : VersionEntity /// /// 字典名称 /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)] + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)] [Ignore] [JsonIgnore] public virtual string Name { get; init; } diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicContent.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicContent.cs index f38f8035..fdbf8b31 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicContent.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_DicContent.cs @@ -4,8 +4,6 @@ namespace NetAdmin.Domain.DbMaps.Sys; /// 字典内容表 /// [SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(CatalogId)}_{nameof(Key)}", $"{nameof(CatalogId)},{nameof(Key)}", true)] -[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(CatalogId)}_{nameof(Value)}", $"{nameof(CatalogId)},{nameof(Value)}" - , true)] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_DicContent))] public record Sys_DicContent : VersionEntity { diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Job.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Job.cs index d3bfa988..3e467221 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Job.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_Job.cs @@ -79,6 +79,22 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary [JsonIgnore] public virtual long? NextTimeId { get; init; } + /// + /// 随机延时起始值(毫秒) + /// + [Column] + [Ignore] + [JsonIgnore] + public virtual int? RandomDelayBegin { get; init; } + + /// + /// 随机延时结束值(毫秒) + /// + [Column] + [Ignore] + [JsonIgnore] + public virtual int? RandomDelayEnd { get; init; } + /// /// 请求体 /// diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs index 49e38fec..a66ad4c6 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs @@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys; /// 计划作业执行记录表 /// [SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)] -[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)] +[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), $"{nameof(CreatedTime)} DESC", false)] [SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(JobId), nameof(JobId), false)] [SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))] diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs index b700a58f..faec5f61 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs @@ -1,36 +1,38 @@ +using HttpMethods = NetAdmin.Domain.Enums.HttpMethods; + namespace NetAdmin.Domain.DbMaps.Sys; /// /// 请求日志表 /// -[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)] -[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)] -[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(UserId), nameof(UserId), false)] -[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)] +[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))] -public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedClient +public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFieldCreatedClientIp { /// /// 接口 /// [Ignore] [JsonIgnore] - [Navigate(nameof(ApiId))] + [Navigate(nameof(ApiPathCrc32), TempPrimary = nameof(Sys_Api.PathCrc32))] public Sys_Api Api { get; init; } /// - /// 接口编号 + /// 接口路径CRC32 /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)] + [Column] [Ignore] [JsonIgnore] - public virtual string ApiId { get; init; } + public virtual int ApiPathCrc32 { get; init; } /// - [Column(Position = -1)] + [Column] [Ignore] [JsonIgnore] - public int? CreatedClientIp { get; init; } + public virtual int? CreatedClientIp { get; init; } /// [Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)] @@ -38,161 +40,55 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedCli [JsonIgnore] public virtual DateTime CreatedTime { get; init; } - /// - #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 + /// + /// 明细 + /// [Ignore] [JsonIgnore] - public virtual string CreatedUserAgent { get; init; } + [Navigate(nameof(Id))] + public Sys_RequestLogDetail Detail { get; init; } /// - /// 执行耗时(微秒) + /// 执行耗时(毫秒) /// [Column] [Ignore] [JsonIgnore] - public virtual long Duration { get; init; } + public virtual int Duration { get; init; } /// - /// 程序响应码 + /// 请求方法 /// - [Column] + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_TINY_INT)] [Ignore] [JsonIgnore] - public virtual ErrorCodes ErrorCode { get; init; } - - /// - /// 异常信息 - /// - #if DBTYPE_SQLSERVER - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] - #else - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - #endif - [Ignore] - [JsonIgnore] - public virtual string Exception { get; init; } + public virtual HttpMethods HttpMethod { get; init; } /// /// HTTP状态码 /// - [Column] + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_SMALL_INT)] [Ignore] [JsonIgnore] public virtual int HttpStatusCode { get; init; } /// - /// 请求方法 + /// 拥有者 /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)] [Ignore] [JsonIgnore] - public virtual string Method { get; init; } + [Navigate(nameof(OwnerId))] + public Sys_User Owner { get; init; } - /// - /// 请求内容 - /// - #if DBTYPE_SQLSERVER - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] - #else - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - #endif - [Ignore] - [JsonIgnore] - public virtual string RequestBody { get; init; } - - /// - /// 请求content-type - /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)] - [Ignore] - [JsonIgnore] - public virtual string RequestContentType { get; init; } - - /// - /// 请求头信息 - /// - #if DBTYPE_SQLSERVER - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] - #else - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - #endif - [Ignore] - [JsonIgnore] - public virtual string RequestHeaders { get; init; } - - /// - /// 请求地址 - /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)] - [Ignore] - [JsonIgnore] - public virtual string RequestUrl { get; init; } - - /// - /// 响应内容 - /// - #if DBTYPE_SQLSERVER - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] - #else - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - #endif - [Ignore] - [JsonIgnore] - public virtual string ResponseBody { get; init; } - - /// - /// 响应content-type - /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)] - [Ignore] - [JsonIgnore] - public virtual string ResponseContentType { get; init; } - - /// - /// 响应头 - /// - #if DBTYPE_SQLSERVER - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] - #else - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - #endif - [Ignore] - [JsonIgnore] - public virtual string ResponseHeaders { get; init; } - - /// - /// 服务器IP - /// + /// [Column] [Ignore] [JsonIgnore] - public virtual int? ServerIp { get; init; } + public virtual long? OwnerDeptId { get; init; } - /// - /// 请求跟踪标识 - /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)] - [Ignore] - [JsonIgnore] - public virtual string TraceId { get; init; } - - /// - /// 用户 - /// - [Ignore] - [JsonIgnore] - [Navigate(nameof(UserId))] - public Sys_User User { get; init; } - - /// - /// 用户编号 - /// + /// [Column] [Ignore] [JsonIgnore] - public virtual long? UserId { get; init; } + public virtual long? OwnerId { get; init; } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLogDetail.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLogDetail.cs new file mode 100644 index 00000000..6183ec24 --- /dev/null +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLogDetail.cs @@ -0,0 +1,132 @@ +namespace NetAdmin.Domain.DbMaps.Sys; + +/// +/// 请求日志明细表 +/// +[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLogDetail))] +public record Sys_RequestLogDetail : SimpleEntity, IFieldCreatedTime, IFieldCreatedClientUserAgent +{ + /// + [Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)] + [Ignore] + [JsonIgnore] + public virtual DateTime CreatedTime { get; init; } + + /// + #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 + [Ignore] + [JsonIgnore] + public virtual string CreatedUserAgent { get; init; } + + /// + /// 程序响应码 + /// + [Column] + [Ignore] + [JsonIgnore] + public virtual ErrorCodes ErrorCode { get; init; } + + /// + /// 异常信息 + /// + #if DBTYPE_SQLSERVER + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] + #else + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] + #endif + [Ignore] + [JsonIgnore] + public virtual string Exception { get; init; } + + /// + /// 请求内容 + /// + #if DBTYPE_SQLSERVER + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] + #else + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] + #endif + [Ignore] + [JsonIgnore] + public virtual string RequestBody { get; init; } + + /// + /// 请求content-type + /// + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)] + [Ignore] + [JsonIgnore] + public virtual string RequestContentType { get; init; } + + /// + /// 请求头信息 + /// + #if DBTYPE_SQLSERVER + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] + #else + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] + #endif + [Ignore] + [JsonIgnore] + public virtual string RequestHeaders { get; init; } + + /// + /// 请求地址 + /// + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)] + [Ignore] + [JsonIgnore] + public virtual string RequestUrl { get; init; } + + /// + /// 响应内容 + /// + #if DBTYPE_SQLSERVER + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] + #else + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] + #endif + [Ignore] + [JsonIgnore] + public virtual string ResponseBody { get; init; } + + /// + /// 响应content-type + /// + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)] + [Ignore] + [JsonIgnore] + public virtual string ResponseContentType { get; init; } + + /// + /// 响应头 + /// + #if DBTYPE_SQLSERVER + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] + #else + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] + #endif + [Ignore] + [JsonIgnore] + public virtual string ResponseHeaders { get; init; } + + /// + /// 服务器IP + /// + [Column] + [Ignore] + [JsonIgnore] + public virtual int? ServerIp { get; init; } + + /// + /// 请求跟踪标识 + /// + [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)] + [Ignore] + [JsonIgnore] + public virtual string TraceId { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Dependency/DynamicFilterInfo.cs b/src/backend/NetAdmin.Domain/Dto/Dependency/DynamicFilterInfo.cs index 607f2ed0..053e0d93 100644 --- a/src/backend/NetAdmin.Domain/Dto/Dependency/DynamicFilterInfo.cs +++ b/src/backend/NetAdmin.Domain/Dto/Dependency/DynamicFilterInfo.cs @@ -37,6 +37,42 @@ public sealed record DynamicFilterInfo : DataAbstraction /// public static implicit operator FreeSql.Internal.Model.DynamicFilterInfo(DynamicFilterInfo d) { - return d.Adapt(); + var ret = d.Adapt(); + ProcessDynamicFilter(ret); + return ret; + } + + private static void ProcessDynamicFilter(FreeSql.Internal.Model.DynamicFilterInfo d) + { + if (d?.Filters != null) { + foreach (var filterInfo in d.Filters) { + ProcessDynamicFilter(filterInfo); + } + } + + if (d?.Operator != DynamicFilterOperator.DateRange) { + return; + } + + var values = ((JsonElement)d.Value).Deserialize(); + if (!DateTime.TryParse(values[0], CultureInfo.InvariantCulture, out _)) { + var result = values[0] + .ExecuteCSharpCodeAsync([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([typeof(DateTime).Assembly], nameof(System)) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + values[1] = $"{result:yyyy-MM-dd HH:mm:ss}"; + } + + d.Value = values; } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/Job/CreateJobReq.cs b/src/backend/NetAdmin.Domain/Dto/Sys/Job/CreateJobReq.cs index a736fabc..92a84e57 100644 --- a/src/backend/NetAdmin.Domain/Dto/Sys/Job/CreateJobReq.cs +++ b/src/backend/NetAdmin.Domain/Dto/Sys/Job/CreateJobReq.cs @@ -32,6 +32,14 @@ public record CreateJobReq : Sys_Job /// public override long? NextTimeId { get; init; } + /// + [Range(1, int.MaxValue, ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.随机延时起始时间不正确))] + public override int? RandomDelayBegin { get; init; } + + /// + [Range(1, int.MaxValue, ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.随机延时结束时间不正确))] + public override int? RandomDelayEnd { get; init; } + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string RequestBody { get; init; } diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/Job/QueryJobRsp.cs b/src/backend/NetAdmin.Domain/Dto/Sys/Job/QueryJobRsp.cs index b8026dd6..7372ab45 100644 --- a/src/backend/NetAdmin.Domain/Dto/Sys/Job/QueryJobRsp.cs +++ b/src/backend/NetAdmin.Domain/Dto/Sys/Job/QueryJobRsp.cs @@ -90,6 +90,14 @@ public record QueryJobRsp : Sys_Job [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override long? NextTimeId { get; init; } + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override int? RandomDelayBegin { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override int? RandomDelayEnd { get; init; } + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string RequestBody { get; init; } diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/CreateRequestLogReq.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/CreateRequestLogReq.cs index f0000cd3..18823b8c 100644 --- a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/CreateRequestLogReq.cs +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/CreateRequestLogReq.cs @@ -1,6 +1,12 @@ +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; + namespace NetAdmin.Domain.Dto.Sys.RequestLog; /// /// 请求:创建请求日志 /// -public sealed record CreateRequestLogReq : Sys_RequestLog; \ No newline at end of file +public sealed record CreateRequestLogReq : Sys_RequestLog +{ + /// + public new CreateRequestLogDetailReq Detail { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/ExportRequestLogRsp.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/ExportRequestLogRsp.cs index c7dcfd7f..9be1816c 100644 --- a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/ExportRequestLogRsp.cs +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/ExportRequestLogRsp.cs @@ -1,4 +1,7 @@ +using NetAdmin.Domain.Dto.Sys.Api; +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; using NetAdmin.Domain.Dto.Sys.User; +using HttpMethods = NetAdmin.Domain.Enums.HttpMethods; namespace NetAdmin.Domain.Dto.Sys.RequestLog; @@ -7,39 +10,51 @@ namespace NetAdmin.Domain.Dto.Sys.RequestLog; /// public record ExportRequestLogRsp : QueryRequestLogRsp { + /// + /// 接口路径 + /// + [CsvIndex(2)] + [JsonInclude] + [Name(nameof(Ln.接口路径))] + public string ApiId => Api.Id; + /// [CsvIndex(6)] [Ignore(false)] [Name(nameof(Ln.客户端IP))] public override string CreatedClientIp => base.CreatedClientIp; + /// + /// 用户名 + /// + [CsvIndex(5)] + [JsonInclude] + [Name(nameof(Ln.用户名))] + public string UserName => Owner?.UserName; + /// [Ignore] - public override string LoginName => base.LoginName; + public override QueryApiRsp Api { get; init; } /// - [CsvIndex(7)] - [Ignore(false)] - [Name(nameof(Ln.操作系统))] - public override string Os => base.Os; + [Ignore] + public override DateTime CreatedTime { get; init; } /// - [CsvIndex(2)] - [Ignore(false)] - [Name(nameof(Ln.接口路径))] - public override string ApiId { get; init; } - - /// - [CsvIndex(8)] - [Ignore(false)] - [Name(nameof(Ln.用户代理))] - public override string CreatedUserAgent { get; init; } + [Ignore] + public override QueryRequestLogDetailRsp Detail { get; init; } /// [CsvIndex(4)] [Ignore(false)] [Name(nameof(Ln.执行耗时))] - public override long Duration { get; init; } + public override int Duration { get; init; } + + /// + [CsvIndex(3)] + [Ignore(false)] + [Name(nameof(Ln.请求方式))] + public override HttpMethods HttpMethod { get; init; } /// [CsvIndex(1)] @@ -54,32 +69,14 @@ public record ExportRequestLogRsp : QueryRequestLogRsp public override long Id { get; init; } /// - [CsvIndex(3)] - [Ignore(false)] - [Name(nameof(Ln.请求方式))] - public override string Method { get; init; } - - /// - [CsvIndex(9)] - [Ignore(false)] - [Name(nameof(Ln.跟踪编号))] - public override string TraceId { get; init; } + [Ignore] + public override QueryUserLiteRsp Owner { get; init; } /// [Ignore] - public override QueryUserRsp User { get; init; } - - /// - /// 用户名 - /// - [CsvIndex(5)] - [Ignore(false)] - [Name(nameof(Ln.用户名))] - public string UserName { get; init; } + public override long? OwnerDeptId { get; init; } /// - public override void Register(TypeAdapterConfig config) - { - _ = config.ForType().Map(d => d.UserName, s => s.User.UserName); - } + [Ignore] + public override long? OwnerId { get; init; } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs index e112cc12..6978037e 100644 --- a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs @@ -1,11 +1,14 @@ +using NetAdmin.Domain.Dto.Sys.Api; +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; using NetAdmin.Domain.Dto.Sys.User; +using HttpMethods = NetAdmin.Domain.Enums.HttpMethods; namespace NetAdmin.Domain.Dto.Sys.RequestLog; /// /// 响应:查询请求日志 /// -public record QueryRequestLogRsp : Sys_RequestLog, IRegister +public record QueryRequestLogRsp : Sys_RequestLog { /// /// 创建者客户端IP @@ -13,101 +16,39 @@ public record QueryRequestLogRsp : Sys_RequestLog, IRegister [JsonInclude] public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4(); - /// - /// 登录名 - /// - [JsonInclude] - public virtual string LoginName => RequestBody?.ToObject()?.Account; - - /// - /// 操作系统 - /// - [JsonInclude] - public virtual string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform; - - /// + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string ApiId { get; init; } + public new virtual QueryApiRsp Api { get; init; } - /// - /// 接口描述 - /// - public string ApiSummary { get; init; } + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override int ApiPathCrc32 { get; init; } /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override DateTime CreatedTime { get; init; } - /// + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string CreatedUserAgent { get; init; } + public new virtual QueryRequestLogDetailRsp Detail { get; init; } /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public override long Duration { get; init; } + public override int Duration { get; init; } - /// + /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public override ErrorCodes ErrorCode { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string Exception { get; init; } + public override HttpMethods HttpMethod { get; init; } /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override int HttpStatusCode { get; init; } - /// + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string Method { get; init; } + public new virtual QueryUserLiteRsp Owner { get; init; } - /// + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string RequestBody { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string RequestContentType { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string RequestHeaders { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string RequestUrl { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string ResponseBody { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string ResponseContentType { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string ResponseHeaders { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override int? ServerIp { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string TraceId { get; init; } - - /// - public new virtual QueryUserRsp User { get; init; } - - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override long? UserId { get; init; } - - /// - public virtual void Register(TypeAdapterConfig config) - { - _ = config.ForType().Map(d => d.ApiSummary, s => s.Api.Summary); - } + public override long? OwnerId { get; init; } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/CreateRequestLogDetailReq.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/CreateRequestLogDetailReq.cs new file mode 100644 index 00000000..dfe5c113 --- /dev/null +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/CreateRequestLogDetailReq.cs @@ -0,0 +1,6 @@ +namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail; + +/// +/// 请求:创建请求日志明细 +/// +public record CreateRequestLogDetailReq : Sys_RequestLogDetail; \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/QueryRequestLogDetailReq.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/QueryRequestLogDetailReq.cs new file mode 100644 index 00000000..6010cf48 --- /dev/null +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/QueryRequestLogDetailReq.cs @@ -0,0 +1,11 @@ +namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail; + +/// +/// 请求:查询请求日志明细 +/// +public sealed record QueryRequestLogDetailReq : Sys_RequestLogDetail +{ + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Id { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/QueryRequestLogDetailRsp.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/QueryRequestLogDetailRsp.cs new file mode 100644 index 00000000..1b04be0e --- /dev/null +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLogDetail/QueryRequestLogDetailRsp.cs @@ -0,0 +1,73 @@ +using NetAdmin.Domain.Dto.Sys.User; + +namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail; + +/// +/// 响应:查询请求日志明细 +/// +public sealed record QueryRequestLogDetailRsp : Sys_RequestLogDetail +{ + /// + /// 登录名 + /// + [JsonInclude] + public string LoginName => RequestBody?.ToObject()?.Account; + + /// + /// 操作系统 + /// + [JsonInclude] + public string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform; + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string CreatedUserAgent { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override ErrorCodes ErrorCode { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string Exception { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Id { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string RequestBody { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string RequestContentType { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string RequestHeaders { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string RequestUrl { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string ResponseBody { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string ResponseContentType { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string ResponseHeaders { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override int? ServerIp { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string TraceId { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/User/QueryUserLiteRsp.cs b/src/backend/NetAdmin.Domain/Dto/Sys/User/QueryUserLiteRsp.cs new file mode 100644 index 00000000..13e5eff0 --- /dev/null +++ b/src/backend/NetAdmin.Domain/Dto/Sys/User/QueryUserLiteRsp.cs @@ -0,0 +1,15 @@ +namespace NetAdmin.Domain.Dto.Sys.User; + +/// +/// 响应:查询用户精简版 +/// +public record QueryUserLiteRsp : Sys_User +{ + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public override long Id { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override string UserName { get; init; } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Events/SqlCommandAfterEvent.cs b/src/backend/NetAdmin.Domain/Events/SqlCommandAfterEvent.cs index a494807d..a320670f 100644 --- a/src/backend/NetAdmin.Domain/Events/SqlCommandAfterEvent.cs +++ b/src/backend/NetAdmin.Domain/Events/SqlCommandAfterEvent.cs @@ -11,20 +11,19 @@ public sealed record SqlCommandAfterEvent : SqlCommandBeforeEvent public SqlCommandAfterEvent(CommandAfterEventArgs e) // : base(e) { - ElapsedMicroseconds = (long)((double)e.ElapsedTicks / Stopwatch.Frequency * 1_000_000); + ElapsedMilliseconds = (long)((double)e.ElapsedTicks / Stopwatch.Frequency * 1_000); EventId = nameof(SqlCommandAfterEvent); } /// - /// 耗时(单位:微秒) + /// 耗时(单位:毫秒) /// /// de - private long ElapsedMicroseconds { get; } + private long ElapsedMilliseconds { get; } /// public override string ToString() { - return string.Format(CultureInfo.InvariantCulture, "SQL-{0}: {2} ms {1}", Id - , Sql?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_SQL), ElapsedMicroseconds / 1000); + return string.Format(CultureInfo.InvariantCulture, "SQL-{0}: {2} ms {1}", Id, Sql, ElapsedMilliseconds); } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/NetAdmin.Domain.csproj b/src/backend/NetAdmin.Domain/NetAdmin.Domain.csproj index 164b150a..89ba658b 100644 --- a/src/backend/NetAdmin.Domain/NetAdmin.Domain.csproj +++ b/src/backend/NetAdmin.Domain/NetAdmin.Domain.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/backend/NetAdmin.Host/BackgroundRunning/WorkBase.cs b/src/backend/NetAdmin.Host/BackgroundRunning/WorkBase.cs index 4f1d8d04..334067f8 100644 --- a/src/backend/NetAdmin.Host/BackgroundRunning/WorkBase.cs +++ b/src/backend/NetAdmin.Host/BackgroundRunning/WorkBase.cs @@ -35,20 +35,15 @@ public abstract class WorkBase /// protected UnitOfWorkManager UowManager { get; } - /// - /// 获取锁 - /// - protected Task GetLockerAsync(string lockId) - { - 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)); - } - /// /// 通用工作流 /// - protected abstract ValueTask WorkflowAsync(CancellationToken cancelToken); + protected abstract ValueTask WorkflowAsync( // + + // ReSharper disable once UnusedParameter.Global + #pragma warning disable SA1114 + CancellationToken cancelToken); + #pragma warning restore SA1114 /// /// 通用工作流 @@ -69,4 +64,14 @@ public abstract class WorkBase await WorkflowAsync(cancelToken).ConfigureAwait(false); } + + /// + /// 获取锁 + /// + private Task GetLockerAsync(string lockId) + { + 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)); + } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Host/Middlewares/RequestAuditMiddleware.cs b/src/backend/NetAdmin.Host/Middlewares/RequestAuditMiddleware.cs index 3fba2bd5..b60ebc22 100644 --- a/src/backend/NetAdmin.Host/Middlewares/RequestAuditMiddleware.cs +++ b/src/backend/NetAdmin.Host/Middlewares/RequestAuditMiddleware.cs @@ -58,7 +58,7 @@ public sealed class RequestAuditMiddleware( .FirstOrDefault() ?.Enum() ?? 0; - _ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMicroseconds, responseBody, errorCode + _ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMilliseconds, responseBody, errorCode , exception) .ConfigureAwait(false); } diff --git a/src/backend/NetAdmin.Host/NetAdmin.Host.csproj b/src/backend/NetAdmin.Host/NetAdmin.Host.csproj index 59c35c58..938dfc6e 100644 --- a/src/backend/NetAdmin.Host/NetAdmin.Host.csproj +++ b/src/backend/NetAdmin.Host/NetAdmin.Host.csproj @@ -5,7 +5,7 @@ - + \ No newline at end of file diff --git a/src/backend/NetAdmin.Host/Utils/RequestLogger.cs b/src/backend/NetAdmin.Host/Utils/RequestLogger.cs index 75f7d039..a60b3b28 100644 --- a/src/backend/NetAdmin.Host/Utils/RequestLogger.cs +++ b/src/backend/NetAdmin.Host/Utils/RequestLogger.cs @@ -1,6 +1,9 @@ using NetAdmin.Domain.Contexts; using NetAdmin.Domain.Dto.Sys.RequestLog; +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; using NetAdmin.Domain.Events.Sys; +using Yitter.IdGenerator; +using HttpMethods = NetAdmin.Domain.Enums.HttpMethods; namespace NetAdmin.Host.Utils; @@ -19,36 +22,39 @@ public sealed class RequestLogger(ILogger logger, IEventPublisher { // 从请求头中读取用户信息 var associatedUser = GetAssociatedUser(context); - var auditData = new CreateRequestLogReq { - Duration = duration - , Method = context.Request.Method - , RequestContentType = context.Request.ContentType - , RequestBody = Array.Exists( // - _textContentTypes - , x => context.Request.ContentType?.Contains( - x, StringComparison.OrdinalIgnoreCase) ?? false) - ? (await context.ReadBodyContentAsync().ConfigureAwait(false)) - ?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT) - : string.Empty - , RequestUrl = context.Request.GetRequestUrlAddress() - , ResponseBody - = responseBody?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT) - , ServerIp = context.GetLocalIpAddressToIPv4()?.IpV4ToInt32() - , ApiId = context.Request.Path.Value?.TrimStart('/') - , RequestHeaders = context.Request.Headers.Json() - , ResponseContentType = context.Response.ContentType - , ResponseHeaders = context.Response.Headers.Json() - , HttpStatusCode = context.Response.StatusCode - , ErrorCode = errorCode - , Exception = exception?.Error.ToString() - , UserId = associatedUser?.UserId - , CreatedUserAgent = context.Request.Headers.UserAgent.ToString() - , CreatedClientIp = context.GetRealIpAddress() - ?.MapToIPv4() - .ToString() - .IpV4ToInt32() - , TraceId = context.TraceIdentifier - }; + var id = YitIdHelper.NextId(); + var requestBody = Array.Exists( // + _textContentTypes + , x => context.Request.ContentType?.Contains(x, StringComparison.OrdinalIgnoreCase) ?? false) + ? await context.ReadBodyContentAsync().ConfigureAwait(false) + : string.Empty; + var auditData = new CreateRequestLogReq // + { + Detail = new CreateRequestLogDetailReq // + { + Id = id + , CreatedUserAgent = context.Request.Headers.UserAgent.ToString() + , ErrorCode = errorCode + , Exception = exception?.Error.ToString() + , RequestBody = requestBody + , RequestContentType = context.Request.ContentType + , RequestHeaders = context.Request.Headers.Json() + , RequestUrl = context.Request.GetRequestUrlAddress() + , ResponseBody = responseBody + , ResponseContentType = context.Response.ContentType + , ResponseHeaders = context.Response.Headers.Json() + , ServerIp = context.GetLocalIpAddressToIPv4()?.IpV4ToInt32() + , TraceId = context.TraceIdentifier + } + , Duration = (int)duration + , HttpMethod = Enum.Parse(context.Request.Method, true) + , ApiPathCrc32 = context.Request.Path.Value!.TrimStart('/').Crc32() + , HttpStatusCode = context.Response.StatusCode + , CreatedClientIp = context.GetRealIpAddress()?.MapToIPv4().ToString().IpV4ToInt32() + , OwnerId = associatedUser?.UserId + , OwnerDeptId = associatedUser?.DeptId + , Id = id + }; // 打印日志 logger.Info(auditData); @@ -59,7 +65,7 @@ public sealed class RequestLogger(ILogger logger, IEventPublisher return auditData; } - private (long UserId, string UserName)? GetAssociatedUser(HttpContext context) + private (long UserId, long DeptId, string UserName)? GetAssociatedUser(HttpContext context) { var token = context.Request.Headers.Authorization.FirstOrDefault(); if (token == null) { @@ -69,7 +75,7 @@ public sealed class RequestLogger(ILogger logger, IEventPublisher ContextUserToken userToken = null; try { var jsonWebToken - = JWTEncryption.ReadJwtToken(token.TrimStart($"{Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA} ")); + = 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(); } @@ -77,6 +83,6 @@ public sealed class RequestLogger(ILogger logger, IEventPublisher logger.Warn($"{Ln.读取用户令牌出错}: {ex}"); } - return userToken == null ? null : (userToken.Id, userToken.UserName); + return userToken == null ? null : (userToken.Id, userToken.DeptId, userToken.UserName); } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs b/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs index 383342a5..b67c4b94 100644 --- a/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs +++ b/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs @@ -104,10 +104,10 @@ public sealed class SqlAuditor : ISingleton case nameof(IFieldCreatedUser.CreatedUserName): SetCreatedUserName(e, userInfo); break; - case nameof(IFieldCreatedClient.CreatedClientIp): + case nameof(IFieldCreatedClientIp.CreatedClientIp): SetCreatedClientIp(e); break; - case nameof(IFieldCreatedClient.CreatedUserAgent): + case nameof(IFieldCreatedClientUserAgent.CreatedUserAgent): SetCreatedUserAgent(e); break; default: diff --git a/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs b/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs index d2850dd3..4fde6e5e 100644 --- a/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs +++ b/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs @@ -29,6 +29,7 @@ public static class Chars public const string FLG_DB_FIELD_TYPE_NVARCHAR_MAX = "nvarchar(max)"; public const string FLG_DB_FIELD_TYPE_SMALL_INT = "smallint"; public const string FLG_DB_FIELD_TYPE_TEXT = "text"; + public const string FLG_DB_FIELD_TYPE_TINY_INT = "tinyint"; public const string FLG_DB_FIELD_TYPE_VARCHAR = "varchar"; public const string FLG_DB_FIELD_TYPE_VARCHAR_1022 = "varchar(1022)"; public const string FLG_DB_FIELD_TYPE_VARCHAR_127 = "varchar(127)"; @@ -54,6 +55,7 @@ public static class Chars public const string FLG_HTTP_HEADER_KEY_USER_AGENT = "User-Agent"; public const string FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN = "X-ACCESS-TOKEN"; public const string FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN_HEADER_KEY = "X-Authorization"; + public const string FLG_HTTP_HEADER_KEY_X_CACHE_CONTROL = "X-Cache-Control"; public const string FLG_HTTP_HEADER_KEY_X_FORWARDED_FOR = "X-Forwarded-For"; public const string FLG_HTTP_HEADER_KEY_X_REAL_IP = "X-Real-IP"; public const string FLG_HTTP_HEADER_VALUE_APPLICATION_JSON = "application/json"; @@ -61,6 +63,7 @@ public static class Chars public const string FLG_HTTP_HEADER_VALUE_APPLICATION_URLENCODED = "application/x-www-form-urlencoded"; public const string FLG_HTTP_HEADER_VALUE_ATTACHMENT = "attachment"; public const string FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA = "Bearer"; + public const string FLG_HTTP_HEADER_VALUE_NO_CACHE = "no-cache"; public const string FLG_HTTP_METHOD_CONNECT = "CONNECT"; public const string FLG_HTTP_METHOD_DELETE = "DELETE"; public const string FLG_HTTP_METHOD_GET = "GET"; diff --git a/src/backend/NetAdmin.Infrastructure/Constant/Numbers.cs b/src/backend/NetAdmin.Infrastructure/Constant/Numbers.cs index 3f7ce98a..bf4118aa 100644 --- a/src/backend/NetAdmin.Infrastructure/Constant/Numbers.cs +++ b/src/backend/NetAdmin.Infrastructure/Constant/Numbers.cs @@ -30,5 +30,6 @@ public static class Numbers 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; // 秒:超时时间-作业 } \ No newline at end of file diff --git a/src/backend/NetAdmin.Infrastructure/Extensions/HttpRequestMessageExtensions.cs b/src/backend/NetAdmin.Infrastructure/Extensions/HttpRequestMessageExtensions.cs index 0158b75d..560dca9c 100644 --- a/src/backend/NetAdmin.Infrastructure/Extensions/HttpRequestMessageExtensions.cs +++ b/src/backend/NetAdmin.Infrastructure/Extensions/HttpRequestMessageExtensions.cs @@ -10,8 +10,7 @@ public static class HttpRequestMessageExtensions /// public static async Task LogAsync(this HttpRequestMessage me, ILogger logger) { - logger.Info( - $"HTTP Request {(await me.BuildJsonAsync().ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}"); + logger.Info($"HTTP Request {await me.BuildJsonAsync().ConfigureAwait(false)}"); return me; } diff --git a/src/backend/NetAdmin.Infrastructure/Extensions/HttpResponseMessageExtensions.cs b/src/backend/NetAdmin.Infrastructure/Extensions/HttpResponseMessageExtensions.cs index cacbf0bc..4b31e1e4 100644 --- a/src/backend/NetAdmin.Infrastructure/Extensions/HttpResponseMessageExtensions.cs +++ b/src/backend/NetAdmin.Infrastructure/Extensions/HttpResponseMessageExtensions.cs @@ -11,8 +11,7 @@ public static class HttpResponseMessageExtensions public static async Task LogAsync(this HttpResponseMessage me, ILogger logger , Func bodyPreHandle = null) { - logger.Info($"HTTP Response {(await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false))?.Sub( - 0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}"); + logger.Info($"HTTP Response {await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false)}"); } /// @@ -21,8 +20,7 @@ public static class HttpResponseMessageExtensions public static async Task LogExceptionAsync(this HttpResponseMessage me, string errors, ILogger logger , Func bodyHandle = null) { - logger.Warn( - $"{errors}: {(await me.BuildJsonAsync(bodyHandle).ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}"); + logger.Warn($"{errors}: {await me.BuildJsonAsync(bodyHandle).ConfigureAwait(false)}"); } /// diff --git a/src/backend/NetAdmin.Infrastructure/Extensions/StringExtensions.cs b/src/backend/NetAdmin.Infrastructure/Extensions/StringExtensions.cs index 7da0d83d..27f6e836 100644 --- a/src/backend/NetAdmin.Infrastructure/Extensions/StringExtensions.cs +++ b/src/backend/NetAdmin.Infrastructure/Extensions/StringExtensions.cs @@ -1,3 +1,6 @@ +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; + namespace NetAdmin.Infrastructure.Extensions; /// @@ -5,8 +8,24 @@ namespace NetAdmin.Infrastructure.Extensions; /// public static class StringExtensions { - private static readonly Regex _regex = new("Options$"); - private static readonly Regex _regex2 = new("Async$"); + /// + /// 计算Crc32 + /// + public static int Crc32(this string me) + { + return BitConverter.ToInt32(System.IO.Hashing.Crc32.Hash(Encoding.UTF8.GetBytes(me))); + } + + /// + /// 执行C#代码 + /// + public static Task ExecuteCSharpCodeAsync(this string me, Assembly[] assemblies + , params string[] importNamespaces) + { + // 使用 Roslyn 编译并执行代码 + return CSharpScript.EvaluateAsync( + me, ScriptOptions.Default.WithReferences(assemblies).WithImports(importNamespaces)); + } /// /// object -> json @@ -31,7 +50,7 @@ public static class StringExtensions public static string TrimEndAsync(this string me) #pragma warning restore VSTHRD200, ASA002, RCS1047 { - return _regex2.Replace(me, string.Empty); + return TrimSuffix(me, "Async"); } /// @@ -39,14 +58,22 @@ public static class StringExtensions /// public static string TrimEndOptions(this string me) { - return _regex.Replace(me, string.Empty); + return TrimSuffix(me, "Options"); } /// /// 去掉前部字符串 /// - public static string TrimStart(this string me, string clearStr) + public static string TrimPrefix(this string me, string clearStr) { return Regex.Replace(me, $"^{clearStr}", string.Empty); } + + /// + /// 去掉尾部字符串 + /// + public static string TrimSuffix(this string me, string clearStr) + { + return Regex.Replace(me, $"{clearStr}$", string.Empty); + } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Infrastructure/GlobalStatic.cs b/src/backend/NetAdmin.Infrastructure/GlobalStatic.cs index b6b84253..360c80eb 100644 --- a/src/backend/NetAdmin.Infrastructure/GlobalStatic.cs +++ b/src/backend/NetAdmin.Infrastructure/GlobalStatic.cs @@ -24,11 +24,6 @@ public static class GlobalStatic #endif ; - /// - /// 日志保存跳过的API编号 - /// - public static string[] LogSavingSkipApiIds => ["api/probe/health.check", "api/adm/device.log/create"]; - /// /// 系统内部密钥 /// diff --git a/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj b/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj index 5a816980..a3cfa2b6 100644 --- a/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj +++ b/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj @@ -8,9 +8,10 @@ - - - + + + + diff --git a/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IJobModule.cs b/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IJobModule.cs index 0cddc190..28672120 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IJobModule.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IJobModule.cs @@ -42,17 +42,17 @@ public interface IJobModule : ICrudModule /// 获取作业记录条形图数据 /// - Task> GetRecordBarChartAsync(QueryReq req); + Task> GetRecordBarChartAsync(QueryReq req); /// /// 状态码分组作业记录饼图数据 /// - Task> GetRecordPieChartByHttpStatusCodeAsync(QueryReq req); + Task> GetRecordPieChartByHttpStatusCodeAsync(QueryReq req); /// /// 名称分组作业记录饼图数据 /// - Task> GetRecordPieChartByNameAsync(QueryReq req); + Task> GetRecordPieChartByNameAsync(QueryReq req); /// /// 分页查询作业记录 diff --git a/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogDetailModule.cs b/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogDetailModule.cs new file mode 100644 index 00000000..9599d737 --- /dev/null +++ b/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogDetailModule.cs @@ -0,0 +1,13 @@ +using NetAdmin.Application.Modules; +using NetAdmin.Domain.Dto.Dependency; +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; + +namespace NetAdmin.SysComponent.Application.Modules.Sys; + +/// +/// 请求日志明细模块 +/// +public interface IRequestLogDetailModule : ICrudModule; \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogModule.cs b/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogModule.cs index 392c8357..5c38652f 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogModule.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Modules/Sys/IRequestLogModule.cs @@ -16,15 +16,15 @@ public interface IRequestLogModule : ICrudModule /// 获取条形图数据 /// - Task> GetBarChartAsync(QueryReq req); + Task> GetBarChartAsync(QueryReq req); /// /// 描述分组饼图数据 /// - Task> GetPieChartByApiSummaryAsync(QueryReq req); + Task> GetPieChartByApiSummaryAsync(QueryReq req); /// /// 状态码分组饼图数据 /// - Task> GetPieChartByHttpStatusCodeAsync(QueryReq req); + Task> GetPieChartByHttpStatusCodeAsync(QueryReq req); } \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ApiService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ApiService.cs index 39ab4d4b..46b08e4c 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ApiService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ApiService.cs @@ -108,13 +108,16 @@ public sealed class ApiService( QueryApiRsp SelectQueryApiRsp(IGrouping group) { var first = group.First()!; + + var id = Regex.Replace( // + first.AttributeRouteInfo!.Template!, $"/{first.ActionName}$", string.Empty); return new QueryApiRsp { - Summary = xmlCommentReader.GetComments(group.Key) - , Name = first.ControllerName - , Id = Regex.Replace( // - first.AttributeRouteInfo!.Template!, $"/{first.ActionName}$", string.Empty) + Summary = xmlCommentReader.GetComments(group.Key) + , Name = first.ControllerName + , Id = id , Children = GetChildren(group) , Namespace = regex.Match(group.Key.Namespace!).Groups[1].Value.ToLowerInvariant() + , PathCrc32 = id.Crc32() }; } } @@ -136,19 +139,25 @@ public sealed class ApiService( private IEnumerable GetChildren(IEnumerable actionDescriptors) { return actionDescriptors // - .Select(x => new QueryApiRsp { - Summary = xmlCommentReader.GetComments(x.MethodInfo) - , Name = x.ActionName - , Id = x.AttributeRouteInfo!.Template - , Method = x.ActionConstraints?.OfType() - .FirstOrDefault() - ?.HttpMethods.First() - }); + .Select(x => { + var id = x.AttributeRouteInfo!.Template; + return new QueryApiRsp { + Summary = xmlCommentReader.GetComments(x.MethodInfo) + , Name = x.ActionName + , Id = id + , Method = x.ActionConstraints?.OfType() + .FirstOrDefault() + ?.HttpMethods.First() + , PathCrc32 = id.Crc32() + }; + }); } private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs index cae388f0..bbd2afc3 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/ConfigService.cs @@ -142,6 +142,8 @@ public sealed class ConfigService(BasicRepository rpo) // .WhereDynamicFilter(req.DynamicFilter) .WhereIf( // req.Filter?.Enabled.HasValue ?? false, a => a.Enabled == req.Filter.Enabled.Value); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IJobRecordService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IJobRecordService.cs index ef70438d..bdfa9b26 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IJobRecordService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IJobRecordService.cs @@ -14,15 +14,15 @@ public interface IJobRecordService : IService, IJobRecordModule /// /// 获取条形图数据 /// - Task> GetBarChartAsync(QueryReq req); + Task> GetBarChartAsync(QueryReq req); /// /// 状态码分组饼图数据 /// - Task> GetPieChartByHttpStatusCodeAsync(QueryReq req); + Task> GetPieChartByHttpStatusCodeAsync(QueryReq req); /// /// 名称分组饼图数据 /// - Task> GetPieChartByNameAsync(QueryReq req); + Task> GetPieChartByNameAsync(QueryReq req); } \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IRequestLogDetailService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IRequestLogDetailService.cs new file mode 100644 index 00000000..384ab585 --- /dev/null +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/Dependency/IRequestLogDetailService.cs @@ -0,0 +1,9 @@ +using NetAdmin.Application.Services; +using NetAdmin.SysComponent.Application.Modules.Sys; + +namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency; + +/// +/// 请求日志明细服务 +/// +public interface IRequestLogDetailService : IService, IRequestLogDetailModule; \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DeptService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DeptService.cs index e555761b..d34b80f6 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DeptService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DeptService.cs @@ -149,6 +149,7 @@ public sealed class DeptService(BasicRepository rpo) // ret = ret.AsTreeCte(); } + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DevService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DevService.cs index 603e05b0..ad9eb54c 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DevService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DevService.cs @@ -175,6 +175,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase await File.WriteAllTextAsync(file, content).ConfigureAwait(false); } + // ReSharper disable once SeparateLocalFunctionsWithJumpStatement IEnumerable Select(QueryApiRsp item) { return item.Children.Select(x => tplInner.Replace("$actionDesc$", x.Summary) diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicCatalogService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicCatalogService.cs index 70eb0217..19a35e4e 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicCatalogService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicCatalogService.cs @@ -128,6 +128,8 @@ public sealed class DicCatalogService(BasicRepository rpo) private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicContentService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicContentService.cs index 3400386d..9b811636 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicContentService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/DicContentService.cs @@ -155,6 +155,8 @@ public sealed class DicContentService(BasicRepository rpo) private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobRecordService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobRecordService.cs index fc1ddcb0..f374a91c 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobRecordService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobRecordService.cs @@ -80,7 +80,7 @@ public sealed class JobRecordService(BasicRepository rpo) / } /// - public async Task> GetBarChartAsync(QueryReq req) + public async Task> GetBarChartAsync(QueryReq req) { req.ThrowIfInvalid(); @@ -105,8 +105,7 @@ public sealed class JobRecordService(BasicRepository rpo) / } /// - public async Task> GetPieChartByHttpStatusCodeAsync( - QueryReq req) + public async Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) { req.ThrowIfInvalid(); var ret = await QueryInternal(req with { Order = Orders.None }) @@ -124,7 +123,7 @@ public sealed class JobRecordService(BasicRepository rpo) / } /// - public async Task> GetPieChartByNameAsync(QueryReq req) + public async Task> GetPieChartByNameAsync(QueryReq req) { req.ThrowIfInvalid(); var ret = await QueryInternal(req with { Order = Orders.None }) @@ -178,6 +177,8 @@ public sealed class JobRecordService(BasicRepository rpo) / req.Keywords?.Length > 0 , a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0) || a.Job.JobName == req.Keywords); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobService.cs index f292dba3..bc0b716f 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/JobService.cs @@ -77,11 +77,13 @@ public sealed class JobService(BasicRepository rpo, IJobRecordSer .Set(a => a.JobName == req.JobName) .SetIf(req.RequestHeaders == null, a => a.RequestHeader, null) .SetIf(req.RequestHeaders != null, a => a.RequestHeader, req.RequestHeaders.Json()) - .Set(a => a.RequestBody == req.RequestBody) - .Set(a => a.RequestUrl == req.RequestUrl) - .Set(a => a.UserId == req.UserId) - .Set(a => a.Summary == req.Summary) - .Where(a => a.Id == req.Id); + .Set(a => a.RequestBody == req.RequestBody) + .Set(a => a.RequestUrl == req.RequestUrl) + .Set(a => a.RandomDelayBegin == req.RandomDelayBegin) + .Set(a => a.RandomDelayEnd == req.RandomDelayEnd) + .Set(a => a.UserId == req.UserId) + .Set(a => a.Summary == req.Summary) + .Where(a => a.Id == req.Id); #if DBTYPE_SQLSERVER return (await update.ExecuteUpdatedAsync().ConfigureAwait(false)).FirstOrDefault()?.Adapt(); @@ -236,22 +238,21 @@ public sealed class JobService(BasicRepository rpo, IJobRecordSer } /// - public Task> GetRecordBarChartAsync(QueryReq req) + public Task> GetRecordBarChartAsync(QueryReq req) { req.ThrowIfInvalid(); return jobRecordService.GetBarChartAsync(req); } /// - public Task> GetRecordPieChartByHttpStatusCodeAsync( - QueryReq req) + public Task> GetRecordPieChartByHttpStatusCodeAsync(QueryReq req) { req.ThrowIfInvalid(); return jobRecordService.GetPieChartByHttpStatusCodeAsync(req); } /// - public Task> GetRecordPieChartByNameAsync(QueryReq req) + public Task> GetRecordPieChartByNameAsync(QueryReq req) { req.ThrowIfInvalid(); return jobRecordService.GetPieChartByNameAsync(req); @@ -335,6 +336,8 @@ public sealed class JobService(BasicRepository rpo, IJobRecordSer .WhereIf( // req.Keywords?.Length > 0 , a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords)); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogDetailService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogDetailService.cs new file mode 100644 index 00000000..30382365 --- /dev/null +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogDetailService.cs @@ -0,0 +1,131 @@ +using NetAdmin.Application.Repositories; +using NetAdmin.Application.Services; +using NetAdmin.Domain.Dto.Dependency; +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; +using NetAdmin.SysComponent.Application.Services.Sys.Dependency; + +namespace NetAdmin.SysComponent.Application.Services.Sys; + +/// +public sealed class RequestLogDetailService(BasicRepository rpo) // + : RepositoryService(rpo), IRequestLogDetailService +{ + /// + public async Task BulkDeleteAsync(BulkReq req) + { + req.ThrowIfInvalid(); + var ret = 0; + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var item in req.Items) { + ret += await DeleteAsync(item).ConfigureAwait(false); + } + + return ret; + } + + /// + public Task CountAsync(QueryReq req) + { + req.ThrowIfInvalid(); + return QueryInternal(req) + #if DBTYPE_SQLSERVER + .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) + #endif + .CountAsync(); + } + + /// + public async Task CreateAsync(CreateRequestLogDetailReq req) + { + req.ThrowIfInvalid(); + var ret = await Rpo.InsertAsync(req).ConfigureAwait(false); + return ret.Adapt(); + } + + /// + public Task DeleteAsync(DelReq req) + { + req.ThrowIfInvalid(); + return Rpo.DeleteAsync(a => a.Id == req.Id); + } + + /// + public Task ExistAsync(QueryReq req) + { + req.ThrowIfInvalid(); + return QueryInternal(req) + #if DBTYPE_SQLSERVER + .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) + #endif + .AnyAsync(); + } + + /// + public Task ExportAsync(QueryReq req) + { + throw new NotImplementedException(); + } + + /// + public async Task GetAsync(QueryRequestLogDetailReq req) + { + req.ThrowIfInvalid(); + var ret = await QueryInternal(new QueryReq { Filter = req }) + .ToOneAsync() + .ConfigureAwait(false); + return ret.Adapt(); + } + + /// + public async Task> PagedQueryAsync( + PagedQueryReq req) + { + req.ThrowIfInvalid(); + var list = await QueryInternal(req) + .Page(req.Page, req.PageSize) + #if DBTYPE_SQLSERVER + .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) + #endif + .Count(out var total) + .ToListAsync() + .ConfigureAwait(false); + + return new PagedQueryRsp(req.Page, req.PageSize, total + , list.Adapt>()); + } + + /// + public async Task> QueryAsync(QueryReq 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>(); + } + + private ISelect QueryInternal(QueryReq req) + { + var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // 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; + } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs index 52298824..1c323dab 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs @@ -8,7 +8,9 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency; namespace NetAdmin.SysComponent.Application.Services.Sys; /// -public sealed class RequestLogService(BasicRepository rpo) // +public sealed class RequestLogService( + BasicRepository rpo + , RequestLogDetailService requestLogDetailService) // : RepositoryService(rpo), IRequestLogService { private static readonly Regex _regex = new(Chars.RGXL_IP_V4); @@ -43,6 +45,7 @@ public sealed class RequestLogService(BasicRepository rpo) { req.ThrowIfInvalid(); var ret = await Rpo.InsertAsync(req).ConfigureAwait(false); + _ = await requestLogDetailService.CreateAsync(req.Detail).ConfigureAwait(false); return ret.Adapt(); } @@ -68,7 +71,17 @@ public sealed class RequestLogService(BasicRepository rpo) public Task ExportAsync(QueryReq req) { req.ThrowIfInvalid(); - return ExportAsync(QueryInternal, req, Ln.请求日志导出); + return ExportAsync( // + QueryInternal, req, Ln.请求日志导出, a => new { + a.Id + , Api = new { a.Api.Id } + , a.CreatedClientIp + , a.CreatedTime + , a.Duration + , a.HttpMethod + , a.HttpStatusCode + , Owner = new { a.Owner.UserName } + }); } /// @@ -76,17 +89,18 @@ public sealed class RequestLogService(BasicRepository rpo) { req.ThrowIfInvalid(); var ret = await QueryInternal(new QueryReq { Filter = req }) + .Include(a => a.Detail) .ToOneAsync() .ConfigureAwait(false); return ret.Adapt(); } /// - public async Task> GetBarChartAsync(QueryReq req) + public async Task> GetBarChartAsync(QueryReq req) { req.ThrowIfInvalid(); - var ret = await QueryInternal(req with { Order = Orders.None }) + var ret = await QueryInternal(req with { Order = Orders.None }, false) #if DBTYPE_SQLSERVER .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) #endif @@ -107,7 +121,7 @@ public sealed class RequestLogService(BasicRepository rpo) } /// - public async Task> GetPieChartByApiSummaryAsync(QueryReq req) + public async Task> GetPieChartByApiSummaryAsync(QueryReq req) { req.ThrowIfInvalid(); var ret = await QueryInternal(req with { Order = Orders.None }) @@ -121,11 +135,10 @@ public sealed class RequestLogService(BasicRepository rpo) } /// - public async Task> GetPieChartByHttpStatusCodeAsync( - QueryReq req) + public async Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) { req.ThrowIfInvalid(); - var ret = await QueryInternal(req with { Order = Orders.None }) + var ret = await QueryInternal(req with { Order = Orders.None }, false) #if DBTYPE_SQLSERVER .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) #endif @@ -148,42 +161,40 @@ public sealed class RequestLogService(BasicRepository rpo) .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) #endif .Count(out var total); - object list = req.DynamicFilter?.Filters?.Exists( - x => nameof(QueryRequestLogReq.ApiId).Equals(x.Field, StringComparison.OrdinalIgnoreCase) && - Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Equals( // - x.Value.ToString(), StringComparison.OrdinalIgnoreCase)) ?? false - ? await select.ToListAsync(a => new { - a.ApiId - , ApiSummary = a.Api.Summary - , a.CreatedClientIp - , a.CreatedTime - , a.Duration - , a.Method - , a.CreatedUserAgent - , a.HttpStatusCode - , a.Id - , a.UserId - , a.User - , a.RequestBody - }) - .ConfigureAwait(false) - : await select.ToListAsync(a => new { - a.ApiId - , ApiSummary = a.Api.Summary - , a.CreatedClientIp - , a.CreatedTime - , a.Duration - , a.Method - , a.CreatedUserAgent - , a.HttpStatusCode - , a.Id - , a.UserId - , a.User - }) - .ConfigureAwait(false); + + 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); return new PagedQueryRsp(req.Page, req.PageSize, total - , list.Adapt>()); + , ret.Adapt>()); } /// @@ -202,7 +213,17 @@ public sealed class RequestLogService(BasicRepository rpo) private ISelect QueryInternal(QueryReq req) { - var ret = Rpo.Select.Include(a => a.Api).Include(a => a.User).WhereDynamicFilter(req.DynamicFilter); + return QueryInternal(req, true); + } + + private ISelect QueryInternal(QueryReq req, bool include) + { + var ret = Rpo.Select; + if (include) { + ret = ret.Include(a => a.Api).Include(a => a.Owner); + } + + ret = ret.WhereDynamicFilter(req.DynamicFilter); if (req.Filter?.Id is not 0) { ret = ret.WhereDynamic(req.Filter); } @@ -210,10 +231,11 @@ public sealed class RequestLogService(BasicRepository rpo) if (req.Keywords?.Length > 0) { ret = _regex.IsMatch(req.Keywords) ? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32()) - : ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.UserId == req.Keywords.Int64Try(0) || - a.User.UserName == req.Keywords || a.RequestBody.Contains(req.Keywords)); + : ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.OwnerId == req.Keywords.Int64Try(0) || + a.Owner.UserName == req.Keywords); } + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RoleService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RoleService.cs index 6393414f..9e62cc66 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RoleService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RoleService.cs @@ -161,6 +161,8 @@ public sealed class RoleService(BasicRepository rpo) // req.Keywords?.Length > 0 , a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) || a.Summary.Contains(req.Keywords)); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgDeptService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgDeptService.cs index e100ed10..608fb4c6 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgDeptService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgDeptService.cs @@ -111,6 +111,8 @@ public sealed class SiteMsgDeptService(BasicRepository rp private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgFlagService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgFlagService.cs index 76e04cd3..8ec8c46a 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgFlagService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgFlagService.cs @@ -119,6 +119,8 @@ public sealed class SiteMsgFlagService(BasicRepository rp private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgRoleService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgRoleService.cs index 428540a6..7b3fc382 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgRoleService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgRoleService.cs @@ -111,6 +111,8 @@ public sealed class SiteMsgRoleService(BasicRepository rp private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgService.cs index 02de7190..ab75dac6 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgService.cs @@ -301,6 +301,8 @@ public sealed class SiteMsgService( req.Keywords?.Length > 0 , a => a.Id == req.Keywords.Int64Try(0) || a.Title.Contains(req.Keywords) || a.Summary.Contains(req.Keywords)); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgUserService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgUserService.cs index 24041f22..52344dfa 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgUserService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/SiteMsgUserService.cs @@ -111,6 +111,8 @@ public sealed class SiteMsgUserService(BasicRepository rp private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs index 5201054b..c73a3bd3 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/UserService.cs @@ -490,6 +490,8 @@ public sealed class UserService( , a => a.Id == req.Keywords.Int64Try(0) || a.UserName == req.Keywords || a.Mobile == req.Keywords || a.Email == req.Keywords || a.Summary.Contains(req.Keywords)); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/VerifyCodeService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/VerifyCodeService.cs index 8e228d0b..44c41fb4 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/VerifyCodeService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/VerifyCodeService.cs @@ -198,6 +198,8 @@ public sealed class VerifyCodeService(BasicRepository rpo, private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Tpl/ExampleService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Tpl/ExampleService.cs index 487f4750..6621c27a 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Tpl/ExampleService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Tpl/ExampleService.cs @@ -113,6 +113,8 @@ public sealed class ExampleService(BasicRepository rpo) // private ISelect QueryInternal(QueryReq req) { var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Cache/Sys/Dependency/IRequestLogDetailCache.cs b/src/backend/NetAdmin.SysComponent.Cache/Sys/Dependency/IRequestLogDetailCache.cs new file mode 100644 index 00000000..95d93d38 --- /dev/null +++ b/src/backend/NetAdmin.SysComponent.Cache/Sys/Dependency/IRequestLogDetailCache.cs @@ -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; + +/// +/// 请求日志明细缓存 +/// +public interface IRequestLogDetailCache : ICache, IRequestLogDetailModule; \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Cache/Sys/DicCache.cs b/src/backend/NetAdmin.SysComponent.Cache/Sys/DicCache.cs index 5dce66ea..83a16870 100644 --- a/src/backend/NetAdmin.SysComponent.Cache/Sys/DicCache.cs +++ b/src/backend/NetAdmin.SysComponent.Cache/Sys/DicCache.cs @@ -81,8 +81,8 @@ public sealed class DicCache(IDistributedCache cache, IDicService service) // public Task GetDicValueAsync(GetDicValueReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetDicValueAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)); #else return Service.GetDicValueAsync(req); diff --git a/src/backend/NetAdmin.SysComponent.Cache/Sys/JobCache.cs b/src/backend/NetAdmin.SysComponent.Cache/Sys/JobCache.cs index b0ec4393..179bebdc 100644 --- a/src/backend/NetAdmin.SysComponent.Cache/Sys/JobCache.cs +++ b/src/backend/NetAdmin.SysComponent.Cache/Sys/JobCache.cs @@ -85,11 +85,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service) } /// - public Task> GetRecordBarChartAsync(QueryReq req) + public Task> GetRecordBarChartAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetRecordBarChartAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); #else return Service.GetRecordBarChartAsync(req); @@ -97,12 +97,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service) } /// - public Task> GetRecordPieChartByHttpStatusCodeAsync( - QueryReq req) + public Task> GetRecordPieChartByHttpStatusCodeAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetRecordPieChartByHttpStatusCodeAsync(req) , TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)); #else @@ -111,11 +110,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service) } /// - public Task> GetRecordPieChartByNameAsync(QueryReq req) + public Task> GetRecordPieChartByNameAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetRecordPieChartByNameAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); #else return Service.GetRecordPieChartByNameAsync(req); diff --git a/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogCache.cs b/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogCache.cs index 07b988b3..bc4eac1a 100644 --- a/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogCache.cs +++ b/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogCache.cs @@ -18,9 +18,22 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService } /// + #if !DEBUG + public async Task CountAsync(QueryReq req) + #else public Task CountAsync(QueryReq req) + #endif { + #if !DEBUG + var ret = await GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // + , async () => (long?)await Service.CountAsync(req).ConfigureAwait(false) + , TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)) + .ConfigureAwait(false); + return ret ?? 0; + #else return Service.CountAsync(req); + #endif } /// @@ -54,11 +67,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService } /// - public Task> GetBarChartAsync(QueryReq req) + public Task> GetBarChartAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetBarChartAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); #else return Service.GetBarChartAsync(req); @@ -66,11 +79,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService } /// - public Task> GetPieChartByApiSummaryAsync(QueryReq req) + public Task> GetPieChartByApiSummaryAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetPieChartByApiSummaryAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); #else return Service.GetPieChartByApiSummaryAsync(req); @@ -78,11 +91,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService } /// - public Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) + public Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.GetPieChartByHttpStatusCodeAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); #else return Service.GetPieChartByHttpStatusCodeAsync(req); diff --git a/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogDetailCache.cs b/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogDetailCache.cs new file mode 100644 index 00000000..3a48061e --- /dev/null +++ b/src/backend/NetAdmin.SysComponent.Cache/Sys/RequestLogDetailCache.cs @@ -0,0 +1,66 @@ +using NetAdmin.Cache; +using NetAdmin.Domain.Dto.Dependency; +using NetAdmin.Domain.Dto.Sys.RequestLogDetail; +using NetAdmin.SysComponent.Application.Services.Sys.Dependency; +using NetAdmin.SysComponent.Cache.Sys.Dependency; + +namespace NetAdmin.SysComponent.Cache.Sys; + +/// +public sealed class RequestLogDetailCache(IDistributedCache cache, IRequestLogDetailService service) + : DistributedCache(cache, service), IScoped, IRequestLogDetailCache +{ + /// + public Task BulkDeleteAsync(BulkReq req) + { + return Service.BulkDeleteAsync(req); + } + + /// + public Task CountAsync(QueryReq req) + { + return Service.CountAsync(req); + } + + /// + public Task CreateAsync(CreateRequestLogDetailReq req) + { + return Service.CreateAsync(req); + } + + /// + public Task DeleteAsync(DelReq req) + { + return Service.DeleteAsync(req); + } + + /// + public Task ExistAsync(QueryReq req) + { + return Service.ExistAsync(req); + } + + /// + public Task ExportAsync(QueryReq req) + { + return Service.ExportAsync(req); + } + + /// + public Task GetAsync(QueryRequestLogDetailReq req) + { + return Service.GetAsync(req); + } + + /// + public Task> PagedQueryAsync(PagedQueryReq req) + { + return Service.PagedQueryAsync(req); + } + + /// + public Task> QueryAsync(QueryReq req) + { + return Service.QueryAsync(req); + } +} \ No newline at end of file diff --git a/src/backend/NetAdmin.SysComponent.Cache/Sys/UserCache.cs b/src/backend/NetAdmin.SysComponent.Cache/Sys/UserCache.cs index 5bef4bfd..96066b2e 100644 --- a/src/backend/NetAdmin.SysComponent.Cache/Sys/UserCache.cs +++ b/src/backend/NetAdmin.SysComponent.Cache/Sys/UserCache.cs @@ -99,8 +99,8 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe public Task> QueryAsync(QueryReq req) { #if !DEBUG - return GetOrCreateAsync( // - GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // + return GetOrCreateAsync( // + GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) // , () => Service.QueryAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)); #else return Service.QueryAsync(req); diff --git a/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/JobController.cs b/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/JobController.cs index 9d94984c..31169c5c 100644 --- a/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/JobController.cs +++ b/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/JobController.cs @@ -119,7 +119,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase /// 获取作业记录条形图数据 /// - public Task> GetRecordBarChartAsync(QueryReq req) + public Task> GetRecordBarChartAsync(QueryReq req) { return Cache.GetRecordBarChartAsync(req); } @@ -127,8 +127,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase /// 状态码分组作业记录饼图数据 /// - public Task> GetRecordPieChartByHttpStatusCodeAsync( - QueryReq req) + public Task> GetRecordPieChartByHttpStatusCodeAsync(QueryReq req) { return Cache.GetRecordPieChartByHttpStatusCodeAsync(req); } @@ -136,7 +135,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase /// 名称分组作业记录饼图数据 /// - public Task> GetRecordPieChartByNameAsync(QueryReq req) + public Task> GetRecordPieChartByNameAsync(QueryReq req) { return Cache.GetRecordPieChartByNameAsync(req); } diff --git a/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/LogController.cs b/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/LogController.cs index 42d4cf85..3649ecb1 100644 --- a/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/LogController.cs +++ b/src/backend/NetAdmin.SysComponent.Host/Controllers/Sys/LogController.cs @@ -82,7 +82,7 @@ public sealed class LogController(IRequestLogCache cache) : ControllerBase /// 获取条形图数据 /// - public Task> GetBarChartAsync(QueryReq req) + public Task> GetBarChartAsync(QueryReq req) { return Cache.GetBarChartAsync(req); } @@ -90,7 +90,7 @@ public sealed class LogController(IRequestLogCache cache) : ControllerBase /// 描述分组饼图数据 /// - public Task> GetPieChartByApiSummaryAsync(QueryReq req) + public Task> GetPieChartByApiSummaryAsync(QueryReq req) { return Cache.GetPieChartByApiSummaryAsync(req); } @@ -98,7 +98,7 @@ public sealed class LogController(IRequestLogCache cache) : ControllerBase /// 状态码分组饼图数据 /// - public Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) + public Task> GetPieChartByHttpStatusCodeAsync(QueryReq req) { return Cache.GetPieChartByHttpStatusCodeAsync(req); } diff --git a/src/backend/NetAdmin.SysComponent.Host/Jobs/ScheduledJob.cs b/src/backend/NetAdmin.SysComponent.Host/Jobs/ScheduledJob.cs index 20cdbddc..af65abc5 100644 --- a/src/backend/NetAdmin.SysComponent.Host/Jobs/ScheduledJob.cs +++ b/src/backend/NetAdmin.SysComponent.Host/Jobs/ScheduledJob.cs @@ -66,7 +66,18 @@ public sealed class ScheduledJob : WorkBase, IJob } var request = BuildRequest(job); - var sw = new Stopwatch(); + + // 随机延时 + if (job.RandomDelayBegin is > 0 && job.RandomDelayEnd is > 0) { + var (start, end) = (job.RandomDelayBegin.Value, job.RandomDelayEnd.Value); + if (start > end) { + (start, end) = (end, start); + } + + await Task.Delay(new[] { start, end }.Rand(), CancellationToken.None).ConfigureAwait(false); + } + + var sw = new Stopwatch(); sw.Start(); var rsp = await request.SendAsync(CancellationToken.None).ConfigureAwait(false); if (rsp.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden) { diff --git a/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs b/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs index 69f2c1cf..f17ec166 100644 --- a/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs +++ b/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs @@ -18,13 +18,6 @@ public sealed class OperationLogger : IEventSubscriber return; } - // 跳过指定的请求 - if (Array.Exists( // - GlobalStatic.LogSavingSkipApiIds - , x => x.Equals(operationEvent.Data.ApiId, StringComparison.OrdinalIgnoreCase))) { - return; - } - operationEvent.Data.TruncateStrings(); _ = await App.GetService().CreateAsync(operationEvent.Data).ConfigureAwait(false); } diff --git a/src/frontend/admin/package.json b/src/frontend/admin/package.json index f5545acd..fce54ba8 100644 --- a/src/frontend/admin/package.json +++ b/src/frontend/admin/package.json @@ -11,14 +11,14 @@ "dependencies": { "@element-plus/icons-vue": "^2.3.1", "ace-builds": "^1.35.2", - "aieditor": "^1.0.12", + "aieditor": "^1.0.13", "axios": "^1.7.2", "clipboard": "^2.0.11", "core-js": "^3.37.1", "cropperjs": "^1.6.2", "crypto-js": "^4.2.0", "echarts": "^5.5.1", - "element-plus": "^2.7.6", + "element-plus": "^2.7.7", "json-bigint": "^1.0.0", "json5-to-table": "^0.1.8", "markdown-it": "^14.1.0", @@ -38,11 +38,11 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.5", - "prettier": "^3.3.2", + "prettier": "^3.3.3", "prettier-plugin-organize-attributes": "^1.0.0", - "sass": "^1.77.6", - "terser": "^5.31.1", - "vite": "^5.3.3" + "sass": "^1.77.8", + "terser": "^5.31.3", + "vite": "^5.3.4" }, "browserslist": [ "> 1%", diff --git a/src/frontend/admin/src/components/naColUser/index.vue b/src/frontend/admin/src/components/naColUser/index.vue index 560baae5..49f92dbd 100644 --- a/src/frontend/admin/src/components/naColUser/index.vue +++ b/src/frontend/admin/src/components/naColUser/index.vue @@ -1,7 +1,10 @@ - - +