From 393de36cb2b9fac750ad96dd9ae1dfb7839209b8 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Thu, 9 Apr 2026 14:49:50 +0200 Subject: [PATCH 1/6] favorites --- app/delivery/web/api/restricted/product.go | 8 ++- app/model/product.go | 1 + app/repos/productsRepo/productsRepo.go | 56 +++++++++++++------ app/service/productService/productService.go | 4 +- .../20260302163122_create_tables.sql | 10 ++++ i18n/migrations/20260319163200_procedures.sql | 6 ++ 6 files changed, 65 insertions(+), 20 deletions(-) diff --git a/app/delivery/web/api/restricted/product.go b/app/delivery/web/api/restricted/product.go index ddd8677..2c9d894 100644 --- a/app/delivery/web/api/restricted/product.go +++ b/app/delivery/web/api/restricted/product.go @@ -90,7 +90,13 @@ func (h *ProductsHandler) ListProducts(c fiber.Ctx) error { JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) } - list, err := h.productService.Find(id_lang, paging, filters) + userID, ok := localeExtractor.GetUserID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + list, err := h.productService.Find(id_lang, userID, paging, filters) if err != nil { return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) diff --git a/app/model/product.go b/app/model/product.go index fa47790..06b599e 100644 --- a/app/model/product.go +++ b/app/model/product.go @@ -70,6 +70,7 @@ type ProductInList struct { Reference string `gorm:"column:reference" json:"reference"` VariantsNumber uint `gorm:"column:variants_number" json:"variants_number"` Quantity int64 `gorm:"column:quantity" json:"quantity"` + IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"` } type ProductFilters struct { diff --git a/app/repos/productsRepo/productsRepo.go b/app/repos/productsRepo/productsRepo.go index 341b348..9e0ab1c 100644 --- a/app/repos/productsRepo/productsRepo.go +++ b/app/repos/productsRepo/productsRepo.go @@ -16,7 +16,7 @@ import ( type UIProductsRepo interface { GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) - Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) + Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) } type ProductsRepo struct{} @@ -37,7 +37,6 @@ func (repo *ProductsRepo) GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_custo return nil, err } - // Optional: validate it's valid JSON if !json.Valid([]byte(productStr)) { return nil, fmt.Errorf("invalid json returned from stored procedure") } @@ -46,37 +45,60 @@ func (repo *ProductsRepo) GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_custo return &raw, nil } -func (repo *ProductsRepo) Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { +func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { var list []model.ProductInList var total int64 query := db.Get(). Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps"). Select(` - ps.id_product AS product_id, - pl.name AS name, - pl.link_rewrite AS link_rewrite, - CONCAT(?, '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link, - cl.name AS category_name, - p.reference AS reference, - COALESCE(v.variants_number, 0) AS variants_number, - sa.quantity AS quantity - `, config.Get().Image.ImagePrefix). + ps.id_product AS product_id, + pl.name AS name, + pl.link_rewrite AS link_rewrite, + CONCAT(?, '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link, + cl.name AS category_name, + p.reference AS reference, + COALESCE(v.variants_number, 0) AS variants_number, + sa.quantity AS quantity, + COALESCE(f.is_favorite, 0) AS is_favorite + `, config.Get().Image.ImagePrefix). Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product"). Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", id_lang). Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1"). Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", id_lang). Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product"). Joins("LEFT JOIN variants v ON v.id_product = ps.id_product"). + Joins("LEFT JOIN favorites f ON f.id_product = ps.id_product"). Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product AND sa.id_product_attribute = 0"). Where("ps.active = ?", 1). Group("ps.id_product"). - Clauses(exclause.With{CTEs: []exclause.CTE{ - { - Name: "variants", - Subquery: exclause.Subquery{DB: db.Get().Model(&dbmodel.PsProductAttributeShop{}).Select("id_product", "COUNT(*) AS variants_number").Group("id_product")}, + Clauses(exclause.With{ + CTEs: []exclause.CTE{ + { + Name: "variants", + Subquery: exclause.Subquery{ + DB: db.Get(). + Model(&dbmodel.PsProductAttributeShop{}). + Select("id_product", "COUNT(*) AS variants_number"). + Group("id_product"), + }, + }, + + { + Name: "favorites", + Subquery: exclause.Subquery{ + DB: db.Get(). + Table("b2b_favorites"). + Select(` + product_id AS id_product, + COUNT(*) > 0 AS is_favorite + `). + Where("user_id = ?", userID). + Group("product_id"), + }, + }, }, - }}). + }). Order("ps.id_product DESC") // Apply all filters diff --git a/app/service/productService/productService.go b/app/service/productService/productService.go index 1a1620e..f5d7ca1 100644 --- a/app/service/productService/productService.go +++ b/app/service/productService/productService.go @@ -29,6 +29,6 @@ func (s *ProductService) GetJSON(p_id_product, p_id_lang, p_id_customer, b2b_id_ return products, nil } -func (s *ProductService) Find(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { - return s.productsRepo.Find(id_lang, p, filters) +func (s *ProductService) Find(id_lang, userID uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { + return s.productsRepo.Find(id_lang, userID, p, filters) } diff --git a/i18n/migrations/20260302163122_create_tables.sql b/i18n/migrations/20260302163122_create_tables.sql index a82cea5..ba4469a 100644 --- a/i18n/migrations/20260302163122_create_tables.sql +++ b/i18n/migrations/20260302163122_create_tables.sql @@ -151,6 +151,16 @@ CREATE TABLE IF NOT EXISTS b2b_carts_products ( CREATE INDEX IF NOT EXISTS idx_carts_products_cart_id ON b2b_carts_products (cart_id); +-- favorites +CREATE TABLE IF NOT EXISTS b2b_favorites ( + user_id BIGINT UNSIGNED NOT NULL, + product_id INT UNSIGNED NOT NULL, + PRIMARY KEY (user_id, product_id), + CONSTRAINT fk_favorites_customer FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fk_favorites_product FOREIGN KEY (product_id) REFERENCES ps_product(id_product) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + + -- refresh_tokens CREATE TABLE IF NOT EXISTS b2b_refresh_tokens ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, diff --git a/i18n/migrations/20260319163200_procedures.sql b/i18n/migrations/20260319163200_procedures.sql index 8f7d5ab..743ca43 100644 --- a/i18n/migrations/20260319163200_procedures.sql +++ b/i18n/migrations/20260319163200_procedures.sql @@ -132,6 +132,12 @@ JSON_OBJECT( m.name, 'category', cl.name, + /* ================= FAVORITE ================= */ + 'is_favorite', + EXISTS( + SELECT 1 FROM b2b_favorites f + WHERE f.user_id = p_id_customer AND f.product_id = p_id_product + ), /* ================= IMAGE ================= */ 'cover_image', JSON_OBJECT( -- 2.49.1 From f1f5daa82b3753606e17698eb38a28be652453ea Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Thu, 9 Apr 2026 14:53:56 +0200 Subject: [PATCH 2/6] and add filtering by is_favorite --- app/delivery/web/api/restricted/product.go | 1 + 1 file changed, 1 insertion(+) diff --git a/app/delivery/web/api/restricted/product.go b/app/delivery/web/api/restricted/product.go index 2c9d894..5ab5b36 100644 --- a/app/delivery/web/api/restricted/product.go +++ b/app/delivery/web/api/restricted/product.go @@ -112,4 +112,5 @@ var columnMappingListProducts map[string]string = map[string]string{ "category_name": "cl.name", "category_id": "cp.id_category", "quantity": "sa.quantity", + "is_favorite": "ps.is_favorite", } -- 2.49.1 From 0a5ce5d9c2e32a1c3f0962d7428d9dee01c2d4a4 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Fri, 10 Apr 2026 09:13:13 +0200 Subject: [PATCH 3/6] ... --- app/delivery/web/api/restricted/product.go | 50 +++++++++++++++++++ app/model/product.go | 9 ++++ app/repos/productsRepo/productsRepo.go | 16 ++++++ app/service/productService/productService.go | 8 +++ app/utils/responseErrors/responseErrors.go | 16 +++++- bruno/api_v1/product/Add To Favorites.yml | 15 ++++++ .../api_v1/product/Remove Form Favorites.yml | 15 ++++++ go.mod | 6 +-- go.sum | 6 --- 9 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 bruno/api_v1/product/Add To Favorites.yml create mode 100644 bruno/api_v1/product/Remove Form Favorites.yml diff --git a/app/delivery/web/api/restricted/product.go b/app/delivery/web/api/restricted/product.go index 5ab5b36..096d5ec 100644 --- a/app/delivery/web/api/restricted/product.go +++ b/app/delivery/web/api/restricted/product.go @@ -34,6 +34,8 @@ func ProductsHandlerRoutes(r fiber.Router) fiber.Router { r.Get("/:id/:country_id/:quantity", handler.GetProductJson) r.Get("/list", handler.ListProducts) + r.Post("/favorite/:product_id", handler.AddToFavorites) + r.Delete("/favorite/:product_id", handler.RemoveFromFavorites) return r } @@ -114,3 +116,51 @@ var columnMappingListProducts map[string]string = map[string]string{ "quantity": "sa.quantity", "is_favorite": "ps.is_favorite", } + +func (h *ProductsHandler) AddToFavorites(c fiber.Ctx) error { + productIDStr := c.Params("product_id") + + productID, err := strconv.Atoi(productIDStr) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + userID, ok := localeExtractor.GetUserID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + err = h.productService.AddToFavorites(userID, uint(productID)) + 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))) +} + +func (h *ProductsHandler) RemoveFromFavorites(c fiber.Ctx) error { + productIDStr := c.Params("product_id") + + productID, err := strconv.Atoi(productIDStr) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + userID, ok := localeExtractor.GetUserID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + err = h.productService.RemoveFromFavorites(userID, uint(productID)) + 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/product.go b/app/model/product.go index 06b599e..e862259 100644 --- a/app/model/product.go +++ b/app/model/product.go @@ -86,3 +86,12 @@ type ProductFilters struct { } type FeatVal = map[uint][]uint + +type B2bFavorite struct { + UserID uint `gorm:"column:user_id;not null;primaryKey" json:"user_id"` + ProductID uint `gorm:"column:product_id;not null;primaryKey" json:"product_id"` +} + +func (*B2bFavorite) TableName() string { + return "b2b_favorites" +} diff --git a/app/repos/productsRepo/productsRepo.go b/app/repos/productsRepo/productsRepo.go index 9e0ab1c..7699c10 100644 --- a/app/repos/productsRepo/productsRepo.go +++ b/app/repos/productsRepo/productsRepo.go @@ -17,6 +17,8 @@ import ( type UIProductsRepo interface { GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) + AddToFavorites(userID uint, productID uint) error + RemoveFromFavorites(userID uint, productID uint) error } type ProductsRepo struct{} @@ -125,3 +127,17 @@ func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filter Count: uint(total), }, nil } + +func (repo *ProductsRepo) AddToFavorites(userID uint, productID uint) error { + fav := model.B2bFavorite{ + UserID: userID, + ProductID: productID, + } + return db.Get().Create(&fav).Error +} + +func (repo *ProductsRepo) RemoveFromFavorites(userID uint, productID uint) error { + return db.Get(). + Where("user_id = ? AND product_id = ?", userID, productID). + Delete(&model.B2bFavorite{}).Error +} diff --git a/app/service/productService/productService.go b/app/service/productService/productService.go index f5d7ca1..03a7132 100644 --- a/app/service/productService/productService.go +++ b/app/service/productService/productService.go @@ -32,3 +32,11 @@ func (s *ProductService) GetJSON(p_id_product, p_id_lang, p_id_customer, b2b_id_ func (s *ProductService) Find(id_lang, userID uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { return s.productsRepo.Find(id_lang, userID, p, filters) } + +func (s *ProductService) AddToFavorites(userID uint, productID uint) error { + return s.productsRepo.AddToFavorites(userID, productID) +} + +func (s *ProductService) RemoveFromFavorites(userID uint, productID uint) error { + return s.productsRepo.RemoveFromFavorites(userID, productID) +} diff --git a/app/utils/responseErrors/responseErrors.go b/app/utils/responseErrors/responseErrors.go index 28802e1..6b3c548 100644 --- a/app/utils/responseErrors/responseErrors.go +++ b/app/utils/responseErrors/responseErrors.go @@ -49,8 +49,11 @@ var ( ErrAIResponseFail = errors.New("AI responded with failure") ErrAIBadOutput = errors.New("AI response does not obey the format") - // Typed errors for product list handler - ErrBadPaging = errors.New("bad or missing paging attribute value in header") + // Typed errors for product handler + ErrBadPaging = errors.New("bad or missing paging attribute value in header") + ErrProductNotFound = errors.New("product with provided id does not exist") + ErrAlreadyInFavorites = errors.New("the product already is in your favorites") + ErrNotInFavorites = errors.New("the product already is not in your favorites") // Typed errors for menu handler ErrNoRootFound = errors.New("no root found in categories table") @@ -170,6 +173,12 @@ func GetErrorCode(c fiber.Ctx, err error) string { case errors.Is(err, ErrBadPaging): return i18n.T_(c, "error.err_bad_paging") + case errors.Is(err, ErrProductNotFound): + return i18n.T_(c, "error.err_product_not_found") + case errors.Is(err, ErrAlreadyInFavorites): + return i18n.T_(c, "error.err_already_in_favorites") + case errors.Is(err, ErrNotInFavorites): + return i18n.T_(c, "error.err_already_not_in_favorites") case errors.Is(err, ErrNoRootFound): return i18n.T_(c, "error.err_no_root_found") @@ -249,6 +258,9 @@ func GetErrorStatus(err error) int { errors.Is(err, ErrInvalidURLSlug), errors.Is(err, ErrInvalidXHTML), errors.Is(err, ErrBadPaging), + errors.Is(err, ErrProductNotFound), + errors.Is(err, ErrAlreadyInFavorites), + errors.Is(err, ErrNotInFavorites), errors.Is(err, ErrNoRootFound), errors.Is(err, ErrCircularDependency), errors.Is(err, ErrStartCategoryNotFound), diff --git a/bruno/api_v1/product/Add To Favorites.yml b/bruno/api_v1/product/Add To Favorites.yml new file mode 100644 index 0000000..71b3d9a --- /dev/null +++ b/bruno/api_v1/product/Add To Favorites.yml @@ -0,0 +1,15 @@ +info: + name: Add To Favorites + type: http + seq: 3 + +http: + method: POST + url: "{{bas_url}}/restricted/product/favorite/51" + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/bruno/api_v1/product/Remove Form Favorites.yml b/bruno/api_v1/product/Remove Form Favorites.yml new file mode 100644 index 0000000..a76feb6 --- /dev/null +++ b/bruno/api_v1/product/Remove Form Favorites.yml @@ -0,0 +1,15 @@ +info: + name: Remove Form Favorites + type: http + seq: 4 + +http: + method: DELETE + url: "{{bas_url}}/restricted/product/favorite/1" + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/go.mod b/go.mod index 1c184da..6141322 100644 --- a/go.mod +++ b/go.mod @@ -36,8 +36,6 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -100,10 +98,10 @@ require ( github.com/valyala/fasthttp v1.69.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xyproto/randomstring v1.2.0 // indirect - golang.org/x/net v0.52.0 + golang.org/x/net v0.52.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/text v0.35.0 gopkg.in/warnings.v0 v0.1.2 // indirect gorm.io/driver/mysql v1.6.0 ) diff --git a/go.sum b/go.sum index 81fe849..d208fb1 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/gofiber/fiber/v2 v2.52.12 h1:0LdToKclcPOj8PktUdIKo9BUohjjwfnQl42Dhw8/WUw= -github.com/gofiber/fiber/v2 v2.52.12/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofiber/fiber/v3 v3.1.0 h1:1p4I820pIa+FGxfwWuQZ5rAyX0WlGZbGT6Hnuxt6hKY= github.com/gofiber/fiber/v3 v3.1.0/go.mod h1:n2nYQovvL9z3Too/FGOfgtERjW3GQcAUqgfoezGBZdU= github.com/gofiber/schema v1.7.0 h1:yNM+FNRZjyYEli9Ey0AXRBrAY9jTnb+kmGs3lJGPvKg= @@ -136,8 +134,6 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= @@ -158,8 +154,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -- 2.49.1 From f7f56c29284bb8d1c6344cdb6ec3631ba9fb5b77 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Fri, 10 Apr 2026 09:33:44 +0200 Subject: [PATCH 4/6] catching errors --- app/repos/productsRepo/productsRepo.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/repos/productsRepo/productsRepo.go b/app/repos/productsRepo/productsRepo.go index 7699c10..010fc2c 100644 --- a/app/repos/productsRepo/productsRepo.go +++ b/app/repos/productsRepo/productsRepo.go @@ -10,6 +10,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" + "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "git.ma-al.com/goc_marek/gormcol" "github.com/WinterYukky/gorm-extra-clause-plugin/exclause" ) @@ -129,6 +130,29 @@ func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filter } func (repo *ProductsRepo) AddToFavorites(userID uint, productID uint) error { + var count int64 + err := db.Get(). + Table(dbmodel.TableNamePsProduct). + Where(dbmodel.PsProductCols.IDProduct.Col()+" = ?", productID). + Count(&count).Error + if err != nil { + return err + } + if count == 0 { + return responseErrors.ErrProductNotFound + } + + err = db.Get(). + Table("b2b_favorites"). + Where("user_id = ? AND product_id = ?", userID, productID). + Count(&count).Error + if err != nil { + return err + } + if count > 0 { + return responseErrors.ErrAlreadyInFavorites + } + fav := model.B2bFavorite{ UserID: userID, ProductID: productID, -- 2.49.1 From 61ccd32c4a822a19c936851b8e630a60841d2dfc Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Fri, 10 Apr 2026 09:43:49 +0200 Subject: [PATCH 5/6] catching errors again --- app/repos/productsRepo/productsRepo.go | 44 +++++++++---------- app/service/productService/productService.go | 33 ++++++++++++++ bruno/api_v1/product/Add To Favorites.yml | 2 +- .../api_v1/product/Remove Form Favorites.yml | 2 +- 4 files changed, 55 insertions(+), 26 deletions(-) diff --git a/app/repos/productsRepo/productsRepo.go b/app/repos/productsRepo/productsRepo.go index 010fc2c..da6409b 100644 --- a/app/repos/productsRepo/productsRepo.go +++ b/app/repos/productsRepo/productsRepo.go @@ -10,7 +10,6 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" - "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "git.ma-al.com/goc_marek/gormcol" "github.com/WinterYukky/gorm-extra-clause-plugin/exclause" ) @@ -20,6 +19,8 @@ type UIProductsRepo interface { Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) AddToFavorites(userID uint, productID uint) error RemoveFromFavorites(userID uint, productID uint) error + ExistsInFavorites(userID uint, productID uint) (int64, error) + ProductInDatabase(productID uint) (int64, error) } type ProductsRepo struct{} @@ -130,29 +131,6 @@ func (repo *ProductsRepo) Find(id_lang, userID uint, p find.Paging, filt *filter } func (repo *ProductsRepo) AddToFavorites(userID uint, productID uint) error { - var count int64 - err := db.Get(). - Table(dbmodel.TableNamePsProduct). - Where(dbmodel.PsProductCols.IDProduct.Col()+" = ?", productID). - Count(&count).Error - if err != nil { - return err - } - if count == 0 { - return responseErrors.ErrProductNotFound - } - - err = db.Get(). - Table("b2b_favorites"). - Where("user_id = ? AND product_id = ?", userID, productID). - Count(&count).Error - if err != nil { - return err - } - if count > 0 { - return responseErrors.ErrAlreadyInFavorites - } - fav := model.B2bFavorite{ UserID: userID, ProductID: productID, @@ -165,3 +143,21 @@ func (repo *ProductsRepo) RemoveFromFavorites(userID uint, productID uint) error Where("user_id = ? AND product_id = ?", userID, productID). Delete(&model.B2bFavorite{}).Error } + +func (repo *ProductsRepo) ExistsInFavorites(userID uint, productID uint) (int64, error) { + var count int64 + err := db.Get(). + Table("b2b_favorites"). + Where("user_id = ? AND product_id = ?", userID, productID). + Count(&count).Error + return count, err +} + +func (repo *ProductsRepo) ProductInDatabase(productID uint) (int64, error) { + var count int64 + err := db.Get(). + Table(dbmodel.TableNamePsProduct). + Where(dbmodel.PsProductCols.IDProduct.Col()+" = ?", productID). + Count(&count).Error + return count, err +} diff --git a/app/service/productService/productService.go b/app/service/productService/productService.go index 03a7132..ae3ddd6 100644 --- a/app/service/productService/productService.go +++ b/app/service/productService/productService.go @@ -8,6 +8,7 @@ import ( constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" + "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" ) type ProductService struct { @@ -34,9 +35,41 @@ func (s *ProductService) Find(id_lang, userID uint, p find.Paging, filters *filt } func (s *ProductService) AddToFavorites(userID uint, productID uint) error { + count, err := s.productsRepo.ProductInDatabase(productID) + if err != nil { + return err + } + if count <= 0 { + return responseErrors.ErrProductNotFound + } + + count, err = s.productsRepo.ExistsInFavorites(userID, productID) + if err != nil { + return err + } + if count >= 1 { + return responseErrors.ErrAlreadyInFavorites + } + return s.productsRepo.AddToFavorites(userID, productID) } func (s *ProductService) RemoveFromFavorites(userID uint, productID uint) error { + count, err := s.productsRepo.ProductInDatabase(productID) + if err != nil { + return err + } + if count <= 0 { + return responseErrors.ErrProductNotFound + } + + count, err = s.productsRepo.ExistsInFavorites(userID, productID) + if err != nil { + return err + } + if count <= 0 { + return responseErrors.ErrNotInFavorites + } + return s.productsRepo.RemoveFromFavorites(userID, productID) } diff --git a/bruno/api_v1/product/Add To Favorites.yml b/bruno/api_v1/product/Add To Favorites.yml index 71b3d9a..29a660d 100644 --- a/bruno/api_v1/product/Add To Favorites.yml +++ b/bruno/api_v1/product/Add To Favorites.yml @@ -5,7 +5,7 @@ info: http: method: POST - url: "{{bas_url}}/restricted/product/favorite/51" + url: "{{bas_url}}/restricted/product/favorite/53" auth: inherit settings: diff --git a/bruno/api_v1/product/Remove Form Favorites.yml b/bruno/api_v1/product/Remove Form Favorites.yml index a76feb6..2b388c2 100644 --- a/bruno/api_v1/product/Remove Form Favorites.yml +++ b/bruno/api_v1/product/Remove Form Favorites.yml @@ -5,7 +5,7 @@ info: http: method: DELETE - url: "{{bas_url}}/restricted/product/favorite/1" + url: "{{bas_url}}/restricted/product/favorite/51" auth: inherit settings: -- 2.49.1 From c5832c0cf587addbcc4c6532cbd744509cefbe38 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Fri, 10 Apr 2026 09:57:07 +0200 Subject: [PATCH 6/6] minor change --- app/repos/productsRepo/productsRepo.go | 12 ++++++------ app/service/productService/productService.go | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/repos/productsRepo/productsRepo.go b/app/repos/productsRepo/productsRepo.go index da6409b..4450b52 100644 --- a/app/repos/productsRepo/productsRepo.go +++ b/app/repos/productsRepo/productsRepo.go @@ -19,8 +19,8 @@ type UIProductsRepo interface { Find(id_lang, userID uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) AddToFavorites(userID uint, productID uint) error RemoveFromFavorites(userID uint, productID uint) error - ExistsInFavorites(userID uint, productID uint) (int64, error) - ProductInDatabase(productID uint) (int64, error) + ExistsInFavorites(userID uint, productID uint) (bool, error) + ProductInDatabase(productID uint) (bool, error) } type ProductsRepo struct{} @@ -144,20 +144,20 @@ func (repo *ProductsRepo) RemoveFromFavorites(userID uint, productID uint) error Delete(&model.B2bFavorite{}).Error } -func (repo *ProductsRepo) ExistsInFavorites(userID uint, productID uint) (int64, error) { +func (repo *ProductsRepo) ExistsInFavorites(userID uint, productID uint) (bool, error) { var count int64 err := db.Get(). Table("b2b_favorites"). Where("user_id = ? AND product_id = ?", userID, productID). Count(&count).Error - return count, err + return count >= 1, err } -func (repo *ProductsRepo) ProductInDatabase(productID uint) (int64, error) { +func (repo *ProductsRepo) ProductInDatabase(productID uint) (bool, error) { var count int64 err := db.Get(). Table(dbmodel.TableNamePsProduct). Where(dbmodel.PsProductCols.IDProduct.Col()+" = ?", productID). Count(&count).Error - return count, err + return count >= 1, err } diff --git a/app/service/productService/productService.go b/app/service/productService/productService.go index ae3ddd6..de6d70e 100644 --- a/app/service/productService/productService.go +++ b/app/service/productService/productService.go @@ -35,19 +35,19 @@ func (s *ProductService) Find(id_lang, userID uint, p find.Paging, filters *filt } func (s *ProductService) AddToFavorites(userID uint, productID uint) error { - count, err := s.productsRepo.ProductInDatabase(productID) + exists, err := s.productsRepo.ProductInDatabase(productID) if err != nil { return err } - if count <= 0 { + if !exists { return responseErrors.ErrProductNotFound } - count, err = s.productsRepo.ExistsInFavorites(userID, productID) + exists, err = s.productsRepo.ExistsInFavorites(userID, productID) if err != nil { return err } - if count >= 1 { + if exists { return responseErrors.ErrAlreadyInFavorites } @@ -55,19 +55,19 @@ func (s *ProductService) AddToFavorites(userID uint, productID uint) error { } func (s *ProductService) RemoveFromFavorites(userID uint, productID uint) error { - count, err := s.productsRepo.ProductInDatabase(productID) + exists, err := s.productsRepo.ProductInDatabase(productID) if err != nil { return err } - if count <= 0 { + if !exists { return responseErrors.ErrProductNotFound } - count, err = s.productsRepo.ExistsInFavorites(userID, productID) + exists, err = s.productsRepo.ExistsInFavorites(userID, productID) if err != nil { return err } - if count <= 0 { + if !exists { return responseErrors.ErrNotInFavorites } -- 2.49.1