🍒 feat(logHttp): 优化 HTTP 请求日志功能

This commit is contained in:
喵你个汪呀 2025-08-24 17:51:58 +08:00
parent a95df252d7
commit e5ccc603fc
12 changed files with 270 additions and 58 deletions

View File

@ -39,7 +39,7 @@ public class CommonConst
public const string SendErrorMail = "Send:ErrorMail";
/// <summary>
/// 远程请求请求头参数键值
/// 远程请求请求头参数键值前缀
/// </summary>
public const string HttpRemoteHeaderKeyPrefix = "__HTTP_CLIENT_";

View File

@ -11,10 +11,11 @@ namespace Admin.NET.Core;
/// <summary>
/// Http请求日志表
/// </summary>
[SugarTable(null, "Http请求日志表")]
[SysTable]
[LogTable]
public partial class SysLogHttp : EntityBaseId
[SugarTable(null, "Http请求日志表")]
[SugarIndex("i_{table}_n", nameof(HttpClientName), OrderByType.Asc)]
public partial class SysLogHttp : EntityTenantId
{
/// <summary>
/// 客户端名称
@ -43,6 +44,12 @@ public partial class SysLogHttp : EntityBaseId
[SugarColumn(ColumnDescription = "是否成功")]
public YesNoEnum? IsSuccessStatusCode { get; set; }
/// <summary>
/// 模块名称
/// </summary>
[SugarColumn(ColumnDescription = "模块名称")]
public string? ActionName { get; set; }
/// <summary>
/// 请求地址
/// </summary>
@ -108,4 +115,17 @@ public partial class SysLogHttp : EntityBaseId
/// </summary>
[SugarColumn(ColumnDescription = "创建时间")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 创建者Id
/// </summary>
[OwnerUser]
[SugarColumn(ColumnDescription = "创建者Id", IsOnlyIgnoreUpdate = true)]
public virtual long? CreateUserId { get; set; }
/// <summary>
/// 创建者姓名
/// </summary>
[SugarColumn(ColumnDescription = "创建者姓名", Length = 64, IsOnlyIgnoreUpdate = true)]
public virtual string? CreateUserName { get; set; }
}

View File

@ -13,8 +13,8 @@ namespace Admin.NET.Core;
/// </summary>
public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
{
private static readonly Lazy<UserManager> _userManager = new(() => App.GetService<UserManager>());
private static readonly Lazy<SqlSugarRepository<SysUser>> _sysUserRep = new(() => App.GetService<SqlSugarRepository<SysUser>>());
private static readonly Lazy<UserManager> _userManager = new(() => App.GetService<UserManager>());
private readonly ILogger<DatabaseLoggingWriter> _logger;
private readonly SysConfigService _sysConfigService;
private readonly IEventPublisher _eventPublisher;

View File

@ -14,9 +14,10 @@ namespace Admin.NET.Core;
/// </summary>
public class HttpLoggingHandler : DelegatingHandler, ITransient
{
private static readonly string HttpNameKey = CommonConst.HttpRemoteHeaderKeyPrefix + "NAME__";
private static readonly string ApiDescKey = CommonConst.HttpRemoteHeaderKeyPrefix + "API_DESC__";
private static readonly string IgnoreLogKey = CommonConst.HttpRemoteHeaderKeyPrefix + "IGNORE_LOG__";
private static readonly string ApiDescKey = CommonConst.HttpRemoteHeaderKeyPrefix + "API_DESC__";
private static readonly string HttpNameKey = CommonConst.HttpRemoteHeaderKeyPrefix + "NAME__";
private static readonly Lazy<UserManager> _userManager = new(() => App.GetService<UserManager>());
private readonly Dictionary<string, bool> _enabledLogMap;
private readonly SysConfigService _sysConfigService;
@ -41,19 +42,26 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
if (!enabledLog) return await base.SendAsync(request, cancellationToken);
// 判断当前配置日志开关
var attrInfo = GetRemoteApiAttrAndRemove(request.Headers);
if (!string.IsNullOrWhiteSpace(attrInfo.HttpName)) enabledLog = _enabledLogMap.GetOrDefault(attrInfo.HttpName);
if (!enabledLog || attrInfo.IgnoreLog) return await base.SendAsync(request, cancellationToken);
(string apiDesc, bool ignoreLog) = GetRemoteApiAttrAndRemove(request.Headers);
_ = request.Options.TryGetValue<string>(HttpNameKey, out var httpName);
if (!string.IsNullOrWhiteSpace(httpName)) enabledLog = _enabledLogMap.GetOrDefault(httpName);
if (!enabledLog || ignoreLog) return await base.SendAsync(request, cancellationToken);
var stopWatch = Stopwatch.StartNew();
var urlList = request.RequestUri?.LocalPath.Split("/") ?? [];
var sysLogHttp = new SysLogHttp
{
HttpApiDesc = attrInfo.ApiDesc,
HttpClientName = attrInfo.HttpName,
HttpClientName = httpName,
HttpApiDesc = apiDesc,
HttpMethod = request.Method.Method,
ActionName = urlList.Length >= 2 ? $"{urlList[^2]}/{urlList[^1]}" : urlList.Length >= 1 ? urlList[^1] : null,
RequestUrl = request.RequestUri?.ToString(),
RequestHeaders = request.Headers.ToDictionary(u => u.Key, u => u.Value.Join(";")).ToJson()
RequestHeaders = request.Headers.ToDictionary(u => u.Key, u => u.Value.Join(";")).ToJson(),
TenantId = _userManager.Value?.TenantId,
CreateUserId = _userManager.Value?.UserId,
CreateUserName = _userManager.Value?.RealName,
};
// _userManager
if (request.Content != null) sysLogHttp.RequestBody = await request.Content.ReadAsStringAsync(cancellationToken);
sysLogHttp.StartTime = DateTime.Now;
@ -88,16 +96,15 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
/// </summary>
/// <param name="headers"></param>
/// <returns></returns>
private static HttpRemoteApiAttrDto GetRemoteApiAttrAndRemove(HttpRequestHeaders headers)
private static (string apiDesc, bool ignoreLog) GetRemoteApiAttrAndRemove(HttpRequestHeaders headers)
{
var result = new HttpRemoteApiAttrDto
var result = new
{
HttpName = headers?.FirstOrDefault(u => u.Key == HttpNameKey).Value?.ToString(),
ApiDesc = headers?.FirstOrDefault(u => u.Key == ApiDescKey).Value?.ToString(),
ApiDesc = headers?.FirstOrDefault(u => u.Key == ApiDescKey).Value?.FirstOrDefault()?.ToString(),
IgnoreLog = headers?.FirstOrDefault(u => u.Key == IgnoreLogKey).Value?.ToBoolean() ?? false,
};
RemoveRemoteApiAttr(headers);
return result;
return (result.ApiDesc, result.IgnoreLog);
}
/// <summary>
@ -108,7 +115,11 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
private static void RemoveRemoteApiAttr(HttpRequestHeaders headers)
{
if (headers == null) return;
foreach (var kv in headers.Where(kv => kv.Key.StartsWith(CommonConst.HttpRemoteHeaderKeyPrefix))) headers.Remove(kv.Key);
var keys = headers
.Where(kv => kv.Key.StartsWith(CommonConst.HttpRemoteHeaderKeyPrefix))
.Select(u => u.Key)
.ToList();
foreach (var key in keys) headers.Remove(key);
}
/// <summary>
@ -121,29 +132,8 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
public static HttpRequestBuilder SetRemoteApiAttr(HttpRequestBuilder builder, string httpName, HttpRemoteApiAttribute attr)
{
builder.WithHeader(IgnoreLogKey, attr.IgnoreLog, replace:true);
builder.WithHeader(HttpNameKey, attr.Desc, replace:true);
builder.WithHeader(ApiDescKey, attr.Desc, replace:true);
builder.SetHttpClientName(httpName);
return builder;
}
}
/// <summary>
/// 远程请求相关属性
/// </summary>
public class HttpRemoteApiAttrDto
{
/// <summary>
/// 客户端名称
/// </summary>
public string HttpName { get; set; }
/// <summary>
/// 接口描述
/// </summary>
public string ApiDesc { get; set; }
/// <summary>
/// 是否忽略日志
/// </summary>
public bool IgnoreLog { get; set; }
}

View File

@ -0,0 +1,31 @@
// // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
// //
// // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
// //
// // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
//
// using System.Net;
//
// namespace Admin.NET.Core;
//
// /// <summary>
// /// 代理管理器接口
// /// </summary>
// public interface IProxyManager
// {
// /// <summary>
// /// 获取当前代理
// /// </summary>
// WebProxy GetCurrentProxy();
//
// /// <summary>
// /// 刷新代理
// /// </summary>
// Task RefreshProxiesAsync();
//
// /// <summary>
// /// 获取所有代理
// /// </summary>
// /// <returns></returns>
// List<WebProxy> GetAllProxies();
// }

View File

@ -0,0 +1,106 @@
// // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
// //
// // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
// //
// // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
//
// using System.Net;
//
// namespace Admin.NET.Core;
//
// /// <summary>
// /// 代理管理器
// /// </summary>
// public class ProxyManager : IProxyManager, IDisposable, ITransient
// {
// private List<WebProxy> _proxies = new();
//
// private readonly HttpRemotesOptions _options;
// private readonly object _lock = new();
// private readonly Timer _refreshTimer;
// private int _currentIndex = 0;
//
// public ProxyManager(IOptions<HttpRemotesOptions> options)
// {
// _options = options.Value;
//
// // 启动后立即加载一次
// RefreshProxiesAsync().Wait();
//
// // 每 24 小时刷新一次(可改为每天固定时间)
// _refreshTimer = new Timer(async _ => await RefreshProxiesAsync(),
// null, TimeSpan.FromHours(24), TimeSpan.FromHours(24));
// }
//
// public async Task RefreshProxiesAsync()
// {
// try
// {
// var newProxies = await FetchProxiesFromRemoteAsync();
// if (newProxies != null && newProxies.Any())
// {
// lock (_lock)
// {
// _proxies = newProxies
// .Select(p => new WebProxy(_options.Proxy.Address)
// {
// Credentials = !string.IsNullOrEmpty(_options.Proxy.Account)
// ? new NetworkCredential(_options.Proxy.Account, _options.Proxy.Password)
// : null
// })
// .ToList();
// _currentIndex = 0; // 重置索引
// }
// Console.WriteLine($"✅ 代理列表已刷新,共 {_proxies.Count} 个代理");
// }
// }
// catch (Exception ex)
// {
// Console.WriteLine($"❌ 刷新代理列表失败: {ex.Message}");
// }
// }
//
// private async Task<List<ProxyConfig>> FetchProxiesFromRemoteAsync()
// {
// // 示例:从远程 API 获取代理
// using var client = new HttpClient();
// var response = await client.GetAsync("https://your-proxy-api.com/daily");
// if (response.IsSuccessStatusCode)
// {
// var json = await response.Content.ReadAsStringAsync();
// return JsonSerializer.Deserialize<List<ProxyConfig>>(json,
// new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
// }
//
// // 可替换为数据库、文件、Redis 等来源
// return new List<ProxyConfig>
// {
// new() { Address = "http://proxy1:8080", Username = "user", Password = "pass" },
// new() { Address = "http://proxy2:8080" },
// };
// }
//
// public WebProxy GetCurrentProxy()
// {
// lock (_lock)
// {
// if (!_proxies.Any()) return null;
// var proxy = _proxies[_currentIndex % _proxies.Count];
// _currentIndex = (_currentIndex + 1) % _proxies.Count;
// return proxy;
// }
// }
//
// public List<WebProxy> GetAllProxies()
// {
// lock (_lock)
// {
// return new List<WebProxy>(_proxies);
// }
// }
//
// public void Dispose()
// {
// _refreshTimer?.Dispose();
// }
// }

View File

@ -87,6 +87,11 @@ public class PageLogHttpInput : BasePageInput
/// </summary>
public string SearchKey { get; set; }
/// <summary>
/// 模块名称
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// 客户端名称
/// </summary>
@ -142,7 +147,6 @@ public class PageLogHttpInput : BasePageInput
/// 创建时间范围
/// </summary>
public DateTime?[] CreateTimeRange { get; set; }
}
/// <summary>
@ -164,6 +168,27 @@ public class ExportLogHttpInput : PageLogHttpInput
[ExcelImporter(SheetIndex = 1, IsOnlyErrorRows = true)]
public class ImportLogHttpInput : BaseImportInput
{
/// <summary>
/// 请求模块
/// </summary>
[ImporterHeader(Name = "请求模块")]
[ExporterHeader("请求模块", Format = "", Width = 25, IsBold = true)]
public string ActionName { get; set; }
/// <summary>
/// 客户端名称
/// </summary>
[ImporterHeader(Name = "客户端名称")]
[ExporterHeader("客户端名称", Format = "", Width = 25, IsBold = true)]
public string HttpClientName { get; set; }
/// <summary>
/// 接口描述
/// </summary>
[ImporterHeader(Name = "接口描述")]
[ExporterHeader("接口描述", Format = "", Width = 25, IsBold = true)]
public string HttpApiDesc { get; set; }
/// <summary>
/// 请求方式
/// </summary>
@ -248,4 +273,11 @@ public class ImportLogHttpInput : BaseImportInput
[ImporterHeader(Name = "耗时(毫秒)")]
[ExporterHeader("耗时(毫秒)", Format = "", Width = 25, IsBold = true)]
public long? Elapsed { get; set; }
/// <summary>
/// 创建用户
/// </summary>
[ImporterHeader(Name = "创建用户")]
[ExporterHeader("创建用户", Format = "", Width = 25, IsBold = true)]
public string CreateUserName { get; set; }
}

View File

@ -16,6 +16,11 @@ public class PageLogHttpOutput
/// </summary>
public long? Id { get; set; }
/// <summary>
/// 请求模块
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// 客户端名称
/// </summary>
@ -91,6 +96,11 @@ public class PageLogHttpOutput
/// </summary>
public DateTime? CreateTime { get; set; }
/// <summary>
/// 创建用户
/// </summary>
public string CreateUserName { get; set; }
}
/// <summary>

View File

@ -33,6 +33,7 @@ public class SysLogHttpService : IDynamicApiController, ITransient
input.Keyword = input.Keyword?.Trim();
var query = _sysLogHttpRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.HttpClientName.Contains(input.Keyword) || u.HttpApiDesc.Contains(input.Keyword) || u.RequestUrl.Contains(input.Keyword) || u.RequestBody.Contains(input.Keyword) || u.ResponseBody.Contains(input.Keyword) || u.Exception.Contains(input.Keyword))
.WhereIF(!string.IsNullOrWhiteSpace(input.ActionName), u => u.ActionName.Contains(input.ActionName.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.HttpClientName), u => u.HttpClientName.Contains(input.HttpClientName.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.HttpApiDesc), u => u.HttpApiDesc.Contains(input.HttpApiDesc.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.RequestUrl), u => u.RequestUrl.Contains(input.RequestUrl.Trim()))
@ -58,7 +59,6 @@ public class SysLogHttpService : IDynamicApiController, ITransient
{
return await _sysLogHttpRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.HttpClientName.Contains(input.Keyword) || u.HttpApiDesc.Contains(input.Keyword) || u.RequestUrl.Contains(input.Keyword) || u.RequestBody.Contains(input.Keyword) || u.ResponseBody.Contains(input.Keyword) || u.Exception.Contains(input.Keyword))
.WhereIF(!string.IsNullOrWhiteSpace(input.HttpClientName), u => u.HttpClientName.Contains(input.HttpClientName.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.HttpApiDesc), u => u.HttpApiDesc.Contains(input.HttpApiDesc.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.RequestUrl?.Trim()), u => u.RequestUrl.Contains(input.RequestUrl.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.RequestBody?.Trim()), u => u.RequestBody.Contains(input.RequestBody.Trim()))
@ -67,6 +67,7 @@ public class SysLogHttpService : IDynamicApiController, ITransient
.WhereIF(!string.IsNullOrWhiteSpace(input.HttpMethod?.Trim()), u => u.HttpMethod == input.HttpMethod)
.WhereIF(input.IsSuccessStatusCode != null, u => u.IsSuccessStatusCode == input.IsSuccessStatusCode)
.WhereIF(input.StatusCode != null, u => u.StatusCode == (HttpStatusCode)input.StatusCode)
.WhereIF(input.HttpClientName != null, u => u.HttpClientName == input.HttpClientName.Trim())
.WhereIF(input.CreateTimeRange?.Length == 2, u => u.CreateTime >= input.CreateTimeRange[0] && u.CreateTime <= input.CreateTimeRange[1])
.ToListAsync();
}
@ -113,6 +114,16 @@ public class SysLogHttpService : IDynamicApiController, ITransient
return list;
}
/// <summary>
/// 获取客户端名称集 🔖
/// </summary>
/// <returns></returns>
[DisplayName("获取客户端名称集")]
public async Task<List<string>> GetHttpClientName()
{
return await _sysLogHttpRep.AsQueryable().Select(u => u.HttpClientName).Distinct().ToListAsync();
}
/// <summary>
/// 导出请求日志记录 🔖
/// </summary>

View File

@ -36,6 +36,7 @@ public class WorkWxBaseService(
CorpId = await sysConfigService.GetConfigValueByCode(WorkWeixinConst.WorkWeixinCorpId),
CorpSecret = await sysConfigService.GetConfigValueByCode(WorkWeixinConst.WorkWeixinCorpSecret)
});
if (string.IsNullOrWhiteSpace(result.AccessToken)) return null;
sysCacheService.Set(WorkWeixinConst.KeyWorkWeixinToken, result.AccessToken, TimeSpan.FromSeconds(result.ExpiresIn));
return result.AccessToken;
}
@ -58,7 +59,14 @@ public class WorkWxBaseService(
var url = opt.BaseAddress + $"/cgi-bin/{attr.Action}?";
if (input is AuthWorkWxInput)
{
// 重试3次
var retryMax = 3;
var token = sysCacheService.Get<string>(WorkWeixinConst.KeyWorkWeixinToken) ?? await GetTokenAsync();
while (retryMax-- > 0 && string.IsNullOrWhiteSpace(token))
{
token = await GetTokenAsync();
await Task.Delay(1500);
}
url += "access_token=" + token;
}
@ -73,13 +81,13 @@ public class WorkWxBaseService(
HttpMethodEnum.Post => await httpRemoteService.PostAsync(url,
builder => builder.SetRemoteApiAttr(opt.HttpName, attr)
.SetContent(new StringContent(JSON.Serialize(input, CustomJsonPropertyConverter.Options), Encoding.UTF8, "application/json"))),
_ => throw Oops.Oh($"[企业微信] 不支持的请求方式{attr.HttpMethod.ToString()}({typeof(T).FullName})"),
_ => throw Oops.Oh($"[企业微信] 不支持的请求方式{attr.HttpMethod.ToString()}({typeof(T).FullName})"),
};
}
catch (Exception ex)
{
Log.Error(ex.Message);
throw Oops.Oh("[企业微信] 服务不可用,请检查网路是否正常" + ex.Message);
throw Oops.Oh("[企业微信] 服务不可用,请检查网路是否正常" + ex.Message);
}
return await HandleHttpResponseAsync<R>(response);
}
@ -101,7 +109,7 @@ public class WorkWxBaseService(
{
var resp = JSON.Deserialize<R>(responseContent, CustomJsonPropertyConverter.Options);
if (resp?.ErrCode == 0) return resp;
throw Oops.Oh("[企业微信] " + resp?.ErrMsg);
throw Oops.Oh("[企业微信] 请求失败:" + resp?.ErrMsg);
}
catch (Exception ex)
{

View File

@ -53,17 +53,18 @@
<script lang="ts" setup>
import {ref, reactive, computed} from 'vue';
import { StringToObj } from '/@/utils/json-utils';
import { SysLogHttp } from '/@/api-services/system/models';
import VueJsonPretty from 'vue-json-pretty';
import { LogHttp } from '/@/api-services/system';
const state = reactive({
visible: false,
selectedTabName: '0',
});
const data = ref<LogHttp>({});
const data = ref<SysLogHttp>({});
const openDialog = (row: any) => {
state.visible = true;
data.value ??= {};
state.selectedTabName = '0';
Object.assign(data.value, row);
data.value.requestUrl = StringToObj(row.requestUrl);
data.value.requestHeaders = StringToObj(row.requestHeaders);
data.value.requestBody = StringToObj(row.requestBody);
@ -73,13 +74,6 @@ const openDialog = (row: any) => {
};
const queryObject = computed(() => Object.fromEntries(new URLSearchParams(new URL(data.value?.requestUrl ?? '').search)) ?? {});
const tryJsonParse = (str: string) => {
try {
return JSON.parse(str);
} catch (e) {
return str;
}
};
defineExpose({
openDialog,
});

View File

@ -8,7 +8,14 @@
<el-row :gutter="10">
<el-col class="mb5" :sm="12" :md="8" :lg="6" :xl="6">
<el-form-item label="客户端" prop="httpClientName">
<el-input v-model="state.queryParams.httpClientName" placeholder="请输入客户端" clearable @keyup.enter.native="handleQuery(true)" />
<el-select v-model="state.queryParams.httpClientName" placeholder="请选择客户端" clearable @keyup.enter.native="handleQuery(true)">
<el-option v-for="item in state.clientNameList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-col>
<el-col class="mb5" :sm="12" :md="8" :lg="6" :xl="6">
<el-form-item label="模块名" prop="actionName">
<el-input v-model="state.queryParams.actionName" placeholder="请输入模块名" clearable @keyup.enter.native="handleQuery(true)" />
</el-form-item>
</el-col>
<el-col class="mb5" :sm="12" :md="8" :lg="6" :xl="6">
@ -87,8 +94,7 @@
</template>
<template #row_requestUrl="{ row, $index }">
<el-button v-if="row.requestUrl" class="ml5" icon="ele-CopyDocument" text type="primary" @click="(event: any) => handleCopyUrl(event, row.requestUrl)" />
{{ row.requestUrl.substring(0, row.requestUrl.length >= 50 ? 50 : row.requestUrl.length) }}
{{ row.requestUrl.length >= 50 ? '...' : ''}}
{{ row.requestUrl }}
</template>
<template #row_requestBody="{ row, $index }">
{{ row.requestBody }}
@ -142,6 +148,7 @@ const state = reactive({
},
visible: false,
logMaxValue: 1,
clientNameList: [] as string[],
});
//
const localPageParamKey = 'localPageParam:sysLogHttp';
@ -155,6 +162,7 @@ const options = useVxeTable<PageLogHttpOutput>(
{ field: 'seq', type: 'seq', title: '序号', width: 60, fixed: 'left' },
{ field: 'createTime', title: '创建时间', minWidth: 150, showOverflow: 'tooltip' },
{ field: 'httpClientName', title: '客户端', minWidth: 110, showOverflow: 'tooltip' },
{ field: 'actionName', title: '模块名', minWidth: 110, showOverflow: 'tooltip' },
{ field: 'httpMethod', title: '请求方式', minWidth: 60, showOverflow: 'tooltip' },
{ field: 'isSuccessStatusCode', title: '是否成功', minWidth: 60, showOverflow: 'tooltip', slots: { default: 'row_isSuccessStatusCode' } },
{ field: 'httpApiDesc', title: '接口描述', minWidth: 150, showOverflow: 'tooltip' },
@ -168,6 +176,7 @@ const options = useVxeTable<PageLogHttpOutput>(
{ field: 'startTime', title: '开始时间', minWidth: 150, showOverflow: 'tooltip', slots: { default: 'row_startTime' } },
{ field: 'endTime', title: '结束时间', minWidth: 150, showOverflow: 'tooltip', slots: { default: 'row_endTime' } },
{ field: 'elapsed', title: '耗时(毫秒)', minWidth: 90, showOverflow: 'tooltip' },
{ field: 'createUserName', title: '创建用户', minWidth: 90, showOverflow: 'tooltip' },
],
},
// vxeGrid()vxe-table
@ -187,6 +196,7 @@ const options = useVxeTable<PageLogHttpOutput>(
//
onMounted(async () => {
state.clientNameList = await getAPI(SysLogHttpApi).apiSysLogHttpHttpClientNameGet().then(res => res.data.result ?? []);
state.localPageParam = Local.get(localPageParamKey) || state.localPageParam;
});