// Package repository package repository import ( "context" "errors" "fmt" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models" ) type ReviewRepo struct { pool *pgxpool.Pool } func NewReviewRepo(pool *pgxpool.Pool) *ReviewRepo { return &ReviewRepo{pool: pool} } func (r *ReviewRepo) Create(ctx context.Context, review *models.Review) error { tx, err := r.pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) err = tx.QueryRow(ctx, `INSERT INTO reviews (user_id, target_type, target_id, rating, text) VALUES ($1, $2, $3, $4, $5) RETURNING id, created_at, updated_at`, review.UserID, review.TargetType, review.TargetID, review.Rating, review.Text, ).Scan(&review.ID, &review.CreatedAt, &review.UpdatedAt) if err != nil { // Проверка нарушения уникальности через код ошибки pgx (23505 = unique_violation) var pgErr *pgconn.PgError if errors.As(err, &pgErr) && pgErr.Code == "23505" { return fmt.Errorf("you already reviewed this") } return fmt.Errorf("create review: %w", err) } // Обновляем рейтинг и количество отзывов у целевой сущности if review.TargetType == "service" { _, err = tx.Exec(ctx, `UPDATE services SET reviews_count = (SELECT COUNT(*) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL), rating = (SELECT COALESCE(AVG(rating), 0) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL) WHERE id = $2`, review.TargetType, review.TargetID) } else { _, err = tx.Exec(ctx, `UPDATE places SET reviews_count = (SELECT COUNT(*) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL), rating = (SELECT COALESCE(AVG(rating), 0) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL) WHERE id = $2`, review.TargetType, review.TargetID) } if err != nil { return fmt.Errorf("update rating: %w", err) } return tx.Commit(ctx) } func (r *ReviewRepo) List(ctx context.Context, targetType, targetID string) ([]*models.Review, error) { rows, err := r.pool.Query(ctx, `SELECT r.id, r.user_id, r.target_type, r.target_id, r.rating, r.text, r.created_at, r.updated_at, u.name, u.avatar_url FROM reviews r JOIN users u ON u.id = r.user_id WHERE r.target_type = $1 AND r.target_id = $2 AND r.deleted_at IS NULL ORDER BY r.created_at DESC`, targetType, targetID) if err != nil { return nil, err } defer rows.Close() var reviews []*models.Review for rows.Next() { var rev models.Review var userName, userAvatar *string if err := rows.Scan( &rev.ID, &rev.UserID, &rev.TargetType, &rev.TargetID, &rev.Rating, &rev.Text, &rev.CreatedAt, &rev.UpdatedAt, &userName, &userAvatar, ); err != nil { return nil, err } rev.User = &models.User{Name: userName, AvatarURL: userAvatar} reviews = append(reviews, &rev) } return reviews, nil }