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:
nutcas3
2026-05-02 01:21:18 +03:00
parent c93dd63474
commit 295649bcea
21 changed files with 163 additions and 177 deletions
@@ -8,15 +8,15 @@ import (
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/handlers"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/service"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/services"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/smdp"
)
type SelectionIntegration struct {
manager *smdp.SMDPManager
selectionService *service.SelectionService
selectionService *services.SelectionService
selectionHandler *handlers.SelectionHandler
smdpService *service.SMDPService
smdpService *services.SMDPService
server *http.Server
}
@@ -33,10 +33,10 @@ func NewSelectionIntegration(repo *repository.PostgresProfileStore) *SelectionIn
manager := smdp.NewSMDPManager(repo, config)
// Create selection service
selectionService := service.NewSelectionService(manager)
selectionService := services.NewSelectionService(manager)
// Create SMDP service
smdpService := service.NewSMDPService(repo)
smdpService := services.NewSMDPService(repo)
return &SelectionIntegration{
manager: manager,
@@ -85,7 +85,7 @@ func (si *SelectionIntegration) GetManager() *smdp.SMDPManager {
}
// GetSelectionService returns the selection service for testing
func (si *SelectionIntegration) GetSelectionService() *service.SelectionService {
func (si *SelectionIntegration) GetSelectionService() *services.SelectionService {
return si.selectionService
}
@@ -6,14 +6,14 @@ import (
"github.com/gin-gonic/gin"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/handlers"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/service"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/services"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/smdp"
"github.com/sirupsen/logrus"
)
// SMDPIntegration integrates the multi-carrier SM-DP+ system with the existing carrier connector
type SMDPIntegration struct {
service *service.SMDPService
service *services.SMDPService
handler *handlers.SMDPHandler
repository *repository.PostgresProfileStore
logger *logrus.Logger
@@ -24,7 +24,7 @@ func NewSMDPIntegration(repo *repository.PostgresProfileStore) *SMDPIntegration
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel)
svc := service.NewSMDPService(repo)
svc := services.NewSMDPService(repo)
manager := svc.GetManager()
hnd := handlers.NewSMDPHandler(manager)
@@ -148,7 +148,7 @@ func (i *SMDPIntegration) metricsHandler(c *gin.Context) {
}
// GetService returns the SM-DP+ service for direct access
func (i *SMDPIntegration) GetService() *service.SMDPService {
func (i *SMDPIntegration) GetService() *services.SMDPService {
return i.service
}
@@ -1,9 +1,10 @@
package currency
package repository
import (
"context"
"fmt"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
@@ -23,7 +24,7 @@ func NewGormRepository(db *gorm.DB, logger *logrus.Logger) *GormRepository {
}
// CreateCurrency creates a new currency
func (r *GormRepository) CreateCurrency(ctx context.Context, currency *Currency) error {
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 {
@@ -35,8 +36,8 @@ func (r *GormRepository) CreateCurrency(ctx context.Context, currency *Currency)
}
// GetCurrency retrieves a currency by code
func (r *GormRepository) GetCurrency(ctx context.Context, code string) (*Currency, error) {
var model CurrencyModel
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)
@@ -49,7 +50,7 @@ func (r *GormRepository) GetCurrency(ctx context.Context, code string) (*Currenc
}
// UpdateCurrency updates an existing currency
func (r *GormRepository) UpdateCurrency(ctx context.Context, currency *Currency) error {
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 {
@@ -62,7 +63,7 @@ func (r *GormRepository) UpdateCurrency(ctx context.Context, currency *Currency)
// DeleteCurrency deletes a currency
func (r *GormRepository) DeleteCurrency(ctx context.Context, code string) error {
if err := r.db.WithContext(ctx).Delete(&CurrencyModel{}, "code = ?", code).Error; err != nil {
if err := r.db.WithContext(ctx).Delete(&currency.CurrencyModel{}, "code = ?", code).Error; err != nil {
r.logger.WithError(err).Error("Failed to delete currency")
return fmt.Errorf("failed to delete currency: %w", err)
}
@@ -71,8 +72,8 @@ func (r *GormRepository) DeleteCurrency(ctx context.Context, code string) error
}
// ListCurrencies retrieves currencies based on filter
func (r *GormRepository) ListCurrencies(ctx context.Context, filter *CurrencyFilter) ([]*Currency, error) {
query := r.db.WithContext(ctx).Model(&CurrencyModel{})
func (r *GormRepository) ListCurrencies(ctx context.Context, filter *currency.CurrencyFilter) ([]*currency.Currency, error) {
query := r.db.WithContext(ctx).Model(&currency.CurrencyModel{})
// Apply filters
if filter.IsActive != nil {
@@ -101,13 +102,13 @@ func (r *GormRepository) ListCurrencies(ctx context.Context, filter *CurrencyFil
query = query.Offset(filter.Offset)
}
var models []CurrencyModel
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, 0, len(models))
currencies := make([]*currency.Currency, 0, len(models))
for _, model := range models {
curr, err := r.modelToCurrency(&model)
if err != nil {
@@ -121,8 +122,8 @@ func (r *GormRepository) ListCurrencies(ctx context.Context, filter *CurrencyFil
}
// CountCurrencies counts currencies based on filter
func (r *GormRepository) CountCurrencies(ctx context.Context, filter *CurrencyFilter) (int, error) {
query := r.db.WithContext(ctx).Model(&CurrencyModel{})
func (r *GormRepository) CountCurrencies(ctx context.Context, filter *currency.CurrencyFilter) (int, error) {
query := r.db.WithContext(ctx).Model(&currency.CurrencyModel{})
// Apply filters
if filter.IsActive != nil {
@@ -142,7 +143,7 @@ func (r *GormRepository) CountCurrencies(ctx context.Context, filter *CurrencyFi
}
// CreateExchangeRate creates a new exchange rate
func (r *GormRepository) CreateExchangeRate(ctx context.Context, rate *ExchangeRate) error {
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 {
@@ -154,8 +155,8 @@ func (r *GormRepository) CreateExchangeRate(ctx context.Context, rate *ExchangeR
}
// GetExchangeRate retrieves an exchange rate by ID
func (r *GormRepository) GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*ExchangeRate, error) {
var model ExchangeRateModel
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)
@@ -168,7 +169,7 @@ func (r *GormRepository) GetExchangeRate(ctx context.Context, fromCurrency, toCu
}
// UpdateExchangeRate updates an existing exchange rate
func (r *GormRepository) UpdateExchangeRate(ctx context.Context, rate *ExchangeRate) error {
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 {
@@ -181,7 +182,7 @@ func (r *GormRepository) UpdateExchangeRate(ctx context.Context, rate *ExchangeR
// DeleteExchangeRate deletes an exchange rate
func (r *GormRepository) DeleteExchangeRate(ctx context.Context, id string) error {
if err := r.db.WithContext(ctx).Delete(&ExchangeRateModel{}, "id = ?", id).Error; err != nil {
if err := r.db.WithContext(ctx).Delete(&currency.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)
}
@@ -1,14 +1,16 @@
package currency
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) *CurrencyModel {
return &CurrencyModel{
func (r *GormRepository) currencyToModel(currency *currency.Currency) *currency.CurrencyModel {
return &currency.CurrencyModel{
Code: currency.Code,
Name: currency.Name,
Symbol: currency.Symbol,
@@ -21,13 +23,13 @@ func (r *GormRepository) currencyToModel(currency *Currency) *CurrencyModel {
}
// modelToCurrency converts database model to currency domain model
func (r *GormRepository) modelToCurrency(model *CurrencyModel) (*Currency, error) {
func (r *GormRepository) modelToCurrency(model *currency.CurrencyModel) (*currency.Currency, error) {
var regions []string
if model.SupportedRegions != "" {
regions = strings.Split(model.SupportedRegions, ",")
}
return &Currency{
return &currency.Currency{
Code: model.Code,
Name: model.Name,
Symbol: model.Symbol,
@@ -40,8 +42,8 @@ func (r *GormRepository) modelToCurrency(model *CurrencyModel) (*Currency, error
}
// exchangeRateToModel converts exchange rate domain model to database model
func (r *GormRepository) exchangeRateToModel(rate *ExchangeRate) *ExchangeRateModel {
return &ExchangeRateModel{
func (r *GormRepository) exchangeRateToModel(rate *currency.ExchangeRate) *currency.ExchangeRateModel {
return &currency.ExchangeRateModel{
ID: rate.ID,
FromCurrency: rate.FromCurrency,
ToCurrency: rate.ToCurrency,
@@ -56,8 +58,8 @@ func (r *GormRepository) exchangeRateToModel(rate *ExchangeRate) *ExchangeRateMo
}
// modelToExchangeRate converts database model to exchange rate domain model
func (r *GormRepository) modelToExchangeRate(model *ExchangeRateModel) (*ExchangeRate, error) {
return &ExchangeRate{
func (r *GormRepository) modelToExchangeRate(model *currency.ExchangeRateModel) (*currency.ExchangeRate, error) {
return &currency.ExchangeRate{
ID: model.ID,
FromCurrency: model.FromCurrency,
ToCurrency: model.ToCurrency,
@@ -72,7 +74,7 @@ func (r *GormRepository) modelToExchangeRate(model *ExchangeRateModel) (*Exchang
}
// transactionToModel converts transaction domain model to database model
func (r *GormRepository) transactionToModel(transaction *Transaction) *TransactionModel {
func (r *GormRepository) transactionToModel(transaction *currency.Transaction) *currency.TransactionModel {
metadata := ""
if transaction.Metadata != nil {
if data, err := json.Marshal(transaction.Metadata); err == nil {
@@ -80,7 +82,7 @@ func (r *GormRepository) transactionToModel(transaction *Transaction) *Transacti
}
}
return &TransactionModel{
return &currency.TransactionModel{
ID: transaction.ID,
ProfileID: transaction.ProfileID,
SubscriptionID: transaction.SubscriptionID,
@@ -99,7 +101,7 @@ func (r *GormRepository) transactionToModel(transaction *Transaction) *Transacti
}
// modelToTransaction converts database model to transaction domain model
func (r *GormRepository) modelToTransaction(model *TransactionModel) (*Transaction, error) {
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 {
@@ -107,18 +109,18 @@ func (r *GormRepository) modelToTransaction(model *TransactionModel) (*Transacti
}
}
return &Transaction{
return &currency.Transaction{
ID: model.ID,
ProfileID: model.ProfileID,
SubscriptionID: model.SubscriptionID,
Type: TransactionType(model.Type),
Type: currency.TransactionType(model.Type),
Amount: model.Amount,
Currency: model.Currency,
BaseAmount: model.BaseAmount,
BaseCurrency: model.BaseCurrency,
ExchangeRate: model.ExchangeRate,
Description: model.Description,
Status: TransactionStatus(model.Status),
Status: currency.TransactionStatus(model.Status),
Metadata: metadata,
CreatedAt: model.CreatedAt,
UpdatedAt: model.UpdatedAt,
@@ -1,27 +1,13 @@
package rateplan
package repository
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()
@@ -1,4 +1,4 @@
package rateplan
package repository
import (
"context"
@@ -6,6 +6,8 @@ import (
"time"
"gorm.io/gorm"
)
// CreateRatePlan creates a new rate plan
@@ -1,4 +1,4 @@
package rateplan
package repository
import (
"context"
@@ -7,6 +7,7 @@ import (
"gorm.io/gorm"
)
// CreateSubscription creates a new rate plan subscription
func (r *GormRepository) CreateSubscription(ctx context.Context, subscription *RatePlanSubscription) error {
now := time.Now()
@@ -85,6 +86,7 @@ func (r *GormRepository) DeleteSubscription(ctx context.Context, id string) erro
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)
@@ -1,4 +1,4 @@
package currency
package repository
import (
"context"
@@ -6,11 +6,13 @@ import (
"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 *ExchangeRateFilter) ([]*ExchangeRate, error) {
query := r.db.WithContext(ctx).Model(&ExchangeRateModel{})
func (r *GormRepository) ListExchangeRates(ctx context.Context, filter *currency.ExchangeRateFilter) ([]*currency.ExchangeRate, error) {
query := r.db.WithContext(ctx).Model(&currency.ExchangeRateModel{})
// Apply filters
if filter.FromCurrency != "" {
@@ -50,13 +52,13 @@ func (r *GormRepository) ListExchangeRates(ctx context.Context, filter *Exchange
query = query.Offset(filter.Offset)
}
var models []ExchangeRateModel
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([]*ExchangeRate, 0, len(models))
rates := make([]*currency.ExchangeRate, 0, len(models))
for _, model := range models {
rate, err := r.modelToExchangeRate(&model)
if err != nil {
@@ -70,9 +72,9 @@ func (r *GormRepository) ListExchangeRates(ctx context.Context, filter *Exchange
}
// GetLatestExchangeRate gets the latest valid exchange rate
func (r *GormRepository) GetLatestExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*ExchangeRate, error) {
func (r *GormRepository) GetLatestExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*currency.ExchangeRate, error) {
now := time.Now()
var model ExchangeRateModel
var model currency.ExchangeRateModel
if err := r.db.WithContext(ctx).
Where("from_currency = ? AND to_currency = ? AND is_active = ?", fromCurrency, toCurrency, true).
@@ -1,14 +1,16 @@
package currency
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 *Transaction) error {
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 {
@@ -20,8 +22,8 @@ func (r *GormRepository) CreateTransaction(ctx context.Context, transaction *Tra
}
// GetTransaction retrieves a transaction by ID
func (r *GormRepository) GetTransaction(ctx context.Context, id string) (*Transaction, error) {
var model TransactionModel
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)
@@ -34,7 +36,7 @@ func (r *GormRepository) GetTransaction(ctx context.Context, id string) (*Transa
}
// UpdateTransaction updates an existing transaction
func (r *GormRepository) UpdateTransaction(ctx context.Context, transaction *Transaction) error {
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 {
@@ -47,7 +49,7 @@ func (r *GormRepository) UpdateTransaction(ctx context.Context, transaction *Tra
// DeleteTransaction deletes a transaction
func (r *GormRepository) DeleteTransaction(ctx context.Context, id string) error {
if err := r.db.WithContext(ctx).Delete(&TransactionModel{}, "id = ?", id).Error; err != nil {
if err := r.db.WithContext(ctx).Delete(&currency.TransactionModel{}, "id = ?", id).Error; err != nil {
r.logger.WithError(err).Error("Failed to delete transaction")
return fmt.Errorf("failed to delete transaction: %w", err)
}
@@ -56,8 +58,8 @@ func (r *GormRepository) DeleteTransaction(ctx context.Context, id string) error
}
// ListTransactions retrieves transactions based on filter
func (r *GormRepository) ListTransactions(ctx context.Context, filter *TransactionFilter) ([]*Transaction, error) {
query := r.db.WithContext(ctx).Model(&TransactionModel{})
func (r *GormRepository) ListTransactions(ctx context.Context, filter *currency.TransactionFilter) ([]*currency.Transaction, error) {
query := r.db.WithContext(ctx).Model(&currency.TransactionModel{})
// Apply filters
if filter.ProfileID != "" {
@@ -107,13 +109,13 @@ func (r *GormRepository) ListTransactions(ctx context.Context, filter *Transacti
query = query.Offset(filter.Offset)
}
var models []TransactionModel
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([]*Transaction, 0, len(models))
transactions := make([]*currency.Transaction, 0, len(models))
for _, model := range models {
tx, err := r.modelToTransaction(&model)
if err != nil {
@@ -127,8 +129,8 @@ func (r *GormRepository) ListTransactions(ctx context.Context, filter *Transacti
}
// CountTransactions counts transactions based on filter
func (r *GormRepository) CountTransactions(ctx context.Context, filter *TransactionFilter) (int, error) {
query := r.db.WithContext(ctx).Model(&TransactionModel{})
func (r *GormRepository) CountTransactions(ctx context.Context, filter *currency.TransactionFilter) (int, error) {
query := r.db.WithContext(ctx).Model(&currency.TransactionModel{})
// Apply filters
if filter.ProfileID != "" {
@@ -1,21 +1,22 @@
package currency
package services
import (
"context"
"fmt"
"time"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
"github.com/sirupsen/logrus"
)
// AnalyticsServiceImpl handles currency analytics operations
type AnalyticsServiceImpl struct {
repository Repository
repository currency.Repository
logger *logrus.Logger
}
// NewAnalyticsService creates a new analytics service
func NewAnalyticsService(repository Repository, logger *logrus.Logger) *AnalyticsServiceImpl {
func NewAnalyticsService(repository currency.Repository, logger *logrus.Logger) *AnalyticsServiceImpl {
return &AnalyticsServiceImpl{
repository: repository,
logger: logger,
@@ -23,12 +24,12 @@ func NewAnalyticsService(repository Repository, logger *logrus.Logger) *Analytic
}
// GetRevenueByCurrency calculates revenue breakdown by currency
func (s *AnalyticsServiceImpl) GetRevenueByCurrency(ctx context.Context, filter *TransactionFilter) (map[string]float64, error) {
func (s *AnalyticsServiceImpl) GetRevenueByCurrency(ctx context.Context, filter *currency.TransactionFilter) (map[string]float64, error) {
if filter == nil {
filter = &TransactionFilter{}
filter = &currency.TransactionFilter{}
}
filter.Status = TransactionStatusCompleted
filter.Status = currency.TransactionStatusCompleted
transactions, err := s.repository.ListTransactions(ctx, filter)
if err != nil {
@@ -39,7 +40,7 @@ func (s *AnalyticsServiceImpl) GetRevenueByCurrency(ctx context.Context, filter
revenueByCurrency := make(map[string]float64)
for _, tx := range transactions {
if tx.Type == TransactionTypeSubscription || tx.Type == TransactionTypeUsage || tx.Type == TransactionTypeOverage {
if tx.Type == currency.TransactionTypeSubscription || tx.Type == currency.TransactionTypeUsage || tx.Type == currency.TransactionTypeOverage {
revenueByCurrency[tx.Currency] += tx.Amount
}
}
@@ -48,9 +49,9 @@ func (s *AnalyticsServiceImpl) GetRevenueByCurrency(ctx context.Context, filter
}
// GetTransactionVolumeByCurrency calculates transaction volume by currency
func (s *AnalyticsServiceImpl) GetTransactionVolumeByCurrency(ctx context.Context, filter *TransactionFilter) (map[string]int64, error) {
func (s *AnalyticsServiceImpl) GetTransactionVolumeByCurrency(ctx context.Context, filter *currency.TransactionFilter) (map[string]int64, error) {
if filter == nil {
filter = &TransactionFilter{}
filter = &currency.TransactionFilter{}
}
transactions, err := s.repository.ListTransactions(ctx, filter)
@@ -69,8 +70,8 @@ func (s *AnalyticsServiceImpl) GetTransactionVolumeByCurrency(ctx context.Contex
}
// GetExchangeRateTrends retrieves exchange rate trends for a currency pair
func (s *AnalyticsServiceImpl) GetExchangeRateTrends(ctx context.Context, fromCurrency, toCurrency string, days int) ([]*ExchangeRate, error) {
filter := &ExchangeRateFilter{
func (s *AnalyticsServiceImpl) GetExchangeRateTrends(ctx context.Context, fromCurrency, toCurrency string, days int) ([]*currency.ExchangeRate, error) {
filter := &currency.ExchangeRateFilter{
FromCurrency: fromCurrency,
ToCurrency: toCurrency,
IsValid: &[]bool{false}[0], // Include historical rates
@@ -89,15 +90,15 @@ func (s *AnalyticsServiceImpl) GetExchangeRateTrends(ctx context.Context, fromCu
}
// GetCurrencyUsageStats retrieves currency usage statistics
func (s *AnalyticsServiceImpl) GetCurrencyUsageStats(ctx context.Context) (*CurrencyUsageStats, error) {
func (s *AnalyticsServiceImpl) GetCurrencyUsageStats(ctx context.Context) (*currency.CurrencyUsageStats, error) {
// Get total currencies
totalCurrencies, err := s.repository.CountCurrencies(ctx, &CurrencyFilter{})
totalCurrencies, err := s.repository.CountCurrencies(ctx, &currency.CurrencyFilter{})
if err != nil {
return nil, fmt.Errorf("failed to count currencies: %w", err)
}
// Get active currencies
activeCurrencies, err := s.repository.CountCurrencies(ctx, &CurrencyFilter{
activeCurrencies, err := s.repository.CountCurrencies(ctx, &currency.CurrencyFilter{
IsActive: &[]bool{true}[0],
})
if err != nil {
@@ -105,14 +106,14 @@ func (s *AnalyticsServiceImpl) GetCurrencyUsageStats(ctx context.Context) (*Curr
}
// Get total transactions
totalTransactions, err := s.repository.CountTransactions(ctx, &TransactionFilter{})
totalTransactions, err := s.repository.CountTransactions(ctx, &currency.TransactionFilter{})
if err != nil {
return nil, fmt.Errorf("failed to count transactions: %w", err)
}
// Get total volume
transactions, err := s.repository.ListTransactions(ctx, &TransactionFilter{
Status: TransactionStatusCompleted,
transactions, err := s.repository.ListTransactions(ctx, &currency.TransactionFilter{
Status: currency.TransactionStatusCompleted,
})
if err != nil {
return nil, fmt.Errorf("failed to get transactions for volume: %w", err)
@@ -138,12 +139,12 @@ func (s *AnalyticsServiceImpl) GetCurrencyUsageStats(ctx context.Context) (*Curr
}
// Get exchange rate count (using ListExchangeRates for now since CountExchangeRates doesn't exist)
exchangeRates, err := s.repository.ListExchangeRates(ctx, &ExchangeRateFilter{})
exchangeRates, err := s.repository.ListExchangeRates(ctx, &currency.ExchangeRateFilter{})
if err != nil {
return nil, fmt.Errorf("failed to count exchange rates: %w", err)
}
return &CurrencyUsageStats{
return &currency.CurrencyUsageStats{
TotalCurrencies: totalCurrencies,
ActiveCurrencies: activeCurrencies,
TotalTransactions: int64(totalTransactions),
@@ -160,8 +161,8 @@ func (s *AnalyticsServiceImpl) GetMonthlyRevenueTrends(ctx context.Context, mont
endDate := time.Now()
startDate := endDate.AddDate(0, -months, 0)
filter := &TransactionFilter{
Status: TransactionStatusCompleted,
filter := &currency.TransactionFilter{
Status: currency.TransactionStatusCompleted,
FromDate: &startDate,
ToDate: &endDate,
}
@@ -183,18 +184,18 @@ func (s *AnalyticsServiceImpl) GetMonthlyRevenueTrends(ctx context.Context, mont
}
// GetTopCurrenciesByRevenue returns top currencies by revenue
func (s *AnalyticsServiceImpl) GetTopCurrenciesByRevenue(ctx context.Context, limit int) ([]*CurrencyRevenue, error) {
revenueByCurrency, err := s.GetRevenueByCurrency(ctx, &TransactionFilter{
Status: TransactionStatusCompleted,
func (s *AnalyticsServiceImpl) GetTopCurrenciesByRevenue(ctx context.Context, limit int) ([]*currency.CurrencyRevenue, error) {
revenueByCurrency, err := s.GetRevenueByCurrency(ctx, &currency.TransactionFilter{
Status: currency.TransactionStatusCompleted,
})
if err != nil {
return nil, err
}
// Convert to slice and sort
var currencyRevenues []*CurrencyRevenue
var currencyRevenues []*currency.CurrencyRevenue
for currency, revenue := range revenueByCurrency {
currencyRevenues = append(currencyRevenues, &CurrencyRevenue{
currencyRevenues = append(currencyRevenues, &currency.CurrencyRevenue{
Currency: currency,
Revenue: revenue,
})
@@ -208,16 +209,10 @@ func (s *AnalyticsServiceImpl) GetTopCurrenciesByRevenue(ctx context.Context, li
return currencyRevenues, nil
}
// CurrencyRevenue represents revenue for a specific currency
type CurrencyRevenue struct {
Currency string `json:"currency"`
Revenue float64 `json:"revenue"`
}
// GetTransactionTypeAnalytics returns analytics by transaction type
func (s *AnalyticsServiceImpl) GetTransactionTypeAnalytics(ctx context.Context, filter *TransactionFilter) (map[string]*TransactionTypeStats, error) {
func (s *AnalyticsServiceImpl) GetTransactionTypeAnalytics(ctx context.Context, filter *currency.TransactionFilter) (map[string]*currency.TransactionTypeStats, error) {
if filter == nil {
filter = &TransactionFilter{}
filter = &currency.TransactionFilter{}
}
transactions, err := s.repository.ListTransactions(ctx, filter)
@@ -226,13 +221,13 @@ func (s *AnalyticsServiceImpl) GetTransactionTypeAnalytics(ctx context.Context,
return nil, fmt.Errorf("failed to get transactions: %w", err)
}
typeStats := make(map[string]*TransactionTypeStats)
typeStats := make(map[string]*currency.TransactionTypeStats)
for _, tx := range transactions {
typeKey := string(tx.Type)
if _, exists := typeStats[typeKey]; !exists {
typeStats[typeKey] = &TransactionTypeStats{
typeStats[typeKey] = &currency.TransactionTypeStats{
Type: tx.Type,
Count: 0,
Amount: 0.0,
@@ -247,11 +242,3 @@ func (s *AnalyticsServiceImpl) GetTransactionTypeAnalytics(ctx context.Context,
return typeStats, nil
}
// TransactionTypeStats represents statistics for a transaction type
type TransactionTypeStats struct {
Type TransactionType `json:"type"`
Count int `json:"count"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
}
@@ -1,4 +1,4 @@
package currency
package services
import (
"context"
@@ -1,36 +1,37 @@
package currency
package services
import (
"context"
"fmt"
"time"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
"github.com/sirupsen/logrus"
)
type ExchangeRateService struct {
repository Repository
repository currency.Repository
logger *logrus.Logger
providers []ExchangeRateProvider
providers []currency.ExchangeRateProvider
baseCurrency string
}
func NewExchangeRateService(repository Repository, logger *logrus.Logger, baseCurrency string) *ExchangeRateService {
func NewExchangeRateService(repository currency.Repository, logger *logrus.Logger, baseCurrency string) *ExchangeRateService {
return &ExchangeRateService{
repository: repository,
logger: logger,
providers: make([]ExchangeRateProvider, 0),
providers: make([]currency.ExchangeRateProvider, 0),
baseCurrency: baseCurrency,
}
}
func (s *ExchangeRateService) AddProvider(provider ExchangeRateProvider) {
func (s *ExchangeRateService) AddProvider(provider currency.ExchangeRateProvider) {
s.providers = append(s.providers, provider)
}
func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*ExchangeRate, error) {
func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*currency.ExchangeRate, error) {
if fromCurrency == toCurrency {
return &ExchangeRate{
return &currency.ExchangeRate{
FromCurrency: fromCurrency,
ToCurrency: toCurrency,
Rate: 1.0,
@@ -48,7 +49,7 @@ func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency,
for _, provider := range s.providers {
providerRate, err := provider.GetRate(ctx, fromCurrency, toCurrency)
if err == nil {
newRate := &ExchangeRate{
newRate := &currency.ExchangeRate{
ID: fmt.Sprintf("%s_%s_%d", fromCurrency, toCurrency, time.Now().Unix()),
FromCurrency: fromCurrency,
ToCurrency: toCurrency,
@@ -69,7 +70,7 @@ func (s *ExchangeRateService) GetExchangeRate(ctx context.Context, fromCurrency,
return nil, fmt.Errorf("exchange rate not found: %s to %s", fromCurrency, toCurrency)
}
func (s *ExchangeRateService) ConvertAmount(ctx context.Context, amount float64, fromCurrency, toCurrency string) (*CurrencyConversionResponse, error) {
func (s *ExchangeRateService) ConvertAmount(ctx context.Context, amount float64, fromCurrency, toCurrency string) (*currency.CurrencyConversionResponse, error) {
rate, err := s.GetExchangeRate(ctx, fromCurrency, toCurrency)
if err != nil {
return nil, fmt.Errorf("failed to get exchange rate: %w", err)
@@ -77,7 +78,7 @@ func (s *ExchangeRateService) ConvertAmount(ctx context.Context, amount float64,
convertedAmount := amount * rate.Rate
return &CurrencyConversionResponse{
return &currency.CurrencyConversionResponse{
OriginalAmount: amount,
OriginalCurrency: fromCurrency,
ConvertedAmount: convertedAmount,
@@ -101,8 +102,8 @@ func (s *ExchangeRateService) RefreshRates(ctx context.Context) error {
return nil
}
func (s *ExchangeRateService) GetRateHistory(ctx context.Context, fromCurrency, toCurrency string, days int) ([]*ExchangeRate, error) {
filter := &ExchangeRateFilter{
func (s *ExchangeRateService) GetRateHistory(ctx context.Context, fromCurrency, toCurrency string, days int) ([]*currency.ExchangeRate, error) {
filter := &currency.ExchangeRateFilter{
FromCurrency: fromCurrency,
ToCurrency: toCurrency,
IsValid: &[]bool{false}[0], // Include historical rates
@@ -119,7 +120,7 @@ func (s *ExchangeRateService) GetRateHistory(ctx context.Context, fromCurrency,
return rates, nil
}
func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *ExchangeRate) error {
func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *currency.ExchangeRate) error {
if rate.Rate <= 0 {
return fmt.Errorf("invalid exchange rate: must be positive")
}
@@ -132,7 +133,7 @@ func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *Exch
rate.ValidFrom = now
rate.IsActive = true
filter := &ExchangeRateFilter{
filter := &currency.ExchangeRateFilter{
FromCurrency: rate.FromCurrency,
ToCurrency: rate.ToCurrency,
IsValid: &[]bool{true}[0],
@@ -163,8 +164,8 @@ func (s *ExchangeRateService) UpdateExchangeRate(ctx context.Context, rate *Exch
return nil
}
func (s *ExchangeRateService) GetSupportedCurrencies(ctx context.Context) ([]*Currency, error) {
filter := &CurrencyFilter{
func (s *ExchangeRateService) GetSupportedCurrencies(ctx context.Context) ([]*currency.Currency, error) {
filter := &currency.CurrencyFilter{
IsActive: &[]bool{true}[0],
}
@@ -1,4 +1,4 @@
package currency
package services
import (
"context"
@@ -7,21 +7,22 @@ import (
"github.com/sirupsen/logrus"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/rateplan"
)
// RatePlanCurrencyIntegrator integrates currency system with rate plans
type RatePlanCurrencyIntegrator struct {
billingService BillingService
billingService currency.BillingService
exchangeService *ExchangeRateService
ratePlanService rateplan.Service
logger *logrus.Logger
baseCurrency string
logger *logrus.Logger
baseCurrency string
}
// NewRatePlanCurrencyIntegrator creates a new rate plan currency integrator
func NewRatePlanCurrencyIntegrator(
billingService BillingService,
billingService currency.BillingService,
exchangeService *ExchangeRateService,
ratePlanService rateplan.Service,
logger *logrus.Logger,
@@ -30,9 +31,9 @@ func NewRatePlanCurrencyIntegrator(
return &RatePlanCurrencyIntegrator{
billingService: billingService,
exchangeService: exchangeService,
ratePlanService: rateplanService,
logger: logger,
baseCurrency: baseCurrency,
ratePlanService: ratePlanService,
logger: logger,
baseCurrency: baseCurrency,
}
}
@@ -47,7 +48,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
// Convert price to requested currency if needed
subscriptionPrice := plan.BasePrice
exchangeRate := 1.0
if currency != plan.Currency {
conversion, err := rpci.exchangeService.ConvertAmount(ctx, plan.BasePrice, plan.Currency, currency)
if err != nil {
@@ -60,10 +61,10 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
// Create subscription with currency information
subscription := &rateplan.RatePlanSubscription{
ProfileID: profileID,
RatePlanID: planID,
Status: rateplan.SubscriptionStatusActive,
StartedAt: time.Now(),
ProfileID: profileID,
RatePlanID: planID,
Status: rateplan.SubscriptionStatusActive,
StartedAt: time.Now(),
Metadata: map[string]interface{}{
"original_currency": plan.Currency,
"subscription_currency": currency,
@@ -89,13 +90,13 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
}
// Process initial billing
billingReq := &BillingRequest{
billingReq := &currency.BillingRequest{
ProfileID: profileID,
SubscriptionID: createdSubscription.ID,
Amount: subscriptionPrice,
Currency: currency,
Amount: subscriptionPrice,
Currency: currency,
Description: fmt.Sprintf("Initial subscription to %s", plan.Name),
BillingDate: time.Now(),
BillingDate: time.Now(),
}
_, err = rpci.billingService.ProcessBilling(ctx, billingReq)
@@ -115,7 +116,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
}
// CalculatePlanCostInCurrency calculates the cost of a rate plan in a specific currency
func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.Context, planID string, currency string, usageData *rateplan.RatePlanUsage) (*BillingSummary, error) {
func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.Context, planID string, currency string, usageData *rateplan.RatePlanUsage) (*currency.BillingSummary, error) {
// Get the rate plan
plan, err := rpci.ratePlanService.GetRatePlan(ctx, planID)
if err != nil {
@@ -138,7 +139,7 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.
// Convert to requested currency
convertedCost := baseCost
exchangeRate := 1.0
if currency != plan.Currency {
conversion, err := rpci.exchangeService.ConvertAmount(ctx, baseCost, plan.Currency, currency)
if err != nil {
@@ -149,15 +150,15 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.
}
// Create billing summary
summary := &BillingSummary{
ProfileID: usageData.ProfileID,
TotalAmount: convertedCost,
Currency: currency,
BaseTotalAmount: baseCost,
BaseCurrency: plan.Currency,
summary := &currency.BillingSummary{
ProfileID: usageData.ProfileID,
TotalAmount: convertedCost,
Currency: currency,
BaseTotalAmount: baseCost,
BaseCurrency: plan.Currency,
TransactionCount: 1,
FromDate: time.Now().AddDate(0, -1, 0),
ToDate: time.Now(),
FromDate: time.Now().AddDate(0, -1, 0),
ToDate: time.Now(),
Breakdown: map[string]interface{}{
"plan_id": planID,
"plan_name": plan.Name,
@@ -1,4 +1,4 @@
package currency
package services
import (
"context"
@@ -85,7 +85,7 @@ func (rpci *RatePlanCurrencyIntegrator) UpdatePlanCurrency(ctx context.Context,
if err != nil {
return fmt.Errorf("failed to update rate plan: %w", err)
}
// Use the updated plan for logging
plan = updatedPlan
@@ -145,7 +145,7 @@ func (rpci *RatePlanCurrencyIntegrator) GetCurrencyUsageForPlan(ctx context.Cont
// Count currencies
currencyUsage := make(map[string]int64)
for _, subscription := range subscriptions {
currency := rpci.baseCurrency // Default to base currency
if subscription.Metadata != nil {
@@ -1,4 +1,4 @@
package service
package services
import (
"context"
@@ -1,4 +1,4 @@
package service
package services
import (
"context"
@@ -1,4 +1,4 @@
package rateplan
package services
import (
"context"
@@ -1,4 +1,4 @@
package rateplan
package services
import (
"context"
@@ -1,4 +1,4 @@
package rateplan
package services
import (
"fmt"
@@ -1,4 +1,4 @@
package rateplan
package services
import (
"context"
@@ -1,4 +1,4 @@
package service
package services
import (
"context"