sno-quiz/backend/internal/repository/reward_repository.go
2025-09-17 22:22:14 +03:00

138 lines
4.5 KiB
Go

package repository
import (
"context"
"sno/internal/models"
"github.com/jackc/pgx/v5/pgxpool"
)
// RewardRepository defines the interface for reward data operations.
type RewardRepository interface {
Create(ctx context.Context, reward *models.Reward) (*models.Reward, error)
GetAllActive(ctx context.Context) ([]models.Reward, error)
GetByID(ctx context.Context, id int) (*models.Reward, error)
UpdateStock(ctx context.Context, id int, quantity int) error
Update(ctx context.Context, id int, reward *models.Reward) (*models.Reward, error)
Delete(ctx context.Context, id int) error
}
// postgresRewardRepository implements the RewardRepository interface.
type postgresRewardRepository struct {
db *pgxpool.Pool
}
// NewRewardRepository creates a new instance of a reward repository.
func NewRewardRepository(db *pgxpool.Pool) RewardRepository {
return &postgresRewardRepository{db: db}
}
// Create inserts a new reward into the database.
func (r *postgresRewardRepository) Create(ctx context.Context, reward *models.Reward) (*models.Reward, error) {
query := `
INSERT INTO rewards (title, description, image_url, price_stars, delivery_type, instructions, stock, created_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id, is_active, created_at
`
err := r.db.QueryRow(ctx, query,
reward.Title, reward.Description, reward.ImageURL, reward.PriceStars, reward.DeliveryType, reward.Instructions, reward.Stock, reward.CreatedBy,
).Scan(&reward.ID, &reward.IsActive, &reward.CreatedAt)
if err != nil {
return nil, err
}
return reward, nil
}
// GetAllActive retrieves all active rewards that are in stock.
func (r *postgresRewardRepository) GetAllActive(ctx context.Context) ([]models.Reward, error) {
query := `
SELECT id, title, description, image_url, price_stars, delivery_type, instructions, stock, is_active, created_at
FROM rewards
WHERE is_active = true AND (stock > 0 OR stock = -1) -- Using -1 for infinite stock
ORDER BY price_stars ASC
`
rows, err := r.db.Query(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()
var rewards []models.Reward
for rows.Next() {
var p models.Reward
if err := rows.Scan(&p.ID, &p.Title, &p.Description, &p.ImageURL, &p.PriceStars, &p.DeliveryType, &p.Instructions, &p.Stock, &p.IsActive, &p.CreatedAt); err != nil {
return nil, err
}
rewards = append(rewards, p)
}
if err := rows.Err(); err != nil {
return nil, err
}
if rewards == nil {
return []models.Reward{}, nil
}
return rewards, nil
}
// GetByID retrieves a single reward by its ID.
func (r *postgresRewardRepository) GetByID(ctx context.Context, id int) (*models.Reward, error) {
query := `
SELECT id, title, description, image_url, price_stars, delivery_type, instructions, stock, is_active, created_at, created_by
FROM rewards
WHERE id = $1
`
var p models.Reward
err := r.db.QueryRow(ctx, query, id).Scan(
&p.ID, &p.Title, &p.Description, &p.ImageURL, &p.PriceStars, &p.DeliveryType, &p.Instructions, &p.Stock, &p.IsActive, &p.CreatedAt, &p.CreatedBy,
)
if err != nil {
return nil, err
}
return &p, nil
}
// UpdateStock updates the stock for a given reward.
func (r *postgresRewardRepository) UpdateStock(ctx context.Context, id int, quantity int) error {
query := `UPDATE rewards SET stock = stock - $1 WHERE id = $2 AND stock != -1` // -1 is infinite stock
querier := getQuerier(ctx, r.db)
_, err := querier.Exec(ctx, query, quantity, id)
return err
}
// Update updates an existing reward
func (r *postgresRewardRepository) Update(ctx context.Context, id int, reward *models.Reward) (*models.Reward, error) {
query := `
UPDATE rewards
SET title = $2, description = $3, image_url = $4, price_stars = $5,
delivery_type = $6, instructions = $7, stock = $8, is_active = $9
WHERE id = $1
RETURNING id, title, description, image_url, price_stars, delivery_type, instructions, stock, is_active, created_at, created_by
`
var p models.Reward
err := r.db.QueryRow(ctx, query,
id, reward.Title, reward.Description, reward.ImageURL, reward.PriceStars,
reward.DeliveryType, reward.Instructions, reward.Stock, reward.IsActive,
).Scan(
&p.ID, &p.Title, &p.Description, &p.ImageURL, &p.PriceStars,
&p.DeliveryType, &p.Instructions, &p.Stock, &p.IsActive, &p.CreatedAt, &p.CreatedBy,
)
if err != nil {
return nil, err
}
return &p, nil
}
// Delete deletes a reward
func (r *postgresRewardRepository) Delete(ctx context.Context, id int) error {
query := `DELETE FROM rewards WHERE id = $1`
_, err := r.db.Exec(ctx, query, id)
return err
}