// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storagedailyusage/get.go package storagedailyusage import ( "context" "fmt" "time" "github.com/gocql/gocql" "go.uber.org/zap" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage" ) func (impl *storageDailyUsageRepositoryImpl) GetByUserAndDay(ctx context.Context, userID gocql.UUID, usageDay time.Time) (*storagedailyusage.StorageDailyUsage, error) { // Ensure usage day is truncated to date only usageDay = usageDay.Truncate(24 * time.Hour) var ( resultUserID gocql.UUID resultUsageDay time.Time totalBytes int64 totalAddBytes int64 totalRemoveBytes int64 ) query := `SELECT user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes FROM maplefile.storage_daily_usage_by_user_id_with_asc_usage_day WHERE user_id = ? AND usage_day = ?` err := impl.Session.Query(query, userID, usageDay).WithContext(ctx).Scan( &resultUserID, &resultUsageDay, &totalBytes, &totalAddBytes, &totalRemoveBytes) if err == gocql.ErrNotFound { return nil, nil } if err != nil { impl.Logger.Error("failed to get storage daily usage", zap.Error(err)) return nil, fmt.Errorf("failed to get storage daily usage: %w", err) } usage := &storagedailyusage.StorageDailyUsage{ UserID: resultUserID, UsageDay: resultUsageDay, TotalBytes: totalBytes, TotalAddBytes: totalAddBytes, TotalRemoveBytes: totalRemoveBytes, } return usage, nil } func (impl *storageDailyUsageRepositoryImpl) GetByUserDateRange(ctx context.Context, userID gocql.UUID, startDay, endDay time.Time) ([]*storagedailyusage.StorageDailyUsage, error) { // Ensure dates are truncated to date only startDay = startDay.Truncate(24 * time.Hour) endDay = endDay.Truncate(24 * time.Hour) query := `SELECT user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes FROM maplefile.storage_daily_usage_by_user_id_with_asc_usage_day WHERE user_id = ? AND usage_day >= ? AND usage_day <= ?` iter := impl.Session.Query(query, userID, startDay, endDay).WithContext(ctx).Iter() var usages []*storagedailyusage.StorageDailyUsage var ( resultUserID gocql.UUID resultUsageDay time.Time totalBytes int64 totalAddBytes int64 totalRemoveBytes int64 ) for iter.Scan(&resultUserID, &resultUsageDay, &totalBytes, &totalAddBytes, &totalRemoveBytes) { usage := &storagedailyusage.StorageDailyUsage{ UserID: resultUserID, UsageDay: resultUsageDay, TotalBytes: totalBytes, TotalAddBytes: totalAddBytes, TotalRemoveBytes: totalRemoveBytes, } usages = append(usages, usage) } if err := iter.Close(); err != nil { impl.Logger.Error("failed to get storage daily usage by date range", zap.Error(err)) return nil, fmt.Errorf("failed to get storage daily usage: %w", err) } return usages, nil } // GetLast7DaysTrend retrieves the last 7 days of storage usage and calculates trends func (impl *storageDailyUsageRepositoryImpl) GetLast7DaysTrend(ctx context.Context, userID gocql.UUID) (*storagedailyusage.StorageUsageTrend, error) { endDay := time.Now().Truncate(24 * time.Hour) startDay := endDay.Add(-6 * 24 * time.Hour) // 7 days including today usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay) if err != nil { return nil, err } return impl.calculateTrend(userID, startDay, endDay, usages), nil } // GetMonthlyTrend retrieves usage trend for a specific month func (impl *storageDailyUsageRepositoryImpl) GetMonthlyTrend(ctx context.Context, userID gocql.UUID, year int, month time.Month) (*storagedailyusage.StorageUsageTrend, error) { startDay := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) endDay := startDay.AddDate(0, 1, -1) // Last day of the month usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay) if err != nil { return nil, err } return impl.calculateTrend(userID, startDay, endDay, usages), nil } // GetYearlyTrend retrieves usage trend for a specific year func (impl *storageDailyUsageRepositoryImpl) GetYearlyTrend(ctx context.Context, userID gocql.UUID, year int) (*storagedailyusage.StorageUsageTrend, error) { startDay := time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC) endDay := time.Date(year, 12, 31, 0, 0, 0, 0, time.UTC) usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay) if err != nil { return nil, err } return impl.calculateTrend(userID, startDay, endDay, usages), nil } // GetCurrentMonthUsage gets the current month's usage summary func (impl *storageDailyUsageRepositoryImpl) GetCurrentMonthUsage(ctx context.Context, userID gocql.UUID) (*storagedailyusage.StorageUsageSummary, error) { now := time.Now() startDay := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC) endDay := now.Truncate(24 * time.Hour) usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay) if err != nil { return nil, err } return impl.calculateSummary(userID, "month", startDay, endDay, usages), nil } // GetCurrentYearUsage gets the current year's usage summary func (impl *storageDailyUsageRepositoryImpl) GetCurrentYearUsage(ctx context.Context, userID gocql.UUID) (*storagedailyusage.StorageUsageSummary, error) { now := time.Now() startDay := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC) endDay := now.Truncate(24 * time.Hour) usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay) if err != nil { return nil, err } return impl.calculateSummary(userID, "year", startDay, endDay, usages), nil } // Helper methods func (impl *storageDailyUsageRepositoryImpl) calculateTrend(userID gocql.UUID, startDay, endDay time.Time, usages []*storagedailyusage.StorageDailyUsage) *storagedailyusage.StorageUsageTrend { trend := &storagedailyusage.StorageUsageTrend{ UserID: userID, StartDate: startDay, EndDate: endDay, DailyUsages: usages, } if len(usages) == 0 { return trend } var peakDay time.Time var peakBytes int64 for _, usage := range usages { trend.TotalAdded += usage.TotalAddBytes trend.TotalRemoved += usage.TotalRemoveBytes if usage.TotalBytes > peakBytes { peakBytes = usage.TotalBytes peakDay = usage.UsageDay } } trend.NetChange = trend.TotalAdded - trend.TotalRemoved if len(usages) > 0 { trend.AverageDailyAdd = trend.TotalAdded / int64(len(usages)) trend.PeakUsageDay = &peakDay trend.PeakUsageBytes = peakBytes } return trend } func (impl *storageDailyUsageRepositoryImpl) calculateSummary(userID gocql.UUID, period string, startDay, endDay time.Time, usages []*storagedailyusage.StorageDailyUsage) *storagedailyusage.StorageUsageSummary { summary := &storagedailyusage.StorageUsageSummary{ UserID: userID, Period: period, StartDate: startDay, EndDate: endDay, DaysWithData: len(usages), } if len(usages) == 0 { return summary } // Get the most recent usage as current summary.CurrentUsage = usages[len(usages)-1].TotalBytes for _, usage := range usages { summary.TotalAdded += usage.TotalAddBytes summary.TotalRemoved += usage.TotalRemoveBytes } summary.NetChange = summary.TotalAdded - summary.TotalRemoved return summary }