reviews.go 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Package repository
  2. package repository
  3. import (
  4. "context"
  5. "errors"
  6. "fmt"
  7. "github.com/jackc/pgx/v5/pgconn"
  8. "github.com/jackc/pgx/v5/pgxpool"
  9. "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models"
  10. )
  11. type ReviewRepo struct {
  12. pool *pgxpool.Pool
  13. }
  14. func NewReviewRepo(pool *pgxpool.Pool) *ReviewRepo {
  15. return &ReviewRepo{pool: pool}
  16. }
  17. func (r *ReviewRepo) Create(ctx context.Context, review *models.Review) error {
  18. tx, err := r.pool.Begin(ctx)
  19. if err != nil {
  20. return err
  21. }
  22. defer tx.Rollback(ctx)
  23. err = tx.QueryRow(ctx,
  24. `INSERT INTO reviews (user_id, target_type, target_id, rating, text)
  25. VALUES ($1, $2, $3, $4, $5)
  26. RETURNING id, created_at, updated_at`,
  27. review.UserID, review.TargetType, review.TargetID, review.Rating, review.Text,
  28. ).Scan(&review.ID, &review.CreatedAt, &review.UpdatedAt)
  29. if err != nil {
  30. // Проверка нарушения уникальности через код ошибки pgx (23505 = unique_violation)
  31. var pgErr *pgconn.PgError
  32. if errors.As(err, &pgErr) && pgErr.Code == "23505" {
  33. return fmt.Errorf("you already reviewed this")
  34. }
  35. return fmt.Errorf("create review: %w", err)
  36. }
  37. // Обновляем рейтинг и количество отзывов у целевой сущности
  38. if review.TargetType == "service" {
  39. _, err = tx.Exec(ctx,
  40. `UPDATE services SET
  41. reviews_count = (SELECT COUNT(*) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL),
  42. rating = (SELECT COALESCE(AVG(rating), 0) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL)
  43. WHERE id = $2`,
  44. review.TargetType, review.TargetID)
  45. } else {
  46. _, err = tx.Exec(ctx,
  47. `UPDATE places SET
  48. reviews_count = (SELECT COUNT(*) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL),
  49. rating = (SELECT COALESCE(AVG(rating), 0) FROM reviews WHERE target_type = $1 AND target_id = $2 AND deleted_at IS NULL)
  50. WHERE id = $2`,
  51. review.TargetType, review.TargetID)
  52. }
  53. if err != nil {
  54. return fmt.Errorf("update rating: %w", err)
  55. }
  56. return tx.Commit(ctx)
  57. }
  58. func (r *ReviewRepo) List(ctx context.Context, targetType, targetID string) ([]*models.Review, error) {
  59. rows, err := r.pool.Query(ctx,
  60. `SELECT r.id, r.user_id, r.target_type, r.target_id, r.rating, r.text,
  61. r.created_at, r.updated_at,
  62. u.name, u.avatar_url
  63. FROM reviews r
  64. JOIN users u ON u.id = r.user_id
  65. WHERE r.target_type = $1 AND r.target_id = $2 AND r.deleted_at IS NULL
  66. ORDER BY r.created_at DESC`, targetType, targetID)
  67. if err != nil {
  68. return nil, err
  69. }
  70. defer rows.Close()
  71. var reviews []*models.Review
  72. for rows.Next() {
  73. var rev models.Review
  74. var userName, userAvatar *string
  75. if err := rows.Scan(
  76. &rev.ID, &rev.UserID, &rev.TargetType, &rev.TargetID, &rev.Rating, &rev.Text,
  77. &rev.CreatedAt, &rev.UpdatedAt,
  78. &userName, &userAvatar,
  79. ); err != nil {
  80. return nil, err
  81. }
  82. rev.User = &models.User{Name: userName, AvatarURL: userAvatar}
  83. reviews = append(reviews, &rev)
  84. }
  85. return reviews, nil
  86. }