Files
gemini-banlancer/internal/service/log_service.go

198 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Filename: internal/service/log_service.go
package service
import (
"context"
"fmt"
"gemini-balancer/internal/models"
"strconv"
"strings"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
type LogService struct {
db *gorm.DB
logger *logrus.Entry
}
func NewLogService(db *gorm.DB, logger *logrus.Logger) *LogService {
return &LogService{
db: db,
logger: logger.WithField("component", "LogService"),
}
}
func (s *LogService) Record(ctx context.Context, log *models.RequestLog) error {
return s.db.WithContext(ctx).Create(log).Error
}
// LogQueryParams 解耦 Gin使用结构体传参
type LogQueryParams struct {
Page int
PageSize int
ModelName string
IsSuccess *bool // 使用指针区分"未设置"和"false"
StatusCode *int
KeyIDs []string
GroupIDs []string
Q string
ErrorCodes []string
StatusCodes []string
}
func (s *LogService) GetLogs(ctx context.Context, params LogQueryParams) ([]models.RequestLog, int64, error) {
// 参数校验
if params.Page < 1 {
params.Page = 1
}
if params.PageSize < 1 || params.PageSize > 100 {
params.PageSize = 20
}
var logs []models.RequestLog
var total int64
// 构建基础查询
query := s.db.WithContext(ctx).Model(&models.RequestLog{})
query = s.applyFilters(query, params)
if err := query.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count logs: %w", err)
}
if total == 0 {
return []models.RequestLog{}, 0, nil
}
offset := (params.Page - 1) * params.PageSize
if err := query.Order("request_time DESC").
Limit(params.PageSize).
Offset(offset).
Find(&logs).Error; err != nil {
return nil, 0, fmt.Errorf("failed to query logs: %w", err)
}
return logs, total, nil
}
func (s *LogService) applyFilters(query *gorm.DB, params LogQueryParams) *gorm.DB {
if params.IsSuccess != nil {
query = query.Where("is_success = ?", *params.IsSuccess)
} else {
query = query.Where("is_success = ?", false)
}
if params.ModelName != "" {
query = query.Where("model_name = ?", params.ModelName)
}
if params.StatusCode != nil {
query = query.Where("status_code = ?", *params.StatusCode)
}
if len(params.KeyIDs) > 0 {
query = query.Where("key_id IN (?)", params.KeyIDs)
}
if len(params.GroupIDs) > 0 {
query = query.Where("group_id IN (?)", params.GroupIDs)
}
hasErrorCodes := len(params.ErrorCodes) > 0
hasStatusCodes := len(params.StatusCodes) > 0
if hasErrorCodes && hasStatusCodes {
query = query.Where(
s.db.Where("error_code IN (?)", params.ErrorCodes).
Or("status_code IN (?)", params.StatusCodes),
)
} else if hasErrorCodes {
query = query.Where("error_code IN (?)", params.ErrorCodes)
} else if hasStatusCodes {
query = query.Where("status_code IN (?)", params.StatusCodes)
}
if params.Q != "" {
searchQuery := "%" + params.Q + "%"
query = query.Where(
"model_name LIKE ? OR error_code LIKE ? OR error_message LIKE ? OR CAST(status_code AS CHAR) LIKE ?",
searchQuery, searchQuery, searchQuery, searchQuery,
)
}
return query
}
// ParseLogQueryParams 在 Handler 层调用,解析 Gin 参数
func ParseLogQueryParams(queryParams map[string]string) (LogQueryParams, error) {
params := LogQueryParams{
Page: 1,
PageSize: 20,
}
if pageStr, ok := queryParams["page"]; ok {
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
params.Page = page
}
}
if pageSizeStr, ok := queryParams["page_size"]; ok {
if pageSize, err := strconv.Atoi(pageSizeStr); err == nil && pageSize > 0 {
params.PageSize = pageSize
}
}
if modelName, ok := queryParams["model_name"]; ok {
params.ModelName = modelName
}
if isSuccessStr, ok := queryParams["is_success"]; ok {
if isSuccess, err := strconv.ParseBool(isSuccessStr); err == nil {
params.IsSuccess = &isSuccess
} else {
return params, fmt.Errorf("invalid is_success parameter: %s", isSuccessStr)
}
}
if statusCodeStr, ok := queryParams["status_code"]; ok {
if statusCode, err := strconv.Atoi(statusCodeStr); err == nil {
params.StatusCode = &statusCode
} else {
return params, fmt.Errorf("invalid status_code parameter: %s", statusCodeStr)
}
}
if keyIDsStr, ok := queryParams["key_ids"]; ok {
params.KeyIDs = strings.Split(keyIDsStr, ",")
}
if groupIDsStr, ok := queryParams["group_ids"]; ok {
params.GroupIDs = strings.Split(groupIDsStr, ",")
}
if errorCodesStr, ok := queryParams["error_codes"]; ok {
params.ErrorCodes = strings.Split(errorCodesStr, ",")
}
if statusCodesStr, ok := queryParams["status_codes"]; ok {
params.StatusCodes = strings.Split(statusCodesStr, ",")
}
if q, ok := queryParams["q"]; ok {
params.Q = q
}
return params, nil
}
// DeleteLogs 删除指定ID的日志
func (s *LogService) DeleteLogs(ctx context.Context, ids []uint) error {
if len(ids) == 0 {
return fmt.Errorf("no log IDs provided")
}
return s.db.WithContext(ctx).Delete(&models.RequestLog{}, ids).Error
}
// DeleteAllLogs 删除所有日志
func (s *LogService) DeleteAllLogs(ctx context.Context) error {
return s.db.WithContext(ctx).Where("1 = 1").Delete(&models.RequestLog{}).Error
}
// DeleteOldLogs 删除指定天数之前的日志
func (s *LogService) DeleteOldLogs(ctx context.Context, days int) (int64, error) {
if days <= 0 {
return 0, fmt.Errorf("days must be positive")
}
result := s.db.WithContext(ctx).
Where("request_time < DATE_SUB(NOW(), INTERVAL ? DAY)", days).
Delete(&models.RequestLog{})
return result.RowsAffected, result.Error
}