259 lines
7.5 KiB
Go
259 lines
7.5 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"github.com/topfans/backend/pkg/logger"
|
||
pbCommon "github.com/topfans/backend/pkg/proto/common"
|
||
pb "github.com/topfans/backend/pkg/proto/user"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// UserInfo 用户信息(简化版,用于好友列表展示)
|
||
type UserInfo struct {
|
||
UserID int64
|
||
Nickname string
|
||
Avatar string
|
||
FanLevel int32
|
||
Social int32 // 好友数量
|
||
}
|
||
|
||
// UserServiceClient 用户服务RPC客户端接口
|
||
type UserServiceClient interface {
|
||
// ValidateUser 验证用户是否存在
|
||
ValidateUser(ctx context.Context, userID int64) (bool, error)
|
||
|
||
// ValidateFanProfile 验证粉丝档案是否存在
|
||
ValidateFanProfile(ctx context.Context, userID, starID int64) (bool, error)
|
||
|
||
// GetUsersByIDs 批量查询用户信息和粉丝档案
|
||
// 返回 map[userID]UserInfo
|
||
GetUsersByIDs(ctx context.Context, userIDs []int64, starID int64) (map[int64]*UserInfo, error)
|
||
|
||
// UpdateFanProfileSocial 更新粉丝档案的social字段(好友数量)
|
||
UpdateFanProfileSocial(ctx context.Context, userID, starID int64, delta int32) error
|
||
}
|
||
|
||
// userServiceClient 用户服务RPC客户端实现
|
||
type userServiceClient struct {
|
||
client pb.UserSocialService
|
||
}
|
||
|
||
// NewUserServiceClient 创建用户服务RPC客户端
|
||
func NewUserServiceClient(client pb.UserSocialService) UserServiceClient {
|
||
return &userServiceClient{
|
||
client: client,
|
||
}
|
||
}
|
||
|
||
// ValidateUser 验证用户是否存在
|
||
func (c *userServiceClient) ValidateUser(ctx context.Context, userID int64) (bool, error) {
|
||
// 调用userService的GetUser接口
|
||
resp, err := c.client.GetUser(ctx, &pb.GetUserRequest{
|
||
UserId: userID,
|
||
})
|
||
|
||
if err != nil {
|
||
logger.Logger.Error("Failed to validate user via RPC",
|
||
zap.Int64("user_id", userID),
|
||
zap.Error(err),
|
||
)
|
||
return false, fmt.Errorf("RPC调用失败: %w", err)
|
||
}
|
||
|
||
// 检查响应
|
||
if resp.Base == nil || resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
|
||
logger.Logger.Warn("User validation failed",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int32("code", int32(resp.Base.Code)),
|
||
)
|
||
return false, nil
|
||
}
|
||
|
||
return resp.User != nil && resp.User.IsActive, nil
|
||
}
|
||
|
||
// ValidateFanProfile 验证粉丝档案是否存在
|
||
func (c *userServiceClient) ValidateFanProfile(ctx context.Context, userID, starID int64) (bool, error) {
|
||
// 调用userService的GetFanProfile接口
|
||
resp, err := c.client.GetFanProfile(ctx, &pb.GetFanProfileRequest{
|
||
UserId: userID,
|
||
StarId: starID,
|
||
})
|
||
|
||
if err != nil {
|
||
logger.Logger.Error("Failed to validate fan profile via RPC",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int64("star_id", starID),
|
||
zap.Error(err),
|
||
)
|
||
return false, fmt.Errorf("RPC调用失败: %w", err)
|
||
}
|
||
|
||
// 检查响应
|
||
if resp.Base == nil || resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
|
||
logger.Logger.Warn("Fan profile validation failed",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int64("star_id", starID),
|
||
zap.Int32("code", int32(resp.Base.Code)),
|
||
)
|
||
return false, nil
|
||
}
|
||
|
||
// FanProfile 不需要检查 IsActive,只要存在就是有效的
|
||
return resp.Profile != nil, nil
|
||
}
|
||
|
||
// GetUsersByIDs 批量查询用户信息和粉丝档案
|
||
func (c *userServiceClient) GetUsersByIDs(ctx context.Context, userIDs []int64, starID int64) (map[int64]*UserInfo, error) {
|
||
if len(userIDs) == 0 {
|
||
return make(map[int64]*UserInfo), nil
|
||
}
|
||
|
||
// 去重
|
||
uniqueIDs := make(map[int64]bool)
|
||
for _, id := range userIDs {
|
||
uniqueIDs[id] = true
|
||
}
|
||
|
||
// 批量查询
|
||
result := make(map[int64]*UserInfo)
|
||
|
||
// TODO: 优化为真正的批量查询接口
|
||
// 目前userService没有批量查询接口,需要逐个查询
|
||
// 后续可以在userService中添加BatchGetUsers和BatchGetFanProfiles接口
|
||
for userID := range uniqueIDs {
|
||
|
||
// 获取粉丝档案信息(包含昵称和头像)
|
||
profileResp, err := c.client.GetFanProfile(ctx, &pb.GetFanProfileRequest{
|
||
UserId: userID,
|
||
StarId: starID,
|
||
})
|
||
|
||
if err != nil || profileResp.Base == nil || profileResp.Base.Code != pbCommon.StatusCode_STATUS_OK || profileResp.Profile == nil {
|
||
logger.Logger.Warn("Failed to get fan profile",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int64("star_id", starID),
|
||
zap.Error(err),
|
||
)
|
||
continue
|
||
}
|
||
|
||
// 合并信息
|
||
result[userID] = &UserInfo{
|
||
UserID: userID,
|
||
Nickname: profileResp.Profile.Nickname,
|
||
Avatar: profileResp.Profile.AvatarUrl, // 从 FanProfile 获取头像
|
||
FanLevel: profileResp.Profile.Level,
|
||
Social: profileResp.Profile.Social,
|
||
}
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// UpdateFanProfileSocial 更新粉丝档案的social字段(好友数量)
|
||
func (c *userServiceClient) UpdateFanProfileSocial(ctx context.Context, userID, starID int64, delta int32) error {
|
||
// 调用userService的UpdateFanProfileSocial接口
|
||
resp, err := c.client.UpdateFanProfileSocial(ctx, &pb.UpdateFanProfileSocialRequest{
|
||
UserId: userID,
|
||
StarId: starID,
|
||
Delta: delta,
|
||
})
|
||
|
||
if err != nil {
|
||
logger.Logger.Error("Failed to update fan profile social via RPC",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int64("star_id", starID),
|
||
zap.Int32("delta", delta),
|
||
zap.Error(err),
|
||
)
|
||
return fmt.Errorf("RPC调用失败: %w", err)
|
||
}
|
||
|
||
// 检查响应
|
||
if resp.Base == nil || resp.Base.Code != 0 {
|
||
logger.Logger.Warn("Update fan profile social failed",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int64("star_id", starID),
|
||
zap.Int32("delta", delta),
|
||
zap.Int32("code", int32(resp.Base.Code)),
|
||
)
|
||
return fmt.Errorf("更新好友数量失败: %s", resp.Base.Message)
|
||
}
|
||
|
||
logger.Logger.Info("Update fan profile social successful",
|
||
zap.Int64("user_id", userID),
|
||
zap.Int64("star_id", starID),
|
||
zap.Int32("delta", delta),
|
||
zap.Int32("new_social", resp.NewSocial),
|
||
)
|
||
|
||
return nil
|
||
}
|
||
|
||
// ========== Mock实现(用于测试) ==========
|
||
|
||
// MockUserServiceClient Mock的用户服务RPC客户端(用于测试)
|
||
type MockUserServiceClient struct {
|
||
ValidUsers map[int64]bool // 有效的用户ID
|
||
ValidFanProfile map[string]bool // 有效的粉丝档案(key: "userID_starID")
|
||
UserInfos map[int64]*UserInfo // 用户信息
|
||
}
|
||
|
||
// NewMockUserServiceClient 创建Mock客户端
|
||
func NewMockUserServiceClient() *MockUserServiceClient {
|
||
return &MockUserServiceClient{
|
||
ValidUsers: make(map[int64]bool),
|
||
ValidFanProfile: make(map[string]bool),
|
||
UserInfos: make(map[int64]*UserInfo),
|
||
}
|
||
}
|
||
|
||
// ValidateUser Mock实现
|
||
func (m *MockUserServiceClient) ValidateUser(ctx context.Context, userID int64) (bool, error) {
|
||
return m.ValidUsers[userID], nil
|
||
}
|
||
|
||
// ValidateFanProfile Mock实现
|
||
func (m *MockUserServiceClient) ValidateFanProfile(ctx context.Context, userID, starID int64) (bool, error) {
|
||
key := fmt.Sprintf("%d_%d", userID, starID)
|
||
return m.ValidFanProfile[key], nil
|
||
}
|
||
|
||
// GetUsersByIDs Mock实现
|
||
func (m *MockUserServiceClient) GetUsersByIDs(ctx context.Context, userIDs []int64, starID int64) (map[int64]*UserInfo, error) {
|
||
result := make(map[int64]*UserInfo)
|
||
for _, userID := range userIDs {
|
||
if info, ok := m.UserInfos[userID]; ok {
|
||
result[userID] = info
|
||
}
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// UpdateFanProfileSocial Mock实现
|
||
func (m *MockUserServiceClient) UpdateFanProfileSocial(ctx context.Context, userID, starID int64, delta int32) error {
|
||
// Mock实现:直接返回成功
|
||
return nil
|
||
}
|
||
|
||
// AddMockUser 添加Mock用户
|
||
func (m *MockUserServiceClient) AddMockUser(userID int64, nickname, avatar string, fanLevel, social int32) {
|
||
m.ValidUsers[userID] = true
|
||
m.UserInfos[userID] = &UserInfo{
|
||
UserID: userID,
|
||
Nickname: nickname,
|
||
Avatar: avatar,
|
||
FanLevel: fanLevel,
|
||
Social: social,
|
||
}
|
||
}
|
||
|
||
// AddMockFanProfile 添加Mock粉丝档案
|
||
func (m *MockUserServiceClient) AddMockFanProfile(userID, starID int64) {
|
||
key := fmt.Sprintf("%d_%d", userID, starID)
|
||
m.ValidFanProfile[key] = true
|
||
}
|