perf: 拆分热力图为独立接口并添加 Redis 缓存

- 新增独立热力图 API 端点 (/api/admin/usage/heatmap, /api/users/me/usage/heatmap)
- 添加 Redis 缓存层 (5分钟 TTL),减少数据库查询
- 用户角色变更时清除热力图缓存
- 前端并行加载统计数据和热力图,添加加载/错误状态显示
- 修复 cache_decorator 缺少 JSON 解析错误处理的问题
- 更新 docker-compose 启动命令提示
This commit is contained in:
fawney19
2026-01-04 22:42:58 +08:00
parent b6bd6357ed
commit a2f33a6c35
13 changed files with 271 additions and 31 deletions

View File

@@ -18,8 +18,22 @@
<span class="flex-shrink-0"></span>
</div>
</div>
<div
v-if="isLoading"
class="h-full min-h-[160px] flex items-center justify-center text-sm text-muted-foreground"
>
<Loader2 class="h-5 w-5 animate-spin mr-2" />
加载中...
</div>
<div
v-else-if="hasError"
class="h-full min-h-[160px] flex items-center justify-center text-sm text-destructive"
>
<AlertCircle class="h-4 w-4 mr-1.5" />
加载失败
</div>
<ActivityHeatmap
v-if="hasData"
v-else-if="hasData"
:data="data"
:show-header="false"
/>
@@ -34,6 +48,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { Loader2, AlertCircle } from 'lucide-vue-next'
import Card from '@/components/ui/card.vue'
import ActivityHeatmap from '@/components/stats/ActivityHeatmap.vue'
import type { ActivityHeatmap as ActivityHeatmapData } from '@/types/activity'
@@ -41,6 +56,8 @@ import type { ActivityHeatmap as ActivityHeatmapData } from '@/types/activity'
const props = defineProps<{
data: ActivityHeatmapData | null
title: string
isLoading?: boolean
hasError?: boolean
}>()
const legendLevels = [0.08, 0.25, 0.45, 0.65, 0.85]

View File

@@ -64,9 +64,6 @@ export function useUsageData(options: UseUsageDataOptions) {
}))
})
// 活跃度热图数据
const activityHeatmapData = computed(() => stats.value.activity_heatmap)
// 加载统计数据(不加载记录)
async function loadStats(dateRange?: DateRangeParams) {
isLoadingStats.value = true
@@ -93,7 +90,7 @@ export function useUsageData(options: UseUsageDataOptions) {
cache_stats: (statsData as any).cache_stats,
period_start: '',
period_end: '',
activity_heatmap: statsData.activity_heatmap || null
activity_heatmap: null
}
modelStats.value = modelData.map(item => ({
@@ -143,7 +140,7 @@ export function useUsageData(options: UseUsageDataOptions) {
avg_response_time: userData.avg_response_time || 0,
period_start: '',
period_end: '',
activity_heatmap: userData.activity_heatmap || null
activity_heatmap: null
}
modelStats.value = (userData.summary_by_model || []).map((item: any) => ({
@@ -305,7 +302,6 @@ export function useUsageData(options: UseUsageDataOptions) {
// 计算属性
enhancedModelStats,
activityHeatmapData,
// 方法
loadStats,

View File

@@ -1,5 +1,3 @@
import type { ActivityHeatmap } from '@/types/activity'
// 统计数据状态
export interface UsageStatsState {
total_requests: number
@@ -17,7 +15,6 @@ export interface UsageStatsState {
}
period_start: string
period_end: string
activity_heatmap: ActivityHeatmap | null
}
// 模型统计
@@ -115,7 +112,6 @@ export function createDefaultStats(): UsageStatsState {
error_rate: undefined,
cache_stats: undefined,
period_start: '',
period_end: '',
activity_heatmap: null
period_end: ''
}
}