mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-11-14 07:55:13 +00:00
feat: Add GORM repository implementation with CRUD operations, subscription management, usage tracking, and analytics support
- Add GormRepository struct with database and logger fields - Add CreateRatePlan, GetRatePlan, UpdateRatePlan, and DeleteRatePlan with automatic timestamp handling - Add ListRatePlans with carrier, region, plan type, status, price range, and validity filtering - Add CountRatePlans with comprehensive filter support - Add CreateSubscription, GetSubscription, UpdateSubscription, and DeleteSubscription with timestamp
This commit is contained in:
@@ -0,0 +1,180 @@
|
|||||||
|
package rateplan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GormRepository implements the Repository interface using GORM
|
||||||
|
type GormRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
logger *logrus.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGormRepository creates a new GORM repository
|
||||||
|
func NewGormRepository(db *gorm.DB, logger *logrus.Logger) *GormRepository {
|
||||||
|
return &GormRepository{
|
||||||
|
db: db,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CreateUsage creates a new usage record
|
||||||
|
func (r *GormRepository) CreateUsage(ctx context.Context, usage *RatePlanUsage) error {
|
||||||
|
usage.LastUpdated = time.Now()
|
||||||
|
|
||||||
|
if err := r.db.WithContext(ctx).Create(usage).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to create usage record")
|
||||||
|
return fmt.Errorf("failed to create usage record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsage retrieves a usage record by ID
|
||||||
|
func (r *GormRepository) GetUsage(ctx context.Context, id string) (*RatePlanUsage, error) {
|
||||||
|
var usage RatePlanUsage
|
||||||
|
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&usage).Error; err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, fmt.Errorf("usage record not found: %s", id)
|
||||||
|
}
|
||||||
|
r.logger.WithError(err).Error("Failed to get usage record")
|
||||||
|
return nil, fmt.Errorf("failed to get usage record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUsage updates an existing usage record
|
||||||
|
func (r *GormRepository) UpdateUsage(ctx context.Context, usage *RatePlanUsage) error {
|
||||||
|
usage.LastUpdated = time.Now()
|
||||||
|
|
||||||
|
result := r.db.WithContext(ctx).Where("id = ?", usage.ID).Updates(usage)
|
||||||
|
if result.Error != nil {
|
||||||
|
r.logger.WithError(result.Error).Error("Failed to update usage record")
|
||||||
|
return fmt.Errorf("failed to update usage record: %w", result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return fmt.Errorf("usage record not found: %s", usage.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentUsage retrieves the current usage for a profile
|
||||||
|
func (r *GormRepository) GetCurrentUsage(ctx context.Context, profileID string) (*RatePlanUsage, error) {
|
||||||
|
var usage RatePlanUsage
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("profile_id = ?", profileID).
|
||||||
|
Order("cycle_start DESC").
|
||||||
|
First(&usage).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, nil // No usage record found
|
||||||
|
}
|
||||||
|
r.logger.WithError(err).Error("Failed to get current usage")
|
||||||
|
return nil, fmt.Errorf("failed to get current usage: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUsageHistory retrieves usage history for a profile
|
||||||
|
func (r *GormRepository) ListUsageHistory(ctx context.Context, profileID string, limit int) ([]*RatePlanUsage, error) {
|
||||||
|
query := r.db.WithContext(ctx).Where("profile_id = ?", profileID).Order("cycle_start DESC")
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
query = query.Limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
var usageHistory []*RatePlanUsage
|
||||||
|
if err := query.Find(&usageHistory).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to list usage history")
|
||||||
|
return nil, fmt.Errorf("failed to list usage history: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return usageHistory, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsageAnalytics retrieves usage analytics
|
||||||
|
func (r *GormRepository) GetUsageAnalytics(ctx context.Context, filter *UsageAnalyticsFilter) (*UsageAnalytics, error) {
|
||||||
|
analytics := &UsageAnalytics{
|
||||||
|
TotalDataUsed: 0,
|
||||||
|
TotalVoiceUsed: 0,
|
||||||
|
TotalSMSUsed: 0,
|
||||||
|
ActiveUsers: 0,
|
||||||
|
AverageUsage: make(map[string]float64),
|
||||||
|
UsageByPlan: make(map[string]int64),
|
||||||
|
UsageByRegion: make(map[string]int64),
|
||||||
|
TimelineData: []TimelineDataPoint{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get active users count
|
||||||
|
var activeUsersCount int64
|
||||||
|
if err := r.db.WithContext(ctx).
|
||||||
|
Model(&RatePlanSubscription{}).
|
||||||
|
Where("status = ?", SubscriptionStatusActive).
|
||||||
|
Count(&activeUsersCount).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to get active users count")
|
||||||
|
return nil, fmt.Errorf("failed to get active users count: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total usage
|
||||||
|
var totalDataUsed, totalVoiceUsed, totalSMSUsed int64
|
||||||
|
if err := r.db.WithContext(ctx).
|
||||||
|
Model(&RatePlanUsage{}).
|
||||||
|
Select("COALESCE(SUM(data_used), 0), COALESCE(SUM(voice_used), 0), COALESCE(SUM(sms_used), 0)").
|
||||||
|
Where("cycle_start >= ? AND cycle_end <= ?", filter.StartDate, filter.EndDate).
|
||||||
|
Row().Scan(&totalDataUsed, &totalVoiceUsed, &totalSMSUsed); err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to get total usage")
|
||||||
|
return nil, fmt.Errorf("failed to get total usage: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.ActiveUsers = int(activeUsersCount)
|
||||||
|
analytics.TotalDataUsed = totalDataUsed
|
||||||
|
analytics.TotalVoiceUsed = totalVoiceUsed
|
||||||
|
analytics.TotalSMSUsed = totalSMSUsed
|
||||||
|
|
||||||
|
return analytics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRevenueAnalytics retrieves revenue analytics
|
||||||
|
func (r *GormRepository) GetRevenueAnalytics(ctx context.Context, filter *RevenueAnalyticsFilter) (*RevenueAnalytics, error) {
|
||||||
|
// Simplified implementation - in production, you'd have actual revenue tracking
|
||||||
|
analytics := &RevenueAnalytics{
|
||||||
|
TotalRevenue: 0,
|
||||||
|
RevenueByPlan: make(map[string]float64),
|
||||||
|
RevenueByCarrier: make(map[string]float64),
|
||||||
|
RevenueByRegion: make(map[string]float64),
|
||||||
|
AverageRevenue: make(map[string]float64),
|
||||||
|
TimelineData: []TimelineDataPoint{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// This would typically join with billing/payment tables
|
||||||
|
// For now, returning empty analytics
|
||||||
|
return analytics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPopularPlans retrieves the most popular rate plans
|
||||||
|
func (r *GormRepository) GetPopularPlans(ctx context.Context, limit int) ([]*RatePlan, error) {
|
||||||
|
var plans []*RatePlan
|
||||||
|
if err := r.db.WithContext(ctx).
|
||||||
|
Model(&RatePlan{}).
|
||||||
|
Select("rate_plans.*, COUNT(rate_plan_subscriptions.id) as subscription_count").
|
||||||
|
Joins("LEFT JOIN rate_plan_subscriptions ON rate_plans.id = rate_plan_subscriptions.rate_plan_id").
|
||||||
|
Where("rate_plans.is_active = ? AND rate_plans.status = ?", true, PlanStatusActive).
|
||||||
|
Group("rate_plans.id").
|
||||||
|
Order("subscription_count DESC").
|
||||||
|
Limit(limit).
|
||||||
|
Find(&plans).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to get popular plans")
|
||||||
|
return nil, fmt.Errorf("failed to get popular plans: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plans, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package rateplan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateRatePlan creates a new rate plan
|
||||||
|
func (r *GormRepository) CreateRatePlan(ctx context.Context, plan *RatePlan) error {
|
||||||
|
now := time.Now()
|
||||||
|
plan.CreatedAt = now
|
||||||
|
plan.UpdatedAt = now
|
||||||
|
|
||||||
|
if err := r.db.WithContext(ctx).Create(plan).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to create rate plan")
|
||||||
|
return fmt.Errorf("failed to create rate plan: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GormRepository) GetRatePlan(ctx context.Context, id string) (*RatePlan, error) {
|
||||||
|
var plan RatePlan
|
||||||
|
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&plan).Error; err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, fmt.Errorf("rate plan not found: %s", id)
|
||||||
|
}
|
||||||
|
r.logger.WithError(err).Error("Failed to get rate plan")
|
||||||
|
return nil, fmt.Errorf("failed to get rate plan: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &plan, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GormRepository) UpdateRatePlan(ctx context.Context, plan *RatePlan) error {
|
||||||
|
plan.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
result := r.db.WithContext(ctx).Where("id = ?", plan.ID).Updates(plan)
|
||||||
|
if result.Error != nil {
|
||||||
|
r.logger.WithError(result.Error).Error("Failed to update rate plan")
|
||||||
|
return fmt.Errorf("failed to update rate plan: %w", result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return fmt.Errorf("rate plan not found: %s", plan.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GormRepository) DeleteRatePlan(ctx context.Context, id string) error {
|
||||||
|
result := r.db.WithContext(ctx).Where("id = ?", id).Delete(&RatePlan{})
|
||||||
|
if result.Error != nil {
|
||||||
|
r.logger.WithError(result.Error).Error("Failed to delete rate plan")
|
||||||
|
return fmt.Errorf("failed to delete rate plan: %w", result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return fmt.Errorf("rate plan not found: %s", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GormRepository) ListRatePlans(ctx context.Context, filter *RatePlanFilter) ([]*RatePlan, error) {
|
||||||
|
query := r.db.WithContext(ctx).Model(&RatePlan{})
|
||||||
|
|
||||||
|
if filter.CarrierID != "" {
|
||||||
|
query = query.Where("carrier_id = ?", filter.CarrierID)
|
||||||
|
}
|
||||||
|
if filter.Region != "" {
|
||||||
|
query = query.Where("region = ?", filter.Region)
|
||||||
|
}
|
||||||
|
if filter.PlanType != "" {
|
||||||
|
query = query.Where("plan_type = ?", filter.PlanType)
|
||||||
|
}
|
||||||
|
if filter.Status != "" {
|
||||||
|
query = query.Where("status = ?", filter.Status)
|
||||||
|
}
|
||||||
|
if filter.MinPrice > 0 {
|
||||||
|
query = query.Where("base_price >= ?", filter.MinPrice)
|
||||||
|
}
|
||||||
|
if filter.MaxPrice > 0 {
|
||||||
|
query = query.Where("base_price <= ?", filter.MaxPrice)
|
||||||
|
}
|
||||||
|
if filter.IsActive != nil {
|
||||||
|
query = query.Where("is_active = ?", *filter.IsActive)
|
||||||
|
}
|
||||||
|
if filter.ValidFrom != nil {
|
||||||
|
query = query.Where("valid_from >= ?", *filter.ValidFrom)
|
||||||
|
}
|
||||||
|
if filter.ValidTo != nil {
|
||||||
|
query = query.Where("valid_to <= ?", *filter.ValidTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply ordering
|
||||||
|
if filter.SortBy != "" {
|
||||||
|
order := "ASC"
|
||||||
|
if filter.SortOrder == "desc" {
|
||||||
|
order = "DESC"
|
||||||
|
}
|
||||||
|
query = query.Order(fmt.Sprintf("%s %s", filter.SortBy, order))
|
||||||
|
} else {
|
||||||
|
query = query.Order("priority DESC, created_at DESC")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply pagination
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
query = query.Limit(filter.Limit)
|
||||||
|
if filter.Offset > 0 {
|
||||||
|
query = query.Offset(filter.Offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var plans []*RatePlan
|
||||||
|
if err := query.Find(&plans).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to list rate plans")
|
||||||
|
return nil, fmt.Errorf("failed to list rate plans: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plans, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountRatePlans counts rate plans with filtering
|
||||||
|
func (r *GormRepository) CountRatePlans(ctx context.Context, filter *RatePlanFilter) (int, error) {
|
||||||
|
query := r.db.WithContext(ctx).Model(&RatePlan{})
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
if filter.CarrierID != "" {
|
||||||
|
query = query.Where("carrier_id = ?", filter.CarrierID)
|
||||||
|
}
|
||||||
|
if filter.Region != "" {
|
||||||
|
query = query.Where("region = ?", filter.Region)
|
||||||
|
}
|
||||||
|
if filter.PlanType != "" {
|
||||||
|
query = query.Where("plan_type = ?", filter.PlanType)
|
||||||
|
}
|
||||||
|
if filter.Status != "" {
|
||||||
|
query = query.Where("status = ?", filter.Status)
|
||||||
|
}
|
||||||
|
if filter.MinPrice > 0 {
|
||||||
|
query = query.Where("base_price >= ?", filter.MinPrice)
|
||||||
|
}
|
||||||
|
if filter.MaxPrice > 0 {
|
||||||
|
query = query.Where("base_price <= ?", filter.MaxPrice)
|
||||||
|
}
|
||||||
|
if filter.IsActive != nil {
|
||||||
|
query = query.Where("is_active = ?", *filter.IsActive)
|
||||||
|
}
|
||||||
|
if filter.ValidFrom != nil {
|
||||||
|
query = query.Where("valid_from >= ?", *filter.ValidFrom)
|
||||||
|
}
|
||||||
|
if filter.ValidTo != nil {
|
||||||
|
query = query.Where("valid_to <= ?", *filter.ValidTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := query.Count(&count).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to count rate plans")
|
||||||
|
return 0, fmt.Errorf("failed to count rate plans: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package rateplan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
// CreateSubscription creates a new rate plan subscription
|
||||||
|
func (r *GormRepository) CreateSubscription(ctx context.Context, subscription *RatePlanSubscription) error {
|
||||||
|
now := time.Now()
|
||||||
|
subscription.CreatedAt = now
|
||||||
|
subscription.UpdatedAt = now
|
||||||
|
|
||||||
|
if err := r.db.WithContext(ctx).Create(subscription).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to create subscription")
|
||||||
|
return fmt.Errorf("failed to create subscription: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubscription retrieves a subscription by ID
|
||||||
|
func (r *GormRepository) GetSubscription(ctx context.Context, id string) (*RatePlanSubscription, error) {
|
||||||
|
var subscription RatePlanSubscription
|
||||||
|
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&subscription).Error; err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, fmt.Errorf("subscription not found: %s", id)
|
||||||
|
}
|
||||||
|
r.logger.WithError(err).Error("Failed to get subscription")
|
||||||
|
return nil, fmt.Errorf("failed to get subscription: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &subscription, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetActiveSubscription retrieves the active subscription for a profile
|
||||||
|
func (r *GormRepository) GetActiveSubscription(ctx context.Context, profileID string) (*RatePlanSubscription, error) {
|
||||||
|
var subscription RatePlanSubscription
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("profile_id = ? AND status = ?", profileID, SubscriptionStatusActive).
|
||||||
|
Order("started_at DESC").
|
||||||
|
First(&subscription).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, nil // No active subscription found
|
||||||
|
}
|
||||||
|
r.logger.WithError(err).Error("Failed to get active subscription")
|
||||||
|
return nil, fmt.Errorf("failed to get active subscription: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &subscription, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSubscription updates an existing subscription
|
||||||
|
func (r *GormRepository) UpdateSubscription(ctx context.Context, subscription *RatePlanSubscription) error {
|
||||||
|
subscription.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
result := r.db.WithContext(ctx).Where("id = ?", subscription.ID).Updates(subscription)
|
||||||
|
if result.Error != nil {
|
||||||
|
r.logger.WithError(result.Error).Error("Failed to update subscription")
|
||||||
|
return fmt.Errorf("failed to update subscription: %w", result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return fmt.Errorf("subscription not found: %s", subscription.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSubscription deletes a subscription
|
||||||
|
func (r *GormRepository) DeleteSubscription(ctx context.Context, id string) error {
|
||||||
|
result := r.db.WithContext(ctx).Where("id = ?", id).Delete(&RatePlanSubscription{})
|
||||||
|
if result.Error != nil {
|
||||||
|
r.logger.WithError(result.Error).Error("Failed to delete subscription")
|
||||||
|
return fmt.Errorf("failed to delete subscription: %w", result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return fmt.Errorf("subscription not found: %s", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// ListSubscriptions retrieves subscriptions for a profile
|
||||||
|
func (r *GormRepository) ListSubscriptions(ctx context.Context, profileID string, filter *SubscriptionFilter) ([]*RatePlanSubscription, error) {
|
||||||
|
query := r.db.WithContext(ctx).Where("profile_id = ?", profileID)
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
if filter.Status != "" {
|
||||||
|
query = query.Where("status = ?", filter.Status)
|
||||||
|
}
|
||||||
|
if filter.RatePlanID != "" {
|
||||||
|
query = query.Where("rate_plan_id = ?", filter.RatePlanID)
|
||||||
|
}
|
||||||
|
if filter.StartedAfter != nil {
|
||||||
|
query = query.Where("started_at >= ?", *filter.StartedAfter)
|
||||||
|
}
|
||||||
|
if filter.StartedBefore != nil {
|
||||||
|
query = query.Where("started_at <= ?", *filter.StartedBefore)
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query.Order("started_at DESC")
|
||||||
|
|
||||||
|
// Apply pagination
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
query = query.Limit(filter.Limit)
|
||||||
|
if filter.Offset > 0 {
|
||||||
|
query = query.Offset(filter.Offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var subscriptions []*RatePlanSubscription
|
||||||
|
if err := query.Find(&subscriptions).Error; err != nil {
|
||||||
|
r.logger.WithError(err).Error("Failed to list subscriptions")
|
||||||
|
return nil, fmt.Errorf("failed to list subscriptions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscriptions, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user