feat(video): add like/unlike/isliked/getlikescount APIs for videos

This commit is contained in:
Leon
2025-12-09 15:30:16 +08:00
parent 72f35f7a7a
commit eeafe9d32a
6 changed files with 228 additions and 1 deletions

View File

@@ -23,7 +23,7 @@ func NewDB(dbcfg config.DatabaseConfig) (*gorm.DB, error) {
}
func AutoMigrate(db *gorm.DB) error {
return db.AutoMigrate(&account.Account{}, &video.Video{})
return db.AutoMigrate(&account.Account{}, &video.Video{}, &video.Like{})
}
func CloseDB(db *gorm.DB) error {

View File

@@ -44,6 +44,21 @@ func SetRouter(db *gorm.DB) *gin.Engine {
{
protectedVideoGroup.POST("/publish", videoHandler.PublishVideo)
}
// like
likeRepository := video.NewLikeRepository(db)
likeService := video.NewLikeService(likeRepository)
likeHandler := video.NewLikeHandler(likeService)
likeGroup := r.Group("/like")
{
likeGroup.POST("/getLikesCount", likeHandler.GetLikesCount)
}
protectedLikeGroup := likeGroup.Group("")
protectedLikeGroup.Use(middleware.JWTAuth(accountRepository))
{
protectedLikeGroup.POST("/like", likeHandler.Like)
protectedLikeGroup.POST("/unlike", likeHandler.Unlike)
protectedLikeGroup.POST("/isLiked", likeHandler.IsLiked)
}
// feed
feedRepository := feed.NewFeedRepository(db)

View File

@@ -0,0 +1,10 @@
package video
import "time"
type Like struct {
ID uint `gorm:"primaryKey"`
VideoID uint `gorm:"index, not null"`
AccountID uint `gorm:"index, not null"`
CreatedAt time.Time
}

View File

@@ -0,0 +1,117 @@
package video
import (
"feedsystem_video_go/internal/middleware"
"github.com/gin-gonic/gin"
)
type LikeRequest struct {
VideoID uint `json:"video_id"`
}
type LikeHandler struct {
service *LikeService
}
func NewLikeHandler(service *LikeService) *LikeHandler {
return &LikeHandler{service: service}
}
func (lh *LikeHandler) Like(c *gin.Context) {
var req LikeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if req.VideoID <= 0 {
c.JSON(400, gin.H{"error": "video_id is required"})
return
}
accountID, err := middleware.GetAccountID(c)
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
like := &Like{
VideoID: req.VideoID,
AccountID: accountID,
}
if err := lh.service.Like(c.Request.Context(), like); err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "like success"})
}
func (lh *LikeHandler) Unlike(c *gin.Context) {
var req LikeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if req.VideoID <= 0 {
c.JSON(400, gin.H{"error": "video_id is required"})
return
}
accountID, err := middleware.GetAccountID(c)
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
like := &Like{
VideoID: req.VideoID,
AccountID: accountID,
}
if err := lh.service.Unlike(c.Request.Context(), like); err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "unlike success"})
}
func (lh *LikeHandler) IsLiked(c *gin.Context) {
var req LikeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if req.VideoID <= 0 {
c.JSON(400, gin.H{"error": "video_id is required"})
return
}
accountID, err := middleware.GetAccountID(c)
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
isLiked, err := lh.service.IsLiked(c.Request.Context(), req.VideoID, accountID)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"is_liked": isLiked})
}
func (lh *LikeHandler) GetLikesCount(c *gin.Context) {
var req LikeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if req.VideoID <= 0 {
c.JSON(400, gin.H{"error": "video_id is required"})
return
}
likesCount, err := lh.service.GetLikesCount(c.Request.Context(), req.VideoID)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"likes_count": likesCount})
}

View File

@@ -0,0 +1,47 @@
package video
import (
"context"
"gorm.io/gorm"
)
type LikeRepository struct {
db *gorm.DB
}
func NewLikeRepository(db *gorm.DB) *LikeRepository {
return &LikeRepository{db: db}
}
func (r *LikeRepository) Like(ctx context.Context, like *Like) error {
return r.db.WithContext(ctx).Create(like).Error
}
func (r *LikeRepository) Unlike(ctx context.Context, like *Like) error {
return r.db.WithContext(ctx).
Where("video_id = ? AND account_id = ?", like.VideoID, like.AccountID).
Delete(&Like{}).Error
}
func (r *LikeRepository) IsLiked(ctx context.Context, videoID, accountID uint) (bool, error) {
var count int64
err := r.db.WithContext(ctx).Model(&Like{}).
Where("video_id = ? AND account_id = ?", videoID, accountID).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
func (r *LikeRepository) GetLikesCount(ctx context.Context, videoID uint) (int64, error) {
var count int64
err := r.db.WithContext(ctx).Model(&Like{}).
Where("video_id = ?", videoID).
Count(&count).Error
if err != nil {
return 0, err
}
return count, nil
}

View File

@@ -0,0 +1,38 @@
package video
import (
"context"
"errors"
"time"
)
type LikeService struct {
repo *LikeRepository
}
func NewLikeService(repo *LikeRepository) *LikeService {
return &LikeService{repo: repo}
}
func (s *LikeService) Like(ctx context.Context, like *Like) error {
if isLiked, err := s.IsLiked(ctx, like.VideoID, like.AccountID); err == nil && isLiked {
return errors.New("user has liked this video")
}
like.CreatedAt = time.Now()
return s.repo.Like(ctx, like)
}
func (s *LikeService) Unlike(ctx context.Context, like *Like) error {
if isLiked, err := s.IsLiked(ctx, like.VideoID, like.AccountID); err == nil && !isLiked {
return errors.New("user has not liked this video")
}
return s.repo.Unlike(ctx, like)
}
func (s *LikeService) IsLiked(ctx context.Context, videoID, accountID uint) (bool, error) {
return s.repo.IsLiked(ctx, videoID, accountID)
}
func (s *LikeService) GetLikesCount(ctx context.Context, videoID uint) (int64, error) {
return s.repo.GetLikesCount(ctx, videoID)
}