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> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.28.0.94264"> <PackageReference Include="SonarAnalyzer.CSharp" Version="9.29.0.95321">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -3,7 +3,7 @@
"devDependencies": { "devDependencies": {
"cz-git": "^1.9.3", "cz-git": "^1.9.3",
"commitizen": "^4.3.0", "commitizen": "^4.3.0",
"prettier": "^3.3.2", "prettier": "^3.3.3",
"standard-version": "^9.5.0" "standard-version": "^9.5.0"
}, },
"config": { "config": {

View File

@ -9,7 +9,7 @@
"packages": [ "packages": [
{ {
"packageName": "Furion.Pure.NS", "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;
using NetAdmin.AdmServer.Host.Extensions; using NetAdmin.AdmServer.Host.Extensions;
using NetAdmin.Host.Extensions; using NetAdmin.Host.Extensions;
@ -79,6 +80,7 @@ namespace NetAdmin.AdmServer.Host
#pragma warning restore ASA001 #pragma warning restore ASA001
{ {
Args = settings; Args = settings;
IdentityModelEventSource.ShowPII = true;
_ = Serve.Run(RunOptions.Default.WithArgs(context.Remaining.Raw.ToArray())); _ = Serve.Run(RunOptions.Default.WithArgs(context.Remaining.Raw.ToArray()));
return Task.FromResult(0); return Task.FromResult(0);
} }

View File

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

View File

@ -30,6 +30,46 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
set => Rpo.DbContextOptions.EnableCascadeSave = value; 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>
/// 更新实体 /// 更新实体
/// </summary> /// </summary>
@ -77,43 +117,6 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
} }
#endif #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( // private IUpdate<TEntity> BuildUpdate( //
TEntity entity // TEntity entity //
, IEnumerable<string> includeFields // , IEnumerable<string> includeFields //

View File

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

View File

@ -7,6 +7,12 @@ namespace NetAdmin.Domain.Contexts;
/// </summary> /// </summary>
public sealed record ContextUserToken : DataAbstraction public sealed record ContextUserToken : DataAbstraction
{ {
/// <summary>
/// 部门编号
/// </summary>
/// ReSharper disable once MemberCanBePrivate.Global
public long DeptId { get; init; }
/// <summary> /// <summary>
/// 用户编号 /// 用户编号
/// </summary> /// </summary>
@ -39,6 +45,8 @@ public sealed record ContextUserToken : DataAbstraction
/// </summary> /// </summary>
public static ContextUserToken Create(QueryUserRsp user) 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; namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary> /// <summary>
/// 创建者客户端字段接口 /// 创建者客户端用户代理字段接口
/// </summary> /// </summary>
public interface IFieldCreatedClient public interface IFieldCreatedClientUserAgent
{ {
/// <summary>
/// 创建者客户端IP
/// </summary>
int? CreatedClientIp { get; init; }
/// <summary> /// <summary>
/// 创建者客户端用户代理 /// 创建者客户端用户代理
/// </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; namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary> /// <summary>
/// 修改客户端字段接口 /// 修改客户端用户代理字段接口
/// </summary> /// </summary>
public interface IFieldModifiedClient public interface IFieldModifiedClientUserAgent
{ {
/// <summary>
/// 客户端IP
/// </summary>
int ModifiedClientIp { get; init; }
/// <summary> /// <summary>
/// 客户端用户代理 /// 客户端用户代理
/// </summary> /// </summary>

View File

@ -4,6 +4,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// Api接口表 /// Api接口表
/// </summary> /// </summary>
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Api))] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Api))]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(PathCrc32), nameof(PathCrc32), true)]
public record Sys_Api : ImmutableEntity<string>, IFieldSummary public record Sys_Api : ImmutableEntity<string>, IFieldSummary
{ {
/// <summary> /// <summary>
@ -54,6 +55,14 @@ public record Sys_Api : ImmutableEntity<string>, IFieldSummary
[JsonIgnore] [JsonIgnore]
public virtual string ParentId { get; init; } public virtual string ParentId { get; init; }
/// <summary>
/// 路径CRC32
/// </summary>
[Column]
[Ignore]
[JsonIgnore]
public int PathCrc32 { get; init; }
/// <summary> /// <summary>
/// 角色集合 /// 角色集合
/// </summary> /// </summary>

View File

@ -18,7 +18,7 @@ public record Sys_DicCatalog : VersionEntity
/// <summary> /// <summary>
/// 字典编码 /// 字典编码
/// </summary> /// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)] [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual string Code { get; init; } public virtual string Code { get; init; }
@ -34,7 +34,7 @@ public record Sys_DicCatalog : VersionEntity
/// <summary> /// <summary>
/// 字典名称 /// 字典名称
/// </summary> /// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)] [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual string Name { get; init; } public virtual string Name { get; init; }

View File

@ -4,8 +4,6 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 字典内容表 /// 字典内容表
/// </summary> /// </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(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))] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_DicContent))]
public record Sys_DicContent : VersionEntity public record Sys_DicContent : VersionEntity
{ {

View File

@ -79,6 +79,22 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
[JsonIgnore] [JsonIgnore]
public virtual long? NextTimeId { get; init; } 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>
/// 请求体 /// 请求体
/// </summary> /// </summary>

View File

@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 计划作业执行记录表 /// 计划作业执行记录表
/// </summary> /// </summary>
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)] [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(JobId), nameof(JobId), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)] [SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))] [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; namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary> /// <summary>
/// 请求日志表 /// 请求日志表
/// </summary> /// </summary>
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)] [SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiPathCrc32), nameof(ApiPathCrc32), false)]
[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(UserId), nameof(UserId), false)] [SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(OwnerId), nameof(OwnerId), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)] [SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))] [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>
/// 接口 /// 接口
/// </summary> /// </summary>
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
[Navigate(nameof(ApiId))] [Navigate(nameof(ApiPathCrc32), TempPrimary = nameof(Sys_Api.PathCrc32))]
public Sys_Api Api { get; init; } public Sys_Api Api { get; init; }
/// <summary> /// <summary>
/// 接口编号 /// 接口路径CRC32
/// </summary> /// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)] [Column]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual string ApiId { get; init; } public virtual int ApiPathCrc32 { get; init; }
/// <inheritdoc /> /// <inheritdoc />
[Column(Position = -1)] [Column]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public int? CreatedClientIp { get; init; } public virtual int? CreatedClientIp { get; init; }
/// <inheritdoc /> /// <inheritdoc />
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)] [Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)]
@ -38,161 +40,55 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedCli
[JsonIgnore] [JsonIgnore]
public virtual DateTime CreatedTime { get; init; } public virtual DateTime CreatedTime { get; init; }
/// <inheritdoc /> /// <summary>
#if DBTYPE_SQLSERVER /// 明细
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)] /// </summary>
#else
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual string CreatedUserAgent { get; init; } [Navigate(nameof(Id))]
public Sys_RequestLogDetail Detail { get; init; }
/// <summary> /// <summary>
/// 执行耗时(秒) /// 执行耗时(秒)
/// </summary> /// </summary>
[Column] [Column]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual long Duration { get; init; } public virtual int Duration { get; init; }
/// <summary> /// <summary>
/// 程序响应码 /// 请求方法
/// </summary> /// </summary>
[Column] [Column(DbType = Chars.FLG_DB_FIELD_TYPE_TINY_INT)]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual ErrorCodes ErrorCode { get; init; } public virtual HttpMethods HttpMethod { 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>
/// HTTP状态码 /// HTTP状态码
/// </summary> /// </summary>
[Column] [Column(DbType = Chars.FLG_DB_FIELD_TYPE_SMALL_INT)]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual int HttpStatusCode { get; init; } public virtual int HttpStatusCode { get; init; }
/// <summary> /// <summary>
/// 请求方法 /// 拥有者
/// </summary> /// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual string Method { get; init; } [Navigate(nameof(OwnerId))]
public Sys_User Owner { get; init; }
/// <summary> /// <inheritdoc />
/// 请求内容
/// </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] [Column]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual int? ServerIp { get; init; } public virtual long? OwnerDeptId { get; init; }
/// <summary> /// <inheritdoc />
/// 请求跟踪标识
/// </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>
[Column] [Column]
[Ignore] [Ignore]
[JsonIgnore] [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> /// </summary>
public static implicit operator FreeSql.Internal.Model.DynamicFilterInfo(DynamicFilterInfo d) 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 /> /// <inheritdoc />
public override long? NextTimeId { get; init; } 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" /> /// <inheritdoc cref="Sys_Job.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; } public override string RequestBody { get; init; }

View File

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

View File

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

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 NetAdmin.Domain.Dto.Sys.User;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.Dto.Sys.RequestLog; namespace NetAdmin.Domain.Dto.Sys.RequestLog;
/// <summary> /// <summary>
/// 响应:查询请求日志 /// 响应:查询请求日志
/// </summary> /// </summary>
public record QueryRequestLogRsp : Sys_RequestLog, IRegister public record QueryRequestLogRsp : Sys_RequestLog
{ {
/// <summary> /// <summary>
/// 创建者客户端IP /// 创建者客户端IP
@ -13,101 +16,39 @@ public record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonInclude] [JsonInclude]
public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4(); public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4();
/// <summary> /// <inheritdoc cref="Sys_RequestLog.Api" />
/// 登录名
/// </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" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ApiId { get; init; } public new virtual QueryApiRsp Api { get; init; }
/// <summary> /// <inheritdoc cref="Sys_RequestLog.ApiPathCrc32" />
/// 接口描述 [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
/// </summary> public override int ApiPathCrc32 { get; init; }
public string ApiSummary { get; init; }
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" /> /// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)] [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; } public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="Sys_RequestLog.CreatedUserAgent" /> /// <inheritdoc cref="Sys_RequestLog.Detail" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserAgent { get; init; } public new virtual QueryRequestLogDetailRsp Detail { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Duration" /> /// <inheritdoc cref="Sys_RequestLog.Duration" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)] [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)] [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override ErrorCodes ErrorCode { get; init; } public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Exception" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Exception { get; init; }
/// <inheritdoc cref="Sys_RequestLog.HttpStatusCode" /> /// <inheritdoc cref="Sys_RequestLog.HttpStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)] [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override int HttpStatusCode { get; init; } public override int HttpStatusCode { get; init; }
/// <inheritdoc cref="Sys_RequestLog.Method" /> /// <inheritdoc cref="Sys_RequestLog.Owner" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [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)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; } public override long? OwnerId { 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);
}
} }

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) // public SqlCommandAfterEvent(CommandAfterEventArgs e) //
: base(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); EventId = nameof(SqlCommandAfterEvent);
} }
/// <summary> /// <summary>
/// 耗时(单位:秒) /// 耗时(单位:秒)
/// </summary> /// </summary>
/// de /// de
private long ElapsedMicroseconds { get; } private long ElapsedMilliseconds { get; }
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return string.Format(CultureInfo.InvariantCulture, "SQL-{0}: {2} ms {1}", Id return string.Format(CultureInfo.InvariantCulture, "SQL-{0}: {2} ms {1}", Id, Sql, ElapsedMilliseconds);
, Sql?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_SQL), ElapsedMicroseconds / 1000);
} }
} }

View File

@ -10,7 +10,7 @@
<ProjectReference Include="../NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj"/> <ProjectReference Include="../NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj"/>
</ItemGroup> </ItemGroup>
<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="Cronos" Version="0.8.4"/>
<PackageReference Include="CsvHelper.NS" Version="33.0.2-ns2"/> <PackageReference Include="CsvHelper.NS" Version="33.0.2-ns2"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.6.24328.4"/> <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> /// </summary>
protected UnitOfWorkManager UowManager { get; } 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>
/// 通用工作流 /// 通用工作流
/// </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> /// <summary>
/// 通用工作流 /// 通用工作流
@ -69,4 +64,14 @@ public abstract class WorkBase<TLogger>
await WorkflowAsync(cancelToken).ConfigureAwait(false); 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() .FirstOrDefault()
?.Enum<ErrorCodes>() ?? 0; ?.Enum<ErrorCodes>() ?? 0;
_ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMicroseconds, responseBody, errorCode _ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMilliseconds, responseBody, errorCode
, exception) , exception)
.ConfigureAwait(false); .ConfigureAwait(false);
} }

View File

@ -5,7 +5,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="IGeekFan.AspNetCore.Knife4jUI.NS" Condition="'$(Configuration)' == 'Debug'" Version="0.0.15-ns2"/> <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"/> <PackageReference Include="prometheus-net.AspNetCore" Condition="'$(Configuration)' != 'Debug'" Version="8.2.1"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,9 @@
using NetAdmin.Domain.Contexts; using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.Dto.Sys.RequestLog; using NetAdmin.Domain.Dto.Sys.RequestLog;
using NetAdmin.Domain.Dto.Sys.RequestLogDetail;
using NetAdmin.Domain.Events.Sys; using NetAdmin.Domain.Events.Sys;
using Yitter.IdGenerator;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Host.Utils; namespace NetAdmin.Host.Utils;
@ -19,35 +22,38 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
{ {
// 从请求头中读取用户信息 // 从请求头中读取用户信息
var associatedUser = GetAssociatedUser(context); var associatedUser = GetAssociatedUser(context);
var auditData = new CreateRequestLogReq { var id = YitIdHelper.NextId();
Duration = duration var requestBody = Array.Exists( //
, Method = context.Request.Method
, RequestContentType = context.Request.ContentType
, RequestBody = Array.Exists( //
_textContentTypes _textContentTypes
, x => context.Request.ContentType?.Contains( , x => context.Request.ContentType?.Contains(x, StringComparison.OrdinalIgnoreCase) ?? false)
x, StringComparison.OrdinalIgnoreCase) ?? false) ? await context.ReadBodyContentAsync().ConfigureAwait(false)
? (await context.ReadBodyContentAsync().ConfigureAwait(false)) : string.Empty;
?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT) var auditData = new CreateRequestLogReq //
: string.Empty {
, RequestUrl = context.Request.GetRequestUrlAddress() Detail = new CreateRequestLogDetailReq //
, ResponseBody {
= responseBody?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT) Id = id
, ServerIp = context.GetLocalIpAddressToIPv4()?.IpV4ToInt32() , CreatedUserAgent = context.Request.Headers.UserAgent.ToString()
, 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 , ErrorCode = errorCode
, Exception = exception?.Error.ToString() , Exception = exception?.Error.ToString()
, UserId = associatedUser?.UserId , RequestBody = requestBody
, CreatedUserAgent = context.Request.Headers.UserAgent.ToString() , RequestContentType = context.Request.ContentType
, CreatedClientIp = context.GetRealIpAddress() , RequestHeaders = context.Request.Headers.Json()
?.MapToIPv4() , RequestUrl = context.Request.GetRequestUrlAddress()
.ToString() , ResponseBody = responseBody
.IpV4ToInt32() , ResponseContentType = context.Response.ContentType
, ResponseHeaders = context.Response.Headers.Json()
, ServerIp = context.GetLocalIpAddressToIPv4()?.IpV4ToInt32()
, TraceId = context.TraceIdentifier , 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
}; };
// 打印日志 // 打印日志
@ -59,7 +65,7 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
return auditData; 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(); var token = context.Request.Headers.Authorization.FirstOrDefault();
if (token == null) { if (token == null) {
@ -69,7 +75,7 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
ContextUserToken userToken = null; ContextUserToken userToken = null;
try { try {
var jsonWebToken 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)); var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken));
userToken = claim?.Value.ToObject<ContextUserToken>(); userToken = claim?.Value.ToObject<ContextUserToken>();
} }
@ -77,6 +83,6 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
logger.Warn($"{Ln.读取用户令牌出错}: {ex}"); 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): case nameof(IFieldCreatedUser.CreatedUserName):
SetCreatedUserName(e, userInfo); SetCreatedUserName(e, userInfo);
break; break;
case nameof(IFieldCreatedClient.CreatedClientIp): case nameof(IFieldCreatedClientIp.CreatedClientIp):
SetCreatedClientIp(e); SetCreatedClientIp(e);
break; break;
case nameof(IFieldCreatedClient.CreatedUserAgent): case nameof(IFieldCreatedClientUserAgent.CreatedUserAgent):
SetCreatedUserAgent(e); SetCreatedUserAgent(e);
break; break;
default: 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_NVARCHAR_MAX = "nvarchar(max)";
public const string FLG_DB_FIELD_TYPE_SMALL_INT = "smallint"; 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_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 = "varchar";
public const string FLG_DB_FIELD_TYPE_VARCHAR_1022 = "varchar(1022)"; public const string FLG_DB_FIELD_TYPE_VARCHAR_1022 = "varchar(1022)";
public const string FLG_DB_FIELD_TYPE_VARCHAR_127 = "varchar(127)"; 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_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 = "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_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_FORWARDED_FOR = "X-Forwarded-For";
public const string FLG_HTTP_HEADER_KEY_X_REAL_IP = "X-Real-IP"; public const string FLG_HTTP_HEADER_KEY_X_REAL_IP = "X-Real-IP";
public const string FLG_HTTP_HEADER_VALUE_APPLICATION_JSON = "application/json"; 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_APPLICATION_URLENCODED = "application/x-www-form-urlencoded";
public const string FLG_HTTP_HEADER_VALUE_ATTACHMENT = "attachment"; 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_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_CONNECT = "CONNECT";
public const string FLG_HTTP_METHOD_DELETE = "DELETE"; public const string FLG_HTTP_METHOD_DELETE = "DELETE";
public const string FLG_HTTP_METHOD_GET = "GET"; 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_EXPIRY = 30; // 秒RedLock-锁过期时间,假如持有锁的进程挂掉,最多在此时间内锁将被释放(如持有锁的进程正常,此值不会生效)
public const int SECS_RED_LOCK_RETRY = 1; // 秒RedLock-锁等待时间内,多久尝试获取一次 public const int SECS_RED_LOCK_RETRY = 1; // 秒RedLock-锁等待时间内,多久尝试获取一次
public const int SECS_RED_LOCK_WAIT = 10; // 秒RedLock-锁等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间 public const int SECS_RED_LOCK_WAIT = 10; // 秒RedLock-锁等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
public const int SECS_TIMEOUT_HTTP_CLIENT = 15; // 秒:超时时间-默认HTTP客户端
public const int SECS_TIMEOUT_JOB = 600; // 秒:超时时间-作业 public const int SECS_TIMEOUT_JOB = 600; // 秒:超时时间-作业
} }

View File

@ -10,8 +10,7 @@ public static class HttpRequestMessageExtensions
/// </summary> /// </summary>
public static async Task<HttpRequestMessage> LogAsync<T>(this HttpRequestMessage me, ILogger<T> logger) public static async Task<HttpRequestMessage> LogAsync<T>(this HttpRequestMessage me, ILogger<T> logger)
{ {
logger.Info( logger.Info($"HTTP Request {await me.BuildJsonAsync().ConfigureAwait(false)}");
$"HTTP Request {(await me.BuildJsonAsync().ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
return me; return me;
} }

View File

@ -11,8 +11,7 @@ public static class HttpResponseMessageExtensions
public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger
, Func<string, string> bodyPreHandle = null) , Func<string, string> bodyPreHandle = null)
{ {
logger.Info($"HTTP Response {(await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false))?.Sub( logger.Info($"HTTP Response {await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false)}");
0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
} }
/// <summary> /// <summary>
@ -21,8 +20,7 @@ public static class HttpResponseMessageExtensions
public static async Task LogExceptionAsync<T>(this HttpResponseMessage me, string errors, ILogger<T> logger public static async Task LogExceptionAsync<T>(this HttpResponseMessage me, string errors, ILogger<T> logger
, Func<string, string> bodyHandle = null) , Func<string, string> bodyHandle = null)
{ {
logger.Warn( logger.Warn($"{errors}: {await me.BuildJsonAsync(bodyHandle).ConfigureAwait(false)}");
$"{errors}: {(await me.BuildJsonAsync(bodyHandle).ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
} }
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
namespace NetAdmin.Infrastructure.Extensions; namespace NetAdmin.Infrastructure.Extensions;
/// <summary> /// <summary>
@ -5,8 +8,24 @@ namespace NetAdmin.Infrastructure.Extensions;
/// </summary> /// </summary>
public static class StringExtensions public static class StringExtensions
{ {
private static readonly Regex _regex = new("Options$"); /// <summary>
private static readonly Regex _regex2 = new("Async$"); /// 计算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> /// <summary>
/// object -> json /// object -> json
@ -31,7 +50,7 @@ public static class StringExtensions
public static string TrimEndAsync(this string me) public static string TrimEndAsync(this string me)
#pragma warning restore VSTHRD200, ASA002, RCS1047 #pragma warning restore VSTHRD200, ASA002, RCS1047
{ {
return _regex2.Replace(me, string.Empty); return TrimSuffix(me, "Async");
} }
/// <summary> /// <summary>
@ -39,14 +58,22 @@ public static class StringExtensions
/// </summary> /// </summary>
public static string TrimEndOptions(this string me) public static string TrimEndOptions(this string me)
{ {
return _regex.Replace(me, string.Empty); return TrimSuffix(me, "Options");
} }
/// <summary> /// <summary>
/// 去掉前部字符串 /// 去掉前部字符串
/// </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); 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 #endif
; ;
/// <summary>
/// 日志保存跳过的API编号
/// </summary>
public static string[] LogSavingSkipApiIds => ["api/probe/health.check", "api/adm/device.log/create"];
/// <summary> /// <summary>
/// 系统内部密钥 /// 系统内部密钥
/// </summary> /// </summary>

View File

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

View File

@ -42,17 +42,17 @@ public interface IJobModule : ICrudModule<CreateJobReq, QueryJobRsp // 创建类
/// <summary> /// <summary>
/// 获取作业记录条形图数据 /// 获取作业记录条形图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req); Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req);
/// <summary> /// <summary>
/// 状态码分组作业记录饼图数据 /// 状态码分组作业记录饼图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req); Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req);
/// <summary> /// <summary>
/// 名称分组作业记录饼图数据 /// 名称分组作业记录饼图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req); Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req);
/// <summary> /// <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>
/// 获取条形图数据 /// 获取条形图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req); Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req);
/// <summary> /// <summary>
/// 描述分组饼图数据 /// 描述分组饼图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req); Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req);
/// <summary> /// <summary>
/// 状态码分组饼图数据 /// 状态码分组饼图数据
/// </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) QueryApiRsp SelectQueryApiRsp(IGrouping<TypeInfo, ControllerActionDescriptor> group)
{ {
var first = group.First()!; var first = group.First()!;
var id = Regex.Replace( //
first.AttributeRouteInfo!.Template!, $"/{first.ActionName}$", string.Empty);
return new QueryApiRsp { return new QueryApiRsp {
Summary = xmlCommentReader.GetComments(group.Key) Summary = xmlCommentReader.GetComments(group.Key)
, Name = first.ControllerName , Name = first.ControllerName
, Id = Regex.Replace( // , Id = id
first.AttributeRouteInfo!.Template!, $"/{first.ActionName}$", string.Empty)
, Children = GetChildren(group) , Children = GetChildren(group)
, Namespace = regex.Match(group.Key.Namespace!).Groups[1].Value.ToLowerInvariant() , 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) private IEnumerable<QueryApiRsp> GetChildren(IEnumerable<ControllerActionDescriptor> actionDescriptors)
{ {
return actionDescriptors // return actionDescriptors //
.Select(x => new QueryApiRsp { .Select(x => {
var id = x.AttributeRouteInfo!.Template;
return new QueryApiRsp {
Summary = xmlCommentReader.GetComments(x.MethodInfo) Summary = xmlCommentReader.GetComments(x.MethodInfo)
, Name = x.ActionName , Name = x.ActionName
, Id = x.AttributeRouteInfo!.Template , Id = id
, Method = x.ActionConstraints?.OfType<HttpMethodActionConstraint>() , Method = x.ActionConstraints?.OfType<HttpMethodActionConstraint>()
.FirstOrDefault() .FirstOrDefault()
?.HttpMethods.First() ?.HttpMethods.First()
, PathCrc32 = id.Crc32()
};
}); });
} }
private ISelect<Sys_Api> QueryInternal(QueryReq<QueryApiReq> req) private ISelect<Sys_Api> QueryInternal(QueryReq<QueryApiReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -142,6 +142,8 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
.WhereDynamicFilter(req.DynamicFilter) .WhereDynamicFilter(req.DynamicFilter)
.WhereIf( // .WhereIf( //
req.Filter?.Enabled.HasValue ?? false, a => a.Enabled == req.Filter.Enabled.Value); req.Filter?.Enabled.HasValue ?? false, a => a.Enabled == req.Filter.Enabled.Value);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -14,15 +14,15 @@ public interface IJobRecordService : IService, IJobRecordModule
/// <summary> /// <summary>
/// 获取条形图数据 /// 获取条形图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req); Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req);
/// <summary> /// <summary>
/// 状态码分组饼图数据 /// 状态码分组饼图数据
/// </summary> /// </summary>
Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req); Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req);
/// <summary> /// <summary>
/// 名称分组饼图数据 /// 名称分组饼图数据
/// </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(); ret = ret.AsTreeCte();
} }
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -175,6 +175,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
await File.WriteAllTextAsync(file, content).ConfigureAwait(false); await File.WriteAllTextAsync(file, content).ConfigureAwait(false);
} }
// ReSharper disable once SeparateLocalFunctionsWithJumpStatement
IEnumerable<string> Select(QueryApiRsp item) IEnumerable<string> Select(QueryApiRsp item)
{ {
return item.Children.Select(x => tplInner.Replace("$actionDesc$", x.Summary) 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) private ISelect<Sys_DicCatalog> QueryInternal(QueryReq<QueryDicCatalogReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -80,7 +80,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req) public async Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryJobRecordReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
@ -105,8 +105,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync( public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
QueryReq<QueryJobRecordReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None }) var ret = await QueryInternal(req with { Order = Orders.None })
@ -124,7 +123,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByNameAsync(QueryReq<QueryJobRecordReq> req) public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None }) 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 req.Keywords?.Length > 0
, a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0) || , a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0) ||
a.Job.JobName == req.Keywords); a.Job.JobName == req.Keywords);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -79,6 +79,8 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
.SetIf(req.RequestHeaders != null, a => a.RequestHeader, req.RequestHeaders.Json()) .SetIf(req.RequestHeaders != null, a => a.RequestHeader, req.RequestHeaders.Json())
.Set(a => a.RequestBody == req.RequestBody) .Set(a => a.RequestBody == req.RequestBody)
.Set(a => a.RequestUrl == req.RequestUrl) .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.UserId == req.UserId)
.Set(a => a.Summary == req.Summary) .Set(a => a.Summary == req.Summary)
.Where(a => a.Id == req.Id); .Where(a => a.Id == req.Id);
@ -236,22 +238,21 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req) public Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
return jobRecordService.GetBarChartAsync(req); return jobRecordService.GetBarChartAsync(req);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync( public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
QueryReq<QueryJobRecordReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
return jobRecordService.GetPieChartByHttpStatusCodeAsync(req); return jobRecordService.GetPieChartByHttpStatusCodeAsync(req);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req) public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
return jobRecordService.GetPieChartByNameAsync(req); return jobRecordService.GetPieChartByNameAsync(req);
@ -335,6 +336,8 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
.WhereIf( // .WhereIf( //
req.Keywords?.Length > 0 req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords)); , a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords));
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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; namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IRequestLogService" /> /// <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 : RepositoryService<Sys_RequestLog, long, IRequestLogService>(rpo), IRequestLogService
{ {
private static readonly Regex _regex = new(Chars.RGXL_IP_V4); 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(); req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false); var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
_ = await requestLogDetailService.CreateAsync(req.Detail).ConfigureAwait(false);
return ret.Adapt<QueryRequestLogRsp>(); return ret.Adapt<QueryRequestLogRsp>();
} }
@ -68,7 +71,17 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
public Task<IActionResult> ExportAsync(QueryReq<QueryRequestLogReq> req) public Task<IActionResult> ExportAsync(QueryReq<QueryRequestLogReq> req)
{ {
req.ThrowIfInvalid(); 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 /> /// <inheritdoc />
@ -76,17 +89,18 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req }) var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req })
.Include(a => a.Detail)
.ToOneAsync() .ToOneAsync()
.ConfigureAwait(false); .ConfigureAwait(false);
return ret.Adapt<QueryRequestLogRsp>(); return ret.Adapt<QueryRequestLogRsp>();
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req) public async Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None }) var ret = await QueryInternal(req with { Order = Orders.None }, false)
#if DBTYPE_SQLSERVER #if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif #endif
@ -107,7 +121,7 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req) public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None }) var ret = await QueryInternal(req with { Order = Orders.None })
@ -121,11 +135,10 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync( public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
QueryReq<QueryRequestLogReq> req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
var ret = await QueryInternal(req with { Order = Orders.None }) var ret = await QueryInternal(req with { Order = Orders.None }, false)
#if DBTYPE_SQLSERVER #if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif #endif
@ -148,42 +161,40 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif #endif
.Count(out var total); .Count(out var total);
object list = req.DynamicFilter?.Filters?.Exists(
x => nameof(QueryRequestLogReq.ApiId).Equals(x.Field, StringComparison.OrdinalIgnoreCase) && object ret
Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Equals( // = req.DynamicFilter?.Filters?.Exists(
x.Value.ToString(), StringComparison.OrdinalIgnoreCase)) ?? false x => nameof(QueryRequestLogReq.ApiPathCrc32).Equals(x.Field, StringComparison.OrdinalIgnoreCase) &&
? await select.ToListAsync(a => new { x.Value.ToString().Int32() == Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Crc32()) ?? false
a.ApiId ? await select.Include(a => a.Detail)
, ApiSummary = a.Api.Summary .ToListAsync(a => new {
Api = new { a.Api.Summary, a.Api.Id }
, Owner = new { a.Owner.Id, a.Owner.UserName }
, a.CreatedClientIp , a.CreatedClientIp
, a.CreatedTime , a.CreatedTime
, a.Duration , a.Duration
, a.Method , a.HttpMethod
, a.CreatedUserAgent
, a.HttpStatusCode , a.HttpStatusCode
, a.Id , a.Id
, a.UserId , a.ApiPathCrc32
, a.User , Detail = new { a.Detail.RequestBody, a.Detail.CreatedUserAgent }
, a.RequestBody
}) })
.ConfigureAwait(false) .ConfigureAwait(false)
: await select.ToListAsync(a => new { : await select.ToListAsync(a => new {
a.ApiId Api = new { a.Api.Summary, a.Api.Id }
, ApiSummary = a.Api.Summary , Owner = new { a.Owner.Id, a.Owner.UserName }
, a.CreatedClientIp , a.CreatedClientIp
, a.CreatedTime , a.CreatedTime
, a.Duration , a.Duration
, a.Method , a.HttpMethod
, a.CreatedUserAgent
, a.HttpStatusCode , a.HttpStatusCode
, a.Id , a.Id
, a.UserId , a.ApiPathCrc32
, a.User
}) })
.ConfigureAwait(false); .ConfigureAwait(false);
return new PagedQueryRsp<QueryRequestLogRsp>(req.Page, req.PageSize, total return new PagedQueryRsp<QueryRequestLogRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryRequestLogRsp>>()); , ret.Adapt<IEnumerable<QueryRequestLogRsp>>());
} }
/// <inheritdoc /> /// <inheritdoc />
@ -202,7 +213,17 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> req) 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) { if (req.Filter?.Id is not 0) {
ret = ret.WhereDynamic(req.Filter); ret = ret.WhereDynamic(req.Filter);
} }
@ -210,10 +231,11 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
if (req.Keywords?.Length > 0) { if (req.Keywords?.Length > 0) {
ret = _regex.IsMatch(req.Keywords) ret = _regex.IsMatch(req.Keywords)
? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32()) ? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32())
: ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.UserId == req.Keywords.Int64Try(0) || : ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.OwnerId == req.Keywords.Int64Try(0) ||
a.User.UserName == req.Keywords || a.RequestBody.Contains(req.Keywords)); a.Owner.UserName == req.Keywords);
} }
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -161,6 +161,8 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
req.Keywords?.Length > 0 req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) || , a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) ||
a.Summary.Contains(req.Keywords)); a.Summary.Contains(req.Keywords));
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Sys_SiteMsgDept> QueryInternal(QueryReq<QuerySiteMsgDeptReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Sys_SiteMsgFlag> QueryInternal(QueryReq<QuerySiteMsgFlagReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Sys_SiteMsgRole> QueryInternal(QueryReq<QuerySiteMsgRoleReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -301,6 +301,8 @@ public sealed class SiteMsgService(
req.Keywords?.Length > 0 req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.Title.Contains(req.Keywords) || , a => a.Id == req.Keywords.Int64Try(0) || a.Title.Contains(req.Keywords) ||
a.Summary.Contains(req.Keywords)); a.Summary.Contains(req.Keywords));
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Sys_SiteMsgUser> QueryInternal(QueryReq<QuerySiteMsgUserReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; return ret;

View File

@ -490,6 +490,8 @@ public sealed class UserService(
, a => a.Id == req.Keywords.Int64Try(0) || a.UserName == req.Keywords || , a => a.Id == req.Keywords.Int64Try(0) || a.UserName == req.Keywords ||
a.Mobile == req.Keywords || a.Mobile == req.Keywords ||
a.Email == req.Keywords || a.Summary.Contains(req.Keywords)); a.Email == req.Keywords || a.Summary.Contains(req.Keywords));
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Sys_VerifyCode> QueryInternal(QueryReq<QueryVerifyCodeReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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) private ISelect<Tpl_Example> QueryInternal(QueryReq<QueryExampleReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) { switch (req.Order) {
case Orders.None: case Orders.None:
return ret; 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

@ -82,7 +82,7 @@ public sealed class DicCache(IDistributedCache cache, IDicService service) //
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetDicValueAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)); , () => Service.GetDicValueAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT));
#else #else
return Service.GetDicValueAsync(req); return Service.GetDicValueAsync(req);

View File

@ -85,11 +85,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req) public Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetRecordBarChartAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); , () => Service.GetRecordBarChartAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART));
#else #else
return Service.GetRecordBarChartAsync(req); return Service.GetRecordBarChartAsync(req);
@ -97,12 +97,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync( public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(QueryReq<QueryJobRecordReq> req)
QueryReq<QueryJobRecordReq> req)
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetRecordPieChartByHttpStatusCodeAsync(req) , () => Service.GetRecordPieChartByHttpStatusCodeAsync(req)
, TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)); , TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT));
#else #else
@ -111,11 +110,11 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req) public Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetRecordPieChartByNameAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); , () => Service.GetRecordPieChartByNameAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART));
#else #else
return Service.GetRecordPieChartByNameAsync(req); return Service.GetRecordPieChartByNameAsync(req);

View File

@ -18,9 +18,22 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
} }
/// <inheritdoc /> /// <inheritdoc />
#if !DEBUG
public async Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
#else
public Task<long> CountAsync(QueryReq<QueryRequestLogReq> req) 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); return Service.CountAsync(req);
#endif
} }
/// <inheritdoc /> /// <inheritdoc />
@ -54,11 +67,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req) public Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetBarChartAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); , () => Service.GetBarChartAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART));
#else #else
return Service.GetBarChartAsync(req); return Service.GetBarChartAsync(req);
@ -66,11 +79,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req) public Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetPieChartByApiSummaryAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); , () => Service.GetPieChartByApiSummaryAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART));
#else #else
return Service.GetPieChartByApiSummaryAsync(req); return Service.GetPieChartByApiSummaryAsync(req);
@ -78,11 +91,11 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IOrderedEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req) public Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.GetPieChartByHttpStatusCodeAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART)); , () => Service.GetPieChartByHttpStatusCodeAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_CHART));
#else #else
return Service.GetPieChartByHttpStatusCodeAsync(req); 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

@ -100,7 +100,7 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
{ {
#if !DEBUG #if !DEBUG
return GetOrCreateAsync( // return GetOrCreateAsync( //
GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) // GetCacheKey(req.Json().Crc32().ToString(CultureInfo.InvariantCulture)) //
, () => Service.QueryAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT)); , () => Service.QueryAsync(req), TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT));
#else #else
return Service.QueryAsync(req); return Service.QueryAsync(req);

View File

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

View File

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

View File

@ -66,6 +66,17 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
} }
var request = BuildRequest(job); var request = BuildRequest(job);
// 随机延时
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(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var rsp = await request.SendAsync(CancellationToken.None).ConfigureAwait(false); var rsp = await request.SendAsync(CancellationToken.None).ConfigureAwait(false);

View File

@ -18,13 +18,6 @@ public sealed class OperationLogger : IEventSubscriber
return; return;
} }
// 跳过指定的请求
if (Array.Exists( //
GlobalStatic.LogSavingSkipApiIds
, x => x.Equals(operationEvent.Data.ApiId, StringComparison.OrdinalIgnoreCase))) {
return;
}
operationEvent.Data.TruncateStrings(); operationEvent.Data.TruncateStrings();
_ = await App.GetService<IRequestLogService>().CreateAsync(operationEvent.Data).ConfigureAwait(false); _ = await App.GetService<IRequestLogService>().CreateAsync(operationEvent.Data).ConfigureAwait(false);
} }

View File

@ -11,14 +11,14 @@
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"ace-builds": "^1.35.2", "ace-builds": "^1.35.2",
"aieditor": "^1.0.12", "aieditor": "^1.0.13",
"axios": "^1.7.2", "axios": "^1.7.2",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"core-js": "^3.37.1", "core-js": "^3.37.1",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"element-plus": "^2.7.6", "element-plus": "^2.7.7",
"json-bigint": "^1.0.0", "json-bigint": "^1.0.0",
"json5-to-table": "^0.1.8", "json5-to-table": "^0.1.8",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
@ -38,11 +38,11 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"prettier": "^3.3.2", "prettier": "^3.3.3",
"prettier-plugin-organize-attributes": "^1.0.0", "prettier-plugin-organize-attributes": "^1.0.0",
"sass": "^1.77.6", "sass": "^1.77.8",
"terser": "^5.31.1", "terser": "^5.31.3",
"vite": "^5.3.3" "vite": "^5.3.4"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@ -1,7 +1,10 @@
<template> <template>
<el-table-column v-bind="$attrs"> <el-table-column v-bind="$attrs">
<template #default="{ row }"> <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> <el-avatar v-if="$TOOL.getNestedProperty(row, $attrs.nestProp)" :src="getAvatar(row, $attrs.nestProp)" size="small"></el-avatar>
<div> <div>
<p>{{ $TOOL.getNestedProperty(row, $attrs.nestProp) }}</p> <p>{{ $TOOL.getNestedProperty(row, $attrs.nestProp) }}</p>

View File

@ -94,7 +94,7 @@ export default {
return { return {
dateShortCuts: [ dateShortCuts: [
{ {
text: this.$t('今'), text: this.$t('今'),
value: () => { value: () => {
const start = new Date() const start = new Date()
start.setHours(0, 0, 0, 0) start.setHours(0, 0, 0, 0)
@ -102,7 +102,7 @@ export default {
}, },
}, },
{ {
text: this.$t('昨'), text: this.$t('昨'),
value: () => { value: () => {
const start = new Date() const start = new Date()
start.setHours(0, 0, 0, 0) 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: () => { value: () => {
const start = new Date() const start = new Date()
start.setHours(0, 0, 0, 0) start.setHours(0, 0, 0, 0)
@ -342,6 +352,16 @@ export default {
return [start, end] 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('昨日此时'), text: this.$t('昨日此时'),
value: () => { value: () => {

View File

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

View File

@ -505,6 +505,10 @@ textarea {
width: 100%; width: 100%;
} }
.h100p {
height: 100%;
}
.text-center { .text-center {
text-align: center; text-align: center;
} }
@ -528,3 +532,23 @@ textarea {
.font-monospace { .font-monospace {
font-family: 'Lucida Console', 'Microsoft YaHei', 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) { tool.unicodeDecode = function (str) {
return str.replace(/\\u([0-9a-fA-F]{4})/g, (match, grp) => String.fromCharCode(parseInt(grp, 16))) 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) { tool.sortProperties = function (obj) {
const sortedKeys = Object.keys(obj).sort() const sortedKeys = Object.keys(obj).sort()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -64,11 +64,7 @@ export default {
saveDialog, saveDialog,
}, },
computed: {}, computed: {},
created() { created() {},
if (this.keywords) {
this.query.keywords = this.keywords
}
},
data() { data() {
return { return {
data: [], data: [],
@ -81,6 +77,7 @@ export default {
filters: [], filters: [],
}, },
filter: {}, filter: {},
keywords: this.keywords,
}, },
} }
}, },
@ -89,7 +86,7 @@ export default {
// //
async getData() { async getData() {
this.loading = true 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.loading = false
this.data = res.data this.data = res.data

View File

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

View File

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

View File

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

View File

@ -181,6 +181,12 @@
icon: 'el-icon-video-play', icon: 'el-icon-video-play',
click: execute, click: execute,
}, },
{
icon: 'el-icon-document-copy',
confirm: true,
title: '复制作业',
click: copyJob,
},
{ {
icon: 'el-icon-delete', icon: 'el-icon-delete',
confirm: true, confirm: true,
@ -191,7 +197,7 @@
) )
" "
:vue="this" :vue="this"
width="200" /> width="220" />
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>
@ -224,25 +230,23 @@ export default {
return table return table
}, },
}, },
created() { 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,
})
},
data() { data() {
return { return {
dialog: {}, dialog: {},
loading: false, loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
filters: [], filters: [
{
field: 'enabled',
operator: 'eq',
value: true,
},
],
}, },
filter: {}, filter: {},
keywords: this.keywords || this.$route.query.keywords,
}, },
selection: [], selection: [],
timer: null, timer: null,
@ -250,6 +254,19 @@ export default {
}, },
inject: ['reload'], inject: ['reload'],
methods: { 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) { async setEnabled(enabled) {
let loading let loading
try { try {

View File

@ -58,6 +58,22 @@
<el-form-item :label="$t('请求的网络地址')" prop="requestUrl"> <el-form-item :label="$t('请求的网络地址')" prop="requestUrl">
<el-input v-model="form.requestUrl" clearable /> <el-input v-model="form.requestUrl" clearable />
</el-form-item> </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-form-item v-if="mode === 'view'" :label="$t('作业状态')" prop="status">
<el-input v-model="form.status" clearable /> <el-input v-model="form.status" clearable />
</el-form-item> </el-form-item>
@ -127,7 +143,7 @@ export default {
requestHeader: `{ "Content-Type": "application/json" }`, requestHeader: `{ "Content-Type": "application/json" }`,
requestBody: '{}', requestBody: '{}',
}, },
loading: false, loading: true,
mode: 'add', mode: 'add',
// //
rules: { 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' }" :default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_job.exportRecord" :export-api="$API.sys_job.exportRecord"
:params="query" :params="query"
@ -153,9 +153,6 @@ export default {
}, },
}, },
created() { created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
if (this.statusCodes) { if (this.statusCodes) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
logic: 'or', logic: 'or',
@ -184,6 +181,7 @@ export default {
], ],
}, },
filter: {}, filter: {},
keywords: this.keywords,
}, },
} }
}, },

View File

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

View File

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