From d6fa655c21f32d4c41c4136eebeaddb2d130b0a7 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Tue, 24 Mar 2026 15:44:31 +0100 Subject: [PATCH] add new endpoints --- app/delivery/web/api/restricted/carts.go | 69 +++++++++++++++++++ app/model/cart.go | 14 ++-- app/repos/cartsRepo/cartsRepo.go | 55 ++++++++++++++- app/service/cartsService/cartsService.go | 24 +++++++ app/utils/responseErrors/responseErrors.go | 12 ++-- .../20260302163122_create_tables.sql | 2 +- 6 files changed, 162 insertions(+), 14 deletions(-) diff --git a/app/delivery/web/api/restricted/carts.go b/app/delivery/web/api/restricted/carts.go index 7fd7a5f..aeed1ee 100644 --- a/app/delivery/web/api/restricted/carts.go +++ b/app/delivery/web/api/restricted/carts.go @@ -29,7 +29,9 @@ func CartsHandlerRoutes(r fiber.Router) fiber.Router { r.Get("/add-new-cart", handler.AddNewCart) r.Get("/change-cart-name", handler.ChangeCartName) + r.Get("/retrieve-carts-info", handler.RetrieveCartsInfo) r.Get("/retrieve-cart", handler.RetrieveCart) + r.Get("/add-product-to-cart", handler.AddProduct) return r } @@ -75,6 +77,22 @@ func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error { return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) } +func (h *CartsHandler) RetrieveCartsInfo(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + carts_info, err := h.cartsService.RetrieveCartsInfo(userID) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&carts_info, 0, i18n.T_(c, response.Message_OK))) +} + func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { userID, ok := c.Locals("userID").(uint) if !ok { @@ -97,3 +115,54 @@ func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { return c.JSON(response.Make(cart, 0, i18n.T_(c, response.Message_OK))) } + +func (h *CartsHandler) AddProduct(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + cart_id_attribute := c.Query("cart_id") + cart_id, err := strconv.Atoi(cart_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + product_id_attribute := c.Query("product_id") + product_id, err := strconv.Atoi(product_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + product_attribute_id_attribute := c.Query("product_attribute_id") + var product_attribute_id *uint + if product_attribute_id_attribute == "" { + product_attribute_id = nil + } else { + val, err := strconv.Atoi(product_attribute_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + uval := uint(val) + product_attribute_id = &uval + } + + amount_attribute := c.Query("amount") + amount, err := strconv.Atoi(amount_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, uint(amount)) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) +} diff --git a/app/model/cart.go b/app/model/cart.go index c7349be..7e30f0f 100644 --- a/app/model/cart.go +++ b/app/model/cart.go @@ -1,8 +1,8 @@ package model type CustomerCart struct { - CartID uint64 `gorm:"column:cart_id;primaryKey;autoIncrement" json:"cart_id"` - UserID uint64 `gorm:"column:user_id;not null;index" json:"user_id"` + CartID uint `gorm:"column:cart_id;primaryKey;autoIncrement" json:"cart_id"` + UserID uint `gorm:"column:user_id;not null;index" json:"-"` Name *string `gorm:"column:name;size:255" json:"name,omitempty"` Products []CartProduct `gorm:"foreignKey:CartID;references:CartID" json:"products,omitempty"` } @@ -12,11 +12,11 @@ func (CustomerCart) TableName() string { } type CartProduct struct { - ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - CartID uint64 `gorm:"column:cart_id;not null;index" json:"cart_id"` - ProductID uint `gorm:"column:product_id;not null" json:"product_id"` - ProductAttributeID *uint64 `gorm:"column:product_attribute_id" json:"product_attribute_id,omitempty"` - Amount int `gorm:"column:amount;not null" json:"amount"` + ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"-"` + CartID uint `gorm:"column:cart_id;not null;index" json:"-"` + ProductID uint `gorm:"column:product_id;not null" json:"product_id"` + ProductAttributeID *uint `gorm:"column:product_attribute_id" json:"product_attribute_id,omitempty"` + Amount uint `gorm:"column:amount;not null" json:"amount"` } func (CartProduct) TableName() string { diff --git a/app/repos/cartsRepo/cartsRepo.go b/app/repos/cartsRepo/cartsRepo.go index 5db3751..b15700c 100644 --- a/app/repos/cartsRepo/cartsRepo.go +++ b/app/repos/cartsRepo/cartsRepo.go @@ -11,7 +11,10 @@ type UICartsRepo interface { CreateNewCart(user_id uint) (model.CustomerCart, error) UserHasCart(user_id uint, cart_id uint) (uint, error) UpdateCartName(user_id uint, cart_id uint, new_name string) error + RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) + CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error) + AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error } type CartsRepo struct{} @@ -38,7 +41,7 @@ func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) { name = constdata.DEFAULT_NEW_CART_NAME cart := model.CustomerCart{ - UserID: uint64(user_id), + UserID: user_id, Name: &name, } err := db.DB.Create(&cart).Error @@ -69,14 +72,62 @@ func (repo *CartsRepo) UpdateCartName(user_id uint, cart_id uint, new_name strin return err } +func (repo *CartsRepo) RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) { + var carts []model.CustomerCart + + err := db.DB. + Table("b2b_customer_carts"). + Where("user_id = ?", user_id). + Scan(&carts). + Error + + return carts, err +} + func (repo *CartsRepo) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) { var cart model.CustomerCart err := db.DB. - Preload("b2b_carts_products"). + Preload("Products"). Where("user_id = ? AND cart_id = ?", user_id, cart_id). First(&cart). Error return &cart, err } + +func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error) { + var amt uint + + if product_attribute_id == nil { + err := db.DB. + Table("ps_product_shop"). + Select("COUNT(*) AS amt"). + Where("id_product = ?", product_id). + Scan(&amt). + Error + return amt, err + + } else { + err := db.DB. + Table("ps_product_shop AS ps"). + Joins("INNER JOIN ps_product_attribute_shop AS pas ON pas.id_product = ps.id_product"). + Select("COUNT(*) AS amt"). + Where("ps.id_product = ? AND pas.id_product_attribute = ?", product_id, *product_attribute_id). + Scan(&amt). + Error + return amt, err + } +} + +func (repo *CartsRepo) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error { + product := model.CartProduct{ + CartID: cart_id, + ProductID: product_id, + ProductAttributeID: product_attribute_id, + Amount: amount, + } + err := db.DB.Create(&product).Error + + return err +} diff --git a/app/service/cartsService/cartsService.go b/app/service/cartsService/cartsService.go index 7fe0568..c82e7b2 100644 --- a/app/service/cartsService/cartsService.go +++ b/app/service/cartsService/cartsService.go @@ -46,6 +46,10 @@ func (s *CartsService) UpdateCartName(user_id uint, cart_id uint, new_name strin return s.repo.UpdateCartName(user_id, cart_id, new_name) } +func (s *CartsService) RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) { + return s.repo.RetrieveCartsInfo(user_id) +} + func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) { amt, err := s.repo.UserHasCart(user_id, cart_id) if err != nil { @@ -57,3 +61,23 @@ func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.Customer return s.repo.RetrieveCart(user_id, cart_id) } + +func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error { + amt, err := s.repo.UserHasCart(user_id, cart_id) + if err != nil { + return err + } + if amt != 1 { + return responseErrors.ErrUserHasNoSuchCart + } + + amt, err = s.repo.CheckProductExists(product_id, product_attribute_id) + if err != nil { + return err + } + if amt != 1 { + return responseErrors.ErrProductOrItsVariationDoesNotExist + } + + return s.repo.AddProduct(user_id, cart_id, product_id, product_attribute_id, amount) +} diff --git a/app/utils/responseErrors/responseErrors.go b/app/utils/responseErrors/responseErrors.go index b0c1172..f658430 100644 --- a/app/utils/responseErrors/responseErrors.go +++ b/app/utils/responseErrors/responseErrors.go @@ -53,8 +53,9 @@ var ( ErrNoRootFound = errors.New("no root found in categories table") // Typed errors for carts handler - ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached") - ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") + ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached") + ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") + ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist") ) // Error represents an error with HTTP status code @@ -148,7 +149,9 @@ func GetErrorCode(c fiber.Ctx, err error) string { case errors.Is(err, ErrMaxAmtOfCartsReached): return i18n.T_(c, "error.max_amt_of_carts_reached") case errors.Is(err, ErrUserHasNoSuchCart): - return i18n.T_(c, "error.max_amt_of_carts_reached") + return i18n.T_(c, "error.user_has_no_such_cart") + case errors.Is(err, ErrProductOrItsVariationDoesNotExist): + return i18n.T_(c, "error.product_or_its_variation_does_not_exist") default: return i18n.T_(c, "error.err_internal_server_error") @@ -187,7 +190,8 @@ func GetErrorStatus(err error) int { errors.Is(err, ErrBadPaging), errors.Is(err, ErrNoRootFound), errors.Is(err, ErrMaxAmtOfCartsReached), - errors.Is(err, ErrUserHasNoSuchCart): + errors.Is(err, ErrUserHasNoSuchCart), + errors.Is(err, ErrProductOrItsVariationDoesNotExist): return fiber.StatusBadRequest case errors.Is(err, ErrEmailExists): return fiber.StatusConflict diff --git a/i18n/migrations/20260302163122_create_tables.sql b/i18n/migrations/20260302163122_create_tables.sql index 903e36a..c2e104b 100644 --- a/i18n/migrations/20260302163122_create_tables.sql +++ b/i18n/migrations/20260302163122_create_tables.sql @@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS b2b_carts_products ( cart_id BIGINT UNSIGNED NOT NULL, product_id INT UNSIGNED NOT NULL, product_attribute_id BIGINT NULL, - amount INT NOT NULL, + amount INT UNSIGNED NOT NULL, CONSTRAINT fk_carts_products_customer_carts FOREIGN KEY (cart_id) REFERENCES b2b_customer_carts (cart_id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_carts_products_product FOREIGN KEY (product_id) REFERENCES ps_product (id_product) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;