From f849a54027e1d71d2e42069d052cad9d1573a833 Mon Sep 17 00:00:00 2001 From: fawney19 Date: Mon, 15 Dec 2025 20:39:57 +0800 Subject: [PATCH] feat(ui): add model mapping cache monitoring panel in admin --- frontend/src/views/admin/CacheMonitoring.vue | 304 ++++++++++++++++++- 1 file changed, 301 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/admin/CacheMonitoring.vue b/frontend/src/views/admin/CacheMonitoring.vue index 53d9d9b..1a5f0e6 100644 --- a/frontend/src/views/admin/CacheMonitoring.vue +++ b/frontend/src/views/admin/CacheMonitoring.vue @@ -18,10 +18,10 @@ import SelectContent from '@/components/ui/select-content.vue' import SelectItem from '@/components/ui/select-item.vue' import SelectValue from '@/components/ui/select-value.vue' import ScatterChart from '@/components/charts/ScatterChart.vue' -import { Trash2, Eraser, Search, X, BarChart3, ChevronDown, ChevronRight } from 'lucide-vue-next' +import { Trash2, Eraser, Search, X, BarChart3, ChevronDown, ChevronRight, Database, ArrowRight } from 'lucide-vue-next' import { useToast } from '@/composables/useToast' import { useConfirm } from '@/composables/useConfirm' -import { cacheApi, type CacheStats, type CacheConfig, type UserAffinity } from '@/api/cache' +import { cacheApi, modelMappingCacheApi, type CacheStats, type CacheConfig, type UserAffinity, type ModelMappingCacheStats } from '@/api/cache' import type { TTLAnalysisUser } from '@/api/cache' import { formatNumber, formatTokens, formatCost, formatRemainingTime } from '@/utils/format' import { @@ -47,6 +47,13 @@ const currentPage = ref(1) const pageSize = ref(20) const currentTime = ref(Math.floor(Date.now() / 1000)) +// ==================== 模型映射缓存 ==================== + +const modelMappingStats = ref(null) +const modelMappingLoading = ref(false) +const clearingModelMapping = ref(false) +const clearingModelName = ref(null) + const { success: showSuccess, error: showError, info: showInfo } = useToast() const { confirm: showConfirm } = useConfirm() @@ -241,13 +248,87 @@ function stopCountdown() { } } +// ==================== 模型映射缓存方法 ==================== + +async function fetchModelMappingStats() { + modelMappingLoading.value = true + try { + modelMappingStats.value = await modelMappingCacheApi.getStats() + } catch (error) { + showError('获取模型映射缓存统计失败') + log.error('获取模型映射缓存统计失败', error) + } finally { + modelMappingLoading.value = false + } +} + +async function clearAllModelMappingCache() { + const confirmed = await showConfirm({ + title: '确认清除', + message: '确定要清除所有模型映射缓存吗?这会影响所有模型的名称解析。', + confirmText: '确认清除', + variant: 'destructive' + }) + + if (!confirmed) return + + clearingModelMapping.value = true + try { + const result = await modelMappingCacheApi.clearAll() + showSuccess(`已清除 ${result.deleted_count} 个缓存键`) + await fetchModelMappingStats() + } catch (error) { + showError('清除模型映射缓存失败') + log.error('清除模型映射缓存失败', error) + } finally { + clearingModelMapping.value = false + } +} + +async function clearModelMappingByName(modelName: string) { + clearingModelName.value = modelName + try { + await modelMappingCacheApi.clearByName(modelName) + showSuccess(`已清除 ${modelName} 的映射缓存`) + await fetchModelMappingStats() + } catch (error) { + showError('清除缓存失败') + log.error('清除模型映射缓存失败', error) + } finally { + clearingModelName.value = null + } +} + +function formatTTL(ttl: number | null): string { + if (ttl === null || ttl < 0) return '-' + if (ttl < 60) return `${ttl}s` + const minutes = Math.floor(ttl / 60) + const seconds = ttl % 60 + if (seconds === 0) return `${minutes}m` + return `${minutes}m${seconds}s` +} + +function getUnmappedStatusBadge(status: string): { variant: 'default' | 'secondary' | 'destructive' | 'outline', text: string } { + switch (status) { + case 'not_found': + return { variant: 'secondary', text: '未找到' } + case 'invalid': + return { variant: 'destructive', text: '无效' } + case 'error': + return { variant: 'destructive', text: '错误' } + default: + return { variant: 'outline', text: status } + } +} + // ==================== 刷新所有数据 ==================== async function refreshData() { await Promise.all([ fetchCacheStats(), fetchCacheConfig(), - fetchAffinityList() + fetchAffinityList(), + fetchModelMappingStats() ]) } @@ -272,6 +353,7 @@ onMounted(() => { fetchCacheStats() fetchCacheConfig() fetchAffinityList() + fetchModelMappingStats() startCountdown() refreshAnalysis() }) @@ -599,6 +681,222 @@ onBeforeUnmount(() => { /> + + +
+
+
+
+
+ + +
+
+
+ + + + + + + 全局模型 + + + + 映射模型 + + + 提供商 + + + 剩余 + + + 操作 + + + + + + +
+
+ {{ mapping.global_model_display_name || mapping.global_model_name }} +
+
+ {{ mapping.global_model_name }} +
+
+ - +
+ + + + + {{ mapping.mapping_name }} + + +
+ + {{ provider }} + + + +{{ mapping.providers.length - 3 }} + +
+ - +
+ + {{ formatTTL(mapping.ttl) }} + + + + +
+
+ + + +
+
+
+ {{ mapping.global_model_display_name || mapping.global_model_name || '-' }} + +
+
+ + {{ mapping.mapping_name }} +
+
+ + {{ provider }} + +
+
+
+ + +
+
+ 未映射的缓存条目 +
+
+ + {{ entry.mapping_name }} + +
+
+ + +
+ 暂无模型解析缓存 +
+ + +
+ {{ modelMappingStats.message || 'Redis 未启用' }} +
+ + +
+ 加载中... +
+
+