package provider import ( "context" "fmt" "strconv" "time" "dubbo.apache.org/dubbo-go/v3/common/constant" "github.com/topfans/backend/pkg/logger" "github.com/topfans/backend/pkg/proto/common" pb "github.com/topfans/backend/pkg/proto/social" "github.com/topfans/backend/services/socialService/service" "go.uber.org/zap" ) // SocialProvider 社交服务Provider实现 // 实现 Triple 协议生成的 SocialServiceHandler 接口 type SocialProvider struct { friendService service.FriendService assetLikeService *service.AssetLikeService } // 确保 SocialProvider 实现了 SocialServiceHandler 接口 var _ pb.SocialServiceHandler = (*SocialProvider)(nil) // NewSocialProvider 创建社交服务Provider实例 func NewSocialProvider(friendService service.FriendService, assetLikeService *service.AssetLikeService) *SocialProvider { return &SocialProvider{ friendService: friendService, assetLikeService: assetLikeService, } } // ========== 好友请求相关 ========== // SendFriendRequest 发送好友请求 func (p *SocialProvider) SendFriendRequest(ctx context.Context, req *pb.SendFriendRequestRequest) (*pb.SendFriendRequestResponse, error) { // 从上下文获取用户信息(由middleware注入) userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("SendFriendRequest called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("friend_user_id", req.FriendUserId), ) return p.friendService.SendFriendRequest(ctx, req, userID, starID) } // GetFriendRequests 获取好友请求列表 func (p *SocialProvider) GetFriendRequests(ctx context.Context, req *pb.GetFriendRequestsRequest) (*pb.GetFriendRequestsResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("GetFriendRequests called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.String("type", req.Type), zap.String("status", req.Status), ) return p.friendService.GetFriendRequests(ctx, req, userID, starID) } // HandleFriendRequest 处理好友请求(接受/拒绝) func (p *SocialProvider) HandleFriendRequest(ctx context.Context, req *pb.HandleFriendRequestRequest) (*pb.HandleFriendRequestResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("HandleFriendRequest called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("request_id", req.RequestId), zap.String("action", req.Action), ) return p.friendService.HandleFriendRequest(ctx, req, userID, starID) } // ========== 好友关系相关 ========== // GetFriendList 获取好友列表 func (p *SocialProvider) GetFriendList(ctx context.Context, req *pb.GetFriendListRequest) (*pb.GetFriendListResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("GetFriendList called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.String("keyword", req.Keyword), ) return p.friendService.GetFriendList(ctx, req, userID, starID) } // DeleteFriend 删除好友 func (p *SocialProvider) DeleteFriend(ctx context.Context, req *pb.DeleteFriendRequest) (*pb.DeleteFriendResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("DeleteFriend called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("friend_user_id", req.FriendUserId), ) return p.friendService.DeleteFriend(ctx, req, userID, starID) } // SetFriendRemark 设置好友备注 func (p *SocialProvider) SetFriendRemark(ctx context.Context, req *pb.SetFriendRemarkRequest) (*pb.SetFriendRemarkResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("SetFriendRemark called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("friend_user_id", req.FriendUserId), ) return p.friendService.SetFriendRemark(ctx, req, userID, starID) } // CheckFriendship 检查好友关系 func (p *SocialProvider) CheckFriendship(ctx context.Context, req *pb.CheckFriendshipRequest) (*pb.CheckFriendshipResponse, error) { logger.Logger.Debug("CheckFriendship called", zap.Int64("user_id", req.UserId), zap.Int64("friend_user_id", req.FriendUserId), ) return p.friendService.CheckFriendship(ctx, req) } // GetFriendCount 获取好友数量 func (p *SocialProvider) GetFriendCount(ctx context.Context, req *pb.GetFriendCountRequest) (*pb.GetFriendCountResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("GetFriendCount called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return p.friendService.GetFriendCount(ctx, req, userID, starID) } // ========== 辅助方法 ========== // SearchUserForFriend 查找用户信息(用于加好友) func (p *SocialProvider) SearchUserForFriend(ctx context.Context, req *pb.SearchUserForFriendRequest) (*pb.SearchUserForFriendResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("SearchUserForFriend called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("friend_fan_profile_id", req.GetFriendFanProfileId()), ) return p.friendService.SearchUserForFriend(ctx, req, userID, starID) } // GetUsersPaged 获取分页用户列表(同一明星身份下) func (p *SocialProvider) GetUsersPaged(ctx context.Context, req *pb.GetUsersPagedRequest) (*pb.GetUsersPagedResponse, error) { userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("GetUsersPaged called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int32("page", req.Page), zap.Int32("page_size", req.PageSize), ) return p.friendService.GetUsersPaged(ctx, req, userID, starID) } // GetRandomUsers 获取随机用户(同一明星下) func (p *SocialProvider) GetRandomUsers(ctx context.Context, req *pb.GetRandomUsersRequest) (*pb.GetRandomUsersResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return nil, err } logger.Logger.Debug("GetRandomUsers called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int32("count", req.Count), ) return p.friendService.GetRandomUsers(ctx, req, userID, starID) } // ========== 资产点赞相关 ========== // LikeAsset 点赞资产 func (p *SocialProvider) LikeAsset(ctx context.Context, req *pb.LikeAssetRequest) (*pb.LikeAssetResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return &pb.LikeAssetResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_UNAUTHORIZED, Message: "Unauthorized: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } logger.Logger.Debug("LikeAsset called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("asset_id", req.AssetId), ) // 调用业务逻辑层 if err := p.assetLikeService.LikeAsset(ctx, req.AssetId, userID, starID); err != nil { logger.Logger.Error("Failed to like asset", zap.Error(err), zap.Int64("asset_id", req.AssetId), zap.Int64("user_id", userID), ) return &pb.LikeAssetResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to like asset: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } return &pb.LikeAssetResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_OK, Message: "Successfully liked asset", Timestamp: time.Now().UnixMilli(), }, }, nil } // UnlikeAsset 取消点赞资产 func (p *SocialProvider) UnlikeAsset(ctx context.Context, req *pb.UnlikeAssetRequest) (*pb.UnlikeAssetResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return &pb.UnlikeAssetResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_UNAUTHORIZED, Message: "Unauthorized: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } logger.Logger.Debug("UnlikeAsset called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("asset_id", req.AssetId), ) // 调用业务逻辑层 if err := p.assetLikeService.UnlikeAsset(ctx, req.AssetId, userID, starID); err != nil { logger.Logger.Error("Failed to unlike asset", zap.Error(err), zap.Int64("asset_id", req.AssetId), zap.Int64("user_id", userID), ) return &pb.UnlikeAssetResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to unlike asset: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } return &pb.UnlikeAssetResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_OK, Message: "Successfully unliked asset", Timestamp: time.Now().UnixMilli(), }, }, nil } // CheckAssetLike 检查是否已点赞资产 func (p *SocialProvider) CheckAssetLike(ctx context.Context, req *pb.CheckAssetLikeRequest) (*pb.CheckAssetLikeResponse, error) { // 从上下文获取用户信息 userID, starID, err := extractUserInfo(ctx) if err != nil { logger.Logger.Warn("Failed to extract user info from context", zap.Error(err)) return &pb.CheckAssetLikeResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_UNAUTHORIZED, Message: "Unauthorized: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, IsLiked: false, }, nil } logger.Logger.Debug("CheckAssetLike called", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("asset_id", req.AssetId), ) // 调用业务逻辑层 isLiked, err := p.assetLikeService.CheckAssetLike(ctx, req.AssetId, userID, starID) if err != nil { logger.Logger.Error("Failed to check asset like", zap.Error(err), zap.Int64("asset_id", req.AssetId), zap.Int64("user_id", userID), ) return &pb.CheckAssetLikeResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to check asset like: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, IsLiked: false, }, nil } return &pb.CheckAssetLikeResponse{ Base: &common.BaseResponse{ Code: common.StatusCode_STATUS_OK, Message: "Successfully checked asset like status", Timestamp: time.Now().UnixMilli(), }, IsLiked: isLiked, }, nil } // extractUserInfo 从 Dubbo attachments 中提取用户信息 // 网关调用:网关已验证 Token 并将 user_id 和 star_id 通过 attachments 传递 func extractUserInfo(ctx context.Context) (int64, int64, error) { logger.Logger.Debug("Extracting user info from Dubbo attachments") // 使用正确的 constant.AttachmentKey 获取 Dubbo attachments if attachments := ctx.Value(constant.AttachmentKey); attachments != nil { logger.Logger.Debug("Found attachments in context", zap.Any("attachments", attachments)) if attMap, ok := attachments.(map[string]interface{}); ok { userID, starID := extractUserInfoFromMap(attMap) if userID > 0 && starID > 0 { logger.Logger.Debug("Successfully extracted user info", zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return userID, starID, nil } logger.Logger.Warn("Extracted zero user_id or star_id", zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) } else { logger.Logger.Warn("Attachments is not a map[string]interface{}", zap.String("type", fmt.Sprintf("%T", attachments)), ) } } else { logger.Logger.Warn("No attachments found in context") } return 0, 0, fmt.Errorf("user info not found in Dubbo attachments (expected user_id and star_id from gateway)") } // extractUserInfoFromMap 从 map 中提取 user_id 和 star_id // 支持多种类型:int64, float64, string func extractUserInfoFromMap(attMap map[string]interface{}) (int64, int64) { var userID, starID int64 // 提取 user_id if v, ok := attMap["user_id"]; ok { userID = parseIntValue(v) } // 提取 star_id if v, ok := attMap["star_id"]; ok { starID = parseIntValue(v) } return userID, starID } // parseIntValue 解析不同类型的整数值 func parseIntValue(v interface{}) int64 { switch val := v.(type) { case int64: return val case int: return int64(val) case float64: return int64(val) case string: if i, err := strconv.ParseInt(val, 10, 64); err == nil { return i } case []string: if len(val) > 0 { if i, err := strconv.ParseInt(val[0], 10, 64); err == nil { return i } } } return 0 }