mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-22 04:34:27 +00:00
feat: Add pricing handlers with CRUD operations, rule application, price calculation, and analytics endpoints
- Add PricingHandler with CreateRule, GetRule, UpdateRule, DeleteRule, ListRules methods - Add CalculatePrice endpoint for price calculation based on active rules - Add ApplyRules endpoint for applying specific pricing rules to context - Add GetAnalytics endpoint for retrieving pricing analytics by tenant - Add RegisterRoutes method to register pricing routes under /pricing group
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id"
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/pricing"
|
||||
)
|
||||
|
||||
// CreateRule creates a new pricing rule
|
||||
func (h *PricingHandler) CreateRule(c *gin.Context) {
|
||||
var req CreateRuleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Get tenant ID from context (assuming middleware sets it)
|
||||
tenantID := c.GetString("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
rule := &pricing.PricingRule{
|
||||
ID: id.GeneratePrefixed("rule"),
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
TenantID: tenantID,
|
||||
Type: req.Type,
|
||||
Priority: req.Priority,
|
||||
IsActive: true,
|
||||
Conditions: req.Conditions,
|
||||
Actions: req.Actions,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
|
||||
createdRule, err := h.service.CreateRule(c.Request.Context(), rule)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, createdRule)
|
||||
}
|
||||
|
||||
// GetRule retrieves a pricing rule
|
||||
func (h *PricingHandler) GetRule(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "rule ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
rule, err := h.service.GetRule(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "rule not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, rule)
|
||||
}
|
||||
|
||||
// UpdateRule updates a pricing rule
|
||||
func (h *PricingHandler) UpdateRule(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "rule ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateRuleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Get existing rule
|
||||
rule, err := h.service.GetRule(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "rule not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Apply updates
|
||||
if req.Name != nil {
|
||||
rule.Name = *req.Name
|
||||
}
|
||||
if req.Description != nil {
|
||||
rule.Description = *req.Description
|
||||
}
|
||||
if req.Type != nil {
|
||||
rule.Type = *req.Type
|
||||
}
|
||||
if req.Priority != nil {
|
||||
rule.Priority = *req.Priority
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
rule.IsActive = *req.IsActive
|
||||
}
|
||||
if req.Conditions != nil {
|
||||
rule.Conditions = *req.Conditions
|
||||
}
|
||||
if req.Actions != nil {
|
||||
rule.Actions = *req.Actions
|
||||
}
|
||||
if req.Metadata != nil {
|
||||
rule.Metadata = req.Metadata
|
||||
}
|
||||
|
||||
updatedRule, err := h.service.UpdateRule(c.Request.Context(), id, rule)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, updatedRule)
|
||||
}
|
||||
|
||||
// DeleteRule deletes a pricing rule
|
||||
func (h *PricingHandler) DeleteRule(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "rule ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
err := h.service.DeleteRule(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "rule deleted successfully"})
|
||||
}
|
||||
|
||||
// ListRules lists pricing rules
|
||||
func (h *PricingHandler) ListRules(c *gin.Context) {
|
||||
tenantID := c.GetString("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters
|
||||
filter := &pricing.PricingFilter{
|
||||
TenantID: tenantID,
|
||||
}
|
||||
|
||||
if ruleType := c.Query("type"); ruleType != "" {
|
||||
filter.Type = ruleType
|
||||
}
|
||||
|
||||
if isActive := c.Query("is_active"); isActive != "" {
|
||||
if active, err := strconv.ParseBool(isActive); err == nil {
|
||||
filter.IsActive = &active
|
||||
}
|
||||
}
|
||||
|
||||
if priority := c.Query("priority"); priority != "" {
|
||||
if prio, err := strconv.Atoi(priority); err == nil {
|
||||
filter.Priority = &prio
|
||||
}
|
||||
}
|
||||
|
||||
if limit := c.Query("limit"); limit != "" {
|
||||
if lim, err := strconv.Atoi(limit); err == nil {
|
||||
filter.Limit = lim
|
||||
}
|
||||
}
|
||||
|
||||
if offset := c.Query("offset"); offset != "" {
|
||||
if off, err := strconv.Atoi(offset); err == nil {
|
||||
filter.Offset = off
|
||||
}
|
||||
}
|
||||
|
||||
rules, err := h.service.ListRules(c.Request.Context(), filter)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, rules)
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/pricing"
|
||||
)
|
||||
|
||||
// PricingHandler handles pricing-related HTTP requests
|
||||
type PricingHandler struct {
|
||||
service pricing.Service
|
||||
}
|
||||
|
||||
// NewPricingHandler creates a new pricing handler
|
||||
func NewPricingHandler(service pricing.Service) *PricingHandler {
|
||||
return &PricingHandler{service: service}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers pricing routes
|
||||
func (h *PricingHandler) RegisterRoutes(router *gin.RouterGroup) {
|
||||
pricing := router.Group("/pricing")
|
||||
{
|
||||
// Rule management
|
||||
pricing.POST("/rules", h.CreateRule)
|
||||
pricing.GET("/rules/:id", h.GetRule)
|
||||
pricing.PUT("/rules/:id", h.UpdateRule)
|
||||
pricing.DELETE("/rules/:id", h.DeleteRule)
|
||||
pricing.GET("/rules", h.ListRules)
|
||||
|
||||
// Pricing calculations
|
||||
pricing.POST("/calculate", h.CalculatePrice)
|
||||
pricing.POST("/apply-rules", h.ApplyRules)
|
||||
|
||||
// Analytics
|
||||
pricing.GET("/analytics", h.GetAnalytics)
|
||||
}
|
||||
}
|
||||
|
||||
type CreateRuleRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Type pricing.RuleType `json:"type" binding:"required"`
|
||||
Priority int `json:"priority"`
|
||||
Conditions pricing.RuleConditions `json:"conditions"`
|
||||
Actions pricing.RuleActions `json:"actions"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
|
||||
type UpdateRuleRequest struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Type *pricing.RuleType `json:"type,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
IsActive *bool `json:"is_active,omitempty"`
|
||||
Conditions *pricing.RuleConditions `json:"conditions,omitempty"`
|
||||
Actions *pricing.RuleActions `json:"actions,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type CalculatePriceRequest struct {
|
||||
TenantID string `json:"tenant_id" binding:"required"`
|
||||
CustomerID string `json:"customer_id" binding:"required"`
|
||||
ProductID string `json:"product_id" binding:"required"`
|
||||
BasePrice float64 `json:"base_price" binding:"required"`
|
||||
Currency string `json:"currency" binding:"required"`
|
||||
Quantity int `json:"quantity" binding:"required"`
|
||||
Location string `json:"location"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
|
||||
type ApplyRulesRequest struct {
|
||||
Context pricing.PricingContext `json:"context" binding:"required"`
|
||||
Rules []*pricing.PricingRule `json:"rules"`
|
||||
}
|
||||
|
||||
// ApplyRules applies specific rules
|
||||
func (h *PricingHandler) ApplyRules(c *gin.Context) {
|
||||
var req ApplyRulesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.ApplyRules(c.Request.Context(), &req.Context, req.Rules)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
// GetAnalytics retrieves pricing analytics
|
||||
func (h *PricingHandler) GetAnalytics(c *gin.Context) {
|
||||
tenantID := c.GetString("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
analytics, err := h.service.GetAnalytics(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, analytics)
|
||||
}
|
||||
|
||||
|
||||
// CalculatePrice calculates price based on active rules
|
||||
func (h *PricingHandler) CalculatePrice(c *gin.Context) {
|
||||
var req CalculatePriceRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
context := &pricing.PricingContext{
|
||||
TenantID: req.TenantID,
|
||||
CustomerID: req.CustomerID,
|
||||
ProductID: req.ProductID,
|
||||
BasePrice: req.BasePrice,
|
||||
Currency: req.Currency,
|
||||
Quantity: req.Quantity,
|
||||
Location: req.Location,
|
||||
Time: time.Now(),
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
|
||||
result, err := h.service.CalculatePrice(c.Request.Context(), context)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user