feat(P2): 粉丝/关注数 + 用户主页统计 (getProfile/getCounts)
This commit is contained in:
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user