diff --git a/backend/internal/account/entity.go b/backend/internal/account/entity.go index 3c9efa5..a9f61e7 100644 --- a/backend/internal/account/entity.go +++ b/backend/internal/account/entity.go @@ -65,3 +65,15 @@ type UpdateProfileRequest struct { type RefreshRequest struct { RefreshToken string `json:"refresh_token"` } + +type GetProfileRequest struct { + AccountID uint `json:"account_id"` +} + +type GetProfileResponse struct { + Account FindByIDResponse `json:"account"` + VideoCount int64 `json:"video_count"` + TotalLikes int64 `json:"total_likes"` + FollowerCount int64 `json:"follower_count"` + VloggerCount int64 `json:"vlogger_count"` +} diff --git a/backend/internal/http/router.go b/backend/internal/http/router.go index 1ca3571..38fdf65 100644 --- a/backend/internal/http/router.go +++ b/backend/internal/http/router.go @@ -126,7 +126,35 @@ func SetRouter(db *gorm.DB, cache *rediscache.Client, rmq *rabbitmq.RabbitMQ) *g protectedSocialGroup.POST("/unfollow", socialLimiter, socialHandler.Unfollow) protectedSocialGroup.POST("/getAllFollowers", socialHandler.GetAllFollowers) protectedSocialGroup.POST("/getAllVloggers", socialHandler.GetAllVloggers) + protectedSocialGroup.POST("/getCounts", socialHandler.GetCounts) } + + accountGroup.POST("/getProfile", func(c *gin.Context) { + var req account.GetProfileRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + if req.AccountID == 0 { + c.JSON(400, gin.H{"error": "account_id is required"}) + return + } + acc, err := accountService.FindByID(c.Request.Context(), req.AccountID) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + videoCount, _ := videoRepository.CountByAuthor(c.Request.Context(), req.AccountID) + totalLikes, _ := videoRepository.TotalLikesByAuthor(c.Request.Context(), req.AccountID) + followerCount, _ := socialRepository.CountFollowers(c.Request.Context(), req.AccountID) + vloggerCount, _ := socialRepository.CountVloggers(c.Request.Context(), req.AccountID) + + c.JSON(200, account.GetProfileResponse{ + Account: account.FindByIDResponse{ID: acc.ID, Username: acc.Username, AvatarURL: acc.AvatarURL, Bio: acc.Bio}, + VideoCount: videoCount, TotalLikes: totalLikes, + FollowerCount: followerCount, VloggerCount: vloggerCount, + }) + }) // feed feedRepository := feed.NewFeedRepository(db) feedService := feed.NewFeedService(feedRepository, likeRepository, cache) diff --git a/backend/internal/social/entity.go b/backend/internal/social/entity.go index 17d9e9b..4f8f640 100644 --- a/backend/internal/social/entity.go +++ b/backend/internal/social/entity.go @@ -1,33 +1,40 @@ -package social - -import "feedsystem_video_go/internal/account" - -type Social struct { - ID uint `gorm:"primaryKey"` - FollowerID uint `gorm:"not null;index:idx_social_follower;uniqueIndex:idx_social_follower_vlogger"` - VloggerID uint `gorm:"not null;index:idx_social_vlogger;uniqueIndex:idx_social_follower_vlogger"` -} - -type FollowRequest struct { - VloggerID uint `json:"vlogger_id"` -} - -type UnfollowRequest struct { - VloggerID uint `json:"vlogger_id"` -} - -type GetAllFollowersRequest struct { - VloggerID uint `json:"vlogger_id"` -} - -type GetAllFollowersResponse struct { - Followers []*account.Account `json:"followers"` -} - -type GetAllVloggersRequest struct { - FollowerID uint `json:"follower_id"` -} - -type GetAllVloggersResponse struct { - Vloggers []*account.Account `json:"vloggers"` +package social + +import "feedsystem_video_go/internal/account" + +type Social struct { + ID uint `gorm:"primaryKey"` + FollowerID uint `gorm:"not null;index:idx_social_follower;uniqueIndex:idx_social_follower_vlogger"` + VloggerID uint `gorm:"not null;index:idx_social_vlogger;uniqueIndex:idx_social_follower_vlogger"` +} + +type FollowRequest struct { + VloggerID uint `json:"vlogger_id"` +} + +type UnfollowRequest struct { + VloggerID uint `json:"vlogger_id"` +} + +type GetAllFollowersRequest struct { + VloggerID uint `json:"vlogger_id"` +} + +type GetAllFollowersResponse struct { + Followers []*account.Account `json:"followers"` + FollowerCount int64 `json:"follower_count"` +} + +type GetAllVloggersResponse struct { + Vloggers []*account.Account `json:"vloggers"` + VloggerCount int64 `json:"vlogger_count"` +} + +type SocialCounts struct { + FollowerCount int64 `json:"follower_count"` + VloggerCount int64 `json:"vlogger_count"` } + +type GetAllVloggersRequest struct { + FollowerID uint `json:"follower_id"` +} diff --git a/backend/internal/social/handler.go b/backend/internal/social/handler.go index 093ecc9..0d735b6 100644 --- a/backend/internal/social/handler.go +++ b/backend/internal/social/handler.go @@ -94,7 +94,8 @@ func (h *SocialHandler) GetAllFollowers(c *gin.Context) { if followers == nil { followers = []*account.Account{} } - c.JSON(http.StatusOK, GetAllFollowersResponse{Followers: followers}) + followerCount, _ := h.service.CountFollowers(c.Request.Context(), vloggerID) + c.JSON(http.StatusOK, GetAllFollowersResponse{Followers: followers, FollowerCount: followerCount}) } func (h *SocialHandler) GetAllVloggers(c *gin.Context) { @@ -122,5 +123,17 @@ func (h *SocialHandler) GetAllVloggers(c *gin.Context) { if vloggers == nil { vloggers = []*account.Account{} } - c.JSON(http.StatusOK, GetAllVloggersResponse{Vloggers: vloggers}) + vloggerCount, _ := h.service.CountVloggers(c.Request.Context(), followerID) + c.JSON(http.StatusOK, GetAllVloggersResponse{Vloggers: vloggers, VloggerCount: vloggerCount}) +} + +func (h *SocialHandler) GetCounts(c *gin.Context) { + accountID, err := jwt.GetAccountID(c) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) + return + } + followerCount, _ := h.service.CountFollowers(c.Request.Context(), accountID) + vloggerCount, _ := h.service.CountVloggers(c.Request.Context(), accountID) + c.JSON(http.StatusOK, SocialCounts{FollowerCount: followerCount, VloggerCount: vloggerCount}) } diff --git a/backend/internal/social/repo.go b/backend/internal/social/repo.go index f990510..1f20841 100644 --- a/backend/internal/social/repo.go +++ b/backend/internal/social/repo.go @@ -91,3 +91,19 @@ func (r *SocialRepository) IsFollowed(ctx context.Context, social *Social) (bool } return count > 0, nil } + +func (r *SocialRepository) CountFollowers(ctx context.Context, vloggerID uint) (int64, error) { + var count int64 + if err := r.db.WithContext(ctx).Model(&Social{}).Where("vlogger_id = ?", vloggerID).Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} + +func (r *SocialRepository) CountVloggers(ctx context.Context, followerID uint) (int64, error) { + var count int64 + if err := r.db.WithContext(ctx).Model(&Social{}).Where("follower_id = ?", followerID).Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} diff --git a/backend/internal/social/service.go b/backend/internal/social/service.go index 8aa2c0f..59d1011 100644 --- a/backend/internal/social/service.go +++ b/backend/internal/social/service.go @@ -1,93 +1,101 @@ -package social - -import ( - "context" - "errors" - "feedsystem_video_go/internal/account" - "feedsystem_video_go/internal/middleware/rabbitmq" -) - -type SocialService struct { - repo *SocialRepository - accountrepo *account.AccountRepository - socialMQ *rabbitmq.SocialMQ -} - -func NewSocialService(repo *SocialRepository, accountrepo *account.AccountRepository, socialMQ *rabbitmq.SocialMQ) *SocialService { - return &SocialService{repo: repo, accountrepo: accountrepo, socialMQ: socialMQ} -} - -func (s *SocialService) Follow(ctx context.Context, social *Social) error { - _, err := s.accountrepo.FindByID(ctx, social.FollowerID) - if err != nil { - return err - } - _, err = s.accountrepo.FindByID(ctx, social.VloggerID) - if err != nil { - return err - } - if social.FollowerID == social.VloggerID { - return errors.New("can not follow self") - } - isFollowed, err := s.repo.IsFollowed(ctx, social) - if err != nil { - return err - } - if isFollowed { - return errors.New("already followed") - } - if s.socialMQ != nil { - s.socialMQ.Follow(ctx, social.FollowerID, social.VloggerID) - } - return s.repo.Follow(ctx, social) -} - -func (s *SocialService) Unfollow(ctx context.Context, social *Social) error { - _, err := s.accountrepo.FindByID(ctx, social.FollowerID) - if err != nil { - return err - } - _, err = s.accountrepo.FindByID(ctx, social.VloggerID) - if err != nil { - return err - } - isFollowed, err := s.repo.IsFollowed(ctx, social) - if err != nil { - return err - } - if !isFollowed { - return errors.New("not followed") - } - if s.socialMQ != nil { - s.socialMQ.UnFollow(ctx, social.FollowerID, social.VloggerID) - } - return s.repo.Unfollow(ctx, social) -} - -func (s *SocialService) GetAllFollowers(ctx context.Context, VloggerID uint) ([]*account.Account, error) { - _, err := s.accountrepo.FindByID(ctx, VloggerID) - if err != nil { - return nil, err - } - return s.repo.GetAllFollowers(ctx, VloggerID) -} - -func (s *SocialService) GetAllVloggers(ctx context.Context, FollowerID uint) ([]*account.Account, error) { - _, err := s.accountrepo.FindByID(ctx, FollowerID) - if err != nil { - return nil, err - } - return s.repo.GetAllVloggers(ctx, FollowerID) -} - -func (s *SocialService) IsFollowed(ctx context.Context, social *Social) (bool, error) { - _, err := s.accountrepo.FindByID(ctx, social.FollowerID) - if err != nil { - return false, err - } - _, err = s.accountrepo.FindByID(ctx, social.VloggerID) - if err != nil { - return false, err - } - return s.repo.IsFollowed(ctx, social) -} +package social + +import ( + "context" + "errors" + "feedsystem_video_go/internal/account" + "feedsystem_video_go/internal/middleware/rabbitmq" +) + +type SocialService struct { + repo *SocialRepository + accountrepo *account.AccountRepository + socialMQ *rabbitmq.SocialMQ +} + +func NewSocialService(repo *SocialRepository, accountrepo *account.AccountRepository, socialMQ *rabbitmq.SocialMQ) *SocialService { + return &SocialService{repo: repo, accountrepo: accountrepo, socialMQ: socialMQ} +} + +func (s *SocialService) Follow(ctx context.Context, social *Social) error { + _, err := s.accountrepo.FindByID(ctx, social.FollowerID) + if err != nil { + return err + } + _, err = s.accountrepo.FindByID(ctx, social.VloggerID) + if err != nil { + return err + } + if social.FollowerID == social.VloggerID { + return errors.New("can not follow self") + } + isFollowed, err := s.repo.IsFollowed(ctx, social) + if err != nil { + return err + } + if isFollowed { + return errors.New("already followed") + } + if s.socialMQ != nil { + s.socialMQ.Follow(ctx, social.FollowerID, social.VloggerID) + } + return s.repo.Follow(ctx, social) +} + +func (s *SocialService) Unfollow(ctx context.Context, social *Social) error { + _, err := s.accountrepo.FindByID(ctx, social.FollowerID) + if err != nil { + return err + } + _, err = s.accountrepo.FindByID(ctx, social.VloggerID) + if err != nil { + return err + } + isFollowed, err := s.repo.IsFollowed(ctx, social) + if err != nil { + return err + } + if !isFollowed { + return errors.New("not followed") + } + if s.socialMQ != nil { + s.socialMQ.UnFollow(ctx, social.FollowerID, social.VloggerID) + } + return s.repo.Unfollow(ctx, social) +} + +func (s *SocialService) GetAllFollowers(ctx context.Context, VloggerID uint) ([]*account.Account, error) { + _, err := s.accountrepo.FindByID(ctx, VloggerID) + if err != nil { + return nil, err + } + return s.repo.GetAllFollowers(ctx, VloggerID) +} + +func (s *SocialService) GetAllVloggers(ctx context.Context, FollowerID uint) ([]*account.Account, error) { + _, err := s.accountrepo.FindByID(ctx, FollowerID) + if err != nil { + return nil, err + } + return s.repo.GetAllVloggers(ctx, FollowerID) +} + +func (s *SocialService) CountFollowers(ctx context.Context, vloggerID uint) (int64, error) { + return s.repo.CountFollowers(ctx, vloggerID) +} + +func (s *SocialService) CountVloggers(ctx context.Context, followerID uint) (int64, error) { + return s.repo.CountVloggers(ctx, followerID) +} + +func (s *SocialService) IsFollowed(ctx context.Context, social *Social) (bool, error) { + _, err := s.accountrepo.FindByID(ctx, social.FollowerID) + if err != nil { + return false, err + } + _, err = s.accountrepo.FindByID(ctx, social.VloggerID) + if err != nil { + return false, err + } + return s.repo.IsFollowed(ctx, social) +} diff --git a/backend/internal/video/video_repo.go b/backend/internal/video/video_repo.go index 2cc5597..9dce48f 100644 --- a/backend/internal/video/video_repo.go +++ b/backend/internal/video/video_repo.go @@ -102,3 +102,19 @@ func (vr *VideoRepository) ChangePopularity(ctx context.Context, id uint, change } return nil } + +func (vr *VideoRepository) CountByAuthor(ctx context.Context, authorID uint) (int64, error) { + var count int64 + if err := vr.db.WithContext(ctx).Model(&Video{}).Where("author_id = ?", authorID).Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} + +func (vr *VideoRepository) TotalLikesByAuthor(ctx context.Context, authorID uint) (int64, error) { + var total int64 + if err := vr.db.WithContext(ctx).Model(&Video{}).Where("author_id = ?", authorID).Select("COALESCE(SUM(likes_count), 0)").Scan(&total).Error; err != nil { + return 0, err + } + return total, nil +}