diff --git a/internal/application/subscription/usecases/listusersubscriptions.go b/internal/application/subscription/usecases/listusersubscriptions.go index f13a946..b9b4d77 100644 --- a/internal/application/subscription/usecases/listusersubscriptions.go +++ b/internal/application/subscription/usecases/listusersubscriptions.go @@ -38,10 +38,16 @@ type ListUserSubscriptionsUseCase struct { planRepo subscription.PlanRepository userRepo user.Repository onlineDeviceCounter OnlineDeviceCounter // optional, nil-safe + quotaService QuotaService // optional, nil-safe logger logger.Interface baseURL string } +// SetQuotaService sets the quota service for data usage reporting (optional). +func (uc *ListUserSubscriptionsUseCase) SetQuotaService(qs QuotaService) { + uc.quotaService = qs +} + func NewListUserSubscriptionsUseCase( subscriptionRepo subscription.SubscriptionRepository, planRepo subscription.PlanRepository, @@ -169,6 +175,15 @@ func (uc *ListUserSubscriptionsUseCase) Execute(ctx context.Context, query ListU if count, ok := onlineCounts[sub.ID()]; ok { opts = append(opts, dto.WithOnlineDeviceCount(count)) } + // Set data usage from QuotaService + if uc.quotaService != nil && plan != nil { + quota, err := uc.quotaService.GetSubscriptionQuotaPreloaded(ctx, sub, plan) + if err != nil { + uc.logger.Warnw("failed to get subscription quota", "error", err, "subscription_id", sub.ID()) + } else if quota != nil { + opts = append(opts, dto.WithDataUsage(quota.UsedBytes, quota.LimitBytes)) + } + } result := dto.ToSubscriptionDTO(sub, plan, subscriptionUser, uc.baseURL, opts...) dtos = append(dtos, result) diff --git a/internal/application/subscription/usecases/quotaservice.go b/internal/application/subscription/usecases/quotaservice.go index fc64f9c..bc33db1 100644 --- a/internal/application/subscription/usecases/quotaservice.go +++ b/internal/application/subscription/usecases/quotaservice.go @@ -31,6 +31,10 @@ type QuotaService interface { // GetSubscriptionQuota returns the quota usage for a single subscription. GetSubscriptionQuota(ctx context.Context, subscriptionID uint) (*QuotaCheckResult, error) + // GetSubscriptionQuotaPreloaded returns quota usage using pre-fetched subscription and plan. + // This avoids redundant DB lookups when subscription and plan are already available (e.g., in list endpoints). + GetSubscriptionQuotaPreloaded(ctx context.Context, sub *subscription.Subscription, plan *subscription.Plan) (*QuotaCheckResult, error) + // GetUserForwardQuota returns quota usage for all Forward-type subscriptions of a user. GetUserForwardQuota(ctx context.Context, userID uint) ([]*QuotaCheckResult, error) @@ -119,6 +123,19 @@ func (s *QuotaServiceImpl) GetSubscriptionQuota(ctx context.Context, subscriptio return s.buildQuotaResult(ctx, sub, plan) } +// GetSubscriptionQuotaPreloaded returns quota usage using pre-fetched subscription and plan. +func (s *QuotaServiceImpl) GetSubscriptionQuotaPreloaded(ctx context.Context, sub *subscription.Subscription, plan *subscription.Plan) (*QuotaCheckResult, error) { + if sub == nil || plan == nil { + return nil, nil + } + + if sub.Status() == vo.StatusSuspended { + return nil, nil + } + + return s.buildQuotaResult(ctx, sub, plan) +} + // GetUserForwardQuota returns quota usage for all Forward-type subscriptions of a user. func (s *QuotaServiceImpl) GetUserForwardQuota(ctx context.Context, userID uint) ([]*QuotaCheckResult, error) { return s.getUserQuotaByPlanType(ctx, userID, vo.PlanTypeForward) diff --git a/internal/interfaces/http/wire_services.go b/internal/interfaces/http/wire_services.go index 843b453..16ce2a4 100644 --- a/internal/interfaces/http/wire_services.go +++ b/internal/interfaces/http/wire_services.go @@ -1379,6 +1379,7 @@ func (c *Container) initRemainingHandlers() { // Set QuotaService on use cases that need real-time usage data ucs.getSubscriptionUC.SetQuotaService(ucs.quotaService) + ucs.listUserSubscriptionsUC.SetQuotaService(ucs.quotaService) ucs.updateSubscriptionUC.SetQuotaService(ucs.quotaService) // Initialize forward quota middleware with QuotaService