diff --git a/apps/carrier-connector/internal/services/tenant_analytics.go b/apps/carrier-connector/internal/services/tenant_analytics.go index 60e531c..3a1179a 100644 --- a/apps/carrier-connector/internal/services/tenant_analytics.go +++ b/apps/carrier-connector/internal/services/tenant_analytics.go @@ -5,10 +5,10 @@ import ( "fmt" "time" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id" "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/tenant" ) -// GetTenantMetrics retrieves tenant metrics func (s *TenantServiceImpl) GetTenantMetrics(ctx context.Context, tenantID string) (*tenant.TenantMetrics, error) { // Get usage stats usageStats, err := s.GetUsageStats(ctx, tenantID) @@ -76,7 +76,6 @@ func (s *TenantServiceImpl) GetTenantMetrics(ctx context.Context, tenantID strin return metrics, nil } -// GetTenantEvents retrieves tenant events func (s *TenantServiceImpl) GetTenantEvents(ctx context.Context, tenantID string, limit int) ([]*tenant.TenantEvent, error) { events, err := s.repository.ListEvents(ctx, tenantID, limit) if err != nil { @@ -86,7 +85,6 @@ func (s *TenantServiceImpl) GetTenantEvents(ctx context.Context, tenantID string return events, nil } -// LogTenantEvent logs a tenant event func (s *TenantServiceImpl) LogTenantEvent(ctx context.Context, event *tenant.TenantEvent) error { if err := s.repository.CreateEvent(ctx, event); err != nil { return fmt.Errorf("failed to create tenant event: %w", err) @@ -95,30 +93,25 @@ func (s *TenantServiceImpl) LogTenantEvent(ctx context.Context, event *tenant.Te return nil } -// GetTenantDashboard returns dashboard data for a tenant func (s *TenantServiceImpl) GetTenantDashboard(ctx context.Context, tenantID string) (*tenant.TenantDashboard, error) { - // Get usage statistics + usageStats, err := s.GetUsageStats(ctx, tenantID) if err != nil { return nil, fmt.Errorf("failed to get usage stats: %w", err) } - // Get tenant metrics metrics, err := s.GetTenantMetrics(ctx, tenantID) if err != nil { return nil, fmt.Errorf("failed to get tenant metrics: %w", err) } - // Get recent events recentEvents, err := s.repository.ListEvents(ctx, tenantID, 10) if err != nil { return nil, fmt.Errorf("failed to get recent events: %w", err) } - // Build quota status from usage stats quotaStatus := s.buildQuotaStatus(usageStats) - // Build comprehensive dashboard dashboard := &tenant.TenantDashboard{ TenantID: tenantID, UsageStats: usageStats, @@ -131,51 +124,23 @@ func (s *TenantServiceImpl) GetTenantDashboard(ctx context.Context, tenantID str return dashboard, nil } -// GetUsageAnalytics returns detailed usage analytics for a tenant -func (s *TenantServiceImpl) GetUsageAnalytics(ctx context.Context, tenantID string, timeRange string) (*tenant.TenantUsageAnalytics, error) { - startDate, endDate := s.parseTimeRange(timeRange) - - // Get usage statistics - usageStats, err := s.GetUsageStats(ctx, tenantID) - if err != nil { - return nil, fmt.Errorf("failed to get usage stats: %w", err) - } - - // Build comprehensive usage analytics - analytics := &tenant.TenantUsageAnalytics{ - TenantID: tenantID, - TimeRange: timeRange, - StartDate: startDate, - EndDate: endDate, - UsageByType: s.buildUsageByType(usageStats), - Trends: s.buildUsageTrends(tenantID, timeRange), - Peaks: s.buildUsagePeaks(tenantID, timeRange), - } - - return analytics, nil -} - -// GetPerformanceAnalytics returns performance analytics for a tenant func (s *TenantServiceImpl) GetPerformanceAnalytics(ctx context.Context, tenantID string, timeRange string) (*tenant.TenantPerformanceAnalytics, error) { startDate, endDate := s.parseTimeRange(timeRange) - // Get tenant events for performance analysis events, err := s.repository.ListEvents(ctx, tenantID, 1000) if err != nil { return nil, fmt.Errorf("failed to get tenant events: %w", err) } - // Parse API request events apiRequests := s.parseAPIRequestEvents(events) - // Build comprehensive performance analytics analytics := &tenant.TenantPerformanceAnalytics{ TenantID: tenantID, TimeRange: timeRange, StartDate: startDate, EndDate: endDate, APIPerformance: s.calculateAPIPerformance(apiRequests), - ResourcePerformance: s.buildResourcePerformance(tenantID, timeRange), + ResourcePerformance: s.buildResourcePerformance(ctx, tenantID, timeRange), Errors: s.parseErrorEvents(events), SlowQueries: s.parseSlowQueryEvents(events), } @@ -183,7 +148,64 @@ func (s *TenantServiceImpl) GetPerformanceAnalytics(ctx context.Context, tenantI return analytics, nil } -// generateUsageID generates a usage ID func generateUsageID(resourceType, tenantID string) string { - return fmt.Sprintf("usage_%s_%s", resourceType, tenantID) -} \ No newline at end of file + return id.GeneratePrefixed("usage") +} + +func (s *TenantServiceImpl) parseAPIRequestEvents(events []*tenant.TenantEvent) []*tenant.APIRequestEvent { + apiRequests := make([]*tenant.APIRequestEvent, 0) + + for _, event := range events { + if event.EventType == "api_request" { + request := s.parseAPIRequestEvent(event) + apiRequests = append(apiRequests, request) + } + } + + return apiRequests +} + +func (s *TenantServiceImpl) buildResourcePerformance(ctx context.Context, tenantID, timeRange string) map[string]*tenant.ResourcePerformance { + resourcePerformance := make(map[string]*tenant.ResourcePerformance) + + resourceTypes := []string{"users", "profiles", "carriers", "api_calls", "storage"} + + for _, resourceType := range resourceTypes { + performance := &tenant.ResourcePerformance{ + ResourceType: resourceType, + ResponseTime: 150.5, // Mock response time in ms + Throughput: 1000.0, // Mock requests per second + ErrorRate: 2.1, // Mock error rate percentage + } + + resourcePerformance[resourceType] = performance + } + + return resourcePerformance +} + +func (s *TenantServiceImpl) parseErrorEvents(events []*tenant.TenantEvent) []*tenant.ErrorEvent { + errorEvents := make([]*tenant.ErrorEvent, 0) + + for _, event := range events { + if event.EventType == "error" { + errorEvent := s.parseErrorEvent(event) + errorEvents = append(errorEvents, errorEvent) + } + } + + return errorEvents +} + +func (s *TenantServiceImpl) parseSlowQueryEvents(events []*tenant.TenantEvent) []*tenant.SlowQuery { + slowQueries := make([]*tenant.SlowQuery, 0) + + for _, event := range events { + if event.EventType == "slow_query" { + slowQuery := s.parseSlowQueryEvent(event) + slowQueries = append(slowQueries, slowQuery) + } + } + + return slowQueries +} diff --git a/apps/carrier-connector/internal/services/tenant_analytics_usage.go b/apps/carrier-connector/internal/services/tenant_analytics_usage.go index 6ab1fe9..b236dfa 100644 --- a/apps/carrier-connector/internal/services/tenant_analytics_usage.go +++ b/apps/carrier-connector/internal/services/tenant_analytics_usage.go @@ -1,147 +1,201 @@ package services import ( + "context" + "fmt" "time" "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/tenant" ) -// buildUsageByType builds usage analytics by resource type -func (s *TenantServiceImpl) buildUsageByType(usageStats *tenant.TenantUsageStats) map[string]*tenant.ResourceUsageAnalytics { +// buildUsageByType builds usage analytics by resource type using repository data +func (s *TenantServiceImpl) buildUsageByType(ctx context.Context, tenantID string, startDate, endDate time.Time) (map[string]*tenant.ResourceUsageAnalytics, error) { usageByType := make(map[string]*tenant.ResourceUsageAnalytics) + // Get usage statistics from repository + usageStats, err := s.repository.ListUsage(ctx, &tenant.TenantUsageFilter{ + TenantID: tenantID, + PeriodStart: startDate, + PeriodEnd: endDate, + }) + if err != nil { + return nil, fmt.Errorf("failed to get usage statistics: %w", err) + } + // Build analytics for each resource type resourceTypes := []string{"users", "profiles", "carriers", "api_calls", "storage"} for _, resourceType := range resourceTypes { - analytics := &tenant.ResourceUsageAnalytics{ - ResourceType: resourceType, - TotalUsage: s.getMockResourceCount(resourceType), - AverageUsage: s.getMockResourceCount(resourceType) / 30, - PeakUsage: s.getMockResourceCount(resourceType), - PeakTime: time.Now().Add(-2 * time.Hour), - } - + analytics := s.calculateResourceUsageAnalytics(usageStats, resourceType, startDate, endDate) usageByType[resourceType] = analytics } - return usageByType + return usageByType, nil +} + +// calculateResourceUsageAnalytics calculates usage analytics from usage statistics +func (s *TenantServiceImpl) calculateResourceUsageAnalytics(usageRecords []*tenant.TenantUsage, resourceType string, startDate, endDate time.Time) *tenant.ResourceUsageAnalytics { + var totalUsage int + var peakUsage int + var peakTime time.Time + + // Calculate totals and find peak + for _, usage := range usageRecords { + if usage.ResourceType == resourceType { + totalUsage += usage.QuotaUsed + if usage.QuotaUsed > peakUsage { + peakUsage = usage.QuotaUsed + peakTime = usage.PeriodEnd + } + } + } + + // Calculate average daily usage + days := int(endDate.Sub(startDate).Hours() / 24) + if days == 0 { + days = 1 + } + averageUsage := totalUsage / days + + return &tenant.ResourceUsageAnalytics{ + ResourceType: resourceType, + TotalUsage: totalUsage, + AverageUsage: averageUsage, + PeakUsage: peakUsage, + PeakTime: peakTime, + } } // buildUsageTrends builds usage trends over time -func (s *TenantServiceImpl) buildUsageTrends(tenantID, timeRange string) map[string][]*tenant.UsageTrend { +func (s *TenantServiceImpl) buildUsageTrends(ctx context.Context, tenantID, timeRange string) map[string][]*tenant.UsageTrend { trends := make(map[string][]*tenant.UsageTrend) + startDate, endDate := s.parseTimeRange(timeRange) // Build trends for each resource type resourceTypes := []string{"users", "profiles", "carriers", "api_calls", "storage"} for _, resourceType := range resourceTypes { - trendData := make([]*tenant.UsageTrend, 0) - - // Generate mock trend data points - now := time.Now() - for i := 6; i >= 0; i-- { - timestamp := now.AddDate(0, 0, -i) - usage := int(float64(s.getMockResourceCount(resourceType)) * (1.0 + float64(6-i)*0.1)) - - trend := &tenant.UsageTrend{ - Timestamp: timestamp, - Usage: usage, - } - - trendData = append(trendData, trend) - } - + trendData := s.buildUsageTrendData(ctx, tenantID, resourceType, startDate, endDate) trends[resourceType] = trendData } return trends } +// buildUsageTrendData builds trend data for a specific resource type +func (s *TenantServiceImpl) buildUsageTrendData(ctx context.Context, tenantID, resourceType string, startDate, endDate time.Time) []*tenant.UsageTrend { + trendData := make([]*tenant.UsageTrend, 0) + + // Get daily usage data from repository + usageStats, err := s.repository.ListUsage(ctx, &tenant.TenantUsageFilter{ + TenantID: tenantID, + ResourceType: resourceType, + PeriodStart: startDate, + PeriodEnd: endDate, + }) + if err != nil { + return trendData + } + + // Group by day and create trend points + dailyUsage := make(map[time.Time]int) + for _, usage := range usageStats { + day := time.Date(usage.PeriodStart.Year(), usage.PeriodStart.Month(), usage.PeriodStart.Day(), 0, 0, 0, 0, usage.PeriodStart.Location()) + dailyUsage[day] += usage.QuotaUsed + } + + // Create trend points for each day + for day := startDate; day.Before(endDate) || day.Equal(endDate); day = day.AddDate(0, 0, 1) { + usage := dailyUsage[time.Date(day.Year(), day.Month(), day.Day(), 0, 0, 0, 0, day.Location())] + + trend := &tenant.UsageTrend{ + Timestamp: day, + Usage: usage, + } + + trendData = append(trendData, trend) + } + + return trendData +} + // buildUsagePeaks builds usage peak information -func (s *TenantServiceImpl) buildUsagePeaks(tenantID, timeRange string) map[string]*tenant.UsagePeak { +func (s *TenantServiceImpl) buildUsagePeaks(ctx context.Context, tenantID, timeRange string) map[string]*tenant.UsagePeak { peaks := make(map[string]*tenant.UsagePeak) + startDate, endDate := s.parseTimeRange(timeRange) // Build peaks for each resource type resourceTypes := []string{"users", "profiles", "carriers", "api_calls", "storage"} for _, resourceType := range resourceTypes { - peak := &tenant.UsagePeak{ - Timestamp: time.Now().Add(-2 * time.Hour), - Usage: s.getMockResourceCount(resourceType), - Context: map[string]any{ - "peak_hour": 14, // 2 PM - "day_of_week": "Wednesday", - "season": "Q2", - "driver": "business_activity", - }, - } - + peak := s.buildUsagePeakData(ctx, tenantID, resourceType, startDate, endDate) peaks[resourceType] = peak } return peaks } -// parseAPIRequestEvents parses API request events from tenant events -func (s *TenantServiceImpl) parseAPIRequestEvents(events []*tenant.TenantEvent) []*tenant.APIRequestEvent { - apiRequests := make([]*tenant.APIRequestEvent, 0) - - for _, event := range events { - if event.EventType == "api_request" { - request := s.parseAPIRequestEvent(event) - apiRequests = append(apiRequests, request) +// buildUsagePeakData builds peak data for a specific resource type +func (s *TenantServiceImpl) buildUsagePeakData(ctx context.Context, tenantID, resourceType string, startDate, endDate time.Time) *tenant.UsagePeak { + // Get usage statistics from repository + usageStats, err := s.repository.ListUsage(ctx, &tenant.TenantUsageFilter{ + TenantID: tenantID, + ResourceType: resourceType, + PeriodStart: startDate, + PeriodEnd: endDate, + }) + if err != nil { + return &tenant.UsagePeak{ + Timestamp: time.Now(), + Usage: 0, + Context: map[string]any{}, } } - return apiRequests -} + // Find peak usage + var peakUsage int + var peakTime time.Time -// buildResourcePerformance builds resource performance metrics -func (s *TenantServiceImpl) buildResourcePerformance(tenantID, timeRange string) map[string]*tenant.ResourcePerformance { - resourcePerformance := make(map[string]*tenant.ResourcePerformance) - - // Build performance for each resource type - resourceTypes := []string{"users", "profiles", "carriers", "api_calls", "storage"} - - for _, resourceType := range resourceTypes { - performance := &tenant.ResourcePerformance{ - ResourceType: resourceType, - ResponseTime: 150.5, // Mock response time in ms - Throughput: 1000.0, // Mock requests per second - ErrorRate: 2.1, // Mock error rate percentage - } - - resourcePerformance[resourceType] = performance - } - - return resourcePerformance -} - -// parseErrorEvents parses error events from tenant events -func (s *TenantServiceImpl) parseErrorEvents(events []*tenant.TenantEvent) []*tenant.ErrorEvent { - errorEvents := make([]*tenant.ErrorEvent, 0) - - for _, event := range events { - if event.EventType == "error" { - errorEvent := s.parseErrorEvent(event) - errorEvents = append(errorEvents, errorEvent) + for _, usage := range usageStats { + if usage.QuotaUsed > peakUsage { + peakUsage = usage.QuotaUsed + peakTime = usage.PeriodEnd } } - return errorEvents + return &tenant.UsagePeak{ + Timestamp: peakTime, + Usage: peakUsage, + Context: map[string]any{ + "peak_hour": peakTime.Hour(), + "day_of_week": peakTime.Weekday().String(), + "season": "Q2", + "driver": "business_activity", + }, + } } -// parseSlowQueryEvents parses slow query events from tenant events -func (s *TenantServiceImpl) parseSlowQueryEvents(events []*tenant.TenantEvent) []*tenant.SlowQuery { - slowQueries := make([]*tenant.SlowQuery, 0) - for _, event := range events { - if event.EventType == "slow_query" { - slowQuery := s.parseSlowQueryEvent(event) - slowQueries = append(slowQueries, slowQuery) - } +// GetUsageAnalytics returns detailed usage analytics for a tenant +func (s *TenantServiceImpl) GetUsageAnalytics(ctx context.Context, tenantID string, timeRange string) (*tenant.TenantUsageAnalytics, error) { + startDate, endDate := s.parseTimeRange(timeRange) + + // Build usage analytics by type + usageByType, err := s.buildUsageByType(ctx, tenantID, startDate, endDate) + if err != nil { + return nil, fmt.Errorf("failed to build usage analytics: %w", err) } - return slowQueries + // Build comprehensive usage analytics + analytics := &tenant.TenantUsageAnalytics{ + TenantID: tenantID, + TimeRange: timeRange, + StartDate: startDate, + EndDate: endDate, + UsageByType: usageByType, + Trends: s.buildUsageTrends(ctx, tenantID, timeRange), + Peaks: s.buildUsagePeaks(ctx, tenantID, timeRange), + } + + return analytics, nil } diff --git a/apps/carrier-connector/internal/services/tenant_quota.go b/apps/carrier-connector/internal/services/tenant_quota.go index 238d566..5fb775c 100644 --- a/apps/carrier-connector/internal/services/tenant_quota.go +++ b/apps/carrier-connector/internal/services/tenant_quota.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id" "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/tenant" ) @@ -97,7 +98,7 @@ func (s *TenantServiceImpl) GetUsageStats(ctx context.Context, tenantID string) func (s *TenantServiceImpl) UpdateUsage(ctx context.Context, tenantID, resourceType string, amount int) error { // Create usage record usage := &tenant.TenantUsage{ - ID: fmt.Sprintf("usage_%d", time.Now().UnixNano()), + ID: id.GeneratePrefixed("usage"), TenantID: tenantID, ResourceType: resourceType, QuotaUsed: amount, @@ -139,7 +140,7 @@ func (s *TenantServiceImpl) CheckRateLimit(ctx context.Context, tenantCtx *tenan func (s *TenantServiceImpl) RecordAPIUsage(ctx context.Context, tenantID, endpoint string, statusCode int, responseTime time.Duration) error { // Create usage record usage := &tenant.TenantUsage{ - ID: fmt.Sprintf("api_%d", time.Now().UnixNano()), + ID: id.GeneratePrefixed("api"), TenantID: tenantID, ResourceType: "api_calls", QuotaUsed: 1,