Files
telecom-platform/apps/carrier-connector/internal/pricing/optimization_service.go
T
nutcas3 ef374ce122 feat: Refactor pricing optimization service by extracting helper functions to separate file
- Move calculateElasticity, calculateCompetitiveIndex, and calculateOptimizationROI to optimization_opt_service.go
- Move generateAnalysis, calculateConfidence, and calculateChurnRate to optimization_opt_service.go
- Simplify calculatePriceElasticity by removing comments and consolidating logic
- Simplify optimizeForRevenue, optimizeForMarketShare, optimizeForProfitMargin, optimizeForCompetitive, and optimizeForChurn
2026-05-03 13:06:57 +03:00

212 lines
6.6 KiB
Go

package pricing
import (
"context"
"fmt"
"time"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
// OptimizationStrategy represents pricing optimization strategies
type OptimizationStrategy string
const (
StrategyRevenueMax OptimizationStrategy = "revenue_maximization"
StrategyMarketShare OptimizationStrategy = "market_share"
StrategyProfitMargin OptimizationStrategy = "profit_margin"
StrategyCompetitive OptimizationStrategy = "competitive"
StrategyChurnReduction OptimizationStrategy = "churn_reduction"
)
// PricingOptimizationService provides automated pricing optimization
type PricingOptimizationService struct {
db *gorm.DB
logger *logrus.Logger
}
// NewPricingOptimizationService creates a new pricing optimization service
func NewPricingOptimizationService(db *gorm.DB, logger *logrus.Logger) *PricingOptimizationService {
return &PricingOptimizationService{
db: db,
logger: logger,
}
}
// OptimizePricing optimizes pricing for rate plans
func (s *PricingOptimizationService) OptimizePricing(ctx context.Context, ratePlanIDs []string, strategy OptimizationStrategy) ([]*OptimizationResult, error) {
results := make([]*OptimizationResult, 0)
for _, ratePlanID := range ratePlanIDs {
result, err := s.optimizeRatePlan(ctx, ratePlanID, strategy)
if err != nil {
s.logger.WithError(err).Error("Failed to optimize rate plan", "rate_plan_id", ratePlanID)
continue
}
results = append(results, result)
}
return results, nil
}
// optimizeRatePlan optimizes a single rate plan
func (s *PricingOptimizationService) optimizeRatePlan(ctx context.Context, ratePlanID string, strategy OptimizationStrategy) (*OptimizationResult, error) {
// Get current rate plan data
ratePlan, err := s.getRatePlan(ctx, ratePlanID)
if err != nil {
return nil, err
}
// Get historical data
historicalData, err := s.getHistoricalData(ctx, ratePlanID)
if err != nil {
return nil, err
}
// Calculate optimal price based on strategy
optimalPrice := s.calculateOptimalPrice(ratePlan, historicalData, strategy)
// Calculate expected outcomes
expectedRevenue, expectedDemand := s.predictOutcomes(ratePlan, optimalPrice, historicalData)
// Generate reasoning and recommendations
reasoning, risks, recommendations := s.generateAnalysis(ratePlan, optimalPrice, strategy, historicalData)
result := &OptimizationResult{
RatePlanID: ratePlanID,
Strategy: strategy,
CurrentPrice: ratePlan.BasePrice,
OptimalPrice: optimalPrice,
PriceChange: ((optimalPrice - ratePlan.BasePrice) / ratePlan.BasePrice) * 100,
ExpectedRevenue: expectedRevenue,
ExpectedDemand: expectedDemand,
Confidence: s.calculateConfidence(historicalData),
Reasoning: reasoning,
Risks: risks,
Recommendations: recommendations,
GeneratedAt: time.Now(),
}
return result, nil
}
// GetPricingMetrics returns pricing performance metrics
func (s *PricingOptimizationService) GetPricingMetrics(ctx context.Context, period string) (*PricingMetrics, error) {
metrics := &PricingMetrics{
Period: period,
GeneratedAt: time.Now(),
}
// Calculate total revenue
var totalRevenue float64
s.db.WithContext(ctx).Table("billing_transactions").
Where("status = ? AND created_at BETWEEN ? AND ?", "completed",
s.getPeriodStart(period), s.getPeriodEnd(period)).
Select("COALESCE(SUM(amount), 0)").
Scan(&totalRevenue)
metrics.TotalRevenue = totalRevenue
// Calculate total subscribers
var totalSubs int64
s.db.WithContext(ctx).Table("profiles").
Where("status = ?", "active").
Count(&totalSubs)
metrics.TotalSubscribers = totalSubs
// Calculate ARPU
if totalSubs > 0 {
metrics.ARPU = totalRevenue / float64(totalSubs)
}
// Calculate churn rate
metrics.ChurnRate = s.calculateChurnRate(ctx, period)
// Calculate price elasticity
metrics.PriceElasticity = s.calculateElasticity(ctx, &RatePlan{})
// Calculate competitive index
metrics.CompetitiveIndex = s.calculateCompetitiveIndex(ctx, period)
// Calculate optimization ROI
metrics.OptimizationROI = s.calculateOptimizationROI(ctx, period)
return metrics, nil
}
// ApplyOptimization applies pricing optimization
func (s *PricingOptimizationService) ApplyOptimization(ctx context.Context, result *OptimizationResult) error {
// Update rate plan price
err := s.db.WithContext(ctx).Table("rate_plans").
Where("id = ?", result.RatePlanID).
Updates(map[string]interface{}{
"base_price": result.OptimalPrice,
"updated_at": time.Now(),
}).Error
if err != nil {
return fmt.Errorf("failed to update rate plan: %w", err)
}
// Log the optimization
s.logger.WithFields(logrus.Fields{
"rate_plan_id": result.RatePlanID,
"strategy": result.Strategy,
"old_price": result.CurrentPrice,
"new_price": result.OptimalPrice,
"price_change": result.PriceChange,
"expected_revenue": result.ExpectedRevenue,
}).Info("Pricing optimization applied")
return nil
}
// getRatePlan retrieves rate plan data
func (s *PricingOptimizationService) getRatePlan(ctx context.Context, ratePlanID string) (*RatePlan, error) {
var ratePlan RatePlan
err := s.db.WithContext(ctx).Where("id = ?", ratePlanID).First(&ratePlan).Error
if err != nil {
return nil, fmt.Errorf("rate plan not found: %w", err)
}
return &ratePlan, nil
}
// getHistoricalData retrieves historical pricing and demand data
func (s *PricingOptimizationService) getHistoricalData(_ context.Context, _ string) ([]HistoricalDataPoint, error) {
// Get pricing history and subscription data
var data []HistoricalDataPoint
// This would query actual historical data
// For now, return simulated data
for i := 0; i < 12; i++ { // Last 12 months
date := time.Now().AddDate(0, -i, 0)
point := HistoricalDataPoint{
Date: date,
Price: 10.0 + float64(i)*0.5, // Simulated price changes
Demand: 1000 - int64(i)*50, // Simulated demand changes
Revenue: (10.0 + float64(i)*0.5) * float64(1000-int64(i)*50),
}
data = append(data, point)
}
return data, nil
}
// calculateOptimalPrice calculates optimal price based on strategy
func (s *PricingOptimizationService) calculateOptimalPrice(ratePlan *RatePlan, data []HistoricalDataPoint, strategy OptimizationStrategy) float64 {
switch strategy {
case StrategyRevenueMax:
return s.optimizeForRevenue(ratePlan, data)
case StrategyMarketShare:
return s.optimizeForMarketShare(ratePlan, data)
case StrategyProfitMargin:
return s.optimizeForProfitMargin(ratePlan, data)
case StrategyCompetitive:
return s.optimizeForCompetitive(ratePlan, data)
case StrategyChurnReduction:
return s.optimizeForChurnReduction(ratePlan, data)
default:
return ratePlan.BasePrice
}
}