perf: 性能优化与错误修复
- 修复 N+1 查询: users/devices/agents 批量 GROUP BY 替代循环查询 - 添加分页: cards/finance/agents/devices API - Redis 初始化根据安装配置 redis.enabled 决定是否连接 - 修复 DefaultVal 解析错误: 使用 sql.NullString 处理 NULL 值 - Dashboard 优化: 替换 ECharts 世界地图为 Chart.js 环形饼图 - 适配前端 cards 页面新 API 响应格式 - 添加数据库索引优化查询性能 - 实现可配置的数据清理定时任务
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"verification-platform-backend/internal/database"
|
||||
"verification-platform-backend/internal/model"
|
||||
)
|
||||
|
||||
type CleanupConfig struct {
|
||||
EnableAutoCleanup bool
|
||||
CleanupIntervalHours int
|
||||
CaptchaRetentionDays int
|
||||
VerifyCodeRetentionDays int
|
||||
ApiUsageRetentionDays int
|
||||
WebhookLogRetentionDays int
|
||||
DeviceSessionRetentionDays int
|
||||
}
|
||||
|
||||
var defaultConfig = CleanupConfig{
|
||||
EnableAutoCleanup: false,
|
||||
CleanupIntervalHours: 24,
|
||||
CaptchaRetentionDays: 1,
|
||||
VerifyCodeRetentionDays: 7,
|
||||
ApiUsageRetentionDays: 30,
|
||||
WebhookLogRetentionDays: 30,
|
||||
DeviceSessionRetentionDays: 7,
|
||||
}
|
||||
|
||||
func GetCleanupConfig() CleanupConfig {
|
||||
cfg := defaultConfig
|
||||
|
||||
var settings []model.Setting
|
||||
database.DB.Where("category = ?", "cleanup").Find(&settings)
|
||||
|
||||
for _, s := range settings {
|
||||
switch s.Key {
|
||||
case "enable_auto_cleanup":
|
||||
cfg.EnableAutoCleanup = s.Value == "true"
|
||||
case "cleanup_interval_hours":
|
||||
cfg.CleanupIntervalHours = parseVal(s.Value, defaultConfig.CleanupIntervalHours)
|
||||
case "captcha_retention_days":
|
||||
cfg.CaptchaRetentionDays = parseVal(s.Value, defaultConfig.CaptchaRetentionDays)
|
||||
case "verify_code_retention_days":
|
||||
cfg.VerifyCodeRetentionDays = parseVal(s.Value, defaultConfig.VerifyCodeRetentionDays)
|
||||
case "api_usage_retention_days":
|
||||
cfg.ApiUsageRetentionDays = parseVal(s.Value, defaultConfig.ApiUsageRetentionDays)
|
||||
case "webhook_log_retention_days":
|
||||
cfg.WebhookLogRetentionDays = parseVal(s.Value, defaultConfig.WebhookLogRetentionDays)
|
||||
case "device_session_retention_days":
|
||||
cfg.DeviceSessionRetentionDays = parseVal(s.Value, defaultConfig.DeviceSessionRetentionDays)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func parseVal(value string, defaultVal int) int {
|
||||
if value == "" {
|
||||
return defaultVal
|
||||
}
|
||||
var result int
|
||||
if _, err := fmt.Sscanf(value, "%d", &result); err != nil || result <= 0 {
|
||||
return defaultVal
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func StartCleanupScheduler() {
|
||||
go func() {
|
||||
for {
|
||||
cfg := GetCleanupConfig()
|
||||
interval := time.Duration(cfg.CleanupIntervalHours) * time.Hour
|
||||
if interval < time.Hour {
|
||||
interval = time.Hour
|
||||
}
|
||||
time.Sleep(interval)
|
||||
|
||||
if !cfg.EnableAutoCleanup {
|
||||
continue
|
||||
}
|
||||
|
||||
RunCleanup(cfg)
|
||||
}
|
||||
}()
|
||||
log.Println("[Scheduler] Cleanup scheduler started")
|
||||
}
|
||||
|
||||
func RunCleanup(cfg CleanupConfig) {
|
||||
now := time.Now()
|
||||
totalCleaned := 0
|
||||
|
||||
if cfg.CaptchaRetentionDays > 0 {
|
||||
cutoff := now.AddDate(0, 0, -cfg.CaptchaRetentionDays)
|
||||
result := database.DB.Where("expires_at < ?", cutoff).Delete(&model.Captcha{})
|
||||
if result.RowsAffected > 0 {
|
||||
log.Printf("[Cleanup] Deleted %d expired captchas", result.RowsAffected)
|
||||
totalCleaned += int(result.RowsAffected)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.VerifyCodeRetentionDays > 0 {
|
||||
cutoff := now.AddDate(0, 0, -cfg.VerifyCodeRetentionDays)
|
||||
emailResult := database.DB.Where("expires_at < ? AND used = ?", cutoff, true).Delete(&model.EmailVerifyCode{})
|
||||
smsResult := database.DB.Where("expires_at < ? AND used = ?", cutoff, true).Delete(&model.SmsVerifyCode{})
|
||||
count := emailResult.RowsAffected + smsResult.RowsAffected
|
||||
if count > 0 {
|
||||
log.Printf("[Cleanup] Deleted %d expired verify codes", count)
|
||||
totalCleaned += int(count)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.ApiUsageRetentionDays > 0 {
|
||||
cutoff := now.AddDate(0, 0, -cfg.ApiUsageRetentionDays)
|
||||
result := database.DB.Where("created_at < ?", cutoff).Delete(&model.ApiUsage{})
|
||||
if result.RowsAffected > 0 {
|
||||
log.Printf("[Cleanup] Deleted %d old api usage records", result.RowsAffected)
|
||||
totalCleaned += int(result.RowsAffected)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.WebhookLogRetentionDays > 0 {
|
||||
cutoff := now.AddDate(0, 0, -cfg.WebhookLogRetentionDays)
|
||||
result := database.DB.Where("created_at < ?", cutoff).Delete(&model.WebhookLog{})
|
||||
if result.RowsAffected > 0 {
|
||||
log.Printf("[Cleanup] Deleted %d old webhook logs", result.RowsAffected)
|
||||
totalCleaned += int(result.RowsAffected)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.DeviceSessionRetentionDays > 0 {
|
||||
cutoff := now.AddDate(0, 0, -cfg.DeviceSessionRetentionDays)
|
||||
result := database.DB.Where("last_heartbeat < ? AND last_heartbeat IS NOT NULL", cutoff).Delete(&model.DeviceSession{})
|
||||
if result.RowsAffected > 0 {
|
||||
log.Printf("[Cleanup] Deleted %d expired device sessions", result.RowsAffected)
|
||||
totalCleaned += int(result.RowsAffected)
|
||||
}
|
||||
}
|
||||
|
||||
if totalCleaned > 0 {
|
||||
log.Printf("[Cleanup] Total cleaned: %d records", totalCleaned)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user