package repository import ( "context" "database/sql" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "shop/internal/models" ) type CartRepository struct { pool *pgxpool.Pool } func NewCartRepository(pool *pgxpool.Pool) *CartRepository { return &CartRepository{pool: pool} } func (r *CartRepository) GetOrCreate(ctx context.Context, sessionKey string, userID *int) (int, error) { if userID != nil { var cartID int err := r.pool.QueryRow(ctx, `SELECT id FROM carts WHERE user_id = $1`, *userID).Scan(&cartID) if err == nil { return cartID, nil } if err != pgx.ErrNoRows { return 0, err } err = r.pool.QueryRow(ctx, ` INSERT INTO carts (user_id, session_key) VALUES ($1, $2) RETURNING id `, *userID, sessionKey).Scan(&cartID) return cartID, err } var cartID int err := r.pool.QueryRow(ctx, ` SELECT id FROM carts WHERE session_key = $1 AND user_id IS NULL `, sessionKey).Scan(&cartID) if err == nil { return cartID, nil } if err != pgx.ErrNoRows { return 0, err } err = r.pool.QueryRow(ctx, ` INSERT INTO carts (session_key) VALUES ($1) RETURNING id `, sessionKey).Scan(&cartID) return cartID, err } func (r *CartRepository) MergeToUser(ctx context.Context, sessionKey string, userID int) error { tx, err := r.pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) var guestCartID int err = tx.QueryRow(ctx, ` SELECT id FROM carts WHERE session_key = $1 AND user_id IS NULL `, sessionKey).Scan(&guestCartID) if err == pgx.ErrNoRows { return tx.Commit(ctx) } if err != nil { return err } var userCartID int err = tx.QueryRow(ctx, `SELECT id FROM carts WHERE user_id = $1`, userID).Scan(&userCartID) if err == pgx.ErrNoRows { _, err = tx.Exec(ctx, `UPDATE carts SET user_id = $1 WHERE id = $2`, userID, guestCartID) if err != nil { return err } return tx.Commit(ctx) } if err != nil { return err } _, err = tx.Exec(ctx, ` INSERT INTO cart_items (cart_id, product_id, quantity) SELECT $1, product_id, quantity FROM cart_items WHERE cart_id = $2 ON CONFLICT (cart_id, product_id) DO UPDATE SET quantity = cart_items.quantity + EXCLUDED.quantity `, userCartID, guestCartID) if err != nil { return err } _, err = tx.Exec(ctx, `DELETE FROM carts WHERE id = $1`, guestCartID) if err != nil { return err } return tx.Commit(ctx) } func (r *CartRepository) AddItem(ctx context.Context, cartID, productID, qty int) error { _, err := r.pool.Exec(ctx, ` INSERT INTO cart_items (cart_id, product_id, quantity) VALUES ($1, $2, $3) ON CONFLICT (cart_id, product_id) DO UPDATE SET quantity = cart_items.quantity + EXCLUDED.quantity `, cartID, productID, qty) return err } func (r *CartRepository) UpdateItem(ctx context.Context, cartID, productID, qty int) error { if qty <= 0 { _, err := r.pool.Exec(ctx, `DELETE FROM cart_items WHERE cart_id = $1 AND product_id = $2`, cartID, productID) return err } _, err := r.pool.Exec(ctx, ` UPDATE cart_items SET quantity = $3 WHERE cart_id = $1 AND product_id = $2 `, cartID, productID, qty) return err } func (r *CartRepository) RemoveItem(ctx context.Context, cartID, productID int) error { _, err := r.pool.Exec(ctx, `DELETE FROM cart_items WHERE cart_id = $1 AND product_id = $2`, cartID, productID) return err } func (r *CartRepository) Clear(ctx context.Context, cartID int) error { _, err := r.pool.Exec(ctx, `DELETE FROM cart_items WHERE cart_id = $1`, cartID) return err } func (r *CartRepository) ItemCount(ctx context.Context, cartID int) (int, error) { var count int err := r.pool.QueryRow(ctx, ` SELECT COALESCE(SUM(quantity), 0) FROM cart_items WHERE cart_id = $1 `, cartID).Scan(&count) return count, err } func (r *CartRepository) Items(ctx context.Context, cartID int) ([]models.CartItem, error) { rows, err := r.pool.Query(ctx, ` SELECT ci.id, ci.cart_id, ci.product_id, ci.quantity, p.id, p.name, p.description, p.details, p.price, COALESCE(p.sale_price, 0), p.image_url, COALESCE(c.name, p.category, 'Разное'), p.category_id, COALESCE(c.slug, ''), p.is_active, p.created_at, p.updated_at FROM cart_items ci JOIN products p ON p.id = ci.product_id LEFT JOIN categories c ON c.id = p.category_id WHERE ci.cart_id = $1 AND p.is_active = true ORDER BY ci.id `, cartID) if err != nil { return nil, err } defer rows.Close() var items []models.CartItem for rows.Next() { var ci models.CartItem var p models.Product var catID sql.NullInt64 if err := rows.Scan( &ci.ID, &ci.CartID, &ci.ProductID, &ci.Quantity, &p.ID, &p.Name, &p.Description, &p.Details, &p.Price, &p.SalePrice, &p.ImageURL, &p.Category, &catID, &p.CategorySlug, &p.IsActive, &p.CreatedAt, &p.UpdatedAt, ); err != nil { return nil, err } if catID.Valid { id := int(catID.Int64) p.CategoryID = &id } ci.Product = p items = append(items, ci) } return items, rows.Err() } func (r *CartRepository) Total(ctx context.Context, cartID int) (float64, error) { var total float64 err := r.pool.QueryRow(ctx, ` SELECT COALESCE(SUM(ci.quantity * CASE WHEN p.sale_price > 0 AND p.sale_price < p.price THEN p.sale_price ELSE p.price END), 0) FROM cart_items ci JOIN products p ON p.id = ci.product_id WHERE ci.cart_id = $1 `, cartID).Scan(&total) return total, err }