Files
mce/internal/service/follow_service.go
Victor_Jay 3df7b2e672 fix: Service 层移除直接持有的 *gorm.DB,引入 Repository Transaction 方法
- ReactionStore/FollowStore/EnergyStore/FavoriteStore 接口新增 Transaction 方法
- EnergyStore.Transaction 支持跨仓库事务(EnergyStore + FundStore)
- ReactionRepo/FollowRepo/FavoriteRepo/EnergyRepo 实现 Transaction 方法
- ReactionRepo 新增 GetPostLikesCount 方法,消除 Service 直查 DB
- ReactionService/FollowService/FavoriteService/EnergyService 移除 db 字段
- EnergyRepo 通过 SetFundStore 注入 FundStore 用于跨仓库事务
- 更新 deps_extra.go 构造函数调用
2026-06-02 16:46:44 +08:00

212 lines
5.6 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 service
import (
"fmt"
"log"
"metazone.cc/metalab/internal/common"
"metazone.cc/metalab/internal/model"
)
// FollowStatus 关注关系状态
type FollowStatus struct {
IFollow bool `json:"i_follow"`
TheyFollow bool `json:"they_follow"`
}
// FollowListResult 关注/粉丝列表结果
type FollowListResult struct {
Items []model.UserFollow `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
TotalPages int `json:"total_pages"`
ListPublic bool `json:"list_public"`
Accessible bool `json:"accessible"`
}
// followNotifier 关注通知接口ISP
type followNotifier interface {
NotifyFollow(followerID, followeeID uint)
}
// FollowService 关注业务逻辑
type FollowService struct {
repo FollowStore
notifier followNotifier
}
// NewFollowService 构造函数
func NewFollowService(repo FollowStore) *FollowService {
return &FollowService{repo: repo}
}
// SetNotifier 注入通知服务
func (s *FollowService) SetNotifier(n followNotifier) {
s.notifier = n
}
// Toggle 关注/取消关注返回操作后的关注状态true=已关注)
func (s *FollowService) Toggle(followerID, followeeID uint) (bool, error) {
if followerID == followeeID {
return false, fmt.Errorf("不能关注自己")
}
var isFollowing bool
err := s.repo.Transaction(func(txRepo FollowStore) error {
exists, err := txRepo.Exists(followerID, followeeID)
if err != nil {
return fmt.Errorf("查询关注状态失败: %w", err)
}
if exists {
if err := txRepo.Delete(followerID, followeeID); err != nil {
return fmt.Errorf("取消关注失败: %w", err)
}
if err := txRepo.IncrFollowingCount(followerID, -1); err != nil {
return fmt.Errorf("更新关注数失败: %w", err)
}
if err := txRepo.IncrFollowersCount(followeeID, -1); err != nil {
return fmt.Errorf("更新粉丝数失败: %w", err)
}
isFollowing = false
} else {
follow := &model.UserFollow{
FollowerID: followerID,
FolloweeID: followeeID,
}
if err := txRepo.Create(follow); err != nil {
return fmt.Errorf("关注失败: %w", err)
}
if err := txRepo.IncrFollowingCount(followerID, 1); err != nil {
return fmt.Errorf("更新关注数失败: %w", err)
}
if err := txRepo.IncrFollowersCount(followeeID, 1); err != nil {
return fmt.Errorf("更新粉丝数失败: %w", err)
}
isFollowing = true
}
return nil
})
if err == nil && isFollowing {
s.sendFollowNotification(followerID, followeeID)
}
return isFollowing, err
}
// sendFollowNotification 发送关注通知(非关键路径)
func (s *FollowService) sendFollowNotification(followerID, followeeID uint) {
if s.notifier != nil && followerID != followeeID {
s.notifier.NotifyFollow(followerID, followeeID)
}
}
// GetStatus 查询当前用户对目标用户的关注关系
func (s *FollowService) GetStatus(currentUserID, targetUserID uint) (*FollowStatus, error) {
if currentUserID == 0 {
return &FollowStatus{}, nil
}
iFollow, err := s.repo.Exists(currentUserID, targetUserID)
if err != nil {
return nil, fmt.Errorf("查询我是否关注: %w", err)
}
theyFollow, err := s.repo.Exists(targetUserID, currentUserID)
if err != nil {
return nil, fmt.Errorf("查询对方是否关注: %w", err)
}
return &FollowStatus{
IFollow: iFollow,
TheyFollow: theyFollow,
}, nil
}
// ListFollowers 粉丝列表(含隐私控制)
func (s *FollowService) ListFollowers(userID, currentUserID uint, page, pageSize int) (*FollowListResult, error) {
listPublic, err := s.repo.GetFollowListPublic(userID)
if err != nil {
log.Printf("[FollowService] ListFollowers GetFollowListPublic error uid=%d: %v, fallback to public", userID, err)
listPublic = true
}
p := common.Pagination{Page: page, PageSize: pageSize}
p.DefaultPagination()
accessible := listPublic || currentUserID == userID
var items []model.UserFollow
var total int64
if accessible {
items, total, err = s.repo.ListFollowers(userID, p.Offset(), p.PageSize)
if err != nil {
return nil, fmt.Errorf("查询粉丝列表: %w", err)
}
} else {
total, err = s.repo.CountFollowers(userID)
if err != nil {
return nil, fmt.Errorf("查询粉丝数: %w", err)
}
items = []model.UserFollow{}
}
if items == nil {
items = []model.UserFollow{}
}
return &FollowListResult{
Items: items,
Total: total,
Page: p.Page,
TotalPages: common.PageCount(total, p.PageSize),
ListPublic: listPublic,
Accessible: accessible,
}, nil
}
// ListFollowing 关注列表(含隐私控制)
func (s *FollowService) ListFollowing(userID, currentUserID uint, page, pageSize int) (*FollowListResult, error) {
listPublic, err := s.repo.GetFollowListPublic(userID)
if err != nil {
log.Printf("[FollowService] ListFollowing GetFollowListPublic error uid=%d: %v, fallback to public", userID, err)
listPublic = true
}
p := common.Pagination{Page: page, PageSize: pageSize}
p.DefaultPagination()
accessible := listPublic || currentUserID == userID
var items []model.UserFollow
var total int64
if accessible {
items, total, err = s.repo.ListFollowing(userID, p.Offset(), p.PageSize)
if err != nil {
return nil, fmt.Errorf("查询关注列表: %w", err)
}
} else {
total, err = s.repo.CountFollowing(userID)
if err != nil {
return nil, fmt.Errorf("查询关注数: %w", err)
}
items = []model.UserFollow{}
}
if items == nil {
items = []model.UserFollow{}
}
return &FollowListResult{
Items: items,
Total: total,
Page: p.Page,
TotalPages: common.PageCount(total, p.PageSize),
ListPublic: listPublic,
Accessible: accessible,
}, nil
}