mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 18:59:50 +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:
@@ -91,7 +91,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import Card from '@/components/ui/card.vue'
|
||||
import { LoadingState, TimeRangePicker } from '@/components/common'
|
||||
import { ErrorDistributionChart, PercentileChart } from '@/components/stats'
|
||||
@@ -112,6 +112,12 @@ const errorLoading = ref(false)
|
||||
|
||||
const providerStatus = ref<ProviderStatus[]>([])
|
||||
const providerLoading = ref(false)
|
||||
let percentilesRequestId = 0
|
||||
let errorsRequestId = 0
|
||||
let providersRequestId = 0
|
||||
let loadAllPromise: Promise<void> | null = null
|
||||
let hasPendingLoadAll = false
|
||||
let loadAllDebounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function buildTimeRangeParams() {
|
||||
return {
|
||||
@@ -124,31 +130,45 @@ function buildTimeRangeParams() {
|
||||
}
|
||||
|
||||
async function loadPercentiles() {
|
||||
const requestId = ++percentilesRequestId
|
||||
percentileLoading.value = true
|
||||
try {
|
||||
percentiles.value = await adminApi.getPercentiles(buildTimeRangeParams())
|
||||
const data = await adminApi.getPercentiles(buildTimeRangeParams())
|
||||
if (requestId !== percentilesRequestId) return
|
||||
percentiles.value = data
|
||||
} finally {
|
||||
percentileLoading.value = false
|
||||
if (requestId === percentilesRequestId) {
|
||||
percentileLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadErrors() {
|
||||
const requestId = ++errorsRequestId
|
||||
errorLoading.value = true
|
||||
try {
|
||||
const response = await adminApi.getErrorDistribution(buildTimeRangeParams())
|
||||
if (requestId !== errorsRequestId) return
|
||||
errorDistribution.value = response.distribution
|
||||
errorTrend.value = response.trend
|
||||
} finally {
|
||||
errorLoading.value = false
|
||||
if (requestId === errorsRequestId) {
|
||||
errorLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadProviders() {
|
||||
const requestId = ++providersRequestId
|
||||
providerLoading.value = true
|
||||
try {
|
||||
providerStatus.value = await dashboardApi.getProviderStatus()
|
||||
const data = await dashboardApi.getProviderStatus()
|
||||
if (requestId !== providersRequestId) return
|
||||
providerStatus.value = data
|
||||
} finally {
|
||||
providerLoading.value = false
|
||||
if (requestId === providersRequestId) {
|
||||
providerLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,10 +186,47 @@ const errorTrendChartData = computed(() => ({
|
||||
}))
|
||||
|
||||
async function loadAll() {
|
||||
await Promise.all([loadPercentiles(), loadErrors(), loadProviders()])
|
||||
if (loadAllPromise) {
|
||||
hasPendingLoadAll = true
|
||||
return loadAllPromise
|
||||
}
|
||||
loadAllPromise = Promise.all([loadPercentiles(), loadErrors(), loadProviders()])
|
||||
.then(() => undefined)
|
||||
.finally(() => {
|
||||
loadAllPromise = null
|
||||
if (hasPendingLoadAll) {
|
||||
hasPendingLoadAll = false
|
||||
void loadAll()
|
||||
}
|
||||
})
|
||||
return loadAllPromise
|
||||
}
|
||||
|
||||
watch(timeRange, loadAll, { deep: true })
|
||||
function scheduleLoadAll() {
|
||||
if (loadAllDebounceTimer) {
|
||||
clearTimeout(loadAllDebounceTimer)
|
||||
}
|
||||
loadAllDebounceTimer = setTimeout(() => {
|
||||
loadAllDebounceTimer = null
|
||||
void loadAll()
|
||||
}, 120)
|
||||
}
|
||||
|
||||
onMounted(loadAll)
|
||||
watch(timeRange, scheduleLoadAll, { deep: true })
|
||||
|
||||
onMounted(() => {
|
||||
void loadAll()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (loadAllDebounceTimer) {
|
||||
clearTimeout(loadAllDebounceTimer)
|
||||
loadAllDebounceTimer = null
|
||||
}
|
||||
hasPendingLoadAll = false
|
||||
loadAllPromise = null
|
||||
percentilesRequestId += 1
|
||||
errorsRequestId += 1
|
||||
providersRequestId += 1
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user