topfans/backend/services/socialService/service/user_rpc_client.go
2026-05-16 02:42:32 +08:00

259 lines
7.5 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 (
"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
}