增强首页MySQL与Redis运行摘要卡片

- 为系统健康摘要接口补充 MySQL 连接负载、QPS、库体积、表数量等指标

- 为系统健康摘要接口补充 Redis key 数量、客户端数、OPS、命中率和运行时间等指标

- 调整首页基础设施卡片为服务摘要面板,分别展示 MySQL 与 Redis 的状态和关键运行信息
This commit is contained in:
liuwei
2026-04-30 16:40:16 +08:00
parent b2d900070c
commit b62c1cf161
2 changed files with 440 additions and 37 deletions

View File

@@ -148,6 +148,29 @@
</div>
<div class="health-item__value">{% raw %}{{ card.value }}{% endraw %}</div>
<div class="health-item__summary">{% raw %}{{ card.summary }}{% endraw %}</div>
<div v-if="card.serviceBlocks && card.serviceBlocks.length" class="health-service-grid">
<div
v-for="service in card.serviceBlocks"
:key="service.key"
class="health-service-panel"
:class="`health-service-panel--${service.status}`">
<div class="health-service-panel__head">
<div>
<div class="health-service-panel__title">{% raw %}{{ service.title }}{% endraw %}</div>
<div class="health-service-panel__summary">{% raw %}{{ service.summary }}{% endraw %}</div>
</div>
<span class="health-service-panel__badge" :class="`health-service-panel__badge--${service.status}`">
{% raw %}{{ getHealthStatusText(service.status) }}{% endraw %}
</span>
</div>
<div class="health-service-metrics">
<div v-for="metric in service.metrics" :key="metric.label" class="health-service-metric">
<span class="health-service-metric__label">{% raw %}{{ metric.label }}{% endraw %}</span>
<span class="health-service-metric__value">{% raw %}{{ metric.value }}{% endraw %}</span>
</div>
</div>
</div>
</div>
<div v-if="card.extra" class="health-item__extra">{% raw %}{{ card.extra }}{% endraw %}</div>
</div>
</div>
@@ -453,9 +476,10 @@
key: 'infrastructure',
title: '基础设施',
status: infrastructure.status || 'warning',
value: infrastructure.status === 'healthy' ? '正常' : '异常',
value: `${this.countHealthyInfrastructureServices(infrastructure)} / 2`,
summary: infrastructure.summary || '暂无状态',
extra: `MySQL${((infrastructure.mysql || {}).status === 'healthy') ? '正常' : '异常'} / Redis${((infrastructure.redis || {}).status === 'healthy') ? '正常' : '异常'}`
serviceBlocks: this.buildInfrastructureServiceBlocks(infrastructure),
extra: '首页展示的是服务摘要;如果后续要做更深入的运维排查,再单独拆详细页会更合适。'
},
{
key: 'ai_runtime',
@@ -539,6 +563,100 @@
};
return statusMap[status] || '未知';
},
formatCompactDuration(seconds) {
const totalSeconds = parseInt(seconds) || 0;
if (totalSeconds <= 0) return '-';
const days = Math.floor(totalSeconds / 86400);
const hours = Math.floor((totalSeconds % 86400) / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
if (days > 0) return `${days}D ${hours}H`;
if (hours > 0) return `${hours}H ${minutes}M`;
return `${minutes}M`;
},
formatMetricNumber(value, fractionDigits = 0) {
if (value === null || value === undefined || value === '') return '-';
const numeric = Number(value);
if (Number.isNaN(numeric)) return String(value);
return numeric.toFixed(fractionDigits);
},
countHealthyInfrastructureServices(infrastructure) {
const mysql = infrastructure.mysql || {};
const redis = infrastructure.redis || {};
let count = 0;
if (mysql.status === 'healthy') count += 1;
if (redis.status === 'healthy') count += 1;
return count;
},
buildInfrastructureServiceBlocks(infrastructure) {
const mysql = infrastructure.mysql || {};
const redis = infrastructure.redis || {};
return [
{
key: 'mysql',
title: 'MySQL',
status: mysql.status || 'warning',
summary: mysql.summary || '暂无状态',
metrics: [
{
label: '连接负载',
value: `${this.formatMetricNumber(mysql.connection_usage_percent, 1)}%`
},
{
label: '连接数',
value: `${this.formatMetricNumber(mysql.threads_connected)} / ${mysql.max_connections || '-'}`
},
{
label: '运行线程',
value: this.formatMetricNumber(mysql.threads_running)
},
{
label: 'QPS',
value: this.formatMetricNumber(mysql.questions_per_second, 2)
},
{
label: '库体积',
value: `${this.formatMetricNumber(mysql.schema_size_mb, 2)} MB`
},
{
label: '表数量',
value: this.formatMetricNumber(mysql.table_count)
}
]
},
{
key: 'redis',
title: 'Redis',
status: redis.status || 'warning',
summary: redis.summary || '暂无状态',
metrics: [
{
label: 'Key 数量',
value: this.formatMetricNumber(redis.key_count)
},
{
label: '客户端',
value: this.formatMetricNumber(redis.connected_clients)
},
{
label: 'OPS/s',
value: this.formatMetricNumber(redis.ops_per_sec)
},
{
label: '内存占用',
value: redis.used_memory_human || '-'
},
{
label: '命中率',
value: `${this.formatMetricNumber(redis.hit_rate_percent, 1)}%`
},
{
label: '运行时间',
value: this.formatCompactDuration(redis.uptime_seconds)
}
]
}
];
},
renderPieChart(chartId, usageValue, label) {
const ctx = document.getElementById(chartId);
if (!ctx) return;
@@ -1095,6 +1213,104 @@
color: #475569;
}
.health-service-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin-top: 16px;
}
.health-service-panel {
padding: 14px;
border-radius: 16px;
border: 1px solid rgba(148, 163, 184, 0.14);
background: rgba(248, 250, 252, 0.72);
}
.health-service-panel--healthy {
box-shadow: inset 0 0 0 1px rgba(16, 185, 129, 0.08);
}
.health-service-panel--warning {
box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.10);
}
.health-service-panel--danger {
box-shadow: inset 0 0 0 1px rgba(239, 68, 68, 0.10);
}
.health-service-panel__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.health-service-panel__title {
font-size: 14px;
font-weight: 700;
color: #0f172a;
margin-bottom: 4px;
}
.health-service-panel__summary {
font-size: 12px;
line-height: 1.6;
color: #64748b;
}
.health-service-panel__badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 44px;
padding: 4px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 700;
flex-shrink: 0;
}
.health-service-panel__badge--healthy {
color: #047857;
background: rgba(16, 185, 129, 0.12);
}
.health-service-panel__badge--warning {
color: #b45309;
background: rgba(245, 158, 11, 0.14);
}
.health-service-panel__badge--danger {
color: #b91c1c;
background: rgba(239, 68, 68, 0.14);
}
.health-service-metrics {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 12px;
}
.health-service-metric {
display: flex;
flex-direction: column;
gap: 4px;
}
.health-service-metric__label {
font-size: 11px;
color: #94a3b8;
}
.health-service-metric__value {
font-size: 13px;
font-weight: 600;
color: #1e293b;
word-break: break-word;
}
.health-item__extra {
margin-top: 12px;
padding-top: 12px;
@@ -1450,6 +1666,10 @@
.health-grid {
grid-template-columns: 1fr;
}
.health-service-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
@@ -1559,6 +1779,10 @@
font-size: 24px;
}
.health-service-metrics {
grid-template-columns: 1fr;
}
.chart-container--large,
.chart-container--panel {
height: 220px;