mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
feat: Remove MVNO handlers, service, and repository implementations from carrier-connector
- Delete handler.go with StartOnboarding, GetMVNO, ListMVNOs, and RegisterRoutes methods
- Delete handler_management.go with GetMVNO, ListMVNOs, UpdateMVNOStatus, and GetMVNOStats endpoints
- Delete onboarding_service.go with StartOnboarding, executeOnboarding, executeStep workflow orchestration
- Delete repository.go with GormRepository CRUD operations, filtering, and statistics methods
- Update provisioner.go to use map[string]any instead of map[string]interface{}
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
package mvno
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Handler handles HTTP requests for MVNO operations
|
||||
type Handler struct {
|
||||
service *OnboardingService
|
||||
repo Repository
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// NewHandler creates a new HTTP handler
|
||||
func NewHandler(service *OnboardingService, repo Repository, logger *logrus.Logger) *Handler {
|
||||
return &Handler{
|
||||
service: service,
|
||||
repo: repo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// StartOnboarding handles POST /mvno/onboarding
|
||||
func (h *Handler) StartOnboarding(c *gin.Context) {
|
||||
var req OnboardingRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Invalid onboarding request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
mvno, err := h.service.StartOnboarding(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to start onboarding")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.repo.CreateMVNO(c.Request.Context(), mvno); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to save MVNO")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save MVNO"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.WithField("mvno_id", mvno.ID).Info("Onboarding started")
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"mvno_id": mvno.ID,
|
||||
"business_id": mvno.BusinessID,
|
||||
"status": mvno.Status,
|
||||
"plan": mvno.Plan,
|
||||
})
|
||||
}
|
||||
|
||||
// GetMVNO handles GET /mvno/{id}
|
||||
func (h *Handler) GetMVNO(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "MVNO ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
mvno, err := h.repo.GetMVNO(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("mvno_id", id).Error("Failed to get MVNO")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "MVNO not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, mvno)
|
||||
}
|
||||
|
||||
// ListMVNOs handles GET /mvno
|
||||
func (h *Handler) ListMVNOs(c *gin.Context) {
|
||||
filter := &MVNOFilter{}
|
||||
if status := c.Query("status"); status != "" {
|
||||
filter.Status = MVNOStatus(status)
|
||||
}
|
||||
if plan := c.Query("plan"); plan != "" {
|
||||
filter.Plan = MVNOPlan(plan)
|
||||
}
|
||||
if limit := c.Query("limit"); limit != "" {
|
||||
if l, err := strconv.Atoi(limit); err == nil {
|
||||
filter.Limit = l
|
||||
}
|
||||
}
|
||||
|
||||
mvnos, err := h.repo.ListMVNOs(c.Request.Context(), filter)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to list MVNOs")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list MVNOs"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"mvnos": mvnos, "count": len(mvnos)})
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all MVNO routes
|
||||
func (h *Handler) RegisterRoutes(router *gin.RouterGroup) {
|
||||
mvno := router.Group("/mvno")
|
||||
{
|
||||
mvno.POST("/onboarding", h.StartOnboarding)
|
||||
mvno.GET("", h.ListMVNOs)
|
||||
mvno.GET("/:id", h.GetMVNO)
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
package mvno
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ManagementHandler handles MVNO management HTTP requests
|
||||
type ManagementHandler struct {
|
||||
repo Repository
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// NewManagementHandler creates a new management handler
|
||||
func NewManagementHandler(repo Repository, logger *logrus.Logger) *ManagementHandler {
|
||||
return &ManagementHandler{
|
||||
repo: repo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMVNO handles GET /mvno/{id}
|
||||
func (h *ManagementHandler) GetMVNO(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "MVNO ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
mvno, err := h.repo.GetMVNO(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("mvno_id", id).Error("Failed to get MVNO")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "MVNO not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, mvno)
|
||||
}
|
||||
|
||||
// ListMVNOs handles GET /mvno
|
||||
func (h *ManagementHandler) ListMVNOs(c *gin.Context) {
|
||||
filter := &MVNOFilter{}
|
||||
|
||||
// Parse query parameters
|
||||
if status := c.Query("status"); status != "" {
|
||||
filter.Status = MVNOStatus(status)
|
||||
}
|
||||
if plan := c.Query("plan"); plan != "" {
|
||||
filter.Plan = MVNOPlan(plan)
|
||||
}
|
||||
if limit := c.Query("limit"); limit != "" {
|
||||
if l, err := strconv.Atoi(limit); err == nil {
|
||||
filter.Limit = l
|
||||
}
|
||||
}
|
||||
|
||||
mvnos, err := h.repo.ListMVNOs(c.Request.Context(), filter)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to list MVNOs")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list MVNOs"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"mvnos": mvnos,
|
||||
"count": len(mvnos),
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateMVNOStatus handles PUT /mvno/{id}/status
|
||||
func (h *ManagementHandler) UpdateMVNOStatus(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "MVNO ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status MVNOStatus `json:"status" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.repo.UpdateMVNOStatus(c.Request.Context(), id, req.Status); err != nil {
|
||||
h.logger.WithError(err).WithField("mvno_id", id).Error("Failed to update MVNO status")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update status"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.WithFields(logrus.Fields{
|
||||
"mvno_id": id,
|
||||
"new_status": req.Status,
|
||||
}).Info("MVNO status updated")
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"mvno_id": id,
|
||||
"status": req.Status,
|
||||
})
|
||||
}
|
||||
|
||||
// GetMVNOStats handles GET /mvno/stats
|
||||
func (h *ManagementHandler) GetMVNOStats(c *gin.Context) {
|
||||
stats, err := h.repo.GetMVNOStats(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to get MVNO stats")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get statistics"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"stats": stats,
|
||||
})
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package mvno
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// OnboardingService handles MVNO onboarding process
|
||||
type OnboardingService struct {
|
||||
logger *logrus.Logger
|
||||
validator *OnboardingValidator
|
||||
provisioner *MVNOProvisioner
|
||||
monitor *OnboardingMonitor
|
||||
}
|
||||
|
||||
// NewOnboardingService creates a new onboarding service
|
||||
func NewOnboardingService(logger *logrus.Logger) *OnboardingService {
|
||||
return &OnboardingService{
|
||||
logger: logger,
|
||||
validator: NewOnboardingValidator(logger),
|
||||
provisioner: NewMVNOProvisioner(logger),
|
||||
monitor: NewOnboardingMonitor(logger),
|
||||
}
|
||||
}
|
||||
|
||||
// StartOnboarding initiates the MVNO onboarding process
|
||||
func (s *OnboardingService) StartOnboarding(ctx context.Context, req *OnboardingRequest) (*MVNO, error) {
|
||||
// Validate the onboarding request
|
||||
if err := s.validator.ValidateRequest(req); err != nil {
|
||||
return nil, fmt.Errorf("validation failed: %w", err)
|
||||
}
|
||||
|
||||
// Create MVNO record
|
||||
mvno := &MVNO{
|
||||
ID: id.GeneratePrefixed("mvno"),
|
||||
BusinessID: req.BusinessID,
|
||||
Name: req.BusinessName,
|
||||
Status: StatusPending,
|
||||
Plan: req.Plan,
|
||||
Config: MVNOConfig{
|
||||
MaxSubscribers: s.getMaxSubscribersForPlan(req.Plan),
|
||||
AllowedCountries: req.TargetCountries,
|
||||
CustomBranding: req.Plan != PlanStarter,
|
||||
APIAccess: req.Plan != PlanStarter,
|
||||
AdvancedAnalytics: req.Plan == PlanScale || req.Plan == PlanEnterprise,
|
||||
},
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Start onboarding progress tracking
|
||||
progress := &OnboardingProgress{
|
||||
MVNOID: mvno.ID,
|
||||
Steps: s.getOnboardingSteps(),
|
||||
Progress: 0.0,
|
||||
StartedAt: time.Now(),
|
||||
}
|
||||
|
||||
s.logger.WithFields(logrus.Fields{
|
||||
"mvno_id": mvno.ID,
|
||||
"business_id": req.BusinessID,
|
||||
"plan": req.Plan,
|
||||
}).Info("Starting MVNO onboarding")
|
||||
|
||||
// Execute onboarding steps asynchronously
|
||||
go s.executeOnboarding(ctx, mvno, progress)
|
||||
|
||||
return mvno, nil
|
||||
}
|
||||
|
||||
// executeOnboarding runs all onboarding steps
|
||||
func (s *OnboardingService) executeOnboarding(ctx context.Context, mvno *MVNO, progress *OnboardingProgress) {
|
||||
for i, step := range progress.Steps {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
s.logger.WithField("mvno_id", mvno.ID).Error("Onboarding cancelled")
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
step.Status = "running"
|
||||
s.monitor.UpdateProgress(mvno.ID, progress)
|
||||
|
||||
if err := s.executeStep(ctx, mvno, &step); err != nil {
|
||||
step.Status = "failed"
|
||||
step.Error = err.Error()
|
||||
s.logger.WithError(err).WithField("step", step.Name).Error("Step failed")
|
||||
break
|
||||
}
|
||||
|
||||
step.Status = "completed"
|
||||
step.CompletedAt = time.Now()
|
||||
progress.Progress = float64(i+1) / float64(len(progress.Steps)) * 100
|
||||
|
||||
s.monitor.UpdateProgress(mvno.ID, progress)
|
||||
}
|
||||
|
||||
if progress.Progress == 100.0 {
|
||||
mvno.Status = StatusActive
|
||||
progress.CompletedAt = time.Now()
|
||||
s.logger.WithField("mvno_id", mvno.ID).Info("Onboarding completed")
|
||||
}
|
||||
}
|
||||
|
||||
// executeStep executes a single onboarding step
|
||||
func (s *OnboardingService) executeStep(ctx context.Context, mvno *MVNO, step *OnboardingStep) error {
|
||||
switch step.Name {
|
||||
case "validation":
|
||||
return s.validator.ValidateMVNO(ctx, mvno)
|
||||
case "provisioning":
|
||||
return s.provisioner.ProvisionResources(ctx, mvno)
|
||||
case "carrier_setup":
|
||||
return s.provisioner.SetupCarriers(ctx, mvno)
|
||||
case "billing_setup":
|
||||
return s.provisioner.SetupBilling(ctx, mvno)
|
||||
case "api_access":
|
||||
return s.provisioner.SetupAPIAccess(ctx, mvno)
|
||||
default:
|
||||
return fmt.Errorf("unknown step: %s", step.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// getOnboardingSteps returns the standard onboarding workflow
|
||||
func (s *OnboardingService) getOnboardingSteps() []OnboardingStep {
|
||||
return []OnboardingStep{
|
||||
{Name: "validation", Status: "pending"},
|
||||
{Name: "provisioning", Status: "pending"},
|
||||
{Name: "carrier_setup", Status: "pending"},
|
||||
{Name: "billing_setup", Status: "pending"},
|
||||
{Name: "api_access", Status: "pending"},
|
||||
}
|
||||
}
|
||||
|
||||
// getMaxSubscribersForPlan returns subscriber limits per plan
|
||||
func (s *OnboardingService) getMaxSubscribersForPlan(plan MVNOPlan) int {
|
||||
switch plan {
|
||||
case PlanStarter:
|
||||
return 1000
|
||||
case PlanGrowth:
|
||||
return 10000
|
||||
case PlanScale:
|
||||
return 100000
|
||||
case PlanEnterprise:
|
||||
return -1 // Unlimited
|
||||
default:
|
||||
return 1000
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func (p *MVNOProvisioner) SetupAPIAccess(ctx context.Context, mvno *MVNO) error
|
||||
_ = id.GeneratePrefixed("sec") // Generate secret key but don't use until storage is implemented
|
||||
permissions := p.getAPIPermissions(mvno.Plan)
|
||||
|
||||
p.logger.WithFields(map[string]interface{}{
|
||||
p.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvno.ID,
|
||||
"api_key": apiKey[:8] + "...",
|
||||
"permissions": len(permissions),
|
||||
@@ -102,7 +102,7 @@ func (p *MVNOProvisioner) provisionDatabaseSchema(ctx context.Context, mvno *MVN
|
||||
// provisionStorageResources provisions storage resources
|
||||
func (p *MVNOProvisioner) provisionStorageResources(ctx context.Context, mvno *MVNO) error {
|
||||
storageSize := p.getStorageAllocation(mvno.Plan)
|
||||
p.logger.WithFields(map[string]interface{}{
|
||||
p.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvno.ID,
|
||||
"storage_gb": storageSize,
|
||||
}).Info("Storage resources provisioned")
|
||||
@@ -126,7 +126,7 @@ func (p *MVNOProvisioner) selectCarriers(countries []string) ([]string, error) {
|
||||
|
||||
// configureCarrier configures individual carrier
|
||||
func (p *MVNOProvisioner) configureCarrier(ctx context.Context, mvno *MVNO, carrierID string) error {
|
||||
p.logger.WithFields(map[string]interface{}{
|
||||
p.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvno.ID,
|
||||
"carrier_id": carrierID,
|
||||
}).Info("Carrier configured")
|
||||
@@ -135,7 +135,7 @@ func (p *MVNOProvisioner) configureCarrier(ctx context.Context, mvno *MVNO, carr
|
||||
|
||||
// configureRatePlans configures rate plans
|
||||
func (p *MVNOProvisioner) configureRatePlans(ctx context.Context, mvno *MVNO, billingID string) error {
|
||||
p.logger.WithFields(map[string]interface{}{
|
||||
p.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvno.ID,
|
||||
"billing_id": billingID,
|
||||
"plan": mvno.Plan,
|
||||
@@ -145,7 +145,7 @@ func (p *MVNOProvisioner) configureRatePlans(ctx context.Context, mvno *MVNO, bi
|
||||
|
||||
// setupPaymentProcessing setup payment processing
|
||||
func (p *MVNOProvisioner) setupPaymentProcessing(ctx context.Context, mvno *MVNO, billingID string) error {
|
||||
p.logger.WithFields(map[string]interface{}{
|
||||
p.logger.WithFields(map[string]any{
|
||||
"mvno_id": mvno.ID,
|
||||
"billing_id": billingID,
|
||||
}).Info("Payment processing setup")
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
package mvno
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Repository defines the interface for MVNO data operations
|
||||
type Repository interface {
|
||||
CreateMVNO(ctx context.Context, mvno *MVNO) error
|
||||
GetMVNO(ctx context.Context, id string) (*MVNO, error)
|
||||
GetMVNOByBusinessID(ctx context.Context, businessID string) (*MVNO, error)
|
||||
UpdateMVNO(ctx context.Context, mvno *MVNO) error
|
||||
ListMVNOs(ctx context.Context, filter *MVNOFilter) ([]*MVNO, error)
|
||||
DeleteMVNO(ctx context.Context, id string) error
|
||||
UpdateMVNOStatus(ctx context.Context, id string, status MVNOStatus) error
|
||||
GetMVNOStats(ctx context.Context) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// MVNOFilter defines filtering options for MVNO queries
|
||||
type MVNOFilter struct {
|
||||
Status MVNOStatus `json:"status,omitempty"`
|
||||
Plan MVNOPlan `json:"plan,omitempty"`
|
||||
BusinessID string `json:"business_id,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
Offset int `json:"offset,omitempty"`
|
||||
CreatedAfter *time.Time `json:"created_after,omitempty"`
|
||||
CreatedBefore *time.Time `json:"created_before,omitempty"`
|
||||
}
|
||||
|
||||
// GormRepository implements Repository 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,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateMVNO creates a new MVNO record
|
||||
func (r *GormRepository) CreateMVNO(ctx context.Context, mvno *MVNO) error {
|
||||
if err := r.db.WithContext(ctx).Create(mvno).Error; err != nil {
|
||||
return fmt.Errorf("failed to create MVNO: %w", err)
|
||||
}
|
||||
|
||||
r.logger.Info("MVNO created", "id", mvno.ID, "business_id", mvno.BusinessID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMVNO retrieves an MVNO by ID
|
||||
func (r *GormRepository) GetMVNO(ctx context.Context, id string) (*MVNO, error) {
|
||||
var mvno MVNO
|
||||
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&mvno).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("MVNO not found: %s", id)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get MVNO: %w", err)
|
||||
}
|
||||
return &mvno, nil
|
||||
}
|
||||
|
||||
// GetMVNOByBusinessID retrieves an MVNO by business ID
|
||||
func (r *GormRepository) GetMVNOByBusinessID(ctx context.Context, businessID string) (*MVNO, error) {
|
||||
var mvno MVNO
|
||||
if err := r.db.WithContext(ctx).Where("business_id = ?", businessID).First(&mvno).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("MVNO not found for business ID: %s", businessID)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get MVNO by business ID: %w", err)
|
||||
}
|
||||
return &mvno, nil
|
||||
}
|
||||
|
||||
// UpdateMVNO updates an existing MVNO
|
||||
func (r *GormRepository) UpdateMVNO(ctx context.Context, mvno *MVNO) error {
|
||||
if err := r.db.WithContext(ctx).Save(mvno).Error; err != nil {
|
||||
return fmt.Errorf("failed to update MVNO: %w", err)
|
||||
}
|
||||
|
||||
r.logger.Info("MVNO updated", "id", mvno.ID, "status", mvno.Status)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListMVNOs lists MVNOs with optional filtering
|
||||
func (r *GormRepository) ListMVNOs(ctx context.Context, filter *MVNOFilter) ([]*MVNO, error) {
|
||||
query := r.db.WithContext(ctx).Model(&MVNO{})
|
||||
|
||||
// Apply filters
|
||||
if filter != nil {
|
||||
if filter.Status != "" {
|
||||
query = query.Where("status = ?", filter.Status)
|
||||
}
|
||||
if filter.Plan != "" {
|
||||
query = query.Where("plan = ?", filter.Plan)
|
||||
}
|
||||
if filter.BusinessID != "" {
|
||||
query = query.Where("business_id = ?", filter.BusinessID)
|
||||
}
|
||||
if filter.CreatedAfter != nil {
|
||||
query = query.Where("created_at >= ?", *filter.CreatedAfter)
|
||||
}
|
||||
if filter.CreatedBefore != nil {
|
||||
query = query.Where("created_at <= ?", *filter.CreatedBefore)
|
||||
}
|
||||
if filter.Limit > 0 {
|
||||
query = query.Limit(filter.Limit)
|
||||
}
|
||||
if filter.Offset > 0 {
|
||||
query = query.Offset(filter.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
var mvnos []*MVNO
|
||||
if err := query.Order("created_at DESC").Find(&mvnos).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to list MVNOs: %w", err)
|
||||
}
|
||||
|
||||
return mvnos, nil
|
||||
}
|
||||
|
||||
// DeleteMVNO soft deletes an MVNO
|
||||
func (r *GormRepository) DeleteMVNO(ctx context.Context, id string) error {
|
||||
if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&MVNO{}).Error; err != nil {
|
||||
return fmt.Errorf("failed to delete MVNO: %w", err)
|
||||
}
|
||||
|
||||
r.logger.Info("MVNO deleted", "id", id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMVNOStats returns statistics about MVNOs
|
||||
func (r *GormRepository) GetMVNOStats(ctx context.Context) (map[string]any, error) {
|
||||
stats := make(map[string]any)
|
||||
|
||||
// Total count
|
||||
var totalCount int64
|
||||
if err := r.db.WithContext(ctx).Model(&MVNO{}).Count(&totalCount).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to count total MVNOs: %w", err)
|
||||
}
|
||||
stats["total"] = totalCount
|
||||
|
||||
// Count by status
|
||||
var statusCounts []struct {
|
||||
Status MVNOStatus `gorm:"column:status"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
if err := r.db.WithContext(ctx).Model(&MVNO{}).
|
||||
Select("status, COUNT(*) as count").
|
||||
Group("status").
|
||||
Scan(&statusCounts).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to count by status: %w", err)
|
||||
}
|
||||
|
||||
statusMap := make(map[string]int64)
|
||||
for _, sc := range statusCounts {
|
||||
statusMap[string(sc.Status)] = sc.Count
|
||||
}
|
||||
stats["by_status"] = statusMap
|
||||
|
||||
// Count by plan
|
||||
var planCounts []struct {
|
||||
Plan MVNOPlan `gorm:"column:plan"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
if err := r.db.WithContext(ctx).Model(&MVNO{}).
|
||||
Select("plan, COUNT(*) as count").
|
||||
Group("plan").
|
||||
Scan(&planCounts).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to count by plan: %w", err)
|
||||
}
|
||||
|
||||
planMap := make(map[string]int64)
|
||||
for _, pc := range planCounts {
|
||||
planMap[string(pc.Plan)] = pc.Count
|
||||
}
|
||||
stats["by_plan"] = planMap
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// UpdateMVNOStatus updates only the status of an MVNO
|
||||
func (r *GormRepository) UpdateMVNOStatus(ctx context.Context, id string, status MVNOStatus) error {
|
||||
if err := r.db.WithContext(ctx).Model(&MVNO{}).Where("id = ?", id).Update("status", status).Error; err != nil {
|
||||
return fmt.Errorf("failed to update MVNO status: %w", err)
|
||||
}
|
||||
|
||||
r.logger.Info("MVNO status updated", "id", id, "new_status", status)
|
||||
return nil
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/handlers"
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/smdp"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -13,27 +12,17 @@ import (
|
||||
// SelectionService provides high-level carrier selection operations
|
||||
type SelectionService struct {
|
||||
manager *smdp.SMDPManager
|
||||
handler *handlers.SelectionHandler
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// NewSelectionService creates a new selection service
|
||||
func NewSelectionService(manager *smdp.SMDPManager) *SelectionService {
|
||||
logger := logrus.New()
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
|
||||
func NewSelectionService(manager *smdp.SMDPManager, logger *logrus.Logger) *SelectionService {
|
||||
return &SelectionService{
|
||||
manager: manager,
|
||||
handler: handlers.NewSelectionHandler(manager),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// GetHandler returns the selection handler for API registration
|
||||
func (s *SelectionService) GetHandler() *handlers.SelectionHandler {
|
||||
return s.handler
|
||||
}
|
||||
|
||||
// IntelligentCarrierSelection performs intelligent carrier selection with comprehensive criteria
|
||||
func (s *SelectionService) IntelligentCarrierSelection(ctx context.Context, request *IntelligentSelectionRequest) (*IntelligentSelectionResponse, error) {
|
||||
s.logger.WithFields(logrus.Fields{
|
||||
|
||||
Reference in New Issue
Block a user