feat: 统计表
This commit is contained in:
@@ -104,6 +104,7 @@ func (a *App) Start() error {
|
|||||||
// 数据库迁移
|
// 数据库迁移
|
||||||
if err := a.db.AutoMigrate(
|
if err := a.db.AutoMigrate(
|
||||||
&models.RequestLog{},
|
&models.RequestLog{},
|
||||||
|
&models.GroupHourlyStat{},
|
||||||
&models.APIKey{},
|
&models.APIKey{},
|
||||||
&models.SystemSetting{},
|
&models.SystemSetting{},
|
||||||
&models.Group{},
|
&models.Group{},
|
||||||
|
@@ -98,3 +98,14 @@ type DashboardStats struct {
|
|||||||
SuccessRate float64 `json:"success_rate"`
|
SuccessRate float64 `json:"success_rate"`
|
||||||
GroupStats []GroupRequestStat `json:"group_stats"`
|
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"`
|
||||||
|
}
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user