From 41115d0dcdefeb76eda13f7189b38466625614cc Mon Sep 17 00:00:00 2001 From: tbphp Date: Sat, 12 Jul 2025 12:40:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E8=AE=A1=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/app.go | 1 + internal/models/types.go | 11 ++++++ internal/services/request_log_service.go | 45 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/internal/app/app.go b/internal/app/app.go index b7dab1f..247554a 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -104,6 +104,7 @@ func (a *App) Start() error { // 数据库迁移 if err := a.db.AutoMigrate( &models.RequestLog{}, + &models.GroupHourlyStat{}, &models.APIKey{}, &models.SystemSetting{}, &models.Group{}, diff --git a/internal/models/types.go b/internal/models/types.go index 2816876..abfc5b3 100644 --- a/internal/models/types.go +++ b/internal/models/types.go @@ -98,3 +98,14 @@ type DashboardStats struct { SuccessRate float64 `json:"success_rate"` GroupStats []GroupRequestStat `json:"group_stats"` } + +// GroupHourlyStat 对应 group_hourly_stats 表,用于存储每个分组每小时的请求统计 +type GroupHourlyStat struct { + ID uint `gorm:"primaryKey;autoIncrement" json:"id"` + Time time.Time `gorm:"type:datetime;not null;uniqueIndex:idx_group_time" json:"time"` // 整点时间 + GroupID uint `gorm:"not null;uniqueIndex:idx_group_time" json:"group_id"` + SuccessCount int64 `gorm:"not null;default:0" json:"success_count"` + FailureCount int64 `gorm:"not null;default:0" json:"failure_count"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/internal/services/request_log_service.go b/internal/services/request_log_service.go index b66f3fc..d8abbce 100644 --- a/internal/services/request_log_service.go +++ b/internal/services/request_log_service.go @@ -13,6 +13,7 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" "gorm.io/gorm" + "gorm.io/gorm/clause" ) const ( @@ -222,6 +223,50 @@ func (s *RequestLogService) writeLogsToDB(logs []*models.RequestLog) error { return fmt.Errorf("failed to batch update api_key stats: %w", err) } } + + // 更新统计表 + hourlyStats := make(map[struct { + Time time.Time + GroupID uint + }]struct{ Success, Failure int64 }) + for _, log := range logs { + hourlyTime := log.Timestamp.Truncate(time.Hour) + key := struct { + Time time.Time + GroupID uint + }{Time: hourlyTime, GroupID: log.GroupID} + + counts := hourlyStats[key] + if log.IsSuccess { + counts.Success++ + } else { + counts.Failure++ + } + hourlyStats[key] = counts + } + + if len(hourlyStats) > 0 { + for key, counts := range hourlyStats { + err := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "time"}, {Name: "group_id"}}, + DoUpdates: clause.Assignments(map[string]interface{}{ + "success_count": gorm.Expr("success_count + ?", counts.Success), + "failure_count": gorm.Expr("failure_count + ?", counts.Failure), + "updated_at": time.Now(), + }), + }).Create(&models.GroupHourlyStat{ + Time: key.Time, + GroupID: key.GroupID, + SuccessCount: counts.Success, + FailureCount: counts.Failure, + }).Error + + if err != nil { + return fmt.Errorf("failed to upsert group hourly stat: %w", err) + } + } + } + return nil }) }