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

[skip ci]

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-07-22 12:58:10 +08:00 committed by GitHub
parent 60ec6ea2c1
commit 1a28e8d5a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
107 changed files with 1256 additions and 664 deletions

View File

@ -134,6 +134,8 @@
重设密码
链接
错误
随机延时结束时间不正确
随机延时起始时间不正确
随机排序
项值
项名

View File

@ -23,7 +23,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.28.0.94264">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.29.0.95321">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

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

View File

@ -9,7 +9,7 @@
"packages": [
{
"packageName": "Furion.Pure.NS",
"version": "4.9.4-ns1"
"version": "4.9.4.6-ns4"
}
]
}

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

View File

@ -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%",

View File

@ -1,7 +1,10 @@
<template>
<el-table-column v-bind="$attrs">
<template #default="{ row }">
<div @click="click($TOOL.getNestedProperty(row, $attrs.prop))" class="avatar">
<div
:style="{ display: $TOOL.getNestedProperty(row, $attrs.prop) ? 'flex' : 'none' }"
@click="click($TOOL.getNestedProperty(row, $attrs.prop))"
class="avatar">
<el-avatar v-if="$TOOL.getNestedProperty(row, $attrs.nestProp)" :src="getAvatar(row, $attrs.nestProp)" size="small"></el-avatar>
<div>
<p>{{ $TOOL.getNestedProperty(row, $attrs.nestProp) }}</p>

View File

@ -94,7 +94,7 @@ export default {
return {
dateShortCuts: [
{
text: this.$t('今'),
text: this.$t('今'),
value: () => {
const start = new Date()
start.setHours(0, 0, 0, 0)
@ -102,7 +102,7 @@ export default {
},
},
{
text: this.$t('昨'),
text: this.$t('昨'),
value: () => {
const start = new Date()
start.setHours(0, 0, 0, 0)
@ -111,7 +111,17 @@ export default {
},
},
{
text: this.$t('最近三天'),
text: this.$t('后退一日'),
value: () => {
try {
const start = new Date(new Date(this.form.dy.createdTime[0]) - 3600 * 1000 * 24)
const end = new Date(new Date(this.form.dy.createdTime[1]) - 3600 * 1000 * 24)
return [start, end]
} catch {}
},
},
{
text: this.$t('最近三日'),
value: () => {
const start = new Date()
start.setHours(0, 0, 0, 0)
@ -342,6 +352,16 @@ export default {
return [start, end]
},
},
{
text: this.$t('后退一时'),
value: () => {
try {
const start = new Date(new Date(this.form.dy.createdTime[0]) - 3600 * 1000)
const end = new Date(new Date(this.form.dy.createdTime[1]) - 3600 * 1000)
return [start, end]
} catch {}
},
},
{
text: this.$t('昨日此时'),
value: () => {

View File

@ -349,9 +349,9 @@ export default {
两次输入密码不一致: 'The two passwords entered are inconsistent',
必须提供当前登录用户密码才能进行更改: 'You must provide the current login user password to change',
设置密码: 'Set password',
: 'Today',
: 'Yesterday',
最近三: 'Last 3 days',
: 'Today',
: 'Yesterday',
最近三: 'Last 3 days',
最近一周: 'Last week',
最近一月: 'Last month',
最近三月: 'Last 3 months',
@ -484,4 +484,6 @@ export default {
'访问趋势(Today)': 'Access trend (Today)',
'作业分布(Today)': 'Job distribution (Today)',
'作业趋势(Today)': 'Job trend (Today)',
后退一时: 'Back an hour',
后退一日: 'Back a day',
}

View File

@ -348,9 +348,9 @@ export default {
两次输入密码不一致: '两次输入密码不一致',
必须提供当前登录用户密码才能进行更改: '必须提供当前登录用户密码才能进行更改',
设置密码: '设置密码',
: '今天',
: '昨天',
最近三: '最近三天',
: '今日',
: '昨日',
最近三: '最近三日',
最近一周: '最近一周',
最近一月: '最近一月',
最近三月: '最近三月',
@ -481,4 +481,6 @@ export default {
'访问趋势(Today)': '访问趋势(Today)',
'作业分布(Today)': '作业分布(Today)',
'作业趋势(Today)': '作业趋势(Today)',
后退一时: '后退一时',
后退一日: '后退一日',
}

View File

@ -505,6 +505,10 @@ textarea {
width: 100%;
}
.h100p {
height: 100%;
}
.text-center {
text-align: center;
}
@ -527,4 +531,24 @@ textarea {
.font-monospace {
font-family: 'Lucida Console', 'Microsoft YaHei', monospace;
}
.trace-log {
font-family: 'Lucida Console', 'Microsoft YaHei', monospace;
&:after {
content: '';
margin: 2rem 0;
border-bottom: var(--el-color-primary) 1px dashed;
display: block;
}
.w15 {
font-weight: bold;
}
}
.keywords-highlight {
background-color: yellow;
color: var(--el-color-black);
}

View File

@ -251,6 +251,15 @@ tool.groupSeparator = function (num) {
tool.unicodeDecode = function (str) {
return str.replace(/\\u([0-9a-fA-F]{4})/g, (match, grp) => String.fromCharCode(parseInt(grp, 16)))
}
// 高亮关键词
tool.highLightKeywords = function (str) {
console.error(str)
var ret = str
.replace(new RegExp('(Body)', 'gi'), '<span class=keywords-highlight>$1</span>')
.replace(new RegExp('(http://cloud_code_adm_api.*?),', 'gi'), '<span class=keywords-highlight>$1</span>')
console.error(ret)
return ret
}
// 属性排序
tool.sortProperties = function (obj) {
const sortedKeys = Object.keys(obj).sort()

View File

@ -6,16 +6,13 @@
file: 'sys_job',
name: 'getRecordBarChart',
label: '今日',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
value: ['DateTime.Now.Date', 'DateTime.Now.Date.AddDays(1)'],
},
{
file: 'sys_job',
name: 'getRecordBarChart',
label: '昨日',
value: [
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
],
value: ['DateTime.Now.Date.AddDays(-1)', 'DateTime.Now.Date'],
},
]"
height="20rem"></chart-bar>

View File

@ -6,16 +6,13 @@
file: 'sys_log',
name: 'getBarChart',
label: '今日',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
value: ['DateTime.Now.Date', 'DateTime.Now.Date.AddDays(1)'],
},
{
file: 'sys_log',
name: 'getBarChart',
label: '昨日',
value: [
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
],
value: ['DateTime.Now.Date.AddDays(-1)', 'DateTime.Now.Date'],
},
]"
height="20rem"></chart-bar>

View File

@ -113,25 +113,23 @@ export default {
return table
},
},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
this.query.dynamicFilter.filters.push({
field: 'enabled',
operator: 'eq',
value: true,
})
},
created() {},
data() {
return {
dialog: {},
loading: false,
query: {
dynamicFilter: {
filters: [],
filters: [
{
field: 'enabled',
operator: 'eq',
value: true,
},
],
},
filter: {},
keywords: this.keywords,
},
selection: [],
}

View File

@ -60,7 +60,7 @@ export default {
form: {
enabled: true,
},
loading: false,
loading: true,
mode: 'add',
//
rules: {

View File

@ -124,25 +124,23 @@ export default {
return table
},
},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
this.query.dynamicFilter.filters.push({
field: 'enabled',
operator: 'eq',
value: true,
})
},
created() {},
data() {
return {
dialog: {},
loading: false,
query: {
dynamicFilter: {
filters: [],
filters: [
{
field: 'enabled',
operator: 'eq',
value: true,
},
],
},
filter: {},
keywords: this.keywords,
},
selection: [],
}

View File

@ -66,7 +66,7 @@ export default {
},
//
form: { enabled: true, sort: 100 },
loading: false,
loading: true,
mode: 'add',
//
rules: {

View File

@ -64,11 +64,7 @@ export default {
saveDialog,
},
computed: {},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
},
created() {},
data() {
return {
data: [],
@ -81,6 +77,7 @@ export default {
filters: [],
},
filter: {},
keywords: this.keywords,
},
}
},
@ -89,7 +86,7 @@ export default {
//
async getData() {
this.loading = true
const res = await this.$API.sys_dic.queryCatalog.post()
const res = await this.$API.sys_dic.queryCatalog.post({ prop: 'name', order: 'ascending' })
this.loading = false
this.data = res.data

View File

@ -87,11 +87,7 @@ export default {
return table
},
},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
},
created() {},
data() {
return {
dialog: {},
@ -101,6 +97,7 @@ export default {
filters: [],
},
filter: {},
keywords: this.keywords,
},
selection: [],
}

View File

@ -40,7 +40,7 @@ export default {
return {
//
form: {},
loading: false,
loading: true,
mode: 'add',
//
rules: {

View File

@ -24,7 +24,7 @@ export default {
return {
//
form: {},
loading: false,
loading: true,
mode: 'add',
//
rules: {

View File

@ -181,6 +181,12 @@
icon: 'el-icon-video-play',
click: execute,
},
{
icon: 'el-icon-document-copy',
confirm: true,
title: '复制作业',
click: copyJob,
},
{
icon: 'el-icon-delete',
confirm: true,
@ -191,7 +197,7 @@
)
"
:vue="this"
width="200" />
width="220" />
</sc-table>
</el-main>
</el-container>
@ -224,25 +230,23 @@ export default {
return table
},
},
created() {
if (this.keywords || this.$route.query.keywords) {
this.query.keywords = this.keywords || this.$route.query.keywords
}
this.query.dynamicFilter.filters.push({
field: 'enabled',
operator: 'eq',
value: true,
})
},
created() {},
data() {
return {
dialog: {},
loading: false,
query: {
dynamicFilter: {
filters: [],
filters: [
{
field: 'enabled',
operator: 'eq',
value: true,
},
],
},
filter: {},
keywords: this.keywords || this.$route.query.keywords,
},
selection: [],
timer: null,
@ -250,6 +254,19 @@ export default {
},
inject: ['reload'],
methods: {
async copyJob(row) {
let loading = this.$loading()
try {
const res = await this.$API.sys_job.create.post(Object.assign({}, row, { id: 0, jobName: row.jobName + '-copy' }))
if (res.data) {
this.$message.success(this.$t('操作成功'))
} else {
this.$message.error(this.$t('操作失败'))
}
this.$refs.table.refresh()
} catch {}
loading?.close()
},
async setEnabled(enabled) {
let loading
try {

View File

@ -58,6 +58,22 @@
<el-form-item :label="$t('请求的网络地址')" prop="requestUrl">
<el-input v-model="form.requestUrl" clearable />
</el-form-item>
<el-form-item :label="$t('随机延时')">
<div class="flex gap05">
<el-input
v-model="form.randomDelayBegin"
:placeholder="$t('起始值(毫秒)')"
class="w15"
clearable
oninput="value=value.replace(/[^0-9]/g,'')" />
<el-input
v-model="form.randomDelayEnd"
:placeholder="$t('结束值(毫秒)')"
class="w15"
clearable
oninput="value=value.replace(/[^0-9]/g,'')" />
</div>
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('作业状态')" prop="status">
<el-input v-model="form.status" clearable />
</el-form-item>
@ -127,7 +143,7 @@ export default {
requestHeader: `{ "Content-Type": "application/json" }`,
requestBody: '{}',
},
loading: false,
loading: true,
mode: 'add',
//
rules: {

View File

@ -54,7 +54,7 @@
}
}
"
:context-menus="['id', 'duration', 'httpMethod', 'requestUrl', 'httpStatusCode', 'createdTime', 'jobId']"
:context-menus="['id', 'duration', 'httpMethod', 'requestUrl', 'httpStatusCode', 'createdTime', 'jobId', 'responseBody']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_job.exportRecord"
:params="query"
@ -153,9 +153,6 @@ export default {
},
},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
if (this.statusCodes) {
this.query.dynamicFilter.filters.push({
logic: 'or',
@ -184,6 +181,7 @@ export default {
],
},
filter: {},
keywords: this.keywords,
},
}
},

View File

@ -51,7 +51,7 @@ export default {
return {
//
form: {},
loading: false,
loading: true,
mode: 'add',
//
rules: {},

View File

@ -47,7 +47,6 @@
:params="query"
:query-api="$API.sys_log.pagedQuery"
:vue="this"
@row-click="rowClick"
ref="table"
remote-filter
remote-sort
@ -60,14 +59,23 @@
{{ row.httpStatusCode === 200 ? '成功' : '失败' }}
</template>
</el-table-column>
<el-table-column :label="$t('登录名')" prop="loginName" width="150" />
<el-table-column :label="$t('登录名')" prop="detail.loginName" width="150" />
<el-table-column :label="$t('客户端IP')" prop="createdClientIp" show-overflow-tooltip sortable="custom" width="200">
<template #default="{ row }">
<na-ip :ip="row.createdClientIp"></na-ip>
</template>
</el-table-column>
<el-table-column :label="$t('操作系统')" align="center" prop="os" width="150" />
<el-table-column :label="$t('用户代理')" prop="createdUserAgent" show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('操作系统')" align="center" prop="detail.os" width="150" />
<el-table-column :label="$t('用户代理')" prop="detail.createdUserAgent" show-overflow-tooltip sortable="custom" />
<na-col-operation
:buttons="[
{
icon: 'el-icon-view',
click: rowClick,
},
]"
:vue="this"
width="70" />
</sc-table>
</el-main>
</el-container>
@ -84,13 +92,10 @@ export default {
},
computed: {},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
this.query.dynamicFilter.filters.push({
field: 'apiId',
field: 'apiPathCrc32',
operator: 'eq',
value: 'api/sys/user/login.by.pwd',
value: '1290209789',
})
},
data() {
@ -110,6 +115,7 @@ export default {
],
},
filter: {},
keywords: this.keywords,
},
selection: [],
}
@ -124,11 +130,11 @@ export default {
},
//
onSearch(form) {
if (this.query.dynamicFilter.filters.findIndex((x) => x.field === 'apiId') < 0) {
if (this.query.dynamicFilter.filters.findIndex((x) => x.field === 'apiPathCrc32') < 0) {
this.query.dynamicFilter.filters.push({
field: 'apiId',
field: 'apiPathCrc32',
operator: 'eq',
value: 'api/sys/user/login.by.pwd',
value: '1290209789',
})
}
if (Array.isArray(form.dy.createdTime)) {
@ -174,7 +180,7 @@ export default {
type: 'root',
})
}
this.$refs.search.form.dy.apiId = 'api/sys/user/login.by.pwd'
this.$refs.search.form.dy['apiPathCrc32'] = '1290209789'
this.$refs.search.form.dy.createdTime = [
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,

Some files were not shown because too many files have changed in this diff Show More