From a44b4f9fb91e22ebd8759a90e7cbfd76013d2147 Mon Sep 17 00:00:00 2001 From: tbphp Date: Wed, 30 Jul 2025 23:20:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E8=AE=A110=E5=88=86=E9=92=9FR?= =?UTF-8?q?PM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/dashboard_handler.go | 51 ++++++++++++++++++++++++--- internal/models/types.go | 2 +- web/src/components/BaseInfoCard.vue | 26 +++++++++----- web/src/types/models.ts | 2 +- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/internal/handler/dashboard_handler.go b/internal/handler/dashboard_handler.go index eeba9c3..8607db8 100644 --- a/internal/handler/dashboard_handler.go +++ b/internal/handler/dashboard_handler.go @@ -11,12 +11,16 @@ import ( // Stats Get dashboard statistics func (s *Server) Stats(c *gin.Context) { - var activeKeys, invalidKeys, groupCount int64 + var activeKeys, invalidKeys int64 s.DB.Model(&models.APIKey{}).Where("status = ?", models.KeyStatusActive).Count(&activeKeys) s.DB.Model(&models.APIKey{}).Where("status = ?", models.KeyStatusInvalid).Count(&invalidKeys) - s.DB.Model(&models.Group{}).Count(&groupCount) now := time.Now() + rpmStats, err := s.getRPMStats(now) + if err != nil { + response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get rpm stats")) + return + } twentyFourHoursAgo := now.Add(-24 * time.Hour) fortyEightHoursAgo := now.Add(-48 * time.Hour) @@ -85,9 +89,7 @@ func (s *Server) Stats(c *gin.Context) { SubValue: invalidKeys, SubValueTip: "无效密钥数量", }, - GroupCount: models.StatCard{ - Value: float64(groupCount), - }, + RPM: rpmStats, RequestCount: models.StatCard{ Value: float64(currentPeriod.TotalRequests), Trend: reqTrend, @@ -179,3 +181,42 @@ func (s *Server) getHourlyStats(startTime, endTime time.Time) (hourlyStatResult, Scan(&result).Error return result, err } + +type rpmStatResult struct { + CurrentRequests int64 + PreviousRequests int64 +} + +func (s *Server) getRPMStats(now time.Time) (models.StatCard, error) { + tenMinutesAgo := now.Add(-10 * time.Minute) + twentyMinutesAgo := now.Add(-20 * time.Minute) + + var result rpmStatResult + err := s.DB.Model(&models.RequestLog{}). + Select("count(case when timestamp >= ? then 1 end) as current_requests, count(case when timestamp >= ? and timestamp < ? then 1 end) as previous_requests", tenMinutesAgo, twentyMinutesAgo, tenMinutesAgo). + Where("timestamp >= ?", twentyMinutesAgo). + Scan(&result).Error + + if err != nil { + return models.StatCard{}, err + } + + currentRPM := float64(result.CurrentRequests) / 10.0 + previousRPM := float64(result.PreviousRequests) / 10.0 + + rpmTrend := 0.0 + rpmTrendIsGrowth := true + if previousRPM > 0 { + rpmTrend = (currentRPM - previousRPM) / previousRPM * 100 + rpmTrendIsGrowth = rpmTrend >= 0 + } else if currentRPM > 0 { + rpmTrend = 100.0 + rpmTrendIsGrowth = true + } + + return models.StatCard{ + Value: currentRPM, + Trend: rpmTrend, + TrendIsGrowth: rpmTrendIsGrowth, + }, nil +} diff --git a/internal/models/types.go b/internal/models/types.go index d73b1e8..7b42006 100644 --- a/internal/models/types.go +++ b/internal/models/types.go @@ -107,7 +107,7 @@ type StatCard struct { // DashboardStatsResponse 用于仪表盘基础统计的API响应 type DashboardStatsResponse struct { KeyCount StatCard `json:"key_count"` - GroupCount StatCard `json:"group_count"` + RPM StatCard `json:"rpm"` RequestCount StatCard `json:"request_count"` ErrorRate StatCard `json:"error_rate"` } diff --git a/web/src/components/BaseInfoCard.vue b/web/src/components/BaseInfoCard.vue index 82047ce..5f8cdec 100644 --- a/web/src/components/BaseInfoCard.vue +++ b/web/src/components/BaseInfoCard.vue @@ -39,7 +39,7 @@ const fetchStats = async () => { key_count: (stats.value?.key_count?.value ?? 0) / ((stats.value?.key_count?.value ?? 1) + (stats.value?.key_count?.sub_value ?? 1)), - group_count: 1, + rpm: Math.min(100 + (stats.value?.rpm?.trend ?? 0), 100) / 100, request_count: Math.min(100 + (stats.value?.request_count?.trend ?? 0), 100) / 100, error_rate: (100 - (stats.value?.error_rate?.value ?? 0)) / 100, }; @@ -93,25 +93,33 @@ onMounted(() => { - +
-
📁
+
⏱️
+ + {{ stats ? formatTrend(stats.rpm.trend) : "--" }} +
- {{ stats?.group_count?.value ?? 0 }} + {{ stats?.rpm?.value.toFixed(1) ?? 0 }}
-
分组数量
+
10分钟RPM
@@ -232,7 +240,7 @@ onMounted(() => { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } -.group-icon { +.rpm-icon { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); } @@ -295,7 +303,7 @@ onMounted(() => { background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); } -.group-bar { +.rpm-bar { background: linear-gradient(90deg, #f093fb 0%, #f5576c 100%); } diff --git a/web/src/types/models.ts b/web/src/types/models.ts index 4dd6eab..3e44117 100644 --- a/web/src/types/models.ts +++ b/web/src/types/models.ts @@ -171,7 +171,7 @@ export interface StatCard { // 仪表盘基础统计响应 export interface DashboardStatsResponse { key_count: StatCard; - group_count: StatCard; + rpm: StatCard; request_count: StatCard; error_rate: StatCard; }