240 lines
6.5 KiB
Go
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)
|
|
}
|