| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- // Package repository
- package repository
- import (
- "context"
- "errors"
- "fmt"
- "math"
- "github.com/jackc/pgx/v5"
- "github.com/jackc/pgx/v5/pgxpool"
- "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models"
- )
- var ErrPlaceNotFound = errors.New("place not found")
- type BookingRepo struct {
- pool *pgxpool.Pool
- }
- func NewBookingRepo(pool *pgxpool.Pool) *BookingRepo {
- return &BookingRepo{pool: pool}
- }
- func (r *BookingRepo) Create(ctx context.Context, b *models.Booking) error {
- tx, err := r.pool.Begin(ctx)
- if err != nil {
- return fmt.Errorf("begin tx: %w", err)
- }
- defer tx.Rollback(ctx)
- var hourlyRate *int
- err = tx.QueryRow(ctx,
- `SELECT hourly_rate, currency FROM places WHERE id = $1 AND deleted_at IS NULL FOR UPDATE`,
- b.PlaceID).Scan(&hourlyRate, &b.Currency)
- if err != nil {
- if errors.Is(err, pgx.ErrNoRows) {
- return ErrPlaceNotFound
- }
- return fmt.Errorf("lock place: %w", err)
- }
- if hourlyRate != nil {
- hours := math.Ceil(b.EndTime.Sub(b.StartTime).Hours())
- if hours < 1 {
- hours = 1
- }
- total := *hourlyRate * int(hours)
- b.TotalPrice = &total
- }
- err = tx.QueryRow(ctx,
- `INSERT INTO bookings (place_id, user_id, start_time, end_time, status, total_price, currency, comment)
- VALUES ($1, $2, $3, $4, 'pending', $5, $6, $7)
- RETURNING id, created_at, updated_at`,
- b.PlaceID, b.UserID, b.StartTime, b.EndTime, b.TotalPrice, b.Currency, b.Comment,
- ).Scan(&b.ID, &b.CreatedAt, &b.UpdatedAt)
- if err != nil {
- return fmt.Errorf("insert booking: %w", err)
- }
- return tx.Commit(ctx)
- }
- func (r *BookingRepo) GetByID(ctx context.Context, id string) (*models.Booking, error) {
- row := r.pool.QueryRow(ctx,
- `SELECT id, place_id, user_id, start_time, end_time, status, total_price, currency, comment,
- created_at, updated_at
- FROM bookings WHERE id = $1`, id)
- var b models.Booking
- err := row.Scan(&b.ID, &b.PlaceID, &b.UserID, &b.StartTime, &b.EndTime,
- &b.Status, &b.TotalPrice, &b.Currency, &b.Comment, &b.CreatedAt, &b.UpdatedAt)
- if err != nil {
- if err == pgx.ErrNoRows {
- return nil, nil
- }
- return nil, err
- }
- return &b, nil
- }
- func (r *BookingRepo) ListByUser(ctx context.Context, userID string) ([]*models.Booking, error) {
- rows, err := r.pool.Query(ctx,
- `SELECT id, place_id, user_id, start_time, end_time, status, total_price, currency, comment,
- created_at, updated_at
- FROM bookings WHERE user_id = $1 ORDER BY start_time DESC`, userID)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- return scanBookings(rows)
- }
- func (r *BookingRepo) ListByPlace(ctx context.Context, placeID string) ([]*models.Booking, error) {
- rows, err := r.pool.Query(ctx,
- `SELECT id, place_id, user_id, start_time, end_time, status, total_price, currency, comment,
- created_at, updated_at
- FROM bookings WHERE place_id = $1 ORDER BY start_time DESC`, placeID)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- return scanBookings(rows)
- }
- func (r *BookingRepo) UpdateStatusIfPending(ctx context.Context, id, status string) (bool, error) {
- tag, err := r.pool.Exec(ctx,
- `UPDATE bookings SET status=$1, updated_at=now() WHERE id=$2 AND status='pending'`, status, id)
- if err != nil {
- return false, fmt.Errorf("update status if pending: %w", err)
- }
- return tag.RowsAffected() > 0, nil
- }
- func (r *BookingRepo) GetPlaceMinHours(ctx context.Context, placeID string) (int, error) {
- var minHours int
- err := r.pool.QueryRow(ctx,
- `SELECT min_hours FROM places WHERE id = $1 AND deleted_at IS NULL`, placeID).Scan(&minHours)
- if err != nil {
- if errors.Is(err, pgx.ErrNoRows) {
- return 0, ErrPlaceNotFound
- }
- return 0, fmt.Errorf("get place min_hours: %w", err)
- }
- return minHours, nil
- }
- func scanBookings(rows pgx.Rows) ([]*models.Booking, error) {
- var bookings []*models.Booking
- for rows.Next() {
- var b models.Booking
- if err := rows.Scan(&b.ID, &b.PlaceID, &b.UserID, &b.StartTime, &b.EndTime,
- &b.Status, &b.TotalPrice, &b.Currency, &b.Comment, &b.CreatedAt, &b.UpdatedAt); err != nil {
- return nil, err
- }
- bookings = append(bookings, &b)
- }
- return bookings, nil
- }
|