mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-22 12:44:29 +00:00
feat: Add carrier integration layer with optimal carrier selection, rate plan recommendations, and analytics support
- Add CarrierSelectionIntegrator struct with rate plan repository, SMDP manager, and logger fields - Add GetOptimalCarrierWithRatePlan with region filtering, carrier grouping, performance scoring, and combined evaluation - Add RecommendRatePlansForCarrier with carrier validation, active plan filtering, and recommendation creation - Add UpdateCarrierSelectionCriteria with 30-day usage analytics
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package rateplan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/smdp"
|
||||
)
|
||||
|
||||
// CarrierSelectionIntegrator integrates rate plans with carrier selection
|
||||
type CarrierSelectionIntegrator struct {
|
||||
ratePlanRepo Repository
|
||||
smdpManager *smdp.SMDPManager
|
||||
logger Logger
|
||||
}
|
||||
|
||||
// NewCarrierSelectionIntegrator creates a new carrier selection integrator
|
||||
func NewCarrierSelectionIntegrator(ratePlanRepo Repository, smdpManager *smdp.SMDPManager, logger Logger) *CarrierSelectionIntegrator {
|
||||
return &CarrierSelectionIntegrator{
|
||||
ratePlanRepo: ratePlanRepo,
|
||||
smdpManager: smdpManager,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// GetOptimalCarrierWithRatePlan finds the optimal carrier considering both performance and rate plan availability
|
||||
func (csi *CarrierSelectionIntegrator) GetOptimalCarrierWithRatePlan(ctx context.Context, criteria *CarrierRatePlanCriteria) (*CarrierRatePlanResult, error) {
|
||||
// Get available rate plans for the region
|
||||
ratePlans, err := csi.getAvailableRatePlans(ctx, criteria.Region, criteria.PlanType, criteria.MaxBudget)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get available rate plans: %w", err)
|
||||
}
|
||||
|
||||
if len(ratePlans) == 0 {
|
||||
return nil, fmt.Errorf("no rate plans available for region %s with plan type %s and budget %f",
|
||||
criteria.Region, criteria.PlanType, criteria.MaxBudget)
|
||||
}
|
||||
|
||||
// Group rate plans by carrier
|
||||
carrierPlans := csi.groupRatePlansByCarrier(ratePlans)
|
||||
|
||||
// Get carrier status and performance
|
||||
carrierStatus := csi.smdpManager.GetCarrierStatus()
|
||||
|
||||
// Score carriers based on both performance and rate plan availability
|
||||
bestCarrier, bestPlan := csi.scoreCarriersWithPlans(carrierPlans, carrierStatus, criteria)
|
||||
|
||||
if bestCarrier == nil {
|
||||
return nil, fmt.Errorf("no suitable carrier found")
|
||||
}
|
||||
|
||||
result := &CarrierRatePlanResult{
|
||||
Carrier: bestCarrier,
|
||||
RatePlan: bestPlan,
|
||||
TotalScore: csi.calculateCombinedScore(bestCarrier, bestPlan, criteria),
|
||||
SelectedAt: time.Now(),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RecommendRatePlansForCarrier recommends rate plans for a specific carrier
|
||||
func (csi *CarrierSelectionIntegrator) RecommendRatePlansForCarrier(ctx context.Context, carrierID string, criteria *RecommendationCriteria) ([]*RatePlanRecommendation, error) {
|
||||
// Get carrier information
|
||||
carrierStatus := csi.smdpManager.GetCarrierStatus()
|
||||
carrier, exists := carrierStatus[carrierID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("carrier %s not found", carrierID)
|
||||
}
|
||||
|
||||
// Get rate plans for this carrier
|
||||
filter := &RatePlanFilter{
|
||||
CarrierID: carrierID,
|
||||
Region: criteria.Region,
|
||||
PlanType: criteria.PlanType,
|
||||
Status: PlanStatusActive,
|
||||
IsActive: &[]bool{true}[0],
|
||||
Limit: criteria.MaxResults,
|
||||
}
|
||||
|
||||
ratePlans, err := csi.ratePlanRepo.ListRatePlans(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get rate plans: %w", err)
|
||||
}
|
||||
|
||||
// Create recommendations
|
||||
recommendations := make([]*RatePlanRecommendation, 0)
|
||||
for _, plan := range ratePlans {
|
||||
recommendation := csi.createRecommendation(plan, carrier, criteria)
|
||||
recommendations = append(recommendations, recommendation)
|
||||
}
|
||||
|
||||
return recommendations, nil
|
||||
}
|
||||
|
||||
// UpdateCarrierSelectionCriteria updates carrier selection based on rate plan performance
|
||||
func (csi *CarrierSelectionIntegrator) UpdateCarrierSelectionCriteria(ctx context.Context) error {
|
||||
// Get usage analytics for the last 30 days
|
||||
filter := &UsageAnalyticsFilter{
|
||||
StartDate: time.Now().AddDate(0, 0, -30),
|
||||
EndDate: time.Now(),
|
||||
}
|
||||
|
||||
analytics, err := csi.ratePlanRepo.GetUsageAnalytics(ctx, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get usage analytics: %w", err)
|
||||
}
|
||||
|
||||
// Update carrier selection weights based on rate plan performance
|
||||
csi.updateCarrierWeights(analytics)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCarrierRatePlanAnalytics returns analytics for carrier and rate plan performance
|
||||
func (csi *CarrierSelectionIntegrator) GetCarrierRatePlanAnalytics(ctx context.Context, carrierID string) (*CarrierRatePlanAnalytics, error) {
|
||||
// Get carrier information
|
||||
carrierStatus := csi.smdpManager.GetCarrierStatus()
|
||||
carrier, exists := carrierStatus[carrierID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("carrier %s not found", carrierID)
|
||||
}
|
||||
|
||||
// Get rate plans for this carrier
|
||||
filter := &RatePlanFilter{
|
||||
CarrierID: carrierID,
|
||||
Status: PlanStatusActive,
|
||||
IsActive: &[]bool{true}[0],
|
||||
}
|
||||
|
||||
ratePlans, err := csi.ratePlanRepo.ListRatePlans(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get rate plans: %w", err)
|
||||
}
|
||||
|
||||
// Get usage analytics for this carrier's plans
|
||||
planIDs := make([]string, len(ratePlans))
|
||||
for i, plan := range ratePlans {
|
||||
planIDs[i] = plan.ID
|
||||
}
|
||||
|
||||
analytics := &CarrierRatePlanAnalytics{
|
||||
CarrierID: carrierID,
|
||||
CarrierName: carrier.Name,
|
||||
Region: carrier.CountryCode,
|
||||
HealthStatus: string(carrier.HealthStatus),
|
||||
Priority: carrier.Priority,
|
||||
TotalPlans: len(ratePlans),
|
||||
ActivePlans: csi.countActivePlans(ratePlans),
|
||||
GeneratedAt: time.Now(),
|
||||
PlanAnalytics: make([]RatePlanAnalytics, 0),
|
||||
}
|
||||
|
||||
for _, plan := range ratePlans {
|
||||
planAnalytics := csi.getPlanAnalytics(ctx, plan)
|
||||
analytics.PlanAnalytics = append(analytics.PlanAnalytics, *planAnalytics)
|
||||
}
|
||||
|
||||
return analytics, nil
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package rateplan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/smdp"
|
||||
)
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) getAvailableRatePlans(ctx context.Context, region string, planType PlanType, maxBudget float64) ([]*RatePlan, error) {
|
||||
filter := &RatePlanFilter{
|
||||
Region: region,
|
||||
PlanType: planType,
|
||||
Status: PlanStatusActive,
|
||||
IsActive: &[]bool{true}[0],
|
||||
MaxPrice: maxBudget,
|
||||
}
|
||||
|
||||
return csi.ratePlanRepo.ListRatePlans(ctx, filter)
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) groupRatePlansByCarrier(plans []*RatePlan) map[string][]*RatePlan {
|
||||
carrierPlans := make(map[string][]*RatePlan)
|
||||
for _, plan := range plans {
|
||||
carrierPlans[plan.CarrierID] = append(carrierPlans[plan.CarrierID], plan)
|
||||
}
|
||||
return carrierPlans
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) scoreCarriersWithPlans(carrierPlans map[string][]*RatePlan, carrierStatus map[string]*smdp.Carrier, criteria *CarrierRatePlanCriteria) (*smdp.Carrier, *RatePlan) {
|
||||
var bestCarrier *smdp.Carrier
|
||||
var bestPlan *RatePlan
|
||||
bestScore := 0.0
|
||||
|
||||
for carrierID, plans := range carrierPlans {
|
||||
carrier, exists := carrierStatus[carrierID]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
if carrier.HealthStatus != "healthy" {
|
||||
continue
|
||||
}
|
||||
|
||||
bestPlanForCarrier := csi.findBestPlanForCarrier(plans, criteria)
|
||||
if bestPlanForCarrier == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
combinedScore := csi.calculateCombinedScore(carrier, bestPlanForCarrier, criteria)
|
||||
|
||||
if combinedScore > bestScore {
|
||||
bestScore = combinedScore
|
||||
bestCarrier = carrier
|
||||
bestPlan = bestPlanForCarrier
|
||||
}
|
||||
}
|
||||
|
||||
return bestCarrier, bestPlan
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) findBestPlanForCarrier(plans []*RatePlan, criteria *CarrierRatePlanCriteria) *RatePlan {
|
||||
var bestPlan *RatePlan
|
||||
bestScore := 0.0
|
||||
|
||||
for _, plan := range plans {
|
||||
score := csi.scoreRatePlan(plan, criteria)
|
||||
if score > bestScore {
|
||||
bestScore = score
|
||||
bestPlan = plan
|
||||
}
|
||||
}
|
||||
|
||||
return bestPlan
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) scoreRatePlan(plan *RatePlan, criteria *CarrierRatePlanCriteria) float64 {
|
||||
score := 0.0
|
||||
|
||||
if criteria.MaxBudget > 0 {
|
||||
priceScore := 1.0 - (plan.BasePrice / criteria.MaxBudget)
|
||||
if priceScore > 0 {
|
||||
score += priceScore * 0.4
|
||||
}
|
||||
}
|
||||
|
||||
priorityScore := float64(plan.Priority) / 100.0
|
||||
score += priorityScore * 0.3
|
||||
|
||||
if plan.Features != nil {
|
||||
featureScore := float64(len(plan.Features)) / 10.0
|
||||
score += featureScore * 0.2
|
||||
}
|
||||
|
||||
if plan.DataAllowance != nil {
|
||||
dataScore := float64(plan.DataAllowance.Amount) / 10000.0 // Normalize by 10GB
|
||||
if dataScore > 1.0 {
|
||||
dataScore = 1.0
|
||||
}
|
||||
score += dataScore * 0.1
|
||||
}
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) calculateCombinedScore(carrier *smdp.Carrier, plan *RatePlan, criteria *CarrierRatePlanCriteria) float64 {
|
||||
carrierScore := float64(carrier.Priority) / 100.0 * 0.7
|
||||
|
||||
planScore := csi.scoreRatePlan(plan, criteria) * 0.3
|
||||
|
||||
return carrierScore + planScore
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) createRecommendation(plan *RatePlan, carrier *smdp.Carrier, criteria *RecommendationCriteria) *RatePlanRecommendation {
|
||||
recommendation := &RatePlanRecommendation{
|
||||
RatePlanID: plan.ID,
|
||||
RatePlanName: plan.Name,
|
||||
CarrierID: carrier.ID,
|
||||
CarrierName: carrier.Name,
|
||||
Price: plan.BasePrice,
|
||||
Currency: plan.Currency,
|
||||
Relevance: csi.calculateRelevance(plan, criteria),
|
||||
Features: plan.Features,
|
||||
DataAllowance: plan.DataAllowance,
|
||||
VoiceAllowance: plan.VoiceAllowance,
|
||||
SMSAllowance: plan.SMSAllowance,
|
||||
RecommendedAt: time.Now(),
|
||||
}
|
||||
|
||||
return recommendation
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) calculateRelevance(plan *RatePlan, criteria *RecommendationCriteria) float64 {
|
||||
relevance := 0.5 // Base relevance
|
||||
|
||||
if criteria.PreferredData > 0 && plan.DataAllowance != nil {
|
||||
if plan.DataAllowance.Amount >= criteria.PreferredData {
|
||||
relevance += 0.2
|
||||
}
|
||||
}
|
||||
|
||||
if criteria.PreferredVoice > 0 && plan.VoiceAllowance != nil {
|
||||
if plan.VoiceAllowance.Minutes >= criteria.PreferredVoice {
|
||||
relevance += 0.2
|
||||
}
|
||||
}
|
||||
|
||||
if criteria.PreferredSMS > 0 && plan.SMSAllowance != nil {
|
||||
if plan.SMSAllowance.Messages >= criteria.PreferredSMS {
|
||||
relevance += 0.1
|
||||
}
|
||||
}
|
||||
|
||||
if relevance > 1.0 {
|
||||
relevance = 1.0
|
||||
}
|
||||
|
||||
return relevance
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) updateCarrierWeights(analytics *UsageAnalytics) {
|
||||
// This would update the carrier selection weights based on usage patterns
|
||||
// Implementation depends on the specific carrier selection algorithm
|
||||
csi.logger.Info("Updated carrier selection weights based on usage analytics")
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) countActivePlans(plans []*RatePlan) int {
|
||||
count := 0
|
||||
for _, plan := range plans {
|
||||
if plan.IsActive && plan.Status == PlanStatusActive {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (csi *CarrierSelectionIntegrator) getPlanAnalytics(ctx context.Context, plan *RatePlan) *RatePlanAnalytics {
|
||||
subscriptions, err := csi.ratePlanRepo.ListSubscriptions(ctx, "", &SubscriptionFilter{
|
||||
RatePlanID: plan.ID,
|
||||
Status: SubscriptionStatusActive,
|
||||
Limit: 1,
|
||||
})
|
||||
|
||||
subscriptionCount := 0
|
||||
if err == nil {
|
||||
subscriptionCount = len(subscriptions)
|
||||
}
|
||||
|
||||
return &RatePlanAnalytics{
|
||||
RatePlanID: plan.ID,
|
||||
RatePlanName: plan.Name,
|
||||
BasePrice: plan.BasePrice,
|
||||
Currency: plan.Currency,
|
||||
ActiveSubscriptions: subscriptionCount,
|
||||
PlanType: plan.PlanType,
|
||||
BillingCycle: plan.BillingCycle,
|
||||
DataAllowance: plan.DataAllowance,
|
||||
VoiceAllowance: plan.VoiceAllowance,
|
||||
SMSAllowance: plan.SMSAllowance,
|
||||
Features: plan.Features,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user