mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-27 23:22:15 +00:00
@@ -5,7 +5,6 @@ import (
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/api-server/internal/handlers"
|
||||
"github.com/nutcas3/telecom-platform/apps/api-server/internal/middleware"
|
||||
"github.com/nutcas3/telecom-platform/apps/api-server/internal/rbac"
|
||||
)
|
||||
@@ -74,9 +73,6 @@ func registerV1Routes(router *gin.Engine, d *serverDeps) {
|
||||
registerBillingRoutes(apiProtected, d)
|
||||
registerConfigRoutes(apiProtected, d)
|
||||
registerChaosRoutes(apiProtected, d)
|
||||
registerAnalyticsRoutes(apiProtected)
|
||||
registerSecurityRoutes(apiProtected)
|
||||
registerCurrencyRoutes(apiProtected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,68 +195,3 @@ func registerSubscriberRoutes(api *gin.RouterGroup, d *serverDeps) {
|
||||
w.POST("/:id/suspend", d.subscriberH.SuspendSubscriber)
|
||||
w.POST("/:id/terminate", d.subscriberH.TerminateSubscriber)
|
||||
}
|
||||
|
||||
func registerAnalyticsRoutes(api *gin.RouterGroup) {
|
||||
h := handlers.NewAnalyticsHandler()
|
||||
analytics := api.Group("/analytics")
|
||||
|
||||
// Churn Analysis
|
||||
churn := analytics.Group("/churn")
|
||||
churn.POST("/predict", h.PredictChurn)
|
||||
churn.GET("/metrics", h.GetChurnMetrics)
|
||||
churn.GET("/at-risk", h.GetAtRiskCustomers)
|
||||
|
||||
// Market Analytics
|
||||
market := analytics.Group("/market")
|
||||
market.GET("/metrics", h.GetMarketMetrics)
|
||||
market.GET("/competitors", h.GetCompetitors)
|
||||
market.GET("/opportunities", h.GetMarketOpportunities)
|
||||
|
||||
// Predictive Maintenance
|
||||
maintenance := analytics.Group("/maintenance")
|
||||
maintenance.GET("/metrics", h.GetMaintenanceMetrics)
|
||||
maintenance.GET("/assets", h.GetAssetsHealth)
|
||||
maintenance.GET("/alerts", h.GetMaintenanceAlerts)
|
||||
maintenance.POST("/predict/:asset_id", h.PredictFailure)
|
||||
|
||||
// Pricing Optimization
|
||||
pricing := analytics.Group("/pricing")
|
||||
pricing.GET("/metrics", h.GetPricingMetrics)
|
||||
pricing.POST("/optimize", h.OptimizePricing)
|
||||
pricing.GET("/elasticity", h.GetPriceElasticity)
|
||||
}
|
||||
|
||||
func registerSecurityRoutes(api *gin.RouterGroup) {
|
||||
h := handlers.NewSecurityHandler()
|
||||
security := api.Group("/security")
|
||||
|
||||
// Fraud Detection
|
||||
fraud := security.Group("/fraud")
|
||||
fraud.POST("/analyze", h.AnalyzeTransaction)
|
||||
fraud.POST("/alerts", h.GetFraudAlerts)
|
||||
fraud.PUT("/alerts/:id", h.UpdateAlertStatus)
|
||||
fraud.GET("/metrics", h.GetFraudMetrics)
|
||||
fraud.GET("/patterns", h.GetFraudPatterns)
|
||||
|
||||
// SIM Swap Protection
|
||||
simswap := security.Group("/simswap")
|
||||
simswap.POST("/verify", h.VerifySIMSwap)
|
||||
simswap.GET("/history/:profile_id", h.GetSIMSwapHistory)
|
||||
}
|
||||
|
||||
func registerCurrencyRoutes(api *gin.RouterGroup) {
|
||||
h := handlers.NewCurrencyHandler()
|
||||
currencyGroup := api.Group("/currency")
|
||||
|
||||
currencyGroup.POST("/convert", h.ConvertCurrency)
|
||||
currencyGroup.GET("/exchange/:from/:to", h.GetExchangeRate)
|
||||
currencyGroup.GET("/exchange/:from/:to/history", h.GetExchangeRateHistory)
|
||||
currencyGroup.GET("/currencies", h.GetSupportedCurrencies)
|
||||
currencyGroup.POST("/exchange/refresh", h.RefreshExchangeRates)
|
||||
|
||||
currencyGroup.POST("/billing", h.ProcessBilling)
|
||||
currencyGroup.GET("/billing/history/:profile_id", h.GetBillingHistory)
|
||||
currencyGroup.GET("/billing/summary/:profile_id", h.GetBillingSummary)
|
||||
currencyGroup.POST("/billing/refund/:transaction_id", h.ProcessRefund)
|
||||
currencyGroup.GET("/billing/analytics", h.GetBillingAnalytics)
|
||||
}
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AnalyticsHandler handles analytics-related HTTP requests
|
||||
type AnalyticsHandler struct{}
|
||||
|
||||
// NewAnalyticsHandler creates a new analytics handler
|
||||
func NewAnalyticsHandler() *AnalyticsHandler {
|
||||
return &AnalyticsHandler{}
|
||||
}
|
||||
|
||||
// ChurnPrediction represents a churn prediction response
|
||||
type ChurnPrediction struct {
|
||||
ProfileID string `json:"profile_id"`
|
||||
RiskLevel string `json:"risk_level"`
|
||||
RiskScore float64 `json:"risk_score"`
|
||||
PredictedChurnDate string `json:"predicted_churn_date,omitempty"`
|
||||
Reasons []string `json:"reasons"`
|
||||
Recommendations []string `json:"recommendations"`
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
}
|
||||
|
||||
// ChurnMetrics represents churn metrics
|
||||
type ChurnMetrics struct {
|
||||
Period string `json:"period"`
|
||||
TotalSubscribers int64 `json:"total_subscribers"`
|
||||
ChurnedSubscribers int64 `json:"churned_subscribers"`
|
||||
ChurnRate float64 `json:"churn_rate_pct"`
|
||||
MonthlyChurnRate float64 `json:"monthly_churn_rate_pct"`
|
||||
AnnualChurnRate float64 `json:"annual_churn_rate_pct"`
|
||||
AverageTenureDays float64 `json:"average_tenure_days"`
|
||||
RiskDistribution map[string]int64 `json:"risk_distribution"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
|
||||
// PredictChurn predicts churn for a profile
|
||||
func (h *AnalyticsHandler) PredictChurn(c *gin.Context) {
|
||||
var req struct {
|
||||
ProfileID string `json:"profile_id" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Simulated churn prediction
|
||||
prediction := ChurnPrediction{
|
||||
ProfileID: req.ProfileID,
|
||||
RiskLevel: "medium",
|
||||
RiskScore: 45.5,
|
||||
PredictedChurnDate: time.Now().AddDate(0, 2, 0).Format("2006-01-02"),
|
||||
Reasons: []string{
|
||||
"Decreased usage over last 30 days",
|
||||
"No recent plan upgrades",
|
||||
"Support tickets increased",
|
||||
},
|
||||
Recommendations: []string{
|
||||
"Offer loyalty discount",
|
||||
"Proactive customer outreach",
|
||||
"Personalized plan recommendation",
|
||||
},
|
||||
LastUpdated: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, prediction)
|
||||
}
|
||||
|
||||
// GetAtRiskCustomers returns customers at risk of churning
|
||||
func (h *AnalyticsHandler) GetAtRiskCustomers(c *gin.Context) {
|
||||
riskLevel := c.DefaultQuery("risk_level", "high")
|
||||
limitStr := c.DefaultQuery("limit", "100")
|
||||
limit, _ := strconv.Atoi(limitStr)
|
||||
if limit > 1000 {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
// Simulated at-risk customers
|
||||
customers := make([]ChurnPrediction, 0)
|
||||
for i := 0; i < min(limit, 10); i++ {
|
||||
customers = append(customers, ChurnPrediction{
|
||||
ProfileID: "profile-" + strconv.Itoa(i+1),
|
||||
RiskLevel: riskLevel,
|
||||
RiskScore: 70.0 + float64(i)*2,
|
||||
PredictedChurnDate: time.Now().AddDate(0, 0, 30+i*7).Format("2006-01-02"),
|
||||
Reasons: []string{"Low engagement", "Billing issues"},
|
||||
Recommendations: []string{"Retention offer", "Account review"},
|
||||
LastUpdated: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, customers)
|
||||
}
|
||||
|
||||
// MarketMetrics represents market analytics
|
||||
type MarketMetrics struct {
|
||||
Period string `json:"period"`
|
||||
TotalMarketSize int64 `json:"total_market_size"`
|
||||
OurSubscribers int64 `json:"our_subscribers"`
|
||||
MarketShare float64 `json:"market_share_pct"`
|
||||
GrowthRate float64 `json:"growth_rate_pct"`
|
||||
ByCountry map[string]any `json:"by_country"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
|
||||
// GetMarketMetrics returns market penetration metrics
|
||||
func (h *AnalyticsHandler) GetMarketMetrics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
metrics := MarketMetrics{
|
||||
Period: period,
|
||||
TotalMarketSize: 5500000000, // Global mobile subscribers ~5.5B
|
||||
OurSubscribers: 150000,
|
||||
MarketShare: 0.0027,
|
||||
GrowthRate: 12.5,
|
||||
ByCountry: map[string]any{
|
||||
"US": map[string]any{
|
||||
"market_size": 330000000,
|
||||
"our_subs": 45000,
|
||||
"penetration": 0.014,
|
||||
"growth_rate": 8.5,
|
||||
},
|
||||
"UK": map[string]any{
|
||||
"market_size": 67000000,
|
||||
"our_subs": 25000,
|
||||
"penetration": 0.037,
|
||||
"growth_rate": 15.2,
|
||||
},
|
||||
"DE": map[string]any{
|
||||
"market_size": 83000000,
|
||||
"our_subs": 30000,
|
||||
"penetration": 0.036,
|
||||
"growth_rate": 11.8,
|
||||
},
|
||||
},
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// MaintenanceMetrics represents predictive maintenance metrics
|
||||
type MaintenanceMetrics struct {
|
||||
Period string `json:"period"`
|
||||
TotalAssets int64 `json:"total_assets"`
|
||||
HealthyAssets int64 `json:"healthy_assets"`
|
||||
AssetsNeedingAttention int64 `json:"assets_needing_attention"`
|
||||
Uptime float64 `json:"uptime_pct"`
|
||||
MeanTimeToFailure float64 `json:"mean_time_to_failure_hours"`
|
||||
MeanTimeToRepair float64 `json:"mean_time_to_repair_hours"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
|
||||
// PricingMetrics represents pricing optimization metrics
|
||||
type PricingMetrics struct {
|
||||
Period string `json:"period"`
|
||||
TotalRevenue float64 `json:"total_revenue"`
|
||||
ARPU float64 `json:"arpu"`
|
||||
PriceElasticity float64 `json:"price_elasticity"`
|
||||
CompetitiveIndex float64 `json:"competitive_index"`
|
||||
OptimizationROI float64 `json:"optimization_roi_pct"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *AnalyticsHandler) GetChurnMetrics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
metrics := ChurnMetrics{
|
||||
Period: period,
|
||||
TotalSubscribers: 150000,
|
||||
ChurnedSubscribers: 2250,
|
||||
ChurnRate: 1.5,
|
||||
MonthlyChurnRate: 1.5,
|
||||
AnnualChurnRate: 18.0,
|
||||
AverageTenureDays: 425,
|
||||
RiskDistribution: map[string]int64{
|
||||
"low": 120000,
|
||||
"medium": 22500,
|
||||
"high": 6000,
|
||||
"critical": 1500,
|
||||
},
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// GetCompetitors returns competitor analysis
|
||||
func (h *AnalyticsHandler) GetCompetitors(c *gin.Context) {
|
||||
competitors := []map[string]any{
|
||||
{
|
||||
"name": "AT&T",
|
||||
"market_share": 35.5,
|
||||
"subscribers": 200000000,
|
||||
"strengths": []string{"Network coverage", "Brand recognition", "Enterprise focus"},
|
||||
"weaknesses": []string{"High prices", "Customer service"},
|
||||
"threat_level": "high",
|
||||
},
|
||||
{
|
||||
"name": "Verizon",
|
||||
"market_share": 32.0,
|
||||
"subscribers": 180000000,
|
||||
"strengths": []string{"Network quality", "5G leadership"},
|
||||
"weaknesses": []string{"Limited international", "Premium pricing"},
|
||||
"threat_level": "high",
|
||||
},
|
||||
{
|
||||
"name": "T-Mobile",
|
||||
"market_share": 21.3,
|
||||
"subscribers": 120000000,
|
||||
"strengths": []string{"Competitive pricing", "Innovation", "Customer experience"},
|
||||
"weaknesses": []string{"Rural coverage"},
|
||||
"threat_level": "medium",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"competitors": competitors})
|
||||
}
|
||||
|
||||
// GetMarketOpportunities returns market opportunities
|
||||
func (h *AnalyticsHandler) GetMarketOpportunities(c *gin.Context) {
|
||||
opportunities := []map[string]any{
|
||||
{
|
||||
"id": "opp-1",
|
||||
"type": "5G Migration",
|
||||
"country": "US",
|
||||
"potential_subs": 50000000,
|
||||
"required_investment": 1000000000,
|
||||
"expected_roi": 25.0,
|
||||
"time_to_market": 24,
|
||||
"confidence": 85.0,
|
||||
},
|
||||
{
|
||||
"id": "opp-2",
|
||||
"type": "IoT Services",
|
||||
"country": "UK",
|
||||
"potential_subs": 20000000,
|
||||
"required_investment": 500000000,
|
||||
"expected_roi": 30.0,
|
||||
"time_to_market": 18,
|
||||
"confidence": 78.0,
|
||||
},
|
||||
{
|
||||
"id": "opp-3",
|
||||
"type": "Enterprise 5G",
|
||||
"country": "DE",
|
||||
"potential_subs": 15000000,
|
||||
"required_investment": 750000000,
|
||||
"expected_roi": 20.0,
|
||||
"time_to_market": 30,
|
||||
"confidence": 72.0,
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"opportunities": opportunities})
|
||||
}
|
||||
|
||||
// GetMaintenanceMetrics returns predictive maintenance metrics
|
||||
func (h *AnalyticsHandler) GetMaintenanceMetrics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
metrics := MaintenanceMetrics{
|
||||
Period: period,
|
||||
TotalAssets: 1250,
|
||||
HealthyAssets: 1180,
|
||||
AssetsNeedingAttention: 70,
|
||||
Uptime: 99.95,
|
||||
MeanTimeToFailure: 8760, // 1 year
|
||||
MeanTimeToRepair: 4, // 4 hours
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// GetAssetsHealth returns assets health status
|
||||
func (h *AnalyticsHandler) GetAssetsHealth(c *gin.Context) {
|
||||
assets := []map[string]any{
|
||||
{"id": "server-1", "name": "Web Server 1", "type": "server", "health_score": 85.0, "status": "healthy"},
|
||||
{"id": "server-2", "name": "Web Server 2", "type": "server", "health_score": 92.0, "status": "healthy"},
|
||||
{"id": "db-1", "name": "Primary Database", "type": "database", "health_score": 78.0, "status": "warning"},
|
||||
{"id": "db-2", "name": "Replica Database", "type": "database", "health_score": 95.0, "status": "healthy"},
|
||||
{"id": "net-1", "name": "Core Switch", "type": "network", "health_score": 88.0, "status": "healthy"},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"assets": assets})
|
||||
}
|
||||
|
||||
// GetMaintenanceAlerts returns maintenance alerts
|
||||
func (h *AnalyticsHandler) GetMaintenanceAlerts(c *gin.Context) {
|
||||
alerts := []map[string]any{
|
||||
{
|
||||
"id": "alert-1",
|
||||
"asset_id": "db-1",
|
||||
"type": "predictive",
|
||||
"severity": "medium",
|
||||
"title": "Database disk space warning",
|
||||
"description": "Disk usage at 78%, predicted to reach 90% in 14 days",
|
||||
"timestamp": time.Now().Add(-2 * time.Hour),
|
||||
},
|
||||
{
|
||||
"id": "alert-2",
|
||||
"asset_id": "server-3",
|
||||
"type": "preventive",
|
||||
"severity": "low",
|
||||
"title": "Scheduled maintenance due",
|
||||
"description": "Server maintenance overdue by 7 days",
|
||||
"timestamp": time.Now().Add(-24 * time.Hour),
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"alerts": alerts})
|
||||
}
|
||||
|
||||
// PredictFailure predicts failure for an asset
|
||||
func (h *AnalyticsHandler) PredictFailure(c *gin.Context) {
|
||||
assetID := c.Param("asset_id")
|
||||
|
||||
prediction := map[string]any{
|
||||
"asset_id": assetID,
|
||||
"failure_probability": 0.15,
|
||||
"predicted_failure": time.Now().AddDate(0, 3, 0).Format("2006-01-02"),
|
||||
"confidence": 82.5,
|
||||
"risk_factors": []string{
|
||||
"Age approaching end of lifecycle",
|
||||
"Increased error rate",
|
||||
"Temperature fluctuations",
|
||||
},
|
||||
"recommendations": []string{
|
||||
"Schedule preventive maintenance",
|
||||
"Monitor closely",
|
||||
"Prepare replacement parts",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, prediction)
|
||||
}
|
||||
|
||||
// GetPricingMetrics returns pricing optimization metrics
|
||||
func (h *AnalyticsHandler) GetPricingMetrics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
metrics := PricingMetrics{
|
||||
Period: period,
|
||||
TotalRevenue: 4500000,
|
||||
ARPU: 30.0,
|
||||
PriceElasticity: -1.2,
|
||||
CompetitiveIndex: 75.0,
|
||||
OptimizationROI: 15.5,
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// OptimizePricing optimizes pricing for rate plans
|
||||
func (h *AnalyticsHandler) OptimizePricing(c *gin.Context) {
|
||||
var req struct {
|
||||
RatePlanIDs []string `json:"rate_plan_ids"`
|
||||
Strategy string `json:"strategy"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
results := make([]map[string]any, 0)
|
||||
for _, planID := range req.RatePlanIDs {
|
||||
results = append(results, map[string]any{
|
||||
"rate_plan_id": planID,
|
||||
"strategy": req.Strategy,
|
||||
"current_price": 29.99,
|
||||
"optimal_price": 32.99,
|
||||
"price_change_pct": 10.0,
|
||||
"expected_revenue": 165000,
|
||||
"expected_demand": 5000,
|
||||
"confidence": 85.0,
|
||||
"reasoning": []string{
|
||||
"Market analysis suggests price increase tolerance",
|
||||
"Competitor prices are higher",
|
||||
"Strong value proposition",
|
||||
},
|
||||
"risks": []string{
|
||||
"Potential short-term churn increase",
|
||||
},
|
||||
"recommendations": []string{
|
||||
"Implement gradually over 2 months",
|
||||
"Monitor churn closely",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"results": results})
|
||||
}
|
||||
|
||||
// GetPriceElasticity returns price elasticity data
|
||||
func (h *AnalyticsHandler) GetPriceElasticity(c *gin.Context) {
|
||||
elasticity := map[string]any{
|
||||
"overall_elasticity": -1.2,
|
||||
"by_segment": map[string]float64{
|
||||
"enterprise": -0.8,
|
||||
"smb": -1.1,
|
||||
"consumer": -1.5,
|
||||
},
|
||||
"by_price_range": map[string]float64{
|
||||
"0-20": -1.8,
|
||||
"20-50": -1.2,
|
||||
"50-100": -0.9,
|
||||
"100+": -0.6,
|
||||
},
|
||||
"generated_at": time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, elasticity)
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CurrencyHandler handles currency and billing HTTP requests
|
||||
type CurrencyHandler struct{}
|
||||
|
||||
// NewCurrencyHandler creates a new currency handler
|
||||
func NewCurrencyHandler() *CurrencyHandler {
|
||||
return &CurrencyHandler{}
|
||||
}
|
||||
|
||||
// ConvertCurrency converts between currencies
|
||||
func (h *CurrencyHandler) ConvertCurrency(c *gin.Context) {
|
||||
var req struct {
|
||||
From string `json:"from" binding:"required"`
|
||||
To string `json:"to" binding:"required"`
|
||||
Amount float64 `json:"amount" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Simulated exchange rates
|
||||
rates := map[string]float64{
|
||||
"USD": 1.0,
|
||||
"EUR": 0.92,
|
||||
"GBP": 0.79,
|
||||
"JPY": 149.50,
|
||||
"CAD": 1.36,
|
||||
"AUD": 1.53,
|
||||
"CHF": 0.88,
|
||||
}
|
||||
|
||||
fromRate := rates[req.From]
|
||||
toRate := rates[req.To]
|
||||
|
||||
if fromRate == 0 || toRate == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported currency"})
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to USD first, then to target currency
|
||||
usdAmount := req.Amount / fromRate
|
||||
converted := usdAmount * toRate
|
||||
rate := toRate / fromRate
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"from": req.From,
|
||||
"to": req.To,
|
||||
"amount": req.Amount,
|
||||
"converted": converted,
|
||||
"rate": rate,
|
||||
"timestamp": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetExchangeRate returns exchange rate between currencies
|
||||
func (h *CurrencyHandler) GetExchangeRate(c *gin.Context) {
|
||||
from := c.Param("from")
|
||||
to := c.Param("to")
|
||||
|
||||
rates := map[string]float64{
|
||||
"USD": 1.0,
|
||||
"EUR": 0.92,
|
||||
"GBP": 0.79,
|
||||
"JPY": 149.50,
|
||||
"CAD": 1.36,
|
||||
"AUD": 1.53,
|
||||
"CHF": 0.88,
|
||||
}
|
||||
|
||||
fromRate := rates[from]
|
||||
toRate := rates[to]
|
||||
|
||||
if fromRate == 0 || toRate == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported currency"})
|
||||
return
|
||||
}
|
||||
|
||||
rate := toRate / fromRate
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"from": from,
|
||||
"to": to,
|
||||
"rate": rate,
|
||||
"timestamp": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetExchangeRateHistory returns exchange rate history
|
||||
func (h *CurrencyHandler) GetExchangeRateHistory(c *gin.Context) {
|
||||
from := c.Param("from")
|
||||
to := c.Param("to")
|
||||
|
||||
// Simulated historical rates
|
||||
history := make([]map[string]any, 0)
|
||||
baseRate := 0.92 // EUR/USD base
|
||||
|
||||
for i := 30; i >= 0; i-- {
|
||||
date := time.Now().AddDate(0, 0, -i)
|
||||
variation := (float64(i%5) - 2) * 0.005 // Small daily variation
|
||||
history = append(history, map[string]any{
|
||||
"from": from,
|
||||
"to": to,
|
||||
"rate": baseRate + variation,
|
||||
"timestamp": date.Format("2006-01-02"),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"history": history})
|
||||
}
|
||||
|
||||
// GetSupportedCurrencies returns list of supported currencies
|
||||
func (h *CurrencyHandler) GetSupportedCurrencies(c *gin.Context) {
|
||||
currencies := []map[string]any{
|
||||
{"code": "USD", "name": "US Dollar", "symbol": "$"},
|
||||
{"code": "EUR", "name": "Euro", "symbol": "€"},
|
||||
{"code": "GBP", "name": "British Pound", "symbol": "£"},
|
||||
{"code": "JPY", "name": "Japanese Yen", "symbol": "¥"},
|
||||
{"code": "CAD", "name": "Canadian Dollar", "symbol": "C$"},
|
||||
{"code": "AUD", "name": "Australian Dollar", "symbol": "A$"},
|
||||
{"code": "CHF", "name": "Swiss Franc", "symbol": "CHF"},
|
||||
{"code": "CNY", "name": "Chinese Yuan", "symbol": "¥"},
|
||||
{"code": "INR", "name": "Indian Rupee", "symbol": "₹"},
|
||||
{"code": "KRW", "name": "South Korean Won", "symbol": "₩"},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"currencies": currencies})
|
||||
}
|
||||
|
||||
// RefreshExchangeRates refreshes exchange rates from external sources
|
||||
func (h *CurrencyHandler) RefreshExchangeRates(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "refreshed",
|
||||
"updated_at": time.Now(),
|
||||
"source": "external_api",
|
||||
"rates_count": 10,
|
||||
})
|
||||
}
|
||||
|
||||
// ProcessBilling processes a billing transaction
|
||||
func (h *CurrencyHandler) ProcessBilling(c *gin.Context) {
|
||||
var req struct {
|
||||
ProfileID string `json:"profile_id" binding:"required"`
|
||||
Amount float64 `json:"amount" binding:"required"`
|
||||
Currency string `json:"currency" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"` // charge, credit, refund
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Type == "" {
|
||||
req.Type = "charge"
|
||||
}
|
||||
|
||||
transaction := map[string]any{
|
||||
"id": "txn-" + time.Now().Format("20060102150405"),
|
||||
"profile_id": req.ProfileID,
|
||||
"amount": req.Amount,
|
||||
"currency": req.Currency,
|
||||
"type": req.Type,
|
||||
"description": req.Description,
|
||||
"status": "completed",
|
||||
"created_at": time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, transaction)
|
||||
}
|
||||
|
||||
// GetBillingHistory returns billing history for a profile
|
||||
func (h *CurrencyHandler) GetBillingHistory(c *gin.Context) {
|
||||
profileID := c.Param("profile_id")
|
||||
|
||||
transactions := []map[string]any{
|
||||
{
|
||||
"id": "txn-001",
|
||||
"profile_id": profileID,
|
||||
"amount": 29.99,
|
||||
"currency": "USD",
|
||||
"type": "charge",
|
||||
"description": "Monthly subscription",
|
||||
"status": "completed",
|
||||
"created_at": time.Now().AddDate(0, 0, -1),
|
||||
},
|
||||
{
|
||||
"id": "txn-002",
|
||||
"profile_id": profileID,
|
||||
"amount": 15.00,
|
||||
"currency": "USD",
|
||||
"type": "charge",
|
||||
"description": "Data top-up",
|
||||
"status": "completed",
|
||||
"created_at": time.Now().AddDate(0, 0, -15),
|
||||
},
|
||||
{
|
||||
"id": "txn-003",
|
||||
"profile_id": profileID,
|
||||
"amount": 29.99,
|
||||
"currency": "USD",
|
||||
"type": "charge",
|
||||
"description": "Monthly subscription",
|
||||
"status": "completed",
|
||||
"created_at": time.Now().AddDate(0, -1, -1),
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"transactions": transactions})
|
||||
}
|
||||
|
||||
// GetBillingSummary returns billing summary for a profile
|
||||
func (h *CurrencyHandler) GetBillingSummary(c *gin.Context) {
|
||||
profileID := c.Param("profile_id")
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
summary := map[string]any{
|
||||
"profile_id": profileID,
|
||||
"period": period,
|
||||
"total_amount": 74.98,
|
||||
"currency": "USD",
|
||||
"transaction_count": 3,
|
||||
"breakdown": map[string]float64{
|
||||
"subscription": 59.98,
|
||||
"data_topup": 15.00,
|
||||
},
|
||||
"average_transaction": 24.99,
|
||||
"generated_at": time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, summary)
|
||||
}
|
||||
|
||||
// ProcessRefund processes a refund for a transaction
|
||||
func (h *CurrencyHandler) ProcessRefund(c *gin.Context) {
|
||||
transactionID := c.Param("transaction_id")
|
||||
|
||||
var req struct {
|
||||
Reason string `json:"reason" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
refund := map[string]any{
|
||||
"id": "ref-" + time.Now().Format("20060102150405"),
|
||||
"original_transaction": transactionID,
|
||||
"amount": 29.99,
|
||||
"currency": "USD",
|
||||
"reason": req.Reason,
|
||||
"status": "completed",
|
||||
"created_at": time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, refund)
|
||||
}
|
||||
|
||||
// GetBillingAnalytics returns billing analytics
|
||||
func (h *CurrencyHandler) GetBillingAnalytics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
analytics := map[string]any{
|
||||
"period": period,
|
||||
"total_revenue": 4500000,
|
||||
"total_transactions": 150000,
|
||||
"average_transaction": 30.0,
|
||||
"by_type": map[string]float64{
|
||||
"subscription": 3500000,
|
||||
"data_topup": 750000,
|
||||
"voice_topup": 150000,
|
||||
"other": 100000,
|
||||
},
|
||||
"by_currency": map[string]float64{
|
||||
"USD": 3000000,
|
||||
"EUR": 1000000,
|
||||
"GBP": 500000,
|
||||
},
|
||||
"growth_rate": 12.5,
|
||||
"churn_impact": -150000,
|
||||
"generated_at": time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, analytics)
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SecurityHandler handles security-related HTTP requests
|
||||
type SecurityHandler struct{}
|
||||
|
||||
// NewSecurityHandler creates a new security handler
|
||||
func NewSecurityHandler() *SecurityHandler {
|
||||
return &SecurityHandler{}
|
||||
}
|
||||
|
||||
// FraudAlert represents a fraud alert
|
||||
type FraudAlert struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Severity string `json:"severity"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
Description string `json:"description"`
|
||||
RiskScore float64 `json:"risk_score"`
|
||||
Evidence []string `json:"evidence"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Status string `json:"status"`
|
||||
ActionsTaken []string `json:"actions_taken"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
|
||||
// FraudMetrics represents fraud detection metrics
|
||||
type FraudMetrics struct {
|
||||
Period string `json:"period"`
|
||||
TotalAlerts int64 `json:"total_alerts"`
|
||||
ResolvedAlerts int64 `json:"resolved_alerts"`
|
||||
FalsePositives int64 `json:"false_positives"`
|
||||
ResolutionRate float64 `json:"resolution_rate_pct"`
|
||||
FalsePositiveRate float64 `json:"false_positive_rate_pct"`
|
||||
ByType map[string]int64 `json:"by_type"`
|
||||
BySeverity map[string]int64 `json:"by_severity"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
|
||||
// AnalyzeTransaction analyzes a transaction for fraud
|
||||
func (h *SecurityHandler) AnalyzeTransaction(c *gin.Context) {
|
||||
var req map[string]any
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
profileID, _ := req["profile_id"].(string)
|
||||
ipAddress, _ := req["ip_address"].(string)
|
||||
|
||||
// Simulated fraud analysis
|
||||
riskScore := 25.0 // Low risk by default
|
||||
|
||||
// Check for suspicious patterns
|
||||
if amount, ok := req["amount"].(float64); ok && amount > 1000 {
|
||||
riskScore += 20
|
||||
}
|
||||
|
||||
alert := FraudAlert{
|
||||
ID: "alert-" + time.Now().Format("20060102150405"),
|
||||
Type: "transaction_analysis",
|
||||
Severity: getSeverityFromScore(riskScore),
|
||||
ProfileID: profileID,
|
||||
Description: "Transaction analyzed for fraud indicators",
|
||||
RiskScore: riskScore,
|
||||
Evidence: []string{
|
||||
"Transaction amount within normal range",
|
||||
"IP address matches historical pattern",
|
||||
"Device fingerprint recognized",
|
||||
},
|
||||
IPAddress: ipAddress,
|
||||
Timestamp: time.Now(),
|
||||
Status: "analyzed",
|
||||
ActionsTaken: []string{"Logged for monitoring"},
|
||||
Metadata: req,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, alert)
|
||||
}
|
||||
|
||||
// GetFraudAlerts returns fraud alerts with filtering
|
||||
func (h *SecurityHandler) GetFraudAlerts(c *gin.Context) {
|
||||
var filter struct {
|
||||
Type string `json:"type"`
|
||||
Severity string `json:"severity"`
|
||||
Status string `json:"status"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
c.ShouldBindJSON(&filter)
|
||||
|
||||
if filter.Limit == 0 || filter.Limit > 100 {
|
||||
filter.Limit = 50
|
||||
}
|
||||
|
||||
// Simulated alerts
|
||||
alerts := []FraudAlert{
|
||||
{
|
||||
ID: "alert-001",
|
||||
Type: "account_takeover",
|
||||
Severity: "high",
|
||||
ProfileID: "profile-123",
|
||||
Description: "Multiple failed login attempts from new IP",
|
||||
RiskScore: 85.0,
|
||||
Evidence: []string{"10 failed logins", "New IP address", "Different country"},
|
||||
IPAddress: "192.168.1.100",
|
||||
Timestamp: time.Now().Add(-2 * time.Hour),
|
||||
Status: "new",
|
||||
},
|
||||
{
|
||||
ID: "alert-002",
|
||||
Type: "payment_fraud",
|
||||
Severity: "medium",
|
||||
ProfileID: "profile-456",
|
||||
Description: "Unusual payment pattern detected",
|
||||
RiskScore: 55.0,
|
||||
Evidence: []string{"Large transaction", "New payment method"},
|
||||
IPAddress: "10.0.0.50",
|
||||
Timestamp: time.Now().Add(-5 * time.Hour),
|
||||
Status: "investigating",
|
||||
},
|
||||
{
|
||||
ID: "alert-003",
|
||||
Type: "sim_swap",
|
||||
Severity: "critical",
|
||||
ProfileID: "profile-789",
|
||||
Description: "SIM swap attempt detected",
|
||||
RiskScore: 95.0,
|
||||
Evidence: []string{"SIM change request", "No prior notification", "High-value account"},
|
||||
IPAddress: "172.16.0.25",
|
||||
Timestamp: time.Now().Add(-30 * time.Minute),
|
||||
Status: "blocked",
|
||||
},
|
||||
}
|
||||
|
||||
// Filter alerts
|
||||
filtered := make([]FraudAlert, 0)
|
||||
for _, alert := range alerts {
|
||||
if filter.Type != "" && alert.Type != filter.Type {
|
||||
continue
|
||||
}
|
||||
if filter.Severity != "" && alert.Severity != filter.Severity {
|
||||
continue
|
||||
}
|
||||
if filter.Status != "" && alert.Status != filter.Status {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, alert)
|
||||
if len(filtered) >= filter.Limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, filtered)
|
||||
}
|
||||
|
||||
// UpdateAlertStatus updates a fraud alert status
|
||||
func (h *SecurityHandler) UpdateAlertStatus(c *gin.Context) {
|
||||
alertID := c.Param("id")
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
Actions []string `json:"actions"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": alertID,
|
||||
"status": req.Status,
|
||||
"actions_taken": req.Actions,
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetFraudMetrics returns fraud detection metrics
|
||||
func (h *SecurityHandler) GetFraudMetrics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
metrics := FraudMetrics{
|
||||
Period: period,
|
||||
TotalAlerts: 1250,
|
||||
ResolvedAlerts: 1100,
|
||||
FalsePositives: 125,
|
||||
ResolutionRate: 88.0,
|
||||
FalsePositiveRate: 10.0,
|
||||
ByType: map[string]int64{
|
||||
"account_takeover": 350,
|
||||
"subscription_fraud": 200,
|
||||
"payment_fraud": 400,
|
||||
"usage_anomaly": 200,
|
||||
"sim_swap": 100,
|
||||
},
|
||||
BySeverity: map[string]int64{
|
||||
"low": 400,
|
||||
"medium": 500,
|
||||
"high": 250,
|
||||
"critical": 100,
|
||||
},
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// GetFraudPatterns returns detected fraud patterns
|
||||
func (h *SecurityHandler) GetFraudPatterns(c *gin.Context) {
|
||||
patterns := []map[string]any{
|
||||
{
|
||||
"id": "pattern-1",
|
||||
"name": "Velocity Attack",
|
||||
"description": "Multiple rapid transactions from same source",
|
||||
"frequency": "high",
|
||||
"indicators": []string{"High transaction rate", "Same IP", "Different accounts"},
|
||||
"mitigation": "Rate limiting, IP blocking",
|
||||
},
|
||||
{
|
||||
"id": "pattern-2",
|
||||
"name": "Account Enumeration",
|
||||
"description": "Systematic probing of account existence",
|
||||
"frequency": "medium",
|
||||
"indicators": []string{"Sequential account checks", "Automated requests"},
|
||||
"mitigation": "CAPTCHA, rate limiting",
|
||||
},
|
||||
{
|
||||
"id": "pattern-3",
|
||||
"name": "SIM Swap Attack",
|
||||
"description": "Unauthorized SIM card replacement",
|
||||
"frequency": "low",
|
||||
"indicators": []string{"Sudden SIM change", "No customer contact", "High-value target"},
|
||||
"mitigation": "Multi-factor verification, cooling period",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"patterns": patterns})
|
||||
}
|
||||
|
||||
// VerifySIMSwap verifies a SIM swap request
|
||||
func (h *SecurityHandler) VerifySIMSwap(c *gin.Context) {
|
||||
var req struct {
|
||||
ProfileID string `json:"profile_id" binding:"required"`
|
||||
MSISDN string `json:"msisdn" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Simulated SIM swap verification
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"profile_id": req.ProfileID,
|
||||
"msisdn": req.MSISDN,
|
||||
"verified": true,
|
||||
"risk_score": 15.0,
|
||||
"risk_level": "low",
|
||||
"last_sim_swap": time.Now().AddDate(-1, 0, 0).Format("2006-01-02"),
|
||||
"checks_passed": []string{
|
||||
"Identity verified",
|
||||
"No recent SIM changes",
|
||||
"Account in good standing",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetSIMSwapHistory returns SIM swap history for a profile
|
||||
func (h *SecurityHandler) GetSIMSwapHistory(c *gin.Context) {
|
||||
profileID := c.Param("profile_id")
|
||||
|
||||
history := []map[string]any{
|
||||
{
|
||||
"id": "swap-001",
|
||||
"profile_id": profileID,
|
||||
"old_iccid": "8901234567890123456",
|
||||
"new_iccid": "8901234567890123457",
|
||||
"timestamp": time.Now().AddDate(-1, 0, 0),
|
||||
"reason": "Device upgrade",
|
||||
"verified": true,
|
||||
"status": "completed",
|
||||
},
|
||||
{
|
||||
"id": "swap-002",
|
||||
"profile_id": profileID,
|
||||
"old_iccid": "8901234567890123455",
|
||||
"new_iccid": "8901234567890123456",
|
||||
"timestamp": time.Now().AddDate(-2, 0, 0),
|
||||
"reason": "Lost device",
|
||||
"verified": true,
|
||||
"status": "completed",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"history": history})
|
||||
}
|
||||
|
||||
func getSeverityFromScore(score float64) string {
|
||||
switch {
|
||||
case score >= 80:
|
||||
return "critical"
|
||||
case score >= 60:
|
||||
return "high"
|
||||
case score >= 40:
|
||||
return "medium"
|
||||
default:
|
||||
return "low"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user