mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-04-20 05:02:50 +08:00
fix: 🐛 操作日志不显示userName (#141)
[skip ci] Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
parent
a3ab97019d
commit
705d027da4
@ -23,7 +23,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.26.0.92422">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.27.0.93347">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -9,7 +9,7 @@
|
||||
"packages": [
|
||||
{
|
||||
"packageName": "Furion.Pure.NS",
|
||||
"version": "4.9.3-ns1"
|
||||
"version": "4.9.4-ns1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -10,11 +10,6 @@ public interface IFieldCreatedClient
|
||||
/// </summary>
|
||||
int? CreatedClientIp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者来源地址
|
||||
/// </summary>
|
||||
string CreatedReferer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者客户端用户代理
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,16 @@
|
||||
using NetAdmin.Domain.Attributes;
|
||||
|
||||
namespace NetAdmin.Domain.DbMaps.Dependency;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract record SimpleEntity : SimpleEntity<long>
|
||||
{
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
|
||||
[Snowflake]
|
||||
public override long Id { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 简单实体
|
||||
/// </summary>
|
||||
|
@ -31,7 +31,7 @@ public record Sys_JobRecord : LiteImmutableEntity
|
||||
/// </summary>
|
||||
[Column]
|
||||
[JsonIgnore]
|
||||
public virtual int HttpStatusCode { get; init; }
|
||||
public int HttpStatusCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 拥有者信息
|
||||
|
@ -8,9 +8,10 @@ namespace NetAdmin.Domain.DbMaps.Sys;
|
||||
/// </summary>
|
||||
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)]
|
||||
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)]
|
||||
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(UserId), nameof(UserId), false)]
|
||||
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
|
||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
|
||||
public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedClient
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口
|
||||
@ -26,23 +27,17 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
[JsonIgnore]
|
||||
public virtual string ApiId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者客户端IP
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[Column(Position = -1)]
|
||||
[JsonIgnore]
|
||||
public int? CreatedClientIp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者来源地址
|
||||
/// </summary>
|
||||
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
/// <inheritdoc />
|
||||
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)]
|
||||
[JsonIgnore]
|
||||
public string CreatedReferer { get; init; }
|
||||
public virtual DateTime CreatedTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者客户端用户代理
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
#if DBTYPE_SQLSERVER
|
||||
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)]
|
||||
#else
|
||||
@ -76,17 +71,6 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
[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
|
||||
[JsonIgnore]
|
||||
public virtual string ExtraData { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP状态码
|
||||
/// </summary>
|
||||
@ -101,13 +85,6 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
[JsonIgnore]
|
||||
public virtual string Method { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 来源地址
|
||||
/// </summary>
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
[JsonIgnore]
|
||||
public virtual string ReferUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求内容
|
||||
/// </summary>
|
||||
@ -179,4 +156,18 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
[Column]
|
||||
[JsonIgnore]
|
||||
public virtual int? ServerIp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[Navigate(nameof(UserId))]
|
||||
public Sys_User User { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户编号
|
||||
/// </summary>
|
||||
[Column]
|
||||
[JsonIgnore]
|
||||
public virtual long? UserId { get; init; }
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using NetAdmin.Domain.DbMaps.Dependency.Fields;
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
using NetAdmin.Domain.Dto.Sys.User;
|
||||
|
||||
namespace NetAdmin.Domain.Dto.Sys.RequestLog;
|
||||
|
||||
@ -13,6 +14,11 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
|
||||
/// </summary>
|
||||
public new string CreatedClientIp => base.CreatedClientIp?.ToIpV4();
|
||||
|
||||
/// <summary>
|
||||
/// 登录名
|
||||
/// </summary>
|
||||
public string LoginName => RequestBody?.ToObject<LoginByPwdReq>()?.Account;
|
||||
|
||||
/// <summary>
|
||||
/// 操作系统
|
||||
/// </summary>
|
||||
@ -35,10 +41,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string CreatedUserAgent { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string CreatedUserName { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.Duration" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Duration { get; init; }
|
||||
@ -51,10 +53,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string Exception { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.ExtraData" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string ExtraData { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.HttpStatusCode" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override int HttpStatusCode { get; init; }
|
||||
@ -63,10 +61,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string Method { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.ReferUrl" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string ReferUrl { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.RequestBody" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string RequestBody { get; init; }
|
||||
@ -99,6 +93,13 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override int? ServerIp { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.User" />
|
||||
public new QueryUserRsp User { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_RequestLog.UserId" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override long? UserId { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(TypeAdapterConfig config)
|
||||
{
|
||||
|
@ -7,17 +7,10 @@ namespace NetAdmin.Host.Utils;
|
||||
/// <summary>
|
||||
/// 请求日志记录器
|
||||
/// </summary>
|
||||
public sealed class RequestLogger(
|
||||
ILogger<RequestLogger> logger
|
||||
, IOptions<SpecificationDocumentSettingsOptions> specificationDocumentSettingsOptions
|
||||
, IEventPublisher eventPublisher) : ISingleton
|
||||
public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher eventPublisher) : ISingleton
|
||||
{
|
||||
private static readonly string[] _textContentTypes = ["text", "json", "xml", "urlencoded"];
|
||||
|
||||
private readonly int _tokenPrefixLength
|
||||
= specificationDocumentSettingsOptions?.Value.SecurityDefinitions?[0]?.Scheme?.Length + 1 ??
|
||||
0; // eg. "Bearer ";
|
||||
|
||||
/// <summary>
|
||||
/// 生成审计数据
|
||||
/// </summary>
|
||||
@ -30,7 +23,6 @@ public sealed class RequestLogger(
|
||||
var auditData = new CreateRequestLogReq {
|
||||
Duration = duration
|
||||
, Method = context.Request.Method
|
||||
, ReferUrl = context.Request.GetRefererUrlAddress()
|
||||
, RequestContentType = context.Request.ContentType
|
||||
, RequestBody = Array.Exists( //
|
||||
_textContentTypes
|
||||
@ -50,8 +42,7 @@ public sealed class RequestLogger(
|
||||
, HttpStatusCode = context.Response.StatusCode
|
||||
, ErrorCode = errorCode
|
||||
, Exception = exception?.Error.ToString()
|
||||
, CreatedUserId = associatedUser?.UserId
|
||||
, CreatedUserName = associatedUser?.UserName
|
||||
, UserId = associatedUser?.UserId
|
||||
, CreatedUserAgent = context.Request.Headers.UserAgent.ToString()
|
||||
, CreatedClientIp = context.GetRealIpAddress()
|
||||
?.MapToIPv4()
|
||||
@ -77,8 +68,9 @@ public sealed class RequestLogger(
|
||||
|
||||
ContextUserToken userToken = null;
|
||||
try {
|
||||
var jsonWebToken = JWTEncryption.ReadJwtToken(token[_tokenPrefixLength..]);
|
||||
var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken));
|
||||
var jsonWebToken
|
||||
= JWTEncryption.ReadJwtToken(token.TrimStart($"{Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA} "));
|
||||
var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken));
|
||||
userToken = claim?.Value.ToObject<ContextUserToken>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
@ -63,13 +63,6 @@ public sealed class SqlAuditor : ISingleton
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetCreatedReferer(AuditValueEventArgs e)
|
||||
{
|
||||
if (e.Value is null or "") {
|
||||
e.Value = App.HttpContext?.Request.GetRefererUrlAddress();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetCreatedTime(AuditValueEventArgs e)
|
||||
{
|
||||
if (e.Value == null || (e.Value is DateTime val && val == default)) {
|
||||
@ -119,9 +112,6 @@ public sealed class SqlAuditor : ISingleton
|
||||
case nameof(IFieldCreatedClient.CreatedUserAgent):
|
||||
SetCreatedUserAgent(e);
|
||||
break;
|
||||
case nameof(IFieldCreatedClient.CreatedReferer):
|
||||
SetCreatedReferer(e);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ public static class Chars
|
||||
public const string FLG_HTTP_METHOD_POST = "POST";
|
||||
public const string FLG_HTTP_METHOD_PUT = "PUT";
|
||||
public const string FLG_HTTP_METHOD_TRACE = "TRACE";
|
||||
public const string FLG_PATH_API_SYS_USER_LOGIN_BY_PWD = "api/sys/user/login.by.pwd";
|
||||
public const string FLG_PATH_PREFIX_HEALTH_CHECK = "probe/health.check";
|
||||
public const string FLG_RANDOM_UNAME_PWD = "VcXlp7WY";
|
||||
public const string FLG_REDIS_INSTANCE_DATA_CACHE = "DataCache";
|
||||
|
@ -9,9 +9,9 @@
|
||||
<PackageReference Include="Cronos" Version="0.8.4"/>
|
||||
<PackageReference Include="FreeSql.DbContext.NS" Version="3.2.821-ns1"/>
|
||||
<PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.821-ns1"/>
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.3"/>
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.3-ns1"/>
|
||||
<PackageReference Include="Furion.Pure.NS" Version="4.9.3-ns1"/>
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.4"/>
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.4-ns1"/>
|
||||
<PackageReference Include="Furion.Pure.NS" Version="4.9.4-ns1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.4.24267.6"/>
|
||||
<PackageReference Include="Minio" Version="6.0.2"/>
|
||||
<PackageReference Include="NSExt" Version="2.1.0"/>
|
||||
|
@ -12,6 +12,8 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||
public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo) //
|
||||
: RepositoryService<Sys_RequestLog, long, IRequestLogService>(rpo), IRequestLogService
|
||||
{
|
||||
private static readonly Regex _regex = new(Chars.RGXL_IP_V4);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||
{
|
||||
@ -134,26 +136,45 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
|
||||
public async Task<PagedQueryRsp<QueryRequestLogRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogReq> 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(a => new {
|
||||
a.ApiId
|
||||
, ApiSummary = a.Api.Summary
|
||||
, a.ExtraData
|
||||
, a.CreatedClientIp
|
||||
, a.CreatedTime
|
||||
, a.CreatedUserName
|
||||
, a.Duration
|
||||
, a.Method
|
||||
, a.CreatedUserAgent
|
||||
, a.HttpStatusCode
|
||||
, a.Id
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
var select = QueryInternal(req)
|
||||
.Page(req.Page, req.PageSize)
|
||||
#if DBTYPE_SQLSERVER
|
||||
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
|
||||
#endif
|
||||
.Count(out var total);
|
||||
object list = req.DynamicFilter?.Filters?.Exists(
|
||||
x => nameof(QueryRequestLogReq.ApiId).Equals(x.Field, StringComparison.OrdinalIgnoreCase) &&
|
||||
Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Equals( //
|
||||
x.Value.ToString(), StringComparison.OrdinalIgnoreCase)) ?? false
|
||||
? await select.ToListAsync(a => new {
|
||||
a.ApiId
|
||||
, ApiSummary = a.Api.Summary
|
||||
, a.CreatedClientIp
|
||||
, a.CreatedTime
|
||||
, a.Duration
|
||||
, a.Method
|
||||
, a.CreatedUserAgent
|
||||
, a.HttpStatusCode
|
||||
, a.Id
|
||||
, a.UserId
|
||||
, a.User
|
||||
, a.RequestBody
|
||||
})
|
||||
.ConfigureAwait(false)
|
||||
: await select.ToListAsync(a => new {
|
||||
a.ApiId
|
||||
, ApiSummary = a.Api.Summary
|
||||
, a.CreatedClientIp
|
||||
, a.CreatedTime
|
||||
, a.Duration
|
||||
, a.Method
|
||||
, a.CreatedUserAgent
|
||||
, a.HttpStatusCode
|
||||
, a.Id
|
||||
, a.UserId
|
||||
, a.User
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new PagedQueryRsp<QueryRequestLogRsp>(req.Page, req.PageSize, total
|
||||
, list.Adapt<IEnumerable<QueryRequestLogRsp>>());
|
||||
@ -175,7 +196,18 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo)
|
||||
|
||||
private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> req)
|
||||
{
|
||||
var ret = Rpo.Select.Include(a => a.Api).WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
|
||||
var ret = Rpo.Select.Include(a => a.Api).Include(a => a.User).WhereDynamicFilter(req.DynamicFilter);
|
||||
if (req.Filter?.Id is not 0) {
|
||||
ret = ret.WhereDynamic(req.Filter);
|
||||
}
|
||||
|
||||
if (req.Keywords?.Length > 0) {
|
||||
ret = _regex.IsMatch(req.Keywords)
|
||||
? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32())
|
||||
: ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.UserId == req.Keywords.Int64Try(0) ||
|
||||
a.User.UserName == req.Keywords || a.RequestBody.Contains(req.Keywords));
|
||||
}
|
||||
|
||||
switch (req.Order) {
|
||||
case Orders.None:
|
||||
return ret;
|
||||
|
@ -1,5 +1,3 @@
|
||||
using NetAdmin.Domain.Dto.Sys.RequestLog;
|
||||
using NetAdmin.Domain.Dto.Sys.User;
|
||||
using NetAdmin.Domain.Events.Sys;
|
||||
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
|
||||
|
||||
@ -27,22 +25,7 @@ public sealed class OperationLogger : IEventSubscriber
|
||||
return;
|
||||
}
|
||||
|
||||
CreateRequestLogReq logReq = null;
|
||||
|
||||
// 登录日志特殊处理
|
||||
if (operationEvent.Data.ApiId.Equals("api/sys/user/login.by.pwd", StringComparison.OrdinalIgnoreCase)) {
|
||||
try {
|
||||
var loginReq = operationEvent.Data.RequestBody.ToObject<LoginByPwdReq>();
|
||||
logReq = operationEvent.Data with { ExtraData = loginReq.Account };
|
||||
}
|
||||
catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
logReq ??= operationEvent.Data;
|
||||
var logService = App.GetService<IRequestLogService>();
|
||||
logReq.TruncateStrings();
|
||||
_ = await logService.CreateAsync(logReq).ConfigureAwait(false);
|
||||
operationEvent.Data.TruncateStrings();
|
||||
_ = await App.GetService<IRequestLogService>().CreateAsync(operationEvent.Data).ConfigureAwait(false);
|
||||
}
|
||||
}
|
@ -15,10 +15,8 @@ public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestO
|
||||
: IClassFixture<WebApplicationFactory<T>>
|
||||
where T : AppStartup
|
||||
{
|
||||
private const string _ACCOUNT = "root";
|
||||
private const string _API_SYS_USER_LOGIN_BY_PWD = "/api/sys/user/login.by.pwd";
|
||||
private const string _AUTH_SCHEMA = "Bearer";
|
||||
private const string _PASSWORD = "1234qwer";
|
||||
private const string _ACCOUNT = "root";
|
||||
private const string _PASSWORD = "1234qwer";
|
||||
private string _accessToken;
|
||||
|
||||
/// <summary>
|
||||
@ -28,7 +26,7 @@ public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestO
|
||||
{
|
||||
var client = factory.CreateClient();
|
||||
if (_accessToken == null) {
|
||||
var loginRsp = await client.PostAsync(_API_SYS_USER_LOGIN_BY_PWD
|
||||
var loginRsp = await client.PostAsync(Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD
|
||||
, JsonContent.Create(
|
||||
new LoginByPwdReq { Password = _PASSWORD, Account = _ACCOUNT }))
|
||||
.ConfigureAwait(false);
|
||||
@ -37,7 +35,8 @@ public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestO
|
||||
_accessToken = loginRspObj.Data.AccessToken;
|
||||
}
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(_AUTH_SCHEMA, _accessToken);
|
||||
client.DefaultRequestHeaders.Authorization
|
||||
= new AuthenticationHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA, _accessToken);
|
||||
var ret = await client.PostAsync(url, content).ConfigureAwait(false);
|
||||
testOutputHelper.WriteLine(await ret.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
return ret;
|
||||
|
@ -130,11 +130,14 @@ export default {
|
||||
<style scoped>
|
||||
.sc-select-filter {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sc-select-filter__item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.sc-select-filter__item-title {
|
||||
|
@ -15,7 +15,7 @@
|
||||
<el-icon-full-screen />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div @click="tasks" class="tasks panel-item">
|
||||
<div v-auth="'sys/job/userbar'" @click="tasks" class="tasks panel-item">
|
||||
<el-icon>
|
||||
<sc-icon-ScheduledJob />
|
||||
</el-icon>
|
||||
|
@ -47,6 +47,9 @@ export default {
|
||||
global.enums = preloads[2]?.data
|
||||
global.numbers = preloads[3]?.data
|
||||
global.chars = preloads[4]?.data
|
||||
global.permissions = tool.recursiveFindProperty(preloads[0]?.data, 'type', 'button').map((x) => x.tag)
|
||||
global.permissions =
|
||||
global.user?.roles.findIndex((x) => x.ignorePermissionControl) >= 0
|
||||
? ['*/*/*']
|
||||
: tool.recursiveFindProperty(preloads[0]?.data, 'type', 'button').map((x) => x.tag)
|
||||
},
|
||||
}
|
@ -23,21 +23,9 @@
|
||||
:controls="[
|
||||
{
|
||||
type: 'input',
|
||||
field: ['dy', 'id'],
|
||||
placeholder: '日志编号',
|
||||
style: 'width:15rem',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: ['dy', 'extraData'],
|
||||
placeholder: '登录名',
|
||||
style: 'width:15rem',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: ['dy', 'createdClientIp'],
|
||||
placeholder: '客户端IP',
|
||||
style: 'width:15rem',
|
||||
field: ['root', 'keywords'],
|
||||
placeholder: '日志编号 / 登录名 / 客户端IP',
|
||||
style: 'width:25rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@ -54,7 +42,7 @@
|
||||
<sc-table
|
||||
v-loading="loading"
|
||||
:apiObj="$API.sys_log.pagedQuery"
|
||||
:context-menus="['id', 'httpStatusCode', 'extraData', 'createdClientIp', 'os', 'createdUserAgent', 'createdTime']"
|
||||
:context-menus="['id', 'httpStatusCode', 'createdClientIp', 'createdUserAgent', 'createdTime']"
|
||||
:context-opers="['view']"
|
||||
:default-sort="{ prop: 'createdTime', order: 'descending' }"
|
||||
:params="query"
|
||||
@ -73,13 +61,13 @@
|
||||
{{ scope.row.httpStatusCode === 200 ? '成功' : '失败' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('登录名')" prop="extraData" sortable="custom" width="200" />
|
||||
<el-table-column :label="$t('客户端IP')" prop="createdClientIp" sortable="custom" width="200">
|
||||
<el-table-column :label="$t('登录名')" prop="loginName" width="150" />
|
||||
<el-table-column :label="$t('客户端IP')" prop="createdClientIp" show-overflow-tooltip sortable="custom" width="200">
|
||||
<template #default="scope">
|
||||
<na-ip :ip="scope.row.createdClientIp"></na-ip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('操作系统')" prop="os" width="150" />
|
||||
<el-table-column :label="$t('操作系统')" align="center" prop="os" width="150" />
|
||||
<el-table-column :label="$t('用户代理')" prop="createdUserAgent" show-overflow-tooltip sortable="custom" />
|
||||
</sc-table>
|
||||
</el-main>
|
||||
@ -120,7 +108,11 @@ export default {
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keepKeywords = this.keywords
|
||||
}
|
||||
this.$refs.search.form.dy.apiId = 'api/sys/user/login.by.pwd'
|
||||
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
|
||||
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
|
||||
@ -128,6 +120,9 @@ export default {
|
||||
]
|
||||
},
|
||||
created() {
|
||||
if (this.keywords) {
|
||||
this.query.keywords = this.keywords
|
||||
}
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'apiId',
|
||||
operator: 'eq',
|
||||
@ -157,41 +152,6 @@ export default {
|
||||
value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
|
||||
})
|
||||
}
|
||||
if (form.dy.httpStatusCode) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'httpStatusCode',
|
||||
operator: 'range',
|
||||
value: form.dy.httpStatusCode,
|
||||
})
|
||||
}
|
||||
if (form.dy.apiId) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'apiId',
|
||||
operator: 'contains',
|
||||
value: form.dy.apiId,
|
||||
})
|
||||
}
|
||||
if (form.dy.id) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'id',
|
||||
operator: 'eq',
|
||||
value: form.dy.id,
|
||||
})
|
||||
}
|
||||
if (form.dy.extraData) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'extraData',
|
||||
operator: 'contains',
|
||||
value: form.dy.extraData,
|
||||
})
|
||||
}
|
||||
if (form.dy.createdClientIp) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'createdClientIp',
|
||||
operator: 'contains',
|
||||
value: form.dy.createdClientIp,
|
||||
})
|
||||
}
|
||||
if (typeof form.dy.loginResult === 'boolean') {
|
||||
this.query.dynamicFilter.filters.push(
|
||||
form.dy.loginResult
|
||||
@ -207,7 +167,6 @@ export default {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
this.$refs.table.upData()
|
||||
},
|
||||
|
||||
|
@ -1,15 +1,26 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header style="height: auto; padding: 0 1rem">
|
||||
<sc-select-filter
|
||||
:data="[
|
||||
{
|
||||
title: $t('操作结果'),
|
||||
key: 'operationResult',
|
||||
options: [
|
||||
{ label: $t('全部'), value: '' },
|
||||
{ label: $t('成功'), value: true },
|
||||
{ label: $t('失败'), value: false },
|
||||
],
|
||||
},
|
||||
]"
|
||||
:label-width="6"
|
||||
@on-change="filterChange"
|
||||
ref="selectFilter"></sc-select-filter>
|
||||
</el-header>
|
||||
<el-header>
|
||||
<div class="left-panel">
|
||||
<na-search
|
||||
:controls="[
|
||||
{
|
||||
type: 'input',
|
||||
field: ['filter', 'id'],
|
||||
placeholder: '日志编号',
|
||||
style: 'width:12rem',
|
||||
},
|
||||
{
|
||||
multiple: true,
|
||||
type: 'select',
|
||||
@ -34,18 +45,13 @@
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: ['dy', 'createdUserName'],
|
||||
placeholder: '用户名',
|
||||
style: 'width:12rem',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: ['dy', 'createdClientIp'],
|
||||
placeholder: '客户端IP',
|
||||
style: 'width:12rem',
|
||||
field: ['root', 'keywords'],
|
||||
placeholder: '日志编号 / 用户 / 客户端IP',
|
||||
style: 'width:25rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
|
||||
@search="onSearch"
|
||||
dateFormat="YYYY-MM-DD HH:mm:ss"
|
||||
dateType="datetimerange"
|
||||
@ -57,19 +63,18 @@
|
||||
<el-main class="nopadding">
|
||||
<sc-table
|
||||
:apiObj="$API.sys_log.pagedQuery"
|
||||
:context-menus="['id', 'httpStatusCode', 'apiId', 'createdUserName', 'method', 'duration', 'createdClientIp', 'os', 'createdTime']"
|
||||
:context-menus="['id', 'httpStatusCode', 'apiId', 'userId', 'method', 'duration', 'createdClientIp', 'createdTime']"
|
||||
:context-opers="[]"
|
||||
:default-sort="{ prop: 'createdTime', order: 'descending' }"
|
||||
:params="query"
|
||||
:vue="this"
|
||||
@row-click="rowClick"
|
||||
ref="table"
|
||||
remoteFilter
|
||||
remoteSort
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column :label="$t('日志编号')" prop="id" sortable="custom" width="150"> </el-table-column
|
||||
><el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
|
||||
<el-table-column :label="$t('日志编号')" prop="id" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
|
||||
<el-table-column :label="$t('响应码')" align="center" prop="httpStatusCode" sortable="custom" width="100">
|
||||
<template #default="{ row }">
|
||||
<sc-status-indicator :type="row.httpStatusCode >= 200 && row.httpStatusCode < 300 ? 'success' : 'danger'" />
|
||||
@ -96,20 +101,33 @@
|
||||
align="right"
|
||||
prop="duration"
|
||||
sortable="custom"
|
||||
width="120">
|
||||
width="90">
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('用户名')" prop="createdUserName" sortable="custom" width="150">
|
||||
<template #default="scope">
|
||||
{{ scope.row.apiId === 'api/sys/user/pwd.login' ? scope.row.extraData : scope.row.createdUserName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<na-col-user
|
||||
v-auth="'sys/log/operation/user'"
|
||||
header-align="center"
|
||||
label="用户"
|
||||
nestProp="user.userName"
|
||||
nestProp2="user.id"
|
||||
prop="userId"
|
||||
sortable="custom"
|
||||
width="170"></na-col-user>
|
||||
<el-table-column :label="$t('客户端IP')" prop="createdClientIp" show-overflow-tooltip sortable="custom" width="200">
|
||||
<template #default="scope">
|
||||
<na-ip :ip="scope.row.createdClientIp"></na-ip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('操作系统')" align="center" prop="os" width="150" />
|
||||
<na-col-operation
|
||||
:buttons="[
|
||||
{
|
||||
icon: 'el-icon-view',
|
||||
click: rowClick,
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
width="70" />
|
||||
</sc-table>
|
||||
</el-main>
|
||||
</el-container>
|
||||
@ -124,7 +142,6 @@ import ScTable from '@/components/scTable/index.vue'
|
||||
import ScStatusIndicator from '@/components/scMini/scStatusIndicator.vue'
|
||||
|
||||
export default {
|
||||
inject: ['reload'],
|
||||
computed: {
|
||||
tool() {
|
||||
return tool
|
||||
@ -136,6 +153,7 @@ export default {
|
||||
naInfo,
|
||||
},
|
||||
watch: {},
|
||||
inject: ['reload'],
|
||||
data() {
|
||||
return {
|
||||
query: {
|
||||
@ -155,13 +173,28 @@ export default {
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keepKeywords = this.keywords
|
||||
}
|
||||
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
|
||||
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
|
||||
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
|
||||
]
|
||||
},
|
||||
created() {
|
||||
if (this.keywords) {
|
||||
this.query.keywords = this.keywords
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filterChange(data) {
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
|
||||
})
|
||||
this.$refs.search.search()
|
||||
},
|
||||
//搜索
|
||||
onSearch(form) {
|
||||
if (Array.isArray(form.dy.createdTime)) {
|
||||
@ -186,19 +219,20 @@ export default {
|
||||
value: form.dy.apiId,
|
||||
})
|
||||
}
|
||||
if (typeof form.dy.createdUserName === 'string' && form.dy.createdUserName.trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'createdUserName',
|
||||
operator: 'eq',
|
||||
value: form.dy.createdUserName,
|
||||
})
|
||||
}
|
||||
if (typeof form.dy.createdClientIp === 'string' && form.dy.createdClientIp.trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'createdClientIp',
|
||||
operator: 'eq',
|
||||
value: form.dy.createdClientIp,
|
||||
})
|
||||
if (typeof form.dy.operationResult === 'boolean') {
|
||||
this.query.dynamicFilter.filters.push(
|
||||
form.dy.operationResult
|
||||
? {
|
||||
field: 'httpStatusCode',
|
||||
operator: 'range',
|
||||
value: '200,299',
|
||||
}
|
||||
: {
|
||||
field: 'httpStatusCode',
|
||||
operator: 'range',
|
||||
value: '300,999',
|
||||
},
|
||||
)
|
||||
}
|
||||
this.$refs.table.upData()
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user