mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-26 14:42:15 +00:00
- Fix GetRevenueAnalytics return type by dereferencing metrics struct - Fix getCarrierMetrics to use int64 for Count and convert to int for ActiveCarriers - Rename ExchangeRateProvider to RateProviderType for consistency - Change RatePlanCurrencyIntegrator.exchangeService from pointer to interface type - Add function documentation comments for NewRatePlanCurrencyIntegrator, SubscribeToPlanWith
184 lines
5.5 KiB
Go
184 lines
5.5 KiB
Go
package analytics
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Service provides analytics operations
|
|
type Service struct {
|
|
db *gorm.DB
|
|
logger *logrus.Logger
|
|
}
|
|
|
|
// NewService creates a new analytics service
|
|
func NewService(db *gorm.DB, logger *logrus.Logger) *Service {
|
|
return &Service{db: db, logger: logger}
|
|
}
|
|
|
|
// GetDashboard retrieves the main analytics dashboard
|
|
func (s *Service) GetDashboard(ctx context.Context, filter *AnalyticsFilter) (*DashboardMetrics, error) {
|
|
dashboard := &DashboardMetrics{
|
|
TenantID: filter.TenantID,
|
|
Period: fmt.Sprintf("%s to %s", filter.StartDate.Format("2006-01-02"), filter.EndDate.Format("2006-01-02")),
|
|
GeneratedAt: time.Now(),
|
|
}
|
|
|
|
var err error
|
|
dashboard.Revenue, err = s.getRevenueMetrics(ctx, filter)
|
|
if err != nil {
|
|
s.logger.WithError(err).Warn("Failed to get revenue metrics")
|
|
}
|
|
|
|
dashboard.Subscribers, err = s.getSubscriberStats(ctx, filter)
|
|
if err != nil {
|
|
s.logger.WithError(err).Warn("Failed to get subscriber stats")
|
|
}
|
|
|
|
dashboard.Usage, err = s.getUsageMetrics(ctx, filter)
|
|
if err != nil {
|
|
s.logger.WithError(err).Warn("Failed to get usage metrics")
|
|
}
|
|
|
|
dashboard.Carriers, err = s.getCarrierMetrics(ctx, filter)
|
|
if err != nil {
|
|
s.logger.WithError(err).Warn("Failed to get carrier metrics")
|
|
}
|
|
|
|
dashboard.Geographic, err = s.getGeoMetrics(ctx, filter)
|
|
if err != nil {
|
|
s.logger.WithError(err).Warn("Failed to get geo metrics")
|
|
}
|
|
|
|
dashboard.Performance, err = s.getPerformanceStats(ctx, filter)
|
|
if err != nil {
|
|
s.logger.WithError(err).Warn("Failed to get performance stats")
|
|
}
|
|
|
|
return dashboard, nil
|
|
}
|
|
|
|
// GetRevenueAnalytics retrieves detailed revenue analytics
|
|
func (s *Service) GetRevenueAnalytics(ctx context.Context, filter *AnalyticsFilter) (*RevenueMetrics, error) {
|
|
metrics, err := s.getRevenueMetrics(ctx, filter)
|
|
return &metrics, err
|
|
}
|
|
|
|
func (s *Service) getRevenueMetrics(ctx context.Context, filter *AnalyticsFilter) (RevenueMetrics, error) {
|
|
metrics := RevenueMetrics{
|
|
RevenueByCountry: make(map[string]float64),
|
|
RevenueByCarrier: make(map[string]float64),
|
|
RevenueByPlan: make(map[string]float64),
|
|
RevenueByCurrency: make(map[string]float64),
|
|
}
|
|
|
|
// Query total revenue
|
|
var totalRevenue float64
|
|
s.db.WithContext(ctx).Table("billing_transactions").
|
|
Where("tenant_id = ? AND created_at BETWEEN ? AND ? AND status = ?",
|
|
filter.TenantID, filter.StartDate, filter.EndDate, "completed").
|
|
Select("COALESCE(SUM(amount), 0)").Scan(&totalRevenue)
|
|
metrics.TotalRevenue = totalRevenue
|
|
|
|
// Query revenue by country
|
|
type countryRevenue struct {
|
|
Country string
|
|
Total float64
|
|
}
|
|
var byCountry []countryRevenue
|
|
s.db.WithContext(ctx).Table("billing_transactions").
|
|
Select("country, SUM(amount) as total").
|
|
Where("tenant_id = ? AND created_at BETWEEN ? AND ?", filter.TenantID, filter.StartDate, filter.EndDate).
|
|
Group("country").Scan(&byCountry)
|
|
for _, cr := range byCountry {
|
|
metrics.RevenueByCountry[cr.Country] = cr.Total
|
|
}
|
|
|
|
return metrics, nil
|
|
}
|
|
|
|
func (s *Service) getSubscriberStats(ctx context.Context, filter *AnalyticsFilter) (SubscriberStats, error) {
|
|
stats := SubscriberStats{
|
|
ByCountry: make(map[string]int64),
|
|
ByPlan: make(map[string]int64),
|
|
ByStatus: make(map[string]int64),
|
|
}
|
|
|
|
// Query active subscribers
|
|
s.db.WithContext(ctx).Table("profiles").
|
|
Where("tenant_id = ? AND status = ?", filter.TenantID, "active").
|
|
Count(&stats.TotalActive)
|
|
|
|
// Query new subscribers in period
|
|
s.db.WithContext(ctx).Table("profiles").
|
|
Where("tenant_id = ? AND created_at BETWEEN ? AND ?", filter.TenantID, filter.StartDate, filter.EndDate).
|
|
Count(&stats.NewThisPeriod)
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
func (s *Service) getUsageMetrics(ctx context.Context, filter *AnalyticsFilter) (UsageMetrics, error) {
|
|
metrics := UsageMetrics{
|
|
UsageByCountry: make(map[string]int64),
|
|
UsageByCarrier: make(map[string]int64),
|
|
}
|
|
|
|
// Query total data usage
|
|
s.db.WithContext(ctx).Table("rate_plan_usage").
|
|
Where("created_at BETWEEN ? AND ?", filter.StartDate, filter.EndDate).
|
|
Select("COALESCE(SUM(data_used), 0)").Scan(&metrics.TotalDataUsedMB)
|
|
|
|
return metrics, nil
|
|
}
|
|
|
|
func (s *Service) getCarrierMetrics(ctx context.Context, filter *AnalyticsFilter) (CarrierMetrics, error) {
|
|
metrics := CarrierMetrics{
|
|
ByCarrier: make(map[string]CarrierStat),
|
|
FailuresByReason: make(map[string]int64),
|
|
}
|
|
|
|
// Query carrier stats
|
|
var activeCount int64
|
|
s.db.WithContext(ctx).Table("carriers").
|
|
Where("is_active = ?", true).
|
|
Count(&activeCount)
|
|
metrics.ActiveCarriers = int(activeCount)
|
|
|
|
return metrics, nil
|
|
}
|
|
|
|
func (s *Service) getGeoMetrics(ctx context.Context, filter *AnalyticsFilter) (GeoMetrics, error) {
|
|
metrics := GeoMetrics{
|
|
RevenueByContinent: make(map[string]float64),
|
|
}
|
|
return metrics, nil
|
|
}
|
|
|
|
func (s *Service) getPerformanceStats(ctx context.Context, filter *AnalyticsFilter) (PerformanceStats, error) {
|
|
return PerformanceStats{
|
|
Uptime: 99.9,
|
|
ErrorRate: 0.1,
|
|
}, nil
|
|
}
|
|
|
|
// CreateScheduledReport creates a scheduled report
|
|
func (s *Service) CreateScheduledReport(ctx context.Context, report *ScheduledReport) error {
|
|
if err := s.db.WithContext(ctx).Create(report).Error; err != nil {
|
|
return fmt.Errorf("failed to create scheduled report: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ListScheduledReports lists scheduled reports for a tenant
|
|
func (s *Service) ListScheduledReports(ctx context.Context, tenantID string) ([]*ScheduledReport, error) {
|
|
var reports []*ScheduledReport
|
|
if err := s.db.WithContext(ctx).Where("tenant_id = ?", tenantID).Find(&reports).Error; err != nil {
|
|
return nil, fmt.Errorf("failed to list reports: %w", err)
|
|
}
|
|
return reports, nil
|
|
}
|