Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5832c0cf5 | ||
|
|
61ccd32c4a | ||
|
|
f7f56c2928 | ||
|
|
0a5ce5d9c2 | ||
|
|
f1f5daa82b | ||
|
|
393de36cb2 | ||
| 9fb8e034fc |
@@ -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
|
||||
}
|
||||
@@ -90,7 +92,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)))
|
||||
@@ -106,4 +114,53 @@ 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",
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -85,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"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ 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)
|
||||
AddToFavorites(userID uint, productID uint) error
|
||||
RemoveFromFavorites(userID uint, productID uint) error
|
||||
ExistsInFavorites(userID uint, productID uint) (bool, error)
|
||||
ProductInDatabase(productID uint) (bool, error)
|
||||
}
|
||||
|
||||
type ProductsRepo struct{}
|
||||
@@ -37,7 +41,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 +49,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
|
||||
@@ -103,3 +129,35 @@ func (repo *ProductsRepo) Find(id_lang uint, p find.Paging, filt *filters.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
|
||||
}
|
||||
|
||||
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 >= 1, err
|
||||
}
|
||||
|
||||
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 >= 1, err
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -29,6 +30,46 @@ 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)
|
||||
}
|
||||
|
||||
func (s *ProductService) AddToFavorites(userID uint, productID uint) error {
|
||||
exists, err := s.productsRepo.ProductInDatabase(productID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return responseErrors.ErrProductNotFound
|
||||
}
|
||||
|
||||
exists, err = s.productsRepo.ExistsInFavorites(userID, productID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return responseErrors.ErrAlreadyInFavorites
|
||||
}
|
||||
|
||||
return s.productsRepo.AddToFavorites(userID, productID)
|
||||
}
|
||||
|
||||
func (s *ProductService) RemoveFromFavorites(userID uint, productID uint) error {
|
||||
exists, err := s.productsRepo.ProductInDatabase(productID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return responseErrors.ErrProductNotFound
|
||||
}
|
||||
|
||||
exists, err = s.productsRepo.ExistsInFavorites(userID, productID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return responseErrors.ErrNotInFavorites
|
||||
}
|
||||
|
||||
return s.productsRepo.RemoveFromFavorites(userID, productID)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
15
bruno/api_v1/product/Add To Favorites.yml
Normal file
15
bruno/api_v1/product/Add To Favorites.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
info:
|
||||
name: Add To Favorites
|
||||
type: http
|
||||
seq: 3
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: "{{bas_url}}/restricted/product/favorite/53"
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
15
bruno/api_v1/product/Remove Form Favorites.yml
Normal file
15
bruno/api_v1/product/Remove Form Favorites.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
info:
|
||||
name: Remove Form Favorites
|
||||
type: http
|
||||
seq: 4
|
||||
|
||||
http:
|
||||
method: DELETE
|
||||
url: "{{bas_url}}/restricted/product/favorite/51"
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
6
go.mod
6
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
|
||||
)
|
||||
|
||||
6
go.sum
6
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=
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user