Files
junhong_cmp_fiber/internal/infrastructure/notification/cleanup.go
2026-07-24 16:07:18 +08:00

73 lines
2.3 KiB
Go

package notification
import (
"context"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// CleanupService 按通知类别的保留期限分批删除过期通知事实。
type CleanupService struct {
db *gorm.DB
logger *zap.Logger
now func() time.Time
}
// NewCleanupService 创建通知保留清理服务。
func NewCleanupService(db *gorm.DB, logger *zap.Logger) *CleanupService {
if logger == nil {
logger = zap.NewNop()
}
return &CleanupService{db: db, logger: logger, now: time.Now}
}
// Run 按类别、创建时间和稳定主键执行有界分批清理。
func (s *CleanupService) Run(ctx context.Context) error {
policies := []struct {
category string
days int
}{
{constants.NotificationCategoryApproval, constants.NotificationApprovalRetentionDays},
{constants.NotificationCategoryExpiry, constants.NotificationExpiryRetentionDays},
{constants.NotificationCategorySync, constants.NotificationSyncRetentionDays},
{constants.NotificationCategorySystem, constants.NotificationSystemRetentionDays},
}
for _, policy := range policies {
deleted, err := s.cleanupCategory(ctx, policy.category, s.now().UTC().AddDate(0, 0, -policy.days))
if err != nil {
return err
}
s.logger.Info("站内通知保留清理完成",
zap.String("category", policy.category), zap.Int64("deleted_count", deleted))
}
return nil
}
func (s *CleanupService) cleanupCategory(ctx context.Context, category string, cutoff time.Time) (int64, error) {
var total int64
for batch := 0; batch < constants.NotificationCleanupMaxBatches; batch++ {
result := s.db.WithContext(ctx).Exec(`WITH candidates AS (
SELECT id FROM tb_notification
WHERE category = ? AND created_at < ?
ORDER BY created_at ASC, id ASC
LIMIT ?
)
DELETE FROM tb_notification AS notification
USING candidates
WHERE notification.id = candidates.id`, category, cutoff, constants.NotificationCleanupBatchSize)
if result.Error != nil {
return total, errors.Wrap(errors.CodeDatabaseError, result.Error, "清理站内通知失败")
}
total += result.RowsAffected
if result.RowsAffected < constants.NotificationCleanupBatchSize {
break
}
}
return total, nil
}