perf: 全栈查询优化、前端缓存去重与页面可见性优化

后端:
- SQL count 查询统一改用 func.count() 子查询替代 query.count()
- Dashboard/Audit 等页面多次独立查询合并为单次聚合查询
- Provider summary 列表改为批量查询消除 N+1 问题
- DailyStats 逐天循环查询改为 CASE 分桶单次查询
- 使用 load_only() 减少不必要的列加载
- cache_decorator 支持嵌套属性路径解析(dotted vary_by)
- 多个管理/公共端点新增 @cache_result 缓存装饰

前端:
- cache.ts 新增 in-flight 请求复用、dedupedRequest、buildCacheKey
- 大量 API 调用添加前端缓存或去重
- 多个页面定时器在标签页隐藏时暂停、可见时恢复
- Auth 检查从 setInterval 改为 storage + visibilitychange 事件驱动
- 请求竞态防护(requestId 模式)

数据库:
- Usage 表新增 idx_usage_status_user_created 复合索引
This commit is contained in:
fawney19
2026-03-03 22:04:40 +08:00
parent 0a60492146
commit 97b0146ce9
66 changed files with 2306 additions and 657 deletions

View File

@@ -1132,6 +1132,8 @@ const endpoints = ref<ProviderEndpointWithKeys[]>([])
const providerKeys = ref<EndpointAPIKey[]>([]) // Provider 级别的 keys
const providerModels = ref<Model[]>([]) // Provider 级别的 models
const providerMappingPreview = ref<ProviderMappingPreviewResponse | null>(null) // 映射预览
let providerLoadRequestId = 0
let endpointsLoadRequestId = 0
// 系统级格式转换配置
const systemFormatConversionEnabled = ref(false)
@@ -1281,12 +1283,19 @@ watch(
}
void autoRefreshQuotaInBackground()
} else if (!newOpen && oldOpen) {
// 使在途请求失效,避免关闭后旧响应回写
providerLoadRequestId += 1
endpointsLoadRequestId += 1
// 停止倒计时定时器
stopCountdownTimer()
// 重置所有状态
loading.value = false
provider.value = null
endpoints.value = []
providerKeys.value = [] // 清空 Provider 级别的 keys
providerModels.value = []
providerMappingPreview.value = null
// 重置分页状态
resetKeysPagination()
@@ -2581,6 +2590,7 @@ async function loadSystemFormatConversionConfig() {
// 加载 Provider 信息
async function loadProvider() {
if (!props.providerId) return
const requestId = ++providerLoadRequestId
try {
loading.value = true
@@ -2589,21 +2599,26 @@ async function loadProvider() {
getProvider(props.providerId),
loadSystemFormatConversionConfig(),
])
if (requestId !== providerLoadRequestId) return
provider.value = providerData
if (!provider.value) {
throw new Error('Provider 不存在')
}
} catch (err: unknown) {
if (requestId !== providerLoadRequestId) return
showError(parseApiError(err, '加载失败'), '错误')
} finally {
loading.value = false
if (requestId === providerLoadRequestId) {
loading.value = false
}
}
}
// 加载端点列表
async function loadEndpoints() {
if (!props.providerId) return
const requestId = ++endpointsLoadRequestId
try {
// 并行加载端点列表、Provider 级别的 keys、models 和映射预览
@@ -2613,6 +2628,7 @@ async function loadEndpoints() {
getProviderModels(props.providerId).catch(() => []),
getProviderMappingPreview(props.providerId).catch(() => null),
])
if (requestId !== endpointsLoadRequestId) return
providerKeys.value = providerKeysResult
providerModels.value = modelsResult
@@ -2627,6 +2643,7 @@ async function loadEndpoints() {
return aIdx - bIdx
})
} catch (err: unknown) {
if (requestId !== endpointsLoadRequestId) return
showError(parseApiError(err, '加载端点失败'), '错误')
}
}