211 lines
6.2 KiB
Go
211 lines
6.2 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"veza-backend-api/internal/models"
|
|
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type CommentService struct {
|
|
db *gorm.DB
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewCommentService(db *gorm.DB, logger *zap.Logger) *CommentService {
|
|
return &CommentService{
|
|
db: db,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// CreateComment creates a new comment on a track
|
|
func (s *CommentService) CreateComment(ctx context.Context, trackID uuid.UUID, userID uuid.UUID, content string, timestamp float64, parentID *uuid.UUID) (*models.TrackComment, error) { // Updated trackID and parentID to uuid.UUID
|
|
// Verify if track exists
|
|
var track models.Track
|
|
if err := s.db.WithContext(ctx).First(&track, "id = ?", trackID).Error; err != nil { // Updated query
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrTrackNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// Verify if parent comment exists (if reply)
|
|
if parentID != nil {
|
|
var parent models.TrackComment
|
|
if err := s.db.WithContext(ctx).First(&parent, "id = ?", *parentID).Error; err != nil { // Updated query
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrParentCommentNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
// Ensure parent belongs to the same track
|
|
if parent.TrackID != trackID {
|
|
return nil, ErrParentTrackMismatch
|
|
}
|
|
}
|
|
|
|
comment := &models.TrackComment{
|
|
TrackID: trackID,
|
|
UserID: userID,
|
|
Content: content,
|
|
Timestamp: timestamp,
|
|
ParentID: parentID,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
if err := s.db.WithContext(ctx).Create(comment).Error; err != nil {
|
|
s.logger.Error("Failed to create comment", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
// Preload user info for response
|
|
if err := s.db.WithContext(ctx).Preload("User").First(comment, comment.ID).Error; err != nil {
|
|
return comment, nil // Return comment without user info if preload fails
|
|
}
|
|
|
|
s.logger.Info("Comment created",
|
|
zap.Any("comment_id", comment.ID), // Changed to zap.Any for uuid.UUID
|
|
zap.Any("track_id", trackID), // Changed to zap.Any for uuid.UUID
|
|
zap.String("user_id", userID.String()))
|
|
|
|
return comment, nil
|
|
}
|
|
|
|
// GetComments retrieves comments for a track
|
|
func (s *CommentService) GetComments(ctx context.Context, trackID uuid.UUID, page, limit int) ([]models.TrackComment, int64, error) { // Updated trackID to uuid.UUID
|
|
var comments []models.TrackComment
|
|
var total int64
|
|
|
|
offset := (page - 1) * limit
|
|
|
|
// Count total top-level comments (or all comments? usually top-level for pagination, replies fetched separately or nested)
|
|
// Here we fetch all top-level comments
|
|
query := s.db.WithContext(ctx).Model(&models.TrackComment{}).Where("track_id = ? AND parent_id IS NULL", trackID)
|
|
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Fetch comments with user info and replies
|
|
// Note: Deep nesting of replies might require recursive query or multiple queries.
|
|
// For simplicity, we just preload direct replies or let frontend handle threading if flat list.
|
|
// Assuming flat list of top level + preloaded replies?
|
|
// Let's just fetch top level and preload their replies one level deep for now
|
|
err := query.
|
|
Preload("User").
|
|
Preload("Replies").
|
|
Preload("Replies.User").
|
|
Order("created_at DESC").
|
|
Limit(limit).
|
|
Offset(offset).
|
|
Find(&comments).Error
|
|
|
|
if err != nil {
|
|
s.logger.Error("Failed to get comments", zap.Error(err))
|
|
return nil, 0, err
|
|
}
|
|
|
|
return comments, total, nil
|
|
}
|
|
|
|
// UpdateComment updates a comment
|
|
func (s *CommentService) UpdateComment(ctx context.Context, commentID uuid.UUID, userID uuid.UUID, content string) (*models.TrackComment, error) { // Updated commentID to uuid.UUID
|
|
var comment models.TrackComment
|
|
if err := s.db.WithContext(ctx).First(&comment, "id = ?", commentID).Error; err != nil { // Updated query
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrCommentNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// Check permission
|
|
if comment.UserID != userID {
|
|
return nil, ErrForbidden
|
|
}
|
|
|
|
comment.Content = content
|
|
comment.IsEdited = true
|
|
comment.UpdatedAt = time.Now()
|
|
|
|
if err := s.db.WithContext(ctx).Save(&comment).Error; err != nil {
|
|
s.logger.Error("Failed to update comment", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Comment updated",
|
|
zap.Any("comment_id", comment.ID), // Changed to zap.Any for uuid.UUID
|
|
zap.String("user_id", userID.String()))
|
|
|
|
return &comment, nil
|
|
}
|
|
|
|
// GetReplies retrieves replies for a given parent comment ID
|
|
func (s *CommentService) GetReplies(ctx context.Context, parentID uuid.UUID, page, limit int) ([]models.TrackComment, int64, error) { // Updated parentID to uuid.UUID
|
|
var replies []models.TrackComment
|
|
var total int64
|
|
|
|
offset := (page - 1) * limit
|
|
|
|
// Verify if parent comment exists
|
|
var parent models.TrackComment
|
|
if err := s.db.WithContext(ctx).First(&parent, "id = ?", parentID).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, 0, ErrParentCommentNotFound
|
|
}
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Count total replies
|
|
query := s.db.WithContext(ctx).Model(&models.TrackComment{}).Where("parent_id = ?", parentID)
|
|
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Fetch replies with user info
|
|
err := query.
|
|
Preload("User").
|
|
Order("created_at ASC"). // Order by oldest first
|
|
Limit(limit).
|
|
Offset(offset).
|
|
Find(&replies).Error
|
|
|
|
if err != nil {
|
|
s.logger.Error("Failed to get replies", zap.Error(err))
|
|
return nil, 0, err
|
|
}
|
|
|
|
return replies, total, nil
|
|
}
|
|
|
|
// DeleteComment deletes a comment
|
|
// MIGRATION UUID: userID migré vers uuid.UUID, commentID reste int64
|
|
func (s *CommentService) DeleteComment(ctx context.Context, commentID uuid.UUID, userID uuid.UUID, isAdmin bool) error { // Updated commentID to uuid.UUID
|
|
var comment models.TrackComment
|
|
if err := s.db.WithContext(ctx).First(&comment, "id = ?", commentID).Error; err != nil { // Updated query
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return ErrCommentNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Check permission
|
|
if comment.UserID != userID && !isAdmin {
|
|
return ErrForbidden
|
|
}
|
|
|
|
// Soft delete or hard delete? Model has DeletedAt so soft delete
|
|
if err := s.db.WithContext(ctx).Delete(&comment).Error; err != nil {
|
|
s.logger.Error("Failed to delete comment", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|