feat: 框架代码同步 (#158)

[skip ci]

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
2024-07-22 12:58:10 +08:00
committed by GitHub
parent 60ec6ea2c1
commit 1a28e8d5a6
107 changed files with 1256 additions and 664 deletions

View File

@ -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<int> 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);
}

View File

@ -979,7 +979,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
{
return default;
}
@ -1075,7 +1075,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
{
return default;
}
@ -1083,7 +1083,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
{
return default;
}
@ -1099,7 +1099,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
{
return default;
}
@ -1107,8 +1107,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(
QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
{
return default;
}
@ -1116,7 +1115,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{
return default;
}

View File

@ -30,6 +30,46 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
set => Rpo.DbContextOptions.EnableCascadeSave = value;
}
/// <summary>
/// 导出实体
/// </summary>
protected async Task<IActionResult> ExportAsync<TQuery, TExport>( //
Func<QueryReq<TQuery>, ISelect<TEntity>> selector, QueryReq<TQuery> query, string fileName
, Expression<Func<TEntity, object>> 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<List<TExport>>();
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteHeader<TExport>();
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);
}
/// <summary>
/// 更新实体
/// </summary>
@ -77,43 +117,6 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
}
#endif
/// <summary>
/// 导出实体
/// </summary>
protected async Task<IActionResult> ExportAsync<TQuery, TExport>( //
Func<QueryReq<TQuery>, ISelect<TEntity>> selector, QueryReq<TQuery> 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<List<TExport>>();
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteHeader<TExport>();
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<TEntity> BuildUpdate( //
TEntity entity //
, IEnumerable<string> includeFields //

View File

@ -70,7 +70,8 @@ public abstract class DistributedCache<TService>(IDistributedCache cache, TServi
, TimeSpan? slideLifeTime = null)
{
var cacheRead = await GetAsync<T>(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;
}

View File

@ -7,6 +7,12 @@ namespace NetAdmin.Domain.Contexts;
/// </summary>
public sealed record ContextUserToken : DataAbstraction
{
/// <summary>
/// 部门编号
/// </summary>
/// ReSharper disable once MemberCanBePrivate.Global
public long DeptId { get; init; }
/// <summary>
/// 用户编号
/// </summary>
@ -39,6 +45,8 @@ public sealed record ContextUserToken : DataAbstraction
/// </summary>
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
};
}
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 创建者客户端IP字段接口
/// </summary>
public interface IFieldCreatedClientIp
{
/// <summary>
/// 创建者客户端IP
/// </summary>
int? CreatedClientIp { get; init; }
}

View File

@ -1,15 +1,10 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 创建者客户端字段接口
/// 创建者客户端用户代理字段接口
/// </summary>
public interface IFieldCreatedClient
public interface IFieldCreatedClientUserAgent
{
/// <summary>
/// 创建者客户端IP
/// </summary>
int? CreatedClientIp { get; init; }
/// <summary>
/// 创建者客户端用户代理
/// </summary>

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 修改客户端IP字段接口
/// </summary>
public interface IFieldModifiedClientIp
{
/// <summary>
/// 客户端IP
/// </summary>
int ModifiedClientIp { get; init; }
}

View File

@ -1,15 +1,10 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 修改客户端字段接口
/// 修改客户端用户代理字段接口
/// </summary>
public interface IFieldModifiedClient
public interface IFieldModifiedClientUserAgent
{
/// <summary>
/// 客户端IP
/// </summary>
int ModifiedClientIp { get; init; }
/// <summary>
/// 客户端用户代理
/// </summary>

View File

@ -4,6 +4,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// Api接口表
/// </summary>
[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<string>, IFieldSummary
{
/// <summary>
@ -54,6 +55,14 @@ public record Sys_Api : ImmutableEntity<string>, IFieldSummary
[JsonIgnore]
public virtual string ParentId { get; init; }
/// <summary>
/// 路径CRC32
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public int PathCrc32 { get; init; }
/// <summary>
/// 角色集合
/// </summary>

View File

@ -18,7 +18,7 @@ public record Sys_DicCatalog : VersionEntity
/// <summary>
/// 字典编码
/// </summary>
[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
/// <summary>
/// 字典名称
/// </summary>
[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; }

View File

@ -4,8 +4,6 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 字典内容表
/// </summary>
[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
{

View File

@ -79,6 +79,22 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
[JsonIgnore]
public virtual long? NextTimeId { get; init; }
/// <summary>
/// 随机延时起始值(毫秒)
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public virtual int? RandomDelayBegin { get; init; }
/// <summary>
/// 随机延时结束值(毫秒)
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public virtual int? RandomDelayEnd { get; init; }
/// <summary>
/// 请求体
/// </summary>

View File

@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 计划作业执行记录表
/// </summary>
[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))]

View File

@ -1,36 +1,38 @@
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 请求日志表
/// </summary>
[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
{
/// <summary>
/// 接口
/// </summary>
[Ignore]
[JsonIgnore]
[Navigate(nameof(ApiId))]
[Navigate(nameof(ApiPathCrc32), TempPrimary = nameof(Sys_Api.PathCrc32))]
public Sys_Api Api { get; init; }
/// <summary>
/// 接口编号
/// 接口路径CRC32
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[Column]
[Ignore]
[JsonIgnore]
public virtual string ApiId { get; init; }
public virtual int ApiPathCrc32 { get; init; }
/// <inheritdoc />
[Column(Position = -1)]
[Column]
[Ignore]
[JsonIgnore]
public int? CreatedClientIp { get; init; }
public virtual int? CreatedClientIp { get; init; }
/// <inheritdoc />
[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; }
/// <inheritdoc />
#if DBTYPE_SQLSERVER
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)]
#else
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
/// <summary>
/// 明细
/// </summary>
[Ignore]
[JsonIgnore]
public virtual string CreatedUserAgent { get; init; }
[Navigate(nameof(Id))]
public Sys_RequestLogDetail Detail { get; init; }
/// <summary>
/// 执行耗时(秒)
/// 执行耗时(秒)
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public virtual long Duration { get; init; }
public virtual int Duration { get; init; }
/// <summary>
/// 程序响应码
/// 请求方法
/// </summary>
[Column]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_TINY_INT)]
[Ignore]
[JsonIgnore]
public virtual ErrorCodes ErrorCode { get; init; }
/// <summary>
/// 异常信息
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string Exception { get; init; }
public virtual HttpMethods HttpMethod { get; init; }
/// <summary>
/// HTTP状态码
/// </summary>
[Column]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_SMALL_INT)]
[Ignore]
[JsonIgnore]
public virtual int HttpStatusCode { get; init; }
/// <summary>
/// 请求方法
/// 拥有者
/// </summary>
[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; }
/// <summary>
/// 请求内容
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string RequestBody { get; init; }
/// <summary>
/// 请求content-type
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[Ignore]
[JsonIgnore]
public virtual string RequestContentType { get; init; }
/// <summary>
/// 请求头信息
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string RequestHeaders { get; init; }
/// <summary>
/// 请求地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[Ignore]
[JsonIgnore]
public virtual string RequestUrl { get; init; }
/// <summary>
/// 响应内容
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string ResponseBody { get; init; }
/// <summary>
/// 响应content-type
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[Ignore]
[JsonIgnore]
public virtual string ResponseContentType { get; init; }
/// <summary>
/// 响应头
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string ResponseHeaders { get; init; }
/// <summary>
/// 服务器IP
/// </summary>
/// <inheritdoc />
[Column]
[Ignore]
[JsonIgnore]
public virtual int? ServerIp { get; init; }
public virtual long? OwnerDeptId { get; init; }
/// <summary>
/// 请求跟踪标识
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
[Ignore]
[JsonIgnore]
public virtual string TraceId { get; init; }
/// <summary>
/// 用户
/// </summary>
[Ignore]
[JsonIgnore]
[Navigate(nameof(UserId))]
public Sys_User User { get; init; }
/// <summary>
/// 用户编号
/// </summary>
/// <inheritdoc />
[Column]
[Ignore]
[JsonIgnore]
public virtual long? UserId { get; init; }
public virtual long? OwnerId { get; init; }
}

View File

@ -0,0 +1,132 @@
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 请求日志明细表
/// </summary>
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLogDetail))]
public record Sys_RequestLogDetail : SimpleEntity, IFieldCreatedTime, IFieldCreatedClientUserAgent
{
/// <inheritdoc />
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)]
[Ignore]
[JsonIgnore]
public virtual DateTime CreatedTime { get; init; }
/// <inheritdoc />
#if DBTYPE_SQLSERVER
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)]
#else
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string CreatedUserAgent { get; init; }
/// <summary>
/// 程序响应码
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public virtual ErrorCodes ErrorCode { get; init; }
/// <summary>
/// 异常信息
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string Exception { get; init; }
/// <summary>
/// 请求内容
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string RequestBody { get; init; }
/// <summary>
/// 请求content-type
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[Ignore]
[JsonIgnore]
public virtual string RequestContentType { get; init; }
/// <summary>
/// 请求头信息
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string RequestHeaders { get; init; }
/// <summary>
/// 请求地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[Ignore]
[JsonIgnore]
public virtual string RequestUrl { get; init; }
/// <summary>
/// 响应内容
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string ResponseBody { get; init; }
/// <summary>
/// 响应content-type
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[Ignore]
[JsonIgnore]
public virtual string ResponseContentType { get; init; }
/// <summary>
/// 响应头
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore]
[JsonIgnore]
public virtual string ResponseHeaders { get; init; }
/// <summary>
/// 服务器IP
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public virtual int? ServerIp { get; init; }
/// <summary>
/// 请求跟踪标识
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
[Ignore]
[JsonIgnore]
public virtual string TraceId { get; init; }
}

View File

@ -37,6 +37,42 @@ public sealed record DynamicFilterInfo : DataAbstraction
/// </summary>
public static implicit operator FreeSql.Internal.Model.DynamicFilterInfo(DynamicFilterInfo d)
{
return d.Adapt<FreeSql.Internal.Model.DynamicFilterInfo>();
var ret = d.Adapt<FreeSql.Internal.Model.DynamicFilterInfo>();
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<string[]>();
if (!DateTime.TryParse(values[0], CultureInfo.InvariantCulture, out _)) {
var result = values[0]
.ExecuteCSharpCodeAsync<DateTime>([typeof(DateTime).Assembly], nameof(System))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
values[0] = $"{result:yyyy-MM-dd HH:mm:ss}";
}
if (!DateTime.TryParse(values[1], CultureInfo.InvariantCulture, out _)) {
var result = values[1]
.ExecuteCSharpCodeAsync<DateTime>([typeof(DateTime).Assembly], nameof(System))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
values[1] = $"{result:yyyy-MM-dd HH:mm:ss}";
}
d.Value = values;
}
}

View File

@ -32,6 +32,14 @@ public record CreateJobReq : Sys_Job
/// <inheritdoc />
public override long? NextTimeId { get; init; }
/// <inheritdoc />
[Range(1, int.MaxValue, ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.随机延时起始时间不正确))]
public override int? RandomDelayBegin { get; init; }
/// <inheritdoc />
[Range(1, int.MaxValue, ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.随机延时结束时间不正确))]
public override int? RandomDelayEnd { get; init; }
/// <inheritdoc cref="Sys_Job.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }

View File

@ -90,6 +90,14 @@ public record QueryJobRsp : Sys_Job
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? NextTimeId { get; init; }
/// <inheritdoc />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? RandomDelayBegin { get; init; }
/// <inheritdoc />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? RandomDelayEnd { get; init; }
/// <inheritdoc cref="Sys_Job.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }

View File

@ -1,6 +1,12 @@
using NetAdmin.Domain.Dto.Sys.RequestLogDetail;
namespace NetAdmin.Domain.Dto.Sys.RequestLog;
/// <summary>
/// 请求:创建请求日志
/// </summary>
public sealed record CreateRequestLogReq : Sys_RequestLog;
public sealed record CreateRequestLogReq : Sys_RequestLog
{
/// <inheritdoc cref="Sys_RequestLog.Detail" />
public new CreateRequestLogDetailReq Detail { get; init; }
}

View File

@ -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;
/// </summary>
public record ExportRequestLogRsp : QueryRequestLogRsp
{
/// <summary>
/// 接口路径
/// </summary>
[CsvIndex(2)]
[JsonInclude]
[Name(nameof(Ln.接口路径))]
public string ApiId => Api.Id;
/// <inheritdoc />
[CsvIndex(6)]
[Ignore(false)]
[Name(nameof(Ln.客户端IP))]
public override string CreatedClientIp => base.CreatedClientIp;
/// <summary>
/// 用户名
/// </summary>
[CsvIndex(5)]
[JsonInclude]
[Name(nameof(Ln.用户名))]
public string UserName => Owner?.UserName;
/// <inheritdoc />
[Ignore]
public override string LoginName => base.LoginName;
public override QueryApiRsp Api { get; init; }
/// <inheritdoc />
[CsvIndex(7)]
[Ignore(false)]
[Name(nameof(Ln.操作系统))]
public override string Os => base.Os;
[Ignore]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIndex(2)]
[Ignore(false)]
[Name(nameof(Ln.接口路径))]
public override string ApiId { get; init; }
/// <inheritdoc />
[CsvIndex(8)]
[Ignore(false)]
[Name(nameof(Ln.用户代理))]
public override string CreatedUserAgent { get; init; }
[Ignore]
public override QueryRequestLogDetailRsp Detail { get; init; }
/// <inheritdoc />
[CsvIndex(4)]
[Ignore(false)]
[Name(nameof(Ln.执行耗时))]
public override long Duration { get; init; }
public override int Duration { get; init; }
/// <inheritdoc />
[CsvIndex(3)]
[Ignore(false)]
[Name(nameof(Ln.请求方式))]
public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc />
[CsvIndex(1)]
@ -54,32 +69,14 @@ public record ExportRequestLogRsp : QueryRequestLogRsp
public override long Id { get; init; }
/// <inheritdoc />
[CsvIndex(3)]
[Ignore(false)]
[Name(nameof(Ln.请求方式))]
public override string Method { get; init; }
/// <inheritdoc />
[CsvIndex(9)]
[Ignore(false)]
[Name(nameof(Ln.跟踪编号))]
public override string TraceId { get; init; }
[Ignore]
public override QueryUserLiteRsp Owner { get; init; }
/// <inheritdoc />
[Ignore]
public override QueryUserRsp User { get; init; }
/// <summary>
/// 用户名
/// </summary>
[CsvIndex(5)]
[Ignore(false)]
[Name(nameof(Ln.用户名))]
public string UserName { get; init; }
public override long? OwnerDeptId { get; init; }
/// <inheritdoc />
public override void Register(TypeAdapterConfig config)
{
_ = config.ForType<Sys_RequestLog, ExportRequestLogRsp>().Map(d => d.UserName, s => s.User.UserName);
}
[Ignore]
public override long? OwnerId { get; init; }
}

View File

@ -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;
/// <summary>
/// 响应:查询请求日志
/// </summary>
public record QueryRequestLogRsp : Sys_RequestLog, IRegister
public record QueryRequestLogRsp : Sys_RequestLog
{
/// <summary>
/// 创建者客户端IP
@ -13,101 +16,39 @@ public record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonInclude]
public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4();
/// <summary>
/// 登录名
/// </summary>
[JsonInclude]
public virtual string LoginName => RequestBody?.ToObject<LoginByPwdReq>()?.Account;
/// <summary>
/// 操作系统
/// </summary>
[JsonInclude]
public virtual string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform;
/// <inheritdoc cref="Sys_RequestLog.ApiId" />
/// <inheritdoc cref="Sys_RequestLog.Api" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ApiId { get; init; }
public new virtual QueryApiRsp Api { get; init; }
/// <summary>
/// 接口描述
/// </summary>
public string ApiSummary { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ApiPathCrc32" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int ApiPathCrc32 { get; init; }
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="Sys_RequestLog.CreatedUserAgent" />
/// <inheritdoc cref="Sys_RequestLog.Detail" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserAgent { get; init; }
public new virtual QueryRequestLogDetailRsp Detail { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Duration" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Duration { get; init; }
public override int Duration { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ErrorCode" />
/// <inheritdoc cref="Sys_RequestLog.HttpMethod" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override ErrorCodes ErrorCode { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Exception" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Exception { get; init; }
public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc cref="Sys_RequestLog.HttpStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int HttpStatusCode { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Method" />
/// <inheritdoc cref="Sys_RequestLog.Owner" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Method { get; init; }
public new virtual QueryUserLiteRsp Owner { get; init; }
/// <inheritdoc cref="Sys_RequestLog.RequestBody" />
/// <inheritdoc cref="IFieldOwner.OwnerId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }
/// <inheritdoc cref="Sys_RequestLog.RequestContentType" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestContentType { get; init; }
/// <inheritdoc cref="Sys_RequestLog.RequestHeaders" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestHeaders { get; init; }
/// <inheritdoc cref="Sys_RequestLog.RequestUrl" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestUrl { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ResponseBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseBody { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ResponseContentType" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseContentType { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ResponseHeaders" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseHeaders { get; init; }
/// <inheritdoc cref="Sys_RequestLog.ServerIp" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? ServerIp { get; init; }
/// <inheritdoc cref="Sys_RequestLog.TraceId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string TraceId { get; init; }
/// <inheritdoc cref="Sys_RequestLog.User" />
public new virtual QueryUserRsp User { get; init; }
/// <inheritdoc cref="Sys_RequestLog.UserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? UserId { get; init; }
/// <inheritdoc />
public virtual void Register(TypeAdapterConfig config)
{
_ = config.ForType<Sys_RequestLog, QueryRequestLogRsp>().Map(d => d.ApiSummary, s => s.Api.Summary);
}
public override long? OwnerId { get; init; }
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail;
/// <summary>
/// 请求:创建请求日志明细
/// </summary>
public record CreateRequestLogDetailReq : Sys_RequestLogDetail;

View File

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

View File

@ -0,0 +1,73 @@
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail;
/// <summary>
/// 响应:查询请求日志明细
/// </summary>
public sealed record QueryRequestLogDetailRsp : Sys_RequestLogDetail
{
/// <summary>
/// 登录名
/// </summary>
[JsonInclude]
public string LoginName => RequestBody?.ToObject<LoginByPwdReq>()?.Account;
/// <summary>
/// 操作系统
/// </summary>
[JsonInclude]
public string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform;
/// <inheritdoc cref="IFieldCreatedClientUserAgent.CreatedUserAgent" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserAgent { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.ErrorCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override ErrorCodes ErrorCode { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.Exception" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Exception { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.RequestContentType" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestContentType { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.RequestHeaders" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestHeaders { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.RequestUrl" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestUrl { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.ResponseBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseBody { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.ResponseContentType" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseContentType { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.ResponseHeaders" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ResponseHeaders { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.ServerIp" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? ServerIp { get; init; }
/// <inheritdoc cref="Sys_RequestLogDetail.TraceId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string TraceId { get; init; }
}

View File

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

View File

@ -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);
}
/// <summary>
/// 耗时(单位:秒)
/// 耗时(单位:秒)
/// </summary>
/// de
private long ElapsedMicroseconds { get; }
private long ElapsedMilliseconds { get; }
/// <inheritdoc />
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);
}
}

View File

@ -10,7 +10,7 @@
<ProjectReference Include="../NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CronExpressionDescriptor" Version="2.34.0"/>
<PackageReference Include="CronExpressionDescriptor" Version="2.36.0"/>
<PackageReference Include="Cronos" Version="0.8.4"/>
<PackageReference Include="CsvHelper.NS" Version="33.0.2-ns2"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.6.24328.4"/>

View File

@ -35,20 +35,15 @@ public abstract class WorkBase<TLogger>
/// </summary>
protected UnitOfWorkManager UowManager { get; }
/// <summary>
/// 获取锁
/// </summary>
protected Task<IRedLock> 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));
}
/// <summary>
/// 通用工作流
/// </summary>
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
/// <summary>
/// 通用工作流
@ -69,4 +64,14 @@ public abstract class WorkBase<TLogger>
await WorkflowAsync(cancelToken).ConfigureAwait(false);
}
/// <summary>
/// 获取锁
/// </summary>
private Task<IRedLock> 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));
}
}

View File

@ -58,7 +58,7 @@ public sealed class RequestAuditMiddleware(
.FirstOrDefault()
?.Enum<ErrorCodes>() ?? 0;
_ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMicroseconds, responseBody, errorCode
_ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMilliseconds, responseBody, errorCode
, exception)
.ConfigureAwait(false);
}

View File

@ -5,7 +5,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IGeekFan.AspNetCore.Knife4jUI.NS" Condition="'$(Configuration)' == 'Debug'" Version="0.0.15-ns2"/>
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.161"/>
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.179"/>
<PackageReference Include="prometheus-net.AspNetCore" Condition="'$(Configuration)' != 'Debug'" Version="8.2.1"/>
</ItemGroup>
</Project>

View File

@ -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<RequestLogger> 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<HttpMethods>(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<RequestLogger> 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<RequestLogger> 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<ContextUserToken>();
}
@ -77,6 +83,6 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
logger.Warn($"{Ln.读取用户令牌出错}: {ex}");
}
return userToken == null ? null : (userToken.Id, userToken.UserName);
return userToken == null ? null : (userToken.Id, userToken.DeptId, userToken.UserName);
}
}

View File

@ -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:

View File

@ -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";

View File

@ -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; // 秒:超时时间-作业
}

View File

@ -10,8 +10,7 @@ public static class HttpRequestMessageExtensions
/// </summary>
public static async Task<HttpRequestMessage> LogAsync<T>(this HttpRequestMessage me, ILogger<T> 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;
}

View File

@ -11,8 +11,7 @@ public static class HttpResponseMessageExtensions
public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger
, Func<string, string> 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)}");
}
/// <summary>
@ -21,8 +20,7 @@ public static class HttpResponseMessageExtensions
public static async Task LogExceptionAsync<T>(this HttpResponseMessage me, string errors, ILogger<T> logger
, Func<string, string> 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)}");
}
/// <summary>

View File

@ -1,3 +1,6 @@
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
namespace NetAdmin.Infrastructure.Extensions;
/// <summary>
@ -5,8 +8,24 @@ namespace NetAdmin.Infrastructure.Extensions;
/// </summary>
public static class StringExtensions
{
private static readonly Regex _regex = new("Options$");
private static readonly Regex _regex2 = new("Async$");
/// <summary>
/// 计算Crc32
/// </summary>
public static int Crc32(this string me)
{
return BitConverter.ToInt32(System.IO.Hashing.Crc32.Hash(Encoding.UTF8.GetBytes(me)));
}
/// <summary>
/// 执行C#代码
/// </summary>
public static Task<T> ExecuteCSharpCodeAsync<T>(this string me, Assembly[] assemblies
, params string[] importNamespaces)
{
// 使用 Roslyn 编译并执行代码
return CSharpScript.EvaluateAsync<T>(
me, ScriptOptions.Default.WithReferences(assemblies).WithImports(importNamespaces));
}
/// <summary>
/// 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");
}
/// <summary>
@ -39,14 +58,22 @@ public static class StringExtensions
/// </summary>
public static string TrimEndOptions(this string me)
{
return _regex.Replace(me, string.Empty);
return TrimSuffix(me, "Options");
}
/// <summary>
/// 去掉前部字符串
/// </summary>
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);
}
/// <summary>
/// 去掉尾部字符串
/// </summary>
public static string TrimSuffix(this string me, string clearStr)
{
return Regex.Replace(me, $"{clearStr}$", string.Empty);
}
}

View File

@ -24,11 +24,6 @@ public static class GlobalStatic
#endif
;
/// <summary>
/// 日志保存跳过的API编号
/// </summary>
public static string[] LogSavingSkipApiIds => ["api/probe/health.check", "api/adm/device.log/create"];
/// <summary>
/// 系统内部密钥
/// </summary>

View File

@ -8,9 +8,10 @@
<ItemGroup>
<PackageReference Include="FreeSql.DbContext.NS" Version="3.2.833-preview20260627-ns1"/>
<PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.833-preview20260627-ns1"/>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.4"/>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.4-ns1"/>
<PackageReference Include="Furion.Pure.NS" Version="4.9.4-ns1"/>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.4.6"/>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.4.6-ns4"/>
<PackageReference Include="Furion.Pure.NS" Version="4.9.4.6-ns4"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.10.0"/>
<PackageReference Include="Minio" Version="6.0.3"/>
<PackageReference Include="NSExt" Version="2.2.0"/>
<PackageReference Include="RedLock.net" Version="2.3.2"/>

View File

@ -42,17 +42,17 @@ public interface IJobModule : ICrudModule<CreateJobReq, QueryJobRsp // 创建类
/// <summary>
/// 获取作业记录条形图数据
/// </summary>
Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req);
Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req);
/// <summary>
/// 状态码分组作业记录饼图数据
/// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req);
Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req);
/// <summary>
/// 名称分组作业记录饼图数据
/// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req);
Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req);
/// <summary>
/// 分页查询作业记录

View File

@ -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;
/// <summary>
/// 请求日志明细模块
/// </summary>
public interface IRequestLogDetailModule : ICrudModule<CreateRequestLogDetailReq, QueryRequestLogDetailRsp // 创建类型
, QueryRequestLogDetailReq, QueryRequestLogDetailRsp // 查询类型
, DelReq // 删除类型
>;

View File

@ -16,15 +16,15 @@ public interface IRequestLogModule : ICrudModule<CreateRequestLogReq, QueryReque
/// <summary>
/// 获取条形图数据
/// </summary>
Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req);
Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req);
/// <summary>
/// 描述分组饼图数据
/// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req);
Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req);
/// <summary>
/// 状态码分组饼图数据
/// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req);
Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req);
}

View File

@ -108,13 +108,16 @@ public sealed class ApiService(
QueryApiRsp SelectQueryApiRsp(IGrouping<TypeInfo, ControllerActionDescriptor> 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<QueryApiRsp> GetChildren(IEnumerable<ControllerActionDescriptor> actionDescriptors)
{
return actionDescriptors //
.Select(x => new QueryApiRsp {
Summary = xmlCommentReader.GetComments(x.MethodInfo)
, Name = x.ActionName
, Id = x.AttributeRouteInfo!.Template
, Method = x.ActionConstraints?.OfType<HttpMethodActionConstraint>()
.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<HttpMethodActionConstraint>()
.FirstOrDefault()
?.HttpMethods.First()
, PathCrc32 = id.Crc32()
};
});
}
private ISelect<Sys_Api> QueryInternal(QueryReq<QueryApiReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -142,6 +142,8 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> 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;

View File

@ -14,15 +14,15 @@ public interface IJobRecordService : IService, IJobRecordModule
/// <summary>
/// 获取条形图数据
/// </summary>
Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req);
Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req);
/// <summary>
/// 状态码分组饼图数据
/// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req);
Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req);
/// <summary>
/// 名称分组饼图数据
/// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByNameAsync(QueryReq<QueryJobRecordReq> req);
Task<IEnumerable<GetPieChartRsp>> GetPieChartByNameAsync(QueryReq<QueryJobRecordReq> req);
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 请求日志明细服务
/// </summary>
public interface IRequestLogDetailService : IService, IRequestLogDetailModule;

View File

@ -149,6 +149,7 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
ret = ret.AsTreeCte();
}
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -175,6 +175,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
await File.WriteAllTextAsync(file, content).ConfigureAwait(false);
}
// ReSharper disable once SeparateLocalFunctionsWithJumpStatement
IEnumerable<string> Select(QueryApiRsp item)
{
return item.Children.Select(x => tplInner.Replace("$actionDesc$", x.Summary)

View File

@ -128,6 +128,8 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
private ISelect<Sys_DicCatalog> QueryInternal(QueryReq<QueryDicCatalogReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -155,6 +155,8 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -80,7 +80,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req)
public async Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
@ -105,8 +105,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(
QueryReq<QueryJobRecordReq> req)
public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None })
@ -124,7 +123,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None })
@ -178,6 +177,8 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> 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;

View File

@ -77,11 +77,13 @@ public sealed class JobService(BasicRepository<Sys_Job, long> 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<QueryJobRsp>();
@ -236,22 +238,21 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
return jobRecordService.GetBarChartAsync(req);
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(
QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
return jobRecordService.GetPieChartByHttpStatusCodeAsync(req);
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
return jobRecordService.GetPieChartByNameAsync(req);
@ -335,6 +336,8 @@ public sealed class JobService(BasicRepository<Sys_Job, long> 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;

View File

@ -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;
/// <inheritdoc cref="IRequestLogDetailService" />
public sealed class RequestLogDetailService(BasicRepository<Sys_RequestLogDetail, long> rpo) //
: RepositoryService<Sys_RequestLogDetail, long, IRequestLogDetailService>(rpo), IRequestLogDetailService
{
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var ret = 0;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var item in req.Items) {
ret += await DeleteAsync(item).ConfigureAwait(false);
}
return ret;
}
/// <inheritdoc />
public Task<long> CountAsync(QueryReq<QueryRequestLogDetailReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.CountAsync();
}
/// <inheritdoc />
public async Task<QueryRequestLogDetailRsp> CreateAsync(CreateRequestLogDetailReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryRequestLogDetailRsp>();
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryRequestLogDetailReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.AnyAsync();
}
/// <inheritdoc />
public Task<IActionResult> ExportAsync(QueryReq<QueryRequestLogDetailReq> req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public async Task<QueryRequestLogDetailRsp> GetAsync(QueryRequestLogDetailReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRequestLogDetailReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryRequestLogDetailRsp>();
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryRequestLogDetailRsp>> PagedQueryAsync(
PagedQueryReq<QueryRequestLogDetailReq> 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<QueryRequestLogDetailRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryRequestLogDetailRsp>>());
}
/// <inheritdoc />
public async Task<IEnumerable<QueryRequestLogDetailRsp>> QueryAsync(QueryReq<QueryRequestLogDetailReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Take(req.Count)
.ToListAsync()
.ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryRequestLogDetailRsp>>();
}
private ISelect<Sys_RequestLogDetail> QueryInternal(QueryReq<QueryRequestLogDetailReq> 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;
}
}

View File

@ -8,7 +8,9 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IRequestLogService" />
public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo) //
public sealed class RequestLogService(
BasicRepository<Sys_RequestLog, long> rpo
, RequestLogDetailService requestLogDetailService) //
: RepositoryService<Sys_RequestLog, long, IRequestLogService>(rpo), IRequestLogService
{
private static readonly Regex _regex = new(Chars.RGXL_IP_V4);
@ -43,6 +45,7 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
_ = await requestLogDetailService.CreateAsync(req.Detail).ConfigureAwait(false);
return ret.Adapt<QueryRequestLogRsp>();
}
@ -68,7 +71,17 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
public Task<IActionResult> ExportAsync(QueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
return ExportAsync<QueryRequestLogReq, ExportRequestLogRsp>(QueryInternal, req, Ln.);
return ExportAsync<QueryRequestLogReq, ExportRequestLogRsp>( //
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 }
});
}
/// <inheritdoc />
@ -76,17 +89,18 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req })
.Include(a => a.Detail)
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryRequestLogRsp>();
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
public async Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> 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<Sys_RequestLog, long> rpo)
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None })
@ -121,11 +135,10 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
}
/// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(
QueryReq<QueryRequestLogReq> req)
public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> 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<Sys_RequestLog, long> 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<QueryRequestLogRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryRequestLogRsp>>());
, ret.Adapt<IEnumerable<QueryRequestLogRsp>>());
}
/// <inheritdoc />
@ -202,7 +213,17 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> req)
{
var ret = Rpo.Select.Include(a => a.Api).Include(a => a.User).WhereDynamicFilter(req.DynamicFilter);
return QueryInternal(req, true);
}
private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> 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<Sys_RequestLog, long> 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;

View File

@ -161,6 +161,8 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> 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;

View File

@ -111,6 +111,8 @@ public sealed class SiteMsgDeptService(BasicRepository<Sys_SiteMsgDept, long> rp
private ISelect<Sys_SiteMsgDept> QueryInternal(QueryReq<QuerySiteMsgDeptReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -119,6 +119,8 @@ public sealed class SiteMsgFlagService(BasicRepository<Sys_SiteMsgFlag, long> rp
private ISelect<Sys_SiteMsgFlag> QueryInternal(QueryReq<QuerySiteMsgFlagReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -111,6 +111,8 @@ public sealed class SiteMsgRoleService(BasicRepository<Sys_SiteMsgRole, long> rp
private ISelect<Sys_SiteMsgRole> QueryInternal(QueryReq<QuerySiteMsgRoleReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -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;

View File

@ -111,6 +111,8 @@ public sealed class SiteMsgUserService(BasicRepository<Sys_SiteMsgUser, long> rp
private ISelect<Sys_SiteMsgUser> QueryInternal(QueryReq<QuerySiteMsgUserReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -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;

View File

@ -198,6 +198,8 @@ public sealed class VerifyCodeService(BasicRepository<Sys_VerifyCode, long> rpo,
private ISelect<Sys_VerifyCode> QueryInternal(QueryReq<QueryVerifyCodeReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -113,6 +113,8 @@ public sealed class ExampleService(BasicRepository<Tpl_Example, long> rpo) //
private ISelect<Tpl_Example> QueryInternal(QueryReq<QueryExampleReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -0,0 +1,10 @@
using NetAdmin.Cache;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// <summary>
/// 请求日志明细缓存
/// </summary>
public interface IRequestLogDetailCache : ICache<IDistributedCache, IRequestLogDetailService>, IRequestLogDetailModule;

View File

@ -81,8 +81,8 @@ public sealed class DicCache(IDistributedCache cache, IDicService service) //
public Task<string> 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);

View File

@ -85,11 +85,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> 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)
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(
QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> 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)
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> 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);

View File

@ -18,9 +18,22 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
}
/// <inheritdoc />
#if !DEBUG
public async Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
#else
public Task<long> CountAsync(QueryReq<QueryRequestLogReq> 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
}
/// <inheritdoc />
@ -54,11 +67,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> 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
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> 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
}
/// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> 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);

View File

@ -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;
/// <inheritdoc cref="IRequestLogDetailCache" />
public sealed class RequestLogDetailCache(IDistributedCache cache, IRequestLogDetailService service)
: DistributedCache<IRequestLogDetailService>(cache, service), IScoped, IRequestLogDetailCache
{
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Service.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<long> CountAsync(QueryReq<QueryRequestLogDetailReq> req)
{
return Service.CountAsync(req);
}
/// <inheritdoc />
public Task<QueryRequestLogDetailRsp> CreateAsync(CreateRequestLogDetailReq req)
{
return Service.CreateAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
return Service.DeleteAsync(req);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryRequestLogDetailReq> req)
{
return Service.ExistAsync(req);
}
/// <inheritdoc />
public Task<IActionResult> ExportAsync(QueryReq<QueryRequestLogDetailReq> req)
{
return Service.ExportAsync(req);
}
/// <inheritdoc />
public Task<QueryRequestLogDetailRsp> GetAsync(QueryRequestLogDetailReq req)
{
return Service.GetAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryRequestLogDetailRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogDetailReq> req)
{
return Service.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryRequestLogDetailRsp>> QueryAsync(QueryReq<QueryRequestLogDetailReq> req)
{
return Service.QueryAsync(req);
}
}

View File

@ -99,8 +99,8 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
public Task<IEnumerable<QueryUserRsp>> QueryAsync(QueryReq<QueryUserReq> 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);

View File

@ -119,7 +119,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, I
/// <summary>
/// 获取作业记录条形图数据
/// </summary>
public Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
{
return Cache.GetRecordBarChartAsync(req);
}
@ -127,8 +127,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, I
/// <summary>
/// 状态码分组作业记录饼图数据
/// </summary>
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(
QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
{
return Cache.GetRecordPieChartByHttpStatusCodeAsync(req);
}
@ -136,7 +135,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, I
/// <summary>
/// 名称分组作业记录饼图数据
/// </summary>
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{
return Cache.GetRecordPieChartByNameAsync(req);
}

View File

@ -82,7 +82,7 @@ public sealed class LogController(IRequestLogCache cache) : ControllerBase<IRequ
/// <summary>
/// 获取条形图数据
/// </summary>
public Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
{
return Cache.GetBarChartAsync(req);
}
@ -90,7 +90,7 @@ public sealed class LogController(IRequestLogCache cache) : ControllerBase<IRequ
/// <summary>
/// 描述分组饼图数据
/// </summary>
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
{
return Cache.GetPieChartByApiSummaryAsync(req);
}
@ -98,7 +98,7 @@ public sealed class LogController(IRequestLogCache cache) : ControllerBase<IRequ
/// <summary>
/// 状态码分组饼图数据
/// </summary>
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
public Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
{
return Cache.GetPieChartByHttpStatusCodeAsync(req);
}

View File

@ -66,7 +66,18 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, 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) {

View File

@ -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<IRequestLogService>().CreateAsync(operationEvent.Data).ConfigureAwait(false);
}