mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
refactor: Extract currency interfaces and GORM models into separate files
- Move Repository, ExchangeRateProvider, BillingService, and AnalyticsService interfaces from types.go to new interface.go - Move BillingSummary and CurrencyUsageStats structs to interface.go - Move CurrencyModel, ExchangeRateModel, and TransactionModel to new models.go - Move GORM hooks (BeforeCreate, BeforeUpdate) and TableName methods to models.go - Remove redundant comments from exchange_rate_service.go
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ExchangeRateService manages currency exchange rates
|
||||
type ExchangeRateService struct {
|
||||
repository Repository
|
||||
logger *logrus.Logger
|
||||
@@ -16,7 +15,6 @@ type ExchangeRateService struct {
|
||||
baseCurrency string
|
||||
}
|
||||
|
||||
// NewExchangeRateService creates a new exchange rate service
|
||||
func NewExchangeRateService(repository Repository, logger *logrus.Logger, baseCurrency string) *ExchangeRateService {
|
||||
return &ExchangeRateService{
|
||||
repository: repository,
|
||||
@@ -26,12 +24,10 @@ func NewExchangeRateService(repository Repository, logger *logrus.Logger, baseCu
|
||||
}
|
||||
}
|
||||
|
||||
// AddProvider adds an exchange rate provider
|
||||
func (s *ExchangeRateService) AddProvider(provider ExchangeRateProvider) {
|
||||
s.providers = append(s.providers, provider)
|
||||
}
|
||||
|
||||
// GetExchangeRate gets the current exchange rate between two currencies
|
||||
func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*ExchangeRate, error) {
|
||||
if fromCurrency == toCurrency {
|
||||
return &ExchangeRate{
|
||||
@@ -44,17 +40,14 @@ func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Try to get from repository first
|
||||
rate, err := s.repository.GetLatestExchangeRate(ctx, fromCurrency, toCurrency)
|
||||
if err == nil {
|
||||
return rate, nil
|
||||
}
|
||||
|
||||
// If not found, try to get from providers
|
||||
for _, provider := range s.providers {
|
||||
providerRate, err := provider.GetRate(ctx, fromCurrency, toCurrency)
|
||||
if err == nil {
|
||||
// Save to repository
|
||||
newRate := &ExchangeRate{
|
||||
ID: fmt.Sprintf("%s_%s_%d", fromCurrency, toCurrency, time.Now().Unix()),
|
||||
FromCurrency: fromCurrency,
|
||||
@@ -76,7 +69,6 @@ func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency,
|
||||
return nil, fmt.Errorf("exchange rate not found: %s to %s", fromCurrency, toCurrency)
|
||||
}
|
||||
|
||||
// ConvertAmount converts an amount from one currency to another
|
||||
func (s *ExchangeRateService) ConvertAmount(ctx context.Context, amount float64, fromCurrency, toCurrency string) (*CurrencyConversionResponse, error) {
|
||||
rate, err := s.GetExchangeRate(ctx, fromCurrency, toCurrency)
|
||||
if err != nil {
|
||||
@@ -95,7 +87,6 @@ func (s *ExchangeRateService) ConvertAmount(ctx context.Context, amount float64,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RefreshRates refreshes exchange rates from all providers
|
||||
func (s *ExchangeRateService) RefreshRates(ctx context.Context) error {
|
||||
s.logger.Info("Refreshing exchange rates")
|
||||
|
||||
@@ -110,7 +101,6 @@ func (s *ExchangeRateService) RefreshRates(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRateHistory gets historical exchange rates
|
||||
func (s *ExchangeRateService) GetRateHistory(ctx context.Context, fromCurrency, toCurrency string, days int) ([]*ExchangeRate, error) {
|
||||
filter := &ExchangeRateFilter{
|
||||
FromCurrency: fromCurrency,
|
||||
@@ -129,9 +119,7 @@ func (s *ExchangeRateService) GetRateHistory(ctx context.Context, fromCurrency,
|
||||
return rates, nil
|
||||
}
|
||||
|
||||
// UpdateExchangeRate updates an exchange rate
|
||||
func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *ExchangeRate) error {
|
||||
// Validate rate
|
||||
if rate.Rate <= 0 {
|
||||
return fmt.Errorf("invalid exchange rate: must be positive")
|
||||
}
|
||||
@@ -140,12 +128,10 @@ func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *Exch
|
||||
return fmt.Errorf("invalid currency pair: from and to currencies cannot be the same")
|
||||
}
|
||||
|
||||
// Set validity
|
||||
now := time.Now()
|
||||
rate.ValidFrom = now
|
||||
rate.IsActive = true
|
||||
|
||||
// Deactivate old rates
|
||||
filter := &ExchangeRateFilter{
|
||||
FromCurrency: rate.FromCurrency,
|
||||
ToCurrency: rate.ToCurrency,
|
||||
@@ -162,7 +148,6 @@ func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *Exch
|
||||
}
|
||||
}
|
||||
|
||||
// Create new rate
|
||||
rate.ID = fmt.Sprintf("%s_%s_%d", rate.FromCurrency, rate.ToCurrency, time.Now().Unix())
|
||||
if err := s.repository.CreateExchangeRate(ctx, rate); err != nil {
|
||||
return fmt.Errorf("failed to create exchange rate: %w", err)
|
||||
@@ -178,7 +163,6 @@ func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *Exch
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSupportedCurrencies gets all supported currencies
|
||||
func (s *ExchangeRateService) GetSupportedCurrencies(ctx context.Context) ([]*Currency, error) {
|
||||
filter := &CurrencyFilter{
|
||||
IsActive: &[]bool{true}[0],
|
||||
@@ -192,9 +176,7 @@ func (s *ExchangeRateService) GetSupportedCurrencies(ctx context.Context) ([]*Cu
|
||||
return currencies, nil
|
||||
}
|
||||
|
||||
// ValidateCurrencyPair validates if a currency pair is supported
|
||||
func (s *ExchangeRateService) ValidateCurrencyPair(ctx context.Context, fromCurrency, toCurrency string) error {
|
||||
// Check if currencies exist
|
||||
_, err := s.repository.GetCurrency(ctx, fromCurrency)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported from currency: %s", fromCurrency)
|
||||
@@ -205,7 +187,6 @@ func (s *ExchangeRateService) ValidateCurrencyPair(ctx context.Context, fromCurr
|
||||
return fmt.Errorf("unsupported to currency: %s", toCurrency)
|
||||
}
|
||||
|
||||
// Check if exchange rate exists or can be obtained
|
||||
_, err = s.GetExchangeRate(ctx, fromCurrency, toCurrency)
|
||||
if err != nil {
|
||||
return fmt.Errorf("no exchange rate available: %s to %s", fromCurrency, toCurrency)
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package currency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Repository defines the interface for currency data operations
|
||||
type Repository interface {
|
||||
// Currency operations
|
||||
CreateCurrency(ctx context.Context, currency *Currency) error
|
||||
GetCurrency(ctx context.Context, code string) (*Currency, error)
|
||||
UpdateCurrency(ctx context.Context, currency *Currency) error
|
||||
DeleteCurrency(ctx context.Context, code string) error
|
||||
ListCurrencies(ctx context.Context, filter *CurrencyFilter) ([]*Currency, error)
|
||||
CountCurrencies(ctx context.Context, filter *CurrencyFilter) (int, error)
|
||||
|
||||
// Exchange rate operations
|
||||
CreateExchangeRate(ctx context.Context, rate *ExchangeRate) error
|
||||
GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*ExchangeRate, error)
|
||||
UpdateExchangeRate(ctx context.Context, rate *ExchangeRate) error
|
||||
DeleteExchangeRate(ctx context.Context, id string) error
|
||||
ListExchangeRates(ctx context.Context, filter *ExchangeRateFilter) ([]*ExchangeRate, error)
|
||||
GetLatestExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*ExchangeRate, error)
|
||||
|
||||
// Transaction operations
|
||||
CreateTransaction(ctx context.Context, transaction *Transaction) error
|
||||
GetTransaction(ctx context.Context, id string) (*Transaction, error)
|
||||
UpdateTransaction(ctx context.Context, transaction *Transaction) error
|
||||
DeleteTransaction(ctx context.Context, id string) error
|
||||
ListTransactions(ctx context.Context, filter *TransactionFilter) ([]*Transaction, error)
|
||||
CountTransactions(ctx context.Context, filter *TransactionFilter) (int, error)
|
||||
}
|
||||
|
||||
// ExchangeRateProvider defines the interface for external exchange rate providers
|
||||
type ExchangeRateProvider interface {
|
||||
GetRates(ctx context.Context) (map[string]float64, error)
|
||||
GetRate(ctx context.Context, fromCurrency, toCurrency string) (float64, error)
|
||||
RefreshRates(ctx context.Context) error
|
||||
}
|
||||
|
||||
// BillingService defines the interface for multi-currency billing operations
|
||||
type BillingService interface {
|
||||
ProcessBilling(ctx context.Context, req *BillingRequest) (*BillingResponse, error)
|
||||
ConvertAmount(ctx context.Context, req *CurrencyConversionRequest) (*CurrencyConversionResponse, error)
|
||||
GetBillingHistory(ctx context.Context, profileID string, filter *TransactionFilter) ([]*Transaction, error)
|
||||
CalculateTotalBilling(ctx context.Context, profileID string, fromDate, toDate time.Time) (*BillingSummary, error)
|
||||
}
|
||||
|
||||
// AnalyticsService defines the interface for currency analytics
|
||||
type AnalyticsService interface {
|
||||
GetRevenueByCurrency(ctx context.Context, filter *TransactionFilter) (map[string]float64, error)
|
||||
GetTransactionVolumeByCurrency(ctx context.Context, filter *TransactionFilter) (map[string]int64, error)
|
||||
GetExchangeRateTrends(ctx context.Context, fromCurrency, toCurrency string, days int) ([]*ExchangeRate, error)
|
||||
GetCurrencyUsageStats(ctx context.Context) (*CurrencyUsageStats, error)
|
||||
}
|
||||
|
||||
// BillingSummary represents a billing summary for a profile
|
||||
type BillingSummary struct {
|
||||
ProfileID string `json:"profile_id"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
Currency string `json:"currency"`
|
||||
BaseTotalAmount float64 `json:"base_total_amount"`
|
||||
BaseCurrency string `json:"base_currency"`
|
||||
TransactionCount int `json:"transaction_count"`
|
||||
FromDate time.Time `json:"from_date"`
|
||||
ToDate time.Time `json:"to_date"`
|
||||
Breakdown map[string]interface{} `json:"breakdown"`
|
||||
}
|
||||
|
||||
// CurrencyUsageStats represents statistics about currency usage
|
||||
type CurrencyUsageStats struct {
|
||||
TotalCurrencies int `json:"total_currencies"`
|
||||
ActiveCurrencies int `json:"active_currencies"`
|
||||
TotalTransactions int64 `json:"total_transactions"`
|
||||
TotalVolume float64 `json:"total_volume"`
|
||||
MostUsedCurrency string `json:"most_used_currency"`
|
||||
CurrencyDistribution map[string]int64 `json:"currency_distribution"`
|
||||
ExchangeRateCount int `json:"exchange_rate_count"`
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package currency
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CurrencyModel represents the database model for currencies
|
||||
type CurrencyModel struct {
|
||||
Code string `gorm:"primaryKey;column:code" json:"code"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Symbol string `gorm:"column:symbol" json:"symbol"`
|
||||
DecimalPlaces int `gorm:"column:decimal_places" json:"decimal_places"`
|
||||
IsActive bool `gorm:"column:is_active" json:"is_active"`
|
||||
SupportedRegions string `gorm:"column:supported_regions;type:text" json:"supported_regions"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for the currency model
|
||||
func (CurrencyModel) TableName() string {
|
||||
return "currencies"
|
||||
}
|
||||
|
||||
// ExchangeRateModel represents the database model for exchange rates
|
||||
type ExchangeRateModel struct {
|
||||
ID string `gorm:"primaryKey;column:id" json:"id"`
|
||||
FromCurrency string `gorm:"column:from_currency;index" json:"from_currency"`
|
||||
ToCurrency string `gorm:"column:to_currency;index" json:"to_currency"`
|
||||
Rate float64 `gorm:"column:rate" json:"rate"`
|
||||
Source string `gorm:"column:source" json:"source"`
|
||||
ValidFrom time.Time `gorm:"column:valid_from;index" json:"valid_from"`
|
||||
ValidTo *time.Time `gorm:"column:valid_to;index" json:"valid_to"`
|
||||
IsActive bool `gorm:"column:is_active" json:"is_active"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for the exchange rate model
|
||||
func (ExchangeRateModel) TableName() string {
|
||||
return "exchange_rates"
|
||||
}
|
||||
|
||||
// TransactionModel represents the database model for transactions
|
||||
type TransactionModel struct {
|
||||
ID string `gorm:"primaryKey;column:id" json:"id"`
|
||||
ProfileID string `gorm:"column:profile_id;index" json:"profile_id"`
|
||||
SubscriptionID string `gorm:"column:subscription_id;index" json:"subscription_id"`
|
||||
Type string `gorm:"column:type;index" json:"type"`
|
||||
Amount float64 `gorm:"column:amount" json:"amount"`
|
||||
Currency string `gorm:"column:currency;index" json:"currency"`
|
||||
BaseAmount float64 `gorm:"column:base_amount" json:"base_amount"`
|
||||
BaseCurrency string `gorm:"column:base_currency" json:"base_currency"`
|
||||
ExchangeRate float64 `gorm:"column:exchange_rate" json:"exchange_rate"`
|
||||
Description string `gorm:"column:description" json:"description"`
|
||||
Status string `gorm:"column:status;index" json:"status"`
|
||||
Metadata string `gorm:"column:metadata;type:text" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;index" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for the transaction model
|
||||
func (TransactionModel) TableName() string {
|
||||
return "transactions"
|
||||
}
|
||||
|
||||
// BeforeCreate hook for CurrencyModel
|
||||
func (c *CurrencyModel) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.CreatedAt.IsZero() {
|
||||
c.CreatedAt = time.Now()
|
||||
}
|
||||
c.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate hook for CurrencyModel
|
||||
func (c *CurrencyModel) BeforeUpdate(tx *gorm.DB) error {
|
||||
c.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeCreate hook for ExchangeRateModel
|
||||
func (e *ExchangeRateModel) BeforeCreate(tx *gorm.DB) error {
|
||||
if e.CreatedAt.IsZero() {
|
||||
e.CreatedAt = time.Now()
|
||||
}
|
||||
e.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate hook for ExchangeRateModel
|
||||
func (e *ExchangeRateModel) BeforeUpdate(tx *gorm.DB) error {
|
||||
e.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeCreate hook for TransactionModel
|
||||
func (t *TransactionModel) BeforeCreate(tx *gorm.DB) error {
|
||||
if t.CreatedAt.IsZero() {
|
||||
t.CreatedAt = time.Now()
|
||||
}
|
||||
t.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate hook for TransactionModel
|
||||
func (t *TransactionModel) BeforeUpdate(tx *gorm.DB) error {
|
||||
t.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user