This commit is contained in:
2026-05-12 01:13:01 +02:00
commit bf304e17c9
46 changed files with 4358 additions and 0 deletions
+241
View File
@@ -0,0 +1,241 @@
package catalog
import (
"context"
"database/sql"
"fmt"
"net/http"
"strings"
"gorm.io/gorm"
)
type ProductPageRequest struct {
ID int64
Slug string
LanguageID int64
ShopID int64
}
type CategoryPageRequest struct {
ID int64
Slug string
LanguageID int64
ShopID int64
}
type ProductPageData struct {
ID int64
Name string
Slug string
ShortDescription string
Description string
Price float64
CoverImageID sql.NullInt64
CategoryID int64
CategorySlug string
CategoryName string
}
type CategoryPageData struct {
ID int64
Name string
Slug string
Description string
Products []CategoryProductCard `gorm:"-"`
}
type CategoryProductCard struct {
ID int64
Name string
Slug string
URL string `gorm:"-"`
Price float64
Description string
EAN13 string
}
type Service struct {
db *gorm.DB
prefix string
}
func NewService(db *gorm.DB, prefix string) *Service {
return &Service{db: db, prefix: prefix}
}
func (s *Service) GetProductPage(ctx context.Context, req ProductPageRequest) (*ProductPageData, error) {
var product ProductPageData
queryByID := fmt.Sprintf(`
SELECT p.id_product AS id,
pl.name AS name,
pl.link_rewrite AS slug,
pl.description_short AS short_description,
pl.description AS description,
ps.price AS price,
i.id_image AS cover_image_id,
p.id_category_default AS category_id,
cl.link_rewrite AS category_slug,
cl.name AS category_name
FROM %sproduct p
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
WHERE p.id_product = ?
AND pl.id_lang = ?
AND ps.id_shop = ?
LIMIT 1
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
queryBySlug := fmt.Sprintf(`
SELECT p.id_product AS id,
pl.name AS name,
pl.link_rewrite AS slug,
pl.description_short AS short_description,
pl.description AS description,
ps.price AS price,
i.id_image AS cover_image_id,
p.id_category_default AS category_id,
cl.link_rewrite AS category_slug,
cl.name AS category_name
FROM %sproduct p
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
WHERE pl.link_rewrite = ?
AND pl.id_lang = ?
AND ps.id_shop = ?
LIMIT 1
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
var result *gorm.DB
if req.ID != 0 {
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryByID), req.ID, req.LanguageID, req.ShopID).Scan(&product)
} else {
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryBySlug), req.Slug, req.LanguageID, req.ShopID).Scan(&product)
}
if result.Error != nil {
return nil, result.Error
}
if result.RowsAffected == 0 {
return nil, gorm.ErrRecordNotFound
}
return &product, nil
}
func (s *Service) GetCategoryPage(ctx context.Context, req CategoryPageRequest) (*CategoryPageData, error) {
var category CategoryPageData
categoryQuery := fmt.Sprintf(`
SELECT c.id_category AS id,
cl.name AS name,
cl.link_rewrite AS slug,
cl.description AS description
FROM %scategory c
JOIN %scategory_lang cl ON cl.id_category = c.id_category
WHERE c.id_category = ?
AND cl.id_lang = ?
LIMIT 1
`, s.prefix, s.prefix)
categoryFallbackQuery := fmt.Sprintf(`
SELECT c.id_category AS id,
cl.name AS name,
cl.link_rewrite AS slug,
cl.description AS description
FROM %scategory c
JOIN %scategory_lang cl ON cl.id_category = c.id_category
WHERE c.id_category = ?
ORDER BY cl.id_lang ASC
LIMIT 1
`, s.prefix, s.prefix)
lookupID := req.ID
if lookupID == 0 {
idQuery := fmt.Sprintf(`
SELECT c.id_category
FROM %scategory c
JOIN %scategory_lang cl ON cl.id_category = c.id_category
WHERE cl.link_rewrite = ?
AND cl.id_lang = ?
LIMIT 1
`, s.prefix, s.prefix)
var row struct {
ID int64 `gorm:"column:id_category"`
}
result := s.db.WithContext(ctx).Raw(strings.TrimSpace(idQuery), req.Slug, req.LanguageID).Scan(&row)
if result.Error != nil {
return nil, result.Error
}
if result.RowsAffected == 0 {
return nil, gorm.ErrRecordNotFound
}
lookupID = row.ID
}
result := s.db.WithContext(ctx).Raw(strings.TrimSpace(categoryQuery), lookupID, req.LanguageID).Scan(&category)
if result.Error != nil {
return nil, result.Error
}
if result.RowsAffected == 0 {
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(categoryFallbackQuery), lookupID).Scan(&category)
if result.Error != nil {
return nil, result.Error
}
if result.RowsAffected == 0 {
return nil, gorm.ErrRecordNotFound
}
}
productQuery := fmt.Sprintf(`
SELECT p.id_product AS id,
pl.name AS name,
pl.link_rewrite AS slug,
p.ean13 AS ean13,
ps.price AS price,
pl.description_short AS description
FROM %scategory_product cp
JOIN %sproduct p ON p.id_product = cp.id_product
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
WHERE cp.id_category = ?
AND pl.id_lang = ?
AND ps.id_shop = ?
ORDER BY cp.position ASC, p.id_product ASC
LIMIT 48
`, s.prefix, s.prefix, s.prefix, s.prefix)
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(productQuery), category.ID, req.LanguageID, req.ShopID).Scan(&category.Products).Error; err != nil {
return nil, err
}
return &category, nil
}
func (s *Service) ResolveLanguageID(ctx context.Context, req *http.Request, fallback int64) int64 {
if req == nil || req.URL == nil {
return fallback
}
path := strings.Trim(req.URL.Path, "/")
if path == "" {
return fallback
}
first := path
if idx := strings.IndexByte(path, '/'); idx >= 0 {
first = path[:idx]
}
first = strings.TrimSpace(first)
if len(first) < 2 || len(first) > 5 {
return fallback
}
var row struct {
ID int64 `gorm:"column:id_lang"`
}
query := fmt.Sprintf("SELECT id_lang FROM %slang WHERE iso_code = ? LIMIT 1", s.prefix)
result := s.db.WithContext(ctx).Raw(query, strings.ToUpper(first)).Scan(&row)
if result.Error != nil || result.RowsAffected == 0 || row.ID == 0 {
return fallback
}
return row.ID
}