tailly_clips/internal/repository/clip_repository.go
admin 57eba68496
Some checks failed
continuous-integration/drone/push Build is failing
v.0.0.1 Создан сервис клипов
2025-09-02 11:58:10 +03:00

336 lines
8.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"tailly_clips/internal/domain"
"time"
)
var (
ErrClipNotFound = errors.New("clip not found")
)
type ClipRepository interface {
Create(ctx context.Context, clip *domain.Clip) error
GetByID(ctx context.Context, id int) (*domain.Clip, error)
GetByAuthorID(ctx context.Context, authorID, limit, offset int) ([]*domain.Clip, int, error)
GetAll(ctx context.Context, limit, offset int) ([]*domain.Clip, int, error)
Delete(ctx context.Context, id int) error
IncrementLikesCount(ctx context.Context, clipID int) error
DecrementLikesCount(ctx context.Context, clipID int) error
IncrementCommentsCount(ctx context.Context, clipID int) error
DecrementCommentsCount(ctx context.Context, clipID int) error
GetClipURLs(ctx context.Context, clipID int) (string, string, error)
GetClipWithURLs(ctx context.Context, clipID int) (*domain.Clip, error)
}
type clipRepository struct {
db *sql.DB
}
func NewClipRepository(db *sql.DB) ClipRepository {
return &clipRepository{db: db}
}
func (r *clipRepository) Create(ctx context.Context, clip *domain.Clip) error {
query := `
INSERT INTO clips (title, video_url, thumbnail_url, duration, author_id, likes_count, comments_count, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id
`
err := r.db.QueryRowContext(ctx, query,
clip.Title,
clip.VideoURL,
clip.ThumbnailURL,
clip.Duration,
clip.AuthorID,
clip.LikesCount,
clip.CommentsCount,
clip.CreatedAt,
clip.UpdatedAt,
).Scan(&clip.ID)
if err != nil {
return fmt.Errorf("failed to create clip: %w", err)
}
return nil
}
func (r *clipRepository) GetByID(ctx context.Context, id int) (*domain.Clip, error) {
query := `
SELECT id, title, video_url, thumbnail_url, duration, author_id,
likes_count, comments_count, created_at, updated_at
FROM clips
WHERE id = $1 AND deleted_at IS NULL
`
clip := &domain.Clip{}
err := r.db.QueryRowContext(ctx, query, id).Scan(
&clip.ID,
&clip.Title,
&clip.VideoURL,
&clip.ThumbnailURL,
&clip.Duration,
&clip.AuthorID,
&clip.LikesCount,
&clip.CommentsCount,
&clip.CreatedAt,
&clip.UpdatedAt,
)
if err == sql.ErrNoRows {
return nil, ErrClipNotFound
}
if err != nil {
return nil, fmt.Errorf("failed to get clip: %w", err)
}
return clip, nil
}
func (r *clipRepository) GetByAuthorID(ctx context.Context, authorID, limit, offset int) ([]*domain.Clip, int, error) {
// Получаем общее количество
countQuery := `SELECT COUNT(*) FROM clips WHERE author_id = $1 AND deleted_at IS NULL`
var totalCount int
err := r.db.QueryRowContext(ctx, countQuery, authorID).Scan(&totalCount)
if err != nil {
return nil, 0, fmt.Errorf("failed to get total count: %w", err)
}
// Получаем клипы
query := `
SELECT id, title, video_url, thumbnail_url, duration, author_id,
likes_count, comments_count, created_at, updated_at
FROM clips
WHERE author_id = $1 AND deleted_at IS NULL
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
`
rows, err := r.db.QueryContext(ctx, query, authorID, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to get clips: %w", err)
}
defer rows.Close()
var clips []*domain.Clip
for rows.Next() {
clip := &domain.Clip{}
err := rows.Scan(
&clip.ID,
&clip.Title,
&clip.VideoURL,
&clip.ThumbnailURL,
&clip.Duration,
&clip.AuthorID,
&clip.LikesCount,
&clip.CommentsCount,
&clip.CreatedAt,
&clip.UpdatedAt,
)
if err != nil {
return nil, 0, fmt.Errorf("failed to scan clip: %w", err)
}
clips = append(clips, clip)
}
if err = rows.Err(); err != nil {
return nil, 0, fmt.Errorf("rows error: %w", err)
}
return clips, totalCount, nil
}
func (r *clipRepository) GetAll(ctx context.Context, limit, offset int) ([]*domain.Clip, int, error) {
// Получаем общее количество
countQuery := `SELECT COUNT(*) FROM clips WHERE deleted_at IS NULL`
var totalCount int
err := r.db.QueryRowContext(ctx, countQuery).Scan(&totalCount)
if err != nil {
return nil, 0, fmt.Errorf("failed to get total count: %w", err)
}
// Получаем клипы
query := `
SELECT id, title, video_url, thumbnail_url, duration, author_id,
likes_count, comments_count, created_at, updated_at
FROM clips
WHERE deleted_at IS NULL
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
`
rows, err := r.db.QueryContext(ctx, query, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to get clips: %w", err)
}
defer rows.Close()
var clips []*domain.Clip
for rows.Next() {
clip := &domain.Clip{}
err := rows.Scan(
&clip.ID,
&clip.Title,
&clip.VideoURL,
&clip.ThumbnailURL,
&clip.Duration,
&clip.AuthorID,
&clip.LikesCount,
&clip.CommentsCount,
&clip.CreatedAt,
&clip.UpdatedAt,
)
if err != nil {
return nil, 0, fmt.Errorf("failed to scan clip: %w", err)
}
clips = append(clips, clip)
}
if err = rows.Err(); err != nil {
return nil, 0, fmt.Errorf("rows error: %w", err)
}
return clips, totalCount, nil
}
func (r *clipRepository) Delete(ctx context.Context, id int) error {
query := `
UPDATE clips
SET deleted_at = $1
WHERE id = $2 AND deleted_at IS NULL
`
result, err := r.db.ExecContext(ctx, query, time.Now(), id)
if err != nil {
return fmt.Errorf("failed to delete clip: %w", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if rowsAffected == 0 {
return ErrClipNotFound
}
return nil
}
func (r *clipRepository) IncrementLikesCount(ctx context.Context, clipID int) error {
query := `
UPDATE clips
SET likes_count = likes_count + 1, updated_at = $1
WHERE id = $2 AND deleted_at IS NULL
`
_, err := r.db.ExecContext(ctx, query, time.Now(), clipID)
if err != nil {
return fmt.Errorf("failed to increment likes count: %w", err)
}
return nil
}
func (r *clipRepository) DecrementLikesCount(ctx context.Context, clipID int) error {
query := `
UPDATE clips
SET likes_count = GREATEST(likes_count - 1, 0), updated_at = $1
WHERE id = $2 AND deleted_at IS NULL
`
_, err := r.db.ExecContext(ctx, query, time.Now(), clipID)
if err != nil {
return fmt.Errorf("failed to decrement likes count: %w", err)
}
return nil
}
func (r *clipRepository) IncrementCommentsCount(ctx context.Context, clipID int) error {
query := `
UPDATE clips
SET comments_count = comments_count + 1, updated_at = $1
WHERE id = $2 AND deleted_at IS NULL
`
_, err := r.db.ExecContext(ctx, query, time.Now(), clipID)
if err != nil {
return fmt.Errorf("failed to increment comments count: %w", err)
}
return nil
}
func (r *clipRepository) DecrementCommentsCount(ctx context.Context, clipID int) error {
query := `
UPDATE clips
SET comments_count = GREATEST(comments_count - 1, 0), updated_at = $1
WHERE id = $2 AND deleted_at IS NULL
`
_, err := r.db.ExecContext(ctx, query, time.Now(), clipID)
if err != nil {
return fmt.Errorf("failed to decrement comments count: %w", err)
}
return nil
}
func (r *clipRepository) GetClipURLs(ctx context.Context, clipID int) (string, string, error) {
query := `
SELECT video_url, thumbnail_url
FROM clips
WHERE id = $1 AND deleted_at IS NULL
`
var videoURL, thumbnailURL string
err := r.db.QueryRowContext(ctx, query, clipID).Scan(&videoURL, &thumbnailURL)
if err == sql.ErrNoRows {
return "", "", ErrClipNotFound
}
if err != nil {
return "", "", fmt.Errorf("failed to get clip URLs: %w", err)
}
return videoURL, thumbnailURL, nil
}
// GetClipWithURLs возвращает клип с URL
func (r *clipRepository) GetClipWithURLs(ctx context.Context, clipID int) (*domain.Clip, error) {
query := `
SELECT id, title, video_url, thumbnail_url, duration, author_id,
likes_count, comments_count, created_at, updated_at
FROM clips
WHERE id = $1 AND deleted_at IS NULL
`
clip := &domain.Clip{}
err := r.db.QueryRowContext(ctx, query, clipID).Scan(
&clip.ID,
&clip.Title,
&clip.VideoURL,
&clip.ThumbnailURL,
&clip.Duration,
&clip.AuthorID,
&clip.LikesCount,
&clip.CommentsCount,
&clip.CreatedAt,
&clip.UpdatedAt,
)
if err == sql.ErrNoRows {
return nil, ErrClipNotFound
}
if err != nil {
return nil, fmt.Errorf("failed to get clip: %w", err)
}
return clip, nil
}