mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
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:
@@ -1158,7 +1158,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
import {
|
||||
Search,
|
||||
Upload,
|
||||
@@ -1243,11 +1243,19 @@ const proxyNodesStore = useProxyNodesStore()
|
||||
// --- Overview ---
|
||||
const poolProviders = ref<PoolOverviewItem[]>([])
|
||||
const overviewLoading = ref(true)
|
||||
let overviewRequestId = 0
|
||||
let selectProviderRequestId = 0
|
||||
let providerDataRequestId = 0
|
||||
let keysRequestId = 0
|
||||
let keysSearchDebounceTimer: number | null = null
|
||||
let suppressFiltersWatch = false
|
||||
|
||||
async function loadOverview() {
|
||||
const requestId = ++overviewRequestId
|
||||
overviewLoading.value = true
|
||||
try {
|
||||
const res = await getPoolOverview()
|
||||
if (requestId !== overviewRequestId) return
|
||||
const enabledProviders = res.items.filter(item => item.pool_enabled)
|
||||
poolProviders.value = enabledProviders
|
||||
|
||||
@@ -1266,9 +1274,12 @@ async function loadOverview() {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (requestId !== overviewRequestId) return
|
||||
showError(parseApiError(err))
|
||||
} finally {
|
||||
overviewLoading.value = false
|
||||
if (requestId === overviewRequestId) {
|
||||
overviewLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1306,6 +1317,7 @@ const showAccountQuotaColumn = computed(() => {
|
||||
})
|
||||
|
||||
async function selectProvider(id: string) {
|
||||
const requestId = ++selectProviderRequestId
|
||||
selectedProviderId.value = id
|
||||
editingKeyDetail.value = null
|
||||
keyPermissionsDialogOpen.value = false
|
||||
@@ -1315,16 +1327,27 @@ async function selectProvider(id: string) {
|
||||
proxyMobilePopoverOpenKeyId.value = null
|
||||
schedulingDetailDesktopPopoverOpenKeyId.value = null
|
||||
schedulingDetailMobilePopoverOpenKeyId.value = null
|
||||
suppressFiltersWatch = true
|
||||
currentPage.value = 1
|
||||
searchQuery.value = ''
|
||||
statusFilter.value = 'all'
|
||||
suppressFiltersWatch = false
|
||||
if (keysSearchDebounceTimer !== null) {
|
||||
clearTimeout(keysSearchDebounceTimer)
|
||||
keysSearchDebounceTimer = null
|
||||
}
|
||||
await Promise.all([loadKeys(), loadProviderData(id)])
|
||||
if (requestId !== selectProviderRequestId) return
|
||||
}
|
||||
|
||||
async function loadProviderData(id: string) {
|
||||
const requestId = ++providerDataRequestId
|
||||
try {
|
||||
selectedProviderData.value = await getProvider(id)
|
||||
const providerData = await getProvider(id)
|
||||
if (requestId !== providerDataRequestId || selectedProviderId.value !== id) return
|
||||
selectedProviderData.value = providerData
|
||||
} catch {
|
||||
if (requestId !== providerDataRequestId || selectedProviderId.value !== id) return
|
||||
selectedProviderData.value = null
|
||||
}
|
||||
}
|
||||
@@ -1434,25 +1457,52 @@ async function refreshCurrentPage() {
|
||||
|
||||
async function loadKeys() {
|
||||
if (!selectedProviderId.value) return
|
||||
const requestId = ++keysRequestId
|
||||
const providerId = selectedProviderId.value
|
||||
const page = currentPage.value
|
||||
const pageSizeValue = pageSize.value
|
||||
const search = searchQuery.value || undefined
|
||||
const status = statusFilter.value as 'all' | 'active' | 'cooldown' | 'inactive'
|
||||
keysLoading.value = true
|
||||
try {
|
||||
keyPage.value = await listPoolKeys(selectedProviderId.value, {
|
||||
page: currentPage.value,
|
||||
page_size: pageSize.value,
|
||||
search: searchQuery.value || undefined,
|
||||
status: statusFilter.value as 'all' | 'active' | 'cooldown' | 'inactive',
|
||||
const nextPage = await listPoolKeys(providerId, {
|
||||
page,
|
||||
page_size: pageSizeValue,
|
||||
search,
|
||||
status,
|
||||
})
|
||||
if (requestId !== keysRequestId || selectedProviderId.value !== providerId) return
|
||||
keyPage.value = nextPage
|
||||
} catch (err) {
|
||||
if (requestId !== keysRequestId || selectedProviderId.value !== providerId) return
|
||||
showError(parseApiError(err))
|
||||
} finally {
|
||||
keysLoading.value = false
|
||||
if (requestId === keysRequestId) {
|
||||
keysLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch([currentPage, pageSize], () => loadKeys())
|
||||
watch([searchQuery, statusFilter], () => {
|
||||
watch([currentPage, pageSize], () => {
|
||||
void loadKeys()
|
||||
})
|
||||
|
||||
watch(statusFilter, () => {
|
||||
if (suppressFiltersWatch) return
|
||||
currentPage.value = 1
|
||||
loadKeys()
|
||||
void loadKeys()
|
||||
})
|
||||
|
||||
watch(searchQuery, () => {
|
||||
if (suppressFiltersWatch) return
|
||||
currentPage.value = 1
|
||||
if (keysSearchDebounceTimer !== null) {
|
||||
clearTimeout(keysSearchDebounceTimer)
|
||||
}
|
||||
keysSearchDebounceTimer = window.setTimeout(() => {
|
||||
keysSearchDebounceTimer = null
|
||||
void loadKeys()
|
||||
}, 300)
|
||||
})
|
||||
|
||||
function normalizeAuthTypeForEdit(authType: string): EndpointAPIKey['auth_type'] {
|
||||
@@ -2224,4 +2274,15 @@ onMounted(async () => {
|
||||
await loadOverview()
|
||||
void refreshCurrentPageQuotaInBackground({ silent: true })
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (keysSearchDebounceTimer !== null) {
|
||||
clearTimeout(keysSearchDebounceTimer)
|
||||
keysSearchDebounceTimer = null
|
||||
}
|
||||
overviewRequestId += 1
|
||||
selectProviderRequestId += 1
|
||||
providerDataRequestId += 1
|
||||
keysRequestId += 1
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user