mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
feat: Add MVNO onboarding monitor with progress tracking and metrics collection
- Add OnboardingMonitor struct with progress map and mutex for concurrent access - Add UpdateProgress and GetProgress methods for tracking individual MVNO onboarding state - Add GetAllProgress with map copy to prevent concurrent access issues - Add GetActiveOnboardingCount and GetCompletedOnboardingCount for status aggregation - Add GetAverageOnboardingTime calculation with completed onboarding duration tracking - Add GetStepSuccess
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
package mvno
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// OnboardingMonitor tracks onboarding progress and status
|
||||
type OnboardingMonitor struct {
|
||||
logger *logrus.Logger
|
||||
progress map[string]*OnboardingProgress
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewOnboardingMonitor creates a new monitor instance
|
||||
func NewOnboardingMonitor(logger *logrus.Logger) *OnboardingMonitor {
|
||||
return &OnboardingMonitor{
|
||||
logger: logger,
|
||||
progress: make(map[string]*OnboardingProgress),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateProgress updates the onboarding progress for an MVNO
|
||||
func (m *OnboardingMonitor) UpdateProgress(mvnoID string, progress *OnboardingProgress) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.progress[mvnoID] = progress
|
||||
|
||||
m.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvnoID,
|
||||
"progress": progress.Progress,
|
||||
"status": m.getCurrentStatus(progress),
|
||||
}).Info("Onboarding progress updated")
|
||||
}
|
||||
|
||||
// GetProgress retrieves current onboarding progress
|
||||
func (m *OnboardingMonitor) GetProgress(mvnoID string) (*OnboardingProgress, bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
progress, exists := m.progress[mvnoID]
|
||||
return progress, exists
|
||||
}
|
||||
|
||||
// GetAllProgress retrieves progress for all MVNOs
|
||||
func (m *OnboardingMonitor) GetAllProgress() map[string]*OnboardingProgress {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
// Return a copy to avoid concurrent access issues
|
||||
result := make(map[string]*OnboardingProgress)
|
||||
maps.Copy(result, m.progress)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetActiveOnboardingCount returns count of active onboarding processes
|
||||
func (m *OnboardingMonitor) GetActiveOnboardingCount() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := 0
|
||||
for _, progress := range m.progress {
|
||||
if progress.Progress < 100.0 && !progress.CompletedAt.IsZero() {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// GetCompletedOnboardingCount returns count of completed onboardings
|
||||
func (m *OnboardingMonitor) GetCompletedOnboardingCount() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := 0
|
||||
for _, progress := range m.progress {
|
||||
if progress.Progress == 100.0 && !progress.CompletedAt.IsZero() {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// GetAverageOnboardingTime calculates average onboarding duration
|
||||
func (m *OnboardingMonitor) GetAverageOnboardingTime() time.Duration {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var totalDuration time.Duration
|
||||
completedCount := 0
|
||||
|
||||
for _, progress := range m.progress {
|
||||
if progress.Progress == 100.0 && !progress.CompletedAt.IsZero() && !progress.StartedAt.IsZero() {
|
||||
totalDuration += progress.CompletedAt.Sub(progress.StartedAt)
|
||||
completedCount++
|
||||
}
|
||||
}
|
||||
|
||||
if completedCount == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return totalDuration / time.Duration(completedCount)
|
||||
}
|
||||
|
||||
// GetStepSuccessRate calculates success rate for each step
|
||||
func (m *OnboardingMonitor) GetStepSuccessRate() map[string]float64 {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
stepStats := make(map[string]map[string]int)
|
||||
|
||||
// Collect step statistics
|
||||
for _, progress := range m.progress {
|
||||
for _, step := range progress.Steps {
|
||||
if _, exists := stepStats[step.Name]; !exists {
|
||||
stepStats[step.Name] = map[string]int{"completed": 0, "failed": 0, "total": 0}
|
||||
}
|
||||
stepStats[step.Name]["total"]++
|
||||
if step.Status == "completed" {
|
||||
stepStats[step.Name]["completed"]++
|
||||
} else if step.Status == "failed" {
|
||||
stepStats[step.Name]["failed"]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate success rates
|
||||
successRates := make(map[string]float64)
|
||||
for stepName, stats := range stepStats {
|
||||
if stats["total"] > 0 {
|
||||
successRates[stepName] = float64(stats["completed"]) / float64(stats["total"]) * 100
|
||||
}
|
||||
}
|
||||
|
||||
return successRates
|
||||
}
|
||||
|
||||
// CleanupOldProgress removes old completed progress records
|
||||
func (m *OnboardingMonitor) CleanupOldProgress(maxAge time.Duration) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
cutoff := time.Now().Add(-maxAge)
|
||||
for mvnoID, progress := range m.progress {
|
||||
if progress.Progress == 100.0 && !progress.CompletedAt.IsZero() && progress.CompletedAt.Before(cutoff) {
|
||||
delete(m.progress, mvnoID)
|
||||
m.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvnoID,
|
||||
"completed_at": progress.CompletedAt,
|
||||
}).Info("Cleaned up old onboarding progress")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFailedOnboardings returns list of failed onboardings
|
||||
func (m *OnboardingMonitor) GetFailedOnboardings() []string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var failed []string
|
||||
for mvnoID, progress := range m.progress {
|
||||
hasFailed := false
|
||||
for _, step := range progress.Steps {
|
||||
if step.Status == "failed" {
|
||||
hasFailed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasFailed {
|
||||
failed = append(failed, mvnoID)
|
||||
}
|
||||
}
|
||||
return failed
|
||||
}
|
||||
|
||||
// getCurrentStatus determines current status from progress
|
||||
func (m *OnboardingMonitor) getCurrentStatus(progress *OnboardingProgress) string {
|
||||
if progress.Progress == 100.0 {
|
||||
return "completed"
|
||||
}
|
||||
|
||||
if progress.Progress == 0.0 {
|
||||
return "pending"
|
||||
}
|
||||
|
||||
// Check for failed steps
|
||||
for _, step := range progress.Steps {
|
||||
if step.Status == "failed" {
|
||||
return "failed"
|
||||
}
|
||||
}
|
||||
|
||||
return "in_progress"
|
||||
}
|
||||
|
||||
// GetOnboardingMetrics returns comprehensive onboarding metrics
|
||||
func (m *OnboardingMonitor) GetOnboardingMetrics() map[string]any {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
return map[string]any{
|
||||
"active_count": m.GetActiveOnboardingCount(),
|
||||
"completed_count": m.GetCompletedOnboardingCount(),
|
||||
"average_duration": m.GetAverageOnboardingTime().String(),
|
||||
"step_success_rates": m.GetStepSuccessRate(),
|
||||
"failed_count": len(m.GetFailedOnboardings()),
|
||||
"total_onboardings": len(m.progress),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user