veza/veza-backend-api/internal/handlers/marketplace_handler.go
2026-03-06 19:13:16 +01:00

240 lines
6.5 KiB
Go

package handlers
import (
"errors"
"net/http"
"strings"
"veza-backend-api/internal/core/marketplace"
apperrors "veza-backend-api/internal/errors"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
)
// MarketplaceExtHandler handles wishlist and cart operations
type MarketplaceExtHandler struct {
service *marketplace.Service
commonHandler *CommonHandler
}
// NewMarketplaceExtHandler creates a new MarketplaceExtHandler
func NewMarketplaceExtHandler(service *marketplace.Service, logger *zap.Logger) *MarketplaceExtHandler {
return &MarketplaceExtHandler{
service: service,
commonHandler: NewCommonHandler(logger),
}
}
// --- Wishlist handlers ---
// GetWishlist returns the authenticated user's wishlist
func (h *MarketplaceExtHandler) GetWishlist(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
items, err := h.service.GetWishlist(c.Request.Context(), userID)
if err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get wishlist", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"items": items})
}
// AddToWishlistRequest is the DTO for adding to wishlist
type AddToWishlistRequest struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}
// AddToWishlist adds a product to the user's wishlist
func (h *MarketplaceExtHandler) AddToWishlist(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
var req AddToWishlistRequest
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
RespondWithAppError(c, appErr)
return
}
productID, err := uuid.Parse(req.ProductID)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("Invalid product ID"))
return
}
item, err := h.service.AddToWishlist(c.Request.Context(), userID, productID)
if err != nil {
if errors.Is(err, marketplace.ErrProductNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("Product"))
return
}
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to add to wishlist", err))
return
}
RespondSuccess(c, http.StatusCreated, item)
}
// RemoveFromWishlist removes a product from the user's wishlist
func (h *MarketplaceExtHandler) RemoveFromWishlist(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
productID, err := uuid.Parse(c.Param("productId"))
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("Invalid product ID"))
return
}
if err := h.service.RemoveFromWishlist(c.Request.Context(), userID, productID); err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to remove from wishlist", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "Removed from wishlist"})
}
// --- Cart handlers ---
// GetCart returns the authenticated user's cart
func (h *MarketplaceExtHandler) GetCart(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
items, err := h.service.GetCart(c.Request.Context(), userID)
if err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get cart", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"items": items})
}
// AddToCartRequest is the DTO for adding to cart
type AddToCartRequest struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
Quantity int `json:"quantity"`
}
// AddToCart adds a product to the user's cart
func (h *MarketplaceExtHandler) AddToCart(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
var req AddToCartRequest
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
RespondWithAppError(c, appErr)
return
}
productID, err := uuid.Parse(req.ProductID)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("Invalid product ID"))
return
}
quantity := req.Quantity
if quantity < 1 {
quantity = 1
}
item, err := h.service.AddToCart(c.Request.Context(), userID, productID, quantity)
if err != nil {
if errors.Is(err, marketplace.ErrProductNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("Product"))
return
}
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to add to cart", err))
return
}
RespondSuccess(c, http.StatusCreated, item)
}
// RemoveFromCart removes an item from the user's cart
func (h *MarketplaceExtHandler) RemoveFromCart(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
itemID, err := uuid.Parse(c.Param("id"))
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("Invalid item ID"))
return
}
if err := h.service.RemoveFromCart(c.Request.Context(), userID, itemID); err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to remove from cart", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "Removed from cart"})
}
// ValidatePromo validates a promo code and returns the discount (v0.402 P2)
func (h *MarketplaceExtHandler) ValidatePromo(c *gin.Context) {
code := strings.TrimSpace(c.Param("code"))
if code == "" {
RespondWithAppError(c, apperrors.NewValidationError("Promo code is required"))
return
}
discount, err := h.service.ValidatePromoCode(c.Request.Context(), code)
if err != nil {
if errors.Is(err, marketplace.ErrPromoCodeInvalid) {
RespondWithAppError(c, apperrors.NewNotFoundError("Invalid or expired promo code"))
return
}
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to validate promo code", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{
"code": discount.Code,
"discount_type": discount.DiscountType,
"discount_value_cents": discount.DiscountValueCents,
})
}
// CheckoutRequest DTO for cart checkout (v0.402 P2: promo_code optional)
type CheckoutRequest struct {
PromoCode string `json:"promo_code,omitempty" binding:"omitempty,max=50"`
}
// Checkout converts the user's cart into an order
func (h *MarketplaceExtHandler) Checkout(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
var req CheckoutRequest
_ = c.ShouldBindJSON(&req) // optional body; ignore bind error for empty body
promoCode := strings.TrimSpace(req.PromoCode)
resp, err := h.service.Checkout(c.Request.Context(), userID, promoCode)
if err != nil {
if errors.Is(err, marketplace.ErrPromoCodeInvalid) {
RespondWithAppError(c, apperrors.NewValidationError("Invalid or expired promo code"))
return
}
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Checkout failed", err))
return
}
RespondSuccess(c, http.StatusCreated, resp)
}