mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-11-15 08:25:12 +00:00
refactor: Rename service package to services and move repository implementations to repository package
- Rename internal/service to internal/services in carrier-connector - Update service imports in selection_integration.go and smdp_integration.go - Move currency repository files from internal/currency to internal/repository - Move rate plan repository files from internal/rateplan to internal/repository - Update package declarations and imports for currency domain types - Add currency package imports
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GormRepository implements the currency repository using GORM
|
||||
type GormRepository struct {
|
||||
db *gorm.DB
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// NewGormRepository creates a new GORM currency repository
|
||||
func NewGormRepository(db *gorm.DB, logger *logrus.Logger) *GormRepository {
|
||||
return &GormRepository{
|
||||
db: db,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCurrency creates a new currency
|
||||
func (r *GormRepository) CreateCurrency(ctx context.Context, currency *currency.Currency) error {
|
||||
model := r.currencyToModel(currency)
|
||||
|
||||
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to create currency")
|
||||
return fmt.Errorf("failed to create currency: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrency retrieves a currency by code
|
||||
func (r *GormRepository) GetCurrency(ctx context.Context, code string) (*currency.Currency, error) {
|
||||
var model currency.CurrencyModel
|
||||
if err := r.db.WithContext(ctx).Where("code = ?", code).First(&model).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("currency not found: %s", code)
|
||||
}
|
||||
r.logger.WithError(err).Error("Failed to get currency")
|
||||
return nil, fmt.Errorf("failed to get currency: %w", err)
|
||||
}
|
||||
|
||||
return r.modelToCurrency(&model)
|
||||
}
|
||||
|
||||
// UpdateCurrency updates an existing currency
|
||||
func (r *GormRepository) UpdateCurrency(ctx context.Context, currency *currency.Currency) error {
|
||||
model := r.currencyToModel(currency)
|
||||
|
||||
if err := r.db.WithContext(ctx).Save(model).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to update currency")
|
||||
return fmt.Errorf("failed to update currency: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteCurrency deletes a currency
|
||||
func (r *GormRepository) DeleteCurrency(ctx context.Context, code string) error {
|
||||
if err := r.db.WithContext(ctx).Delete(¤cy.CurrencyModel{}, "code = ?", code).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to delete currency")
|
||||
return fmt.Errorf("failed to delete currency: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListCurrencies retrieves currencies based on filter
|
||||
func (r *GormRepository) ListCurrencies(ctx context.Context, filter *currency.CurrencyFilter) ([]*currency.Currency, error) {
|
||||
query := r.db.WithContext(ctx).Model(¤cy.CurrencyModel{})
|
||||
|
||||
// Apply filters
|
||||
if filter.IsActive != nil {
|
||||
query = query.Where("is_active = ?", *filter.IsActive)
|
||||
}
|
||||
if filter.Region != "" {
|
||||
query = query.Where("supported_regions LIKE ?", "%"+filter.Region+"%")
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
if filter.SortBy != "" {
|
||||
order := filter.SortBy
|
||||
if filter.SortOrder == "desc" {
|
||||
order += " DESC"
|
||||
}
|
||||
query = query.Order(order)
|
||||
} else {
|
||||
query = query.Order("code ASC")
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
if filter.Limit > 0 {
|
||||
query = query.Limit(filter.Limit)
|
||||
}
|
||||
if filter.Offset > 0 {
|
||||
query = query.Offset(filter.Offset)
|
||||
}
|
||||
|
||||
var models []currency.CurrencyModel
|
||||
if err := query.Find(&models).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to list currencies")
|
||||
return nil, fmt.Errorf("failed to list currencies: %w", err)
|
||||
}
|
||||
|
||||
currencies := make([]*currency.Currency, 0, len(models))
|
||||
for _, model := range models {
|
||||
curr, err := r.modelToCurrency(&model)
|
||||
if err != nil {
|
||||
r.logger.WithError(err).Error("Failed to convert currency model")
|
||||
continue
|
||||
}
|
||||
currencies = append(currencies, curr)
|
||||
}
|
||||
|
||||
return currencies, nil
|
||||
}
|
||||
|
||||
// CountCurrencies counts currencies based on filter
|
||||
func (r *GormRepository) CountCurrencies(ctx context.Context, filter *currency.CurrencyFilter) (int, error) {
|
||||
query := r.db.WithContext(ctx).Model(¤cy.CurrencyModel{})
|
||||
|
||||
// Apply filters
|
||||
if filter.IsActive != nil {
|
||||
query = query.Where("is_active = ?", *filter.IsActive)
|
||||
}
|
||||
if filter.Region != "" {
|
||||
query = query.Where("supported_regions LIKE ?", "%"+filter.Region+"%")
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := query.Count(&count).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to count currencies")
|
||||
return 0, fmt.Errorf("failed to count currencies: %w", err)
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
// CreateExchangeRate creates a new exchange rate
|
||||
func (r *GormRepository) CreateExchangeRate(ctx context.Context, rate *currency.ExchangeRate) error {
|
||||
model := r.exchangeRateToModel(rate)
|
||||
|
||||
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to create exchange rate")
|
||||
return fmt.Errorf("failed to create exchange rate: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExchangeRate retrieves an exchange rate by ID
|
||||
func (r *GormRepository) GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*currency.ExchangeRate, error) {
|
||||
var model currency.ExchangeRateModel
|
||||
if err := r.db.WithContext(ctx).Where("from_currency = ? AND to_currency = ?", fromCurrency, toCurrency).First(&model).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("exchange rate not found: %s to %s", fromCurrency, toCurrency)
|
||||
}
|
||||
r.logger.WithError(err).Error("Failed to get exchange rate")
|
||||
return nil, fmt.Errorf("failed to get exchange rate: %w", err)
|
||||
}
|
||||
|
||||
return r.modelToExchangeRate(&model)
|
||||
}
|
||||
|
||||
// UpdateExchangeRate updates an existing exchange rate
|
||||
func (r *GormRepository) UpdateExchangeRate(ctx context.Context, rate *currency.ExchangeRate) error {
|
||||
model := r.exchangeRateToModel(rate)
|
||||
|
||||
if err := r.db.WithContext(ctx).Save(model).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to update exchange rate")
|
||||
return fmt.Errorf("failed to update exchange rate: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteExchangeRate deletes an exchange rate
|
||||
func (r *GormRepository) DeleteExchangeRate(ctx context.Context, id string) error {
|
||||
if err := r.db.WithContext(ctx).Delete(¤cy.ExchangeRateModel{}, "id = ?", id).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to delete exchange rate")
|
||||
return fmt.Errorf("failed to delete exchange rate: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
|
||||
)
|
||||
|
||||
// currencyToModel converts currency domain model to database model
|
||||
func (r *GormRepository) currencyToModel(currency *currency.Currency) *currency.CurrencyModel {
|
||||
return ¤cy.CurrencyModel{
|
||||
Code: currency.Code,
|
||||
Name: currency.Name,
|
||||
Symbol: currency.Symbol,
|
||||
DecimalPlaces: currency.DecimalPlaces,
|
||||
IsActive: currency.IsActive,
|
||||
SupportedRegions: strings.Join(currency.SupportedRegions, ","),
|
||||
CreatedAt: currency.CreatedAt,
|
||||
UpdatedAt: currency.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// modelToCurrency converts database model to currency domain model
|
||||
func (r *GormRepository) modelToCurrency(model *currency.CurrencyModel) (*currency.Currency, error) {
|
||||
var regions []string
|
||||
if model.SupportedRegions != "" {
|
||||
regions = strings.Split(model.SupportedRegions, ",")
|
||||
}
|
||||
|
||||
return ¤cy.Currency{
|
||||
Code: model.Code,
|
||||
Name: model.Name,
|
||||
Symbol: model.Symbol,
|
||||
DecimalPlaces: model.DecimalPlaces,
|
||||
IsActive: model.IsActive,
|
||||
SupportedRegions: regions,
|
||||
CreatedAt: model.CreatedAt,
|
||||
UpdatedAt: model.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// exchangeRateToModel converts exchange rate domain model to database model
|
||||
func (r *GormRepository) exchangeRateToModel(rate *currency.ExchangeRate) *currency.ExchangeRateModel {
|
||||
return ¤cy.ExchangeRateModel{
|
||||
ID: rate.ID,
|
||||
FromCurrency: rate.FromCurrency,
|
||||
ToCurrency: rate.ToCurrency,
|
||||
Rate: rate.Rate,
|
||||
Source: rate.Source,
|
||||
ValidFrom: rate.ValidFrom,
|
||||
ValidTo: rate.ValidTo,
|
||||
IsActive: rate.IsActive,
|
||||
CreatedAt: rate.CreatedAt,
|
||||
UpdatedAt: rate.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// modelToExchangeRate converts database model to exchange rate domain model
|
||||
func (r *GormRepository) modelToExchangeRate(model *currency.ExchangeRateModel) (*currency.ExchangeRate, error) {
|
||||
return ¤cy.ExchangeRate{
|
||||
ID: model.ID,
|
||||
FromCurrency: model.FromCurrency,
|
||||
ToCurrency: model.ToCurrency,
|
||||
Rate: model.Rate,
|
||||
Source: model.Source,
|
||||
ValidFrom: model.ValidFrom,
|
||||
ValidTo: model.ValidTo,
|
||||
IsActive: model.IsActive,
|
||||
CreatedAt: model.CreatedAt,
|
||||
UpdatedAt: model.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// transactionToModel converts transaction domain model to database model
|
||||
func (r *GormRepository) transactionToModel(transaction *currency.Transaction) *currency.TransactionModel {
|
||||
metadata := ""
|
||||
if transaction.Metadata != nil {
|
||||
if data, err := json.Marshal(transaction.Metadata); err == nil {
|
||||
metadata = string(data)
|
||||
}
|
||||
}
|
||||
|
||||
return ¤cy.TransactionModel{
|
||||
ID: transaction.ID,
|
||||
ProfileID: transaction.ProfileID,
|
||||
SubscriptionID: transaction.SubscriptionID,
|
||||
Type: string(transaction.Type),
|
||||
Amount: transaction.Amount,
|
||||
Currency: transaction.Currency,
|
||||
BaseAmount: transaction.BaseAmount,
|
||||
BaseCurrency: transaction.BaseCurrency,
|
||||
ExchangeRate: transaction.ExchangeRate,
|
||||
Description: transaction.Description,
|
||||
Status: string(transaction.Status),
|
||||
Metadata: metadata,
|
||||
CreatedAt: transaction.CreatedAt,
|
||||
UpdatedAt: transaction.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// modelToTransaction converts database model to transaction domain model
|
||||
func (r *GormRepository) modelToTransaction(model *currency.TransactionModel) (*currency.Transaction, error) {
|
||||
var metadata map[string]interface{}
|
||||
if model.Metadata != "" {
|
||||
if err := json.Unmarshal([]byte(model.Metadata), &metadata); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse metadata: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return ¤cy.Transaction{
|
||||
ID: model.ID,
|
||||
ProfileID: model.ProfileID,
|
||||
SubscriptionID: model.SubscriptionID,
|
||||
Type: currency.TransactionType(model.Type),
|
||||
Amount: model.Amount,
|
||||
Currency: model.Currency,
|
||||
BaseAmount: model.BaseAmount,
|
||||
BaseCurrency: model.BaseCurrency,
|
||||
ExchangeRate: model.ExchangeRate,
|
||||
Description: model.Description,
|
||||
Status: currency.TransactionStatus(model.Status),
|
||||
Metadata: metadata,
|
||||
CreatedAt: model.CreatedAt,
|
||||
UpdatedAt: model.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 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,169 @@
|
||||
package repository
|
||||
|
||||
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,125 @@
|
||||
package repository
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
|
||||
)
|
||||
|
||||
// ListExchangeRates retrieves exchange rates based on filter
|
||||
func (r *GormRepository) ListExchangeRates(ctx context.Context, filter *currency.ExchangeRateFilter) ([]*currency.ExchangeRate, error) {
|
||||
query := r.db.WithContext(ctx).Model(¤cy.ExchangeRateModel{})
|
||||
|
||||
// Apply filters
|
||||
if filter.FromCurrency != "" {
|
||||
query = query.Where("from_currency = ?", filter.FromCurrency)
|
||||
}
|
||||
if filter.ToCurrency != "" {
|
||||
query = query.Where("to_currency = ?", filter.ToCurrency)
|
||||
}
|
||||
if filter.Source != "" {
|
||||
query = query.Where("source = ?", filter.Source)
|
||||
}
|
||||
if filter.IsValid != nil {
|
||||
now := time.Now()
|
||||
if *filter.IsValid {
|
||||
query = query.Where("valid_from <= ? AND (valid_to IS NULL OR valid_to >= ?)", now, now)
|
||||
} else {
|
||||
query = query.Where("valid_to < ?", now)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
if filter.SortBy != "" {
|
||||
order := filter.SortBy
|
||||
if filter.SortOrder == "desc" {
|
||||
order += " DESC"
|
||||
}
|
||||
query = query.Order(order)
|
||||
} else {
|
||||
query = query.Order("from_currency, to_currency, valid_from DESC")
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
if filter.Limit > 0 {
|
||||
query = query.Limit(filter.Limit)
|
||||
}
|
||||
if filter.Offset > 0 {
|
||||
query = query.Offset(filter.Offset)
|
||||
}
|
||||
|
||||
var models []currency.ExchangeRateModel
|
||||
if err := query.Find(&models).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to list exchange rates")
|
||||
return nil, fmt.Errorf("failed to list exchange rates: %w", err)
|
||||
}
|
||||
|
||||
rates := make([]*currency.ExchangeRate, 0, len(models))
|
||||
for _, model := range models {
|
||||
rate, err := r.modelToExchangeRate(&model)
|
||||
if err != nil {
|
||||
r.logger.WithError(err).Error("Failed to convert exchange rate model")
|
||||
continue
|
||||
}
|
||||
rates = append(rates, rate)
|
||||
}
|
||||
|
||||
return rates, nil
|
||||
}
|
||||
|
||||
// GetLatestExchangeRate gets the latest valid exchange rate
|
||||
func (r *GormRepository) GetLatestExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*currency.ExchangeRate, error) {
|
||||
now := time.Now()
|
||||
var model currency.ExchangeRateModel
|
||||
|
||||
if err := r.db.WithContext(ctx).
|
||||
Where("from_currency = ? AND to_currency = ? AND is_active = ?", fromCurrency, toCurrency, true).
|
||||
Where("valid_from <= ? AND (valid_to IS NULL OR valid_to >= ?)", now, now).
|
||||
Order("valid_from DESC").
|
||||
First(&model).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("no valid exchange rate found: %s to %s", fromCurrency, toCurrency)
|
||||
}
|
||||
r.logger.WithError(err).Error("Failed to get latest exchange rate")
|
||||
return nil, fmt.Errorf("failed to get latest exchange rate: %w", err)
|
||||
}
|
||||
|
||||
return r.modelToExchangeRate(&model)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
|
||||
)
|
||||
|
||||
// CreateTransaction creates a new transaction
|
||||
func (r *GormRepository) CreateTransaction(ctx context.Context, transaction *currency.Transaction) error {
|
||||
model := r.transactionToModel(transaction)
|
||||
|
||||
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to create transaction")
|
||||
return fmt.Errorf("failed to create transaction: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransaction retrieves a transaction by ID
|
||||
func (r *GormRepository) GetTransaction(ctx context.Context, id string) (*currency.Transaction, error) {
|
||||
var model currency.TransactionModel
|
||||
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&model).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("transaction not found: %s", id)
|
||||
}
|
||||
r.logger.WithError(err).Error("Failed to get transaction")
|
||||
return nil, fmt.Errorf("failed to get transaction: %w", err)
|
||||
}
|
||||
|
||||
return r.modelToTransaction(&model)
|
||||
}
|
||||
|
||||
// UpdateTransaction updates an existing transaction
|
||||
func (r *GormRepository) UpdateTransaction(ctx context.Context, transaction *currency.Transaction) error {
|
||||
model := r.transactionToModel(transaction)
|
||||
|
||||
if err := r.db.WithContext(ctx).Save(model).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to update transaction")
|
||||
return fmt.Errorf("failed to update transaction: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTransaction deletes a transaction
|
||||
func (r *GormRepository) DeleteTransaction(ctx context.Context, id string) error {
|
||||
if err := r.db.WithContext(ctx).Delete(¤cy.TransactionModel{}, "id = ?", id).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to delete transaction")
|
||||
return fmt.Errorf("failed to delete transaction: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListTransactions retrieves transactions based on filter
|
||||
func (r *GormRepository) ListTransactions(ctx context.Context, filter *currency.TransactionFilter) ([]*currency.Transaction, error) {
|
||||
query := r.db.WithContext(ctx).Model(¤cy.TransactionModel{})
|
||||
|
||||
// Apply filters
|
||||
if filter.ProfileID != "" {
|
||||
query = query.Where("profile_id = ?", filter.ProfileID)
|
||||
}
|
||||
if filter.SubscriptionID != "" {
|
||||
query = query.Where("subscription_id = ?", filter.SubscriptionID)
|
||||
}
|
||||
if filter.Type != "" {
|
||||
query = query.Where("type = ?", filter.Type)
|
||||
}
|
||||
if filter.Status != "" {
|
||||
query = query.Where("status = ?", filter.Status)
|
||||
}
|
||||
if filter.Currency != "" {
|
||||
query = query.Where("currency = ?", filter.Currency)
|
||||
}
|
||||
if filter.FromDate != nil {
|
||||
query = query.Where("created_at >= ?", *filter.FromDate)
|
||||
}
|
||||
if filter.ToDate != nil {
|
||||
query = query.Where("created_at <= ?", *filter.ToDate)
|
||||
}
|
||||
if filter.MinAmount > 0 {
|
||||
query = query.Where("amount >= ?", filter.MinAmount)
|
||||
}
|
||||
if filter.MaxAmount > 0 {
|
||||
query = query.Where("amount <= ?", filter.MaxAmount)
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
if filter.SortBy != "" {
|
||||
order := filter.SortBy
|
||||
if filter.SortOrder == "desc" {
|
||||
order += " DESC"
|
||||
}
|
||||
query = query.Order(order)
|
||||
} else {
|
||||
query = query.Order("created_at DESC")
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
if filter.Limit > 0 {
|
||||
query = query.Limit(filter.Limit)
|
||||
}
|
||||
if filter.Offset > 0 {
|
||||
query = query.Offset(filter.Offset)
|
||||
}
|
||||
|
||||
var models []currency.TransactionModel
|
||||
if err := query.Find(&models).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to list transactions")
|
||||
return nil, fmt.Errorf("failed to list transactions: %w", err)
|
||||
}
|
||||
|
||||
transactions := make([]*currency.Transaction, 0, len(models))
|
||||
for _, model := range models {
|
||||
tx, err := r.modelToTransaction(&model)
|
||||
if err != nil {
|
||||
r.logger.WithError(err).Error("Failed to convert transaction model")
|
||||
continue
|
||||
}
|
||||
transactions = append(transactions, tx)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
// CountTransactions counts transactions based on filter
|
||||
func (r *GormRepository) CountTransactions(ctx context.Context, filter *currency.TransactionFilter) (int, error) {
|
||||
query := r.db.WithContext(ctx).Model(¤cy.TransactionModel{})
|
||||
|
||||
// Apply filters
|
||||
if filter.ProfileID != "" {
|
||||
query = query.Where("profile_id = ?", filter.ProfileID)
|
||||
}
|
||||
if filter.SubscriptionID != "" {
|
||||
query = query.Where("subscription_id = ?", filter.SubscriptionID)
|
||||
}
|
||||
if filter.Type != "" {
|
||||
query = query.Where("type = ?", filter.Type)
|
||||
}
|
||||
if filter.Status != "" {
|
||||
query = query.Where("status = ?", filter.Status)
|
||||
}
|
||||
if filter.Currency != "" {
|
||||
query = query.Where("currency = ?", filter.Currency)
|
||||
}
|
||||
if filter.FromDate != nil {
|
||||
query = query.Where("created_at >= ?", *filter.FromDate)
|
||||
}
|
||||
if filter.ToDate != nil {
|
||||
query = query.Where("created_at <= ?", *filter.ToDate)
|
||||
}
|
||||
if filter.MinAmount > 0 {
|
||||
query = query.Where("amount >= ?", filter.MinAmount)
|
||||
}
|
||||
if filter.MaxAmount > 0 {
|
||||
query = query.Where("amount <= ?", filter.MaxAmount)
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := query.Count(&count).Error; err != nil {
|
||||
r.logger.WithError(err).Error("Failed to count transactions")
|
||||
return 0, fmt.Errorf("failed to count transactions: %w", err)
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
||||
Reference in New Issue
Block a user