优化系统健康评分与缓存监测

This commit is contained in:
Cheng Zhou
2026-06-30 10:43:24 +08:00
parent b25055775e
commit 668e719b6d
10 changed files with 412 additions and 42 deletions
@@ -44,24 +44,37 @@
</template>
</el-progress>
</div>
<div class="score-breakdown">
<span v-for="item in healthScoreItems" :key="item.label">
{{ item.label }} {{ item.deduction ? `-${item.deduction}` : "正常" }}
</span>
</div>
<div v-if="!health.sample_sufficient" class="sample-warning">
1 小时仅 {{ health.last_hour.total_checks }} 次检查样本不足当前评分仅供参考
</div>
<div v-if="health.issues.length > 0" class="health-issues">
<div v-for="(issue, index) in health.issues" :key="index" class="issue-item">
<span class="issue-dot" />
<span>{{ issue }}</span>
</div>
</div>
<div v-else class="health-ok">
<div v-else-if="health.sample_sufficient" class="health-ok">
<span class="ok-icon">&#10003;</span>
<span>系统运行正常未发现问题</span>
</div>
</div>
<div v-if="metrics" class="cache-card">
<h3>缓存效率</h3>
<div class="cache-title">
<h3>缓存效率</h3>
<span> 1 小时</span>
</div>
<div class="cache-stats">
<div class="cache-stat primary">
<div class="cache-stat-header">
<span class="stat-label">命中率</span>
<span class="stat-label">
综合命中率{{ metrics.cache_metrics.cache_hits }}/{{ metrics.cache_metrics.total_accesses }}
</span>
<strong class="stat-value" :style="{ color: getProgressColor(metrics.cache_metrics.hit_rate) }">
{{ metrics.cache_metrics.hit_rate.toFixed(1) }}%
</strong>
@@ -73,6 +86,22 @@
:show-text="false"
/>
</div>
<div class="cache-scope-details">
<span>
项目权限
{{ metrics.cache_metrics.by_cache.project_permissions.hit_rate.toFixed(1) }}%
{{ metrics.cache_metrics.by_cache.project_permissions.cache_hits }}/{{
metrics.cache_metrics.by_cache.project_permissions.total_accesses
}}
</span>
<span>
成员角色
{{ metrics.cache_metrics.by_cache.member_roles.hit_rate.toFixed(1) }}%
{{ metrics.cache_metrics.by_cache.member_roles.cache_hits }}/{{
metrics.cache_metrics.by_cache.member_roles.total_accesses
}}
</span>
</div>
<div class="cache-stat-row">
<div class="cache-stat-mini">
<span class="stat-label">缓存项目</span>
@@ -173,7 +202,7 @@
</template>
<script setup lang="ts">
import { ref, computed, h, nextTick, onMounted } from "vue";
import { ref, computed, h, nextTick, onMounted, onUnmounted } from "vue";
import type {
PermissionMetricsResponse,
AlertsResponse,
@@ -304,6 +333,16 @@ const summaryCards = computed(() => {
];
});
const healthScoreItems = computed(() => {
if (!health.value) return [];
return [
{ label: "性能", deduction: health.value.score_breakdown.performance.deduction },
{ label: "拒绝率", deduction: health.value.score_breakdown.deny_rate.deduction },
{ label: "缓存", deduction: health.value.score_breakdown.cache.deduction },
{ label: "告警", deduction: health.value.score_breakdown.alerts.deduction },
];
});
const loadOverviewData = async () => {
const [metricsRes, healthRes, alertsRes, deniedRes, summaryRes] = await Promise.allSettled([
fetchPermissionMetrics(),
@@ -335,7 +374,18 @@ const refresh = () => {
else if (activeTab.value === "ip-locations") ipLocationsRef.value?.refresh();
};
onMounted(loadOverviewData);
let overviewRefreshTimer: number | undefined;
onMounted(() => {
loadOverviewData();
overviewRefreshTimer = window.setInterval(() => {
if (activeTab.value === "overview") loadOverviewData();
}, 60_000);
});
onUnmounted(() => {
if (overviewRefreshTimer !== undefined) window.clearInterval(overviewRefreshTimer);
});
defineExpose({ refresh });
</script>
@@ -632,6 +682,32 @@ defineExpose({ refresh });
padding: 8px 0 16px;
}
.score-breakdown {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 6px;
margin: -6px 0 12px;
}
.score-breakdown span {
padding: 3px 8px;
border-radius: 999px;
background: #f1f5f9;
color: var(--mon-muted);
font-size: 11px;
}
.sample-warning {
margin-bottom: 10px;
padding: 9px 12px;
border-radius: 10px;
background: #fffbeb;
color: #b45309;
font-size: 12px;
text-align: center;
}
.score-inner {
display: flex;
flex-direction: column;
@@ -699,6 +775,36 @@ defineExpose({ refresh });
gap: 14px;
}
.cache-title {
display: flex;
align-items: center;
justify-content: space-between;
}
.cache-title h3 {
margin: 0;
}
.cache-title span {
color: var(--mon-muted);
font-size: 12px;
}
.cache-scope-details {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.cache-scope-details span {
flex: 1 1 180px;
padding: 8px 10px;
border-radius: 8px;
background: #f8fafc;
color: var(--mon-muted);
font-size: 12px;
}
.cache-stat.primary {
padding: 14px;
background: #f8fafc;