package site import ( "context" "fmt" "time" "go.uber.org/zap" domainsite "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/site" ) // ResetMonthlyUsageUseCase handles resetting monthly usage counters for all sites (for billing cycles) type ResetMonthlyUsageUseCase struct { siteRepo domainsite.Repository logger *zap.Logger } // ProvideResetMonthlyUsageUseCase creates a new ResetMonthlyUsageUseCase func ProvideResetMonthlyUsageUseCase( siteRepo domainsite.Repository, logger *zap.Logger, ) *ResetMonthlyUsageUseCase { return &ResetMonthlyUsageUseCase{ siteRepo: siteRepo, logger: logger.Named("reset-monthly-usage-usecase"), } } // ResetUsageOutput is the output after resetting usage counters type ResetUsageOutput struct { ProcessedSites int `json:"processed_sites"` ResetCount int `json:"reset_count"` FailedCount int `json:"failed_count"` ProcessedAt time.Time `json:"processed_at"` } // Execute resets monthly usage counters for all sites (for billing cycles) func (uc *ResetMonthlyUsageUseCase) Execute(ctx context.Context) (*ResetUsageOutput, error) { uc.logger.Info("starting monthly usage counter reset for all sites") startTime := time.Now() processedSites := 0 resetCount := 0 failedCount := 0 // Pagination settings const pageSize = 100 var pageState []byte // Iterate through all sites using pagination for { // Get a batch of sites sites, nextPageState, err := uc.siteRepo.GetAllSitesForUsageReset(ctx, pageSize, pageState) if err != nil { uc.logger.Error("failed to get sites for usage reset", zap.Error(err)) return nil, fmt.Errorf("failed to get sites: %w", err) } // Process each site in the batch for _, site := range sites { processedSites++ // Check if usage needs to be reset (monthly billing cycle) now := time.Now() needsReset := false // Check if it's been a month since last reset if site.LastResetAt.AddDate(0, 1, 0).Before(now) { needsReset = true } if !needsReset { uc.logger.Debug("site usage not due for reset", zap.String("site_id", site.ID.String()), zap.String("domain", site.Domain), zap.Time("last_reset_at", site.LastResetAt)) continue } // Reset the usage counters site.ResetMonthlyUsage() // Update the site in database if err := uc.siteRepo.UpdateUsage(ctx, site); err != nil { uc.logger.Error("failed to reset usage for site", zap.String("site_id", site.ID.String()), zap.String("domain", site.Domain), zap.Error(err)) failedCount++ continue } resetCount++ uc.logger.Debug("reset usage for site", zap.String("site_id", site.ID.String()), zap.String("domain", site.Domain), zap.Time("last_reset_at", site.LastResetAt)) } // Check if there are more pages if len(nextPageState) == 0 { break } pageState = nextPageState uc.logger.Info("processed batch of sites", zap.Int("batch_size", len(sites)), zap.Int("total_processed", processedSites), zap.Int("reset_count", resetCount), zap.Int("failed_count", failedCount)) } uc.logger.Info("monthly usage counter reset completed", zap.Int("processed_sites", processedSites), zap.Int("reset_count", resetCount), zap.Int("failed_count", failedCount), zap.Duration("duration", time.Since(startTime))) return &ResetUsageOutput{ ProcessedSites: processedSites, ResetCount: resetCount, FailedCount: failedCount, ProcessedAt: time.Now(), }, nil }