- 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 构造函数调用
212 lines
5.6 KiB
Go
212 lines
5.6 KiB
Go
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
|
||
}
|