package provider import ( "context" "fmt" "dubbo.apache.org/dubbo-go/v3/common/constant" appErrors "github.com/topfans/backend/pkg/errors" "github.com/topfans/backend/pkg/logger" pbCommon "github.com/topfans/backend/pkg/proto/common" pb "github.com/topfans/backend/pkg/proto/gallery" "github.com/topfans/backend/services/galleryService/service" "go.uber.org/zap" ) // GalleryProvider 展馆服务Provider实现 // 实现 Triple 协议生成的 GalleryServiceHandler 接口 type GalleryProvider struct { galleryService service.GalleryService slotService service.SlotService exhibitionService service.ExhibitionService } // 确保 GalleryProvider 实现了 GalleryServiceHandler 接口 var _ pb.GalleryServiceHandler = (*GalleryProvider)(nil) // NewGalleryProvider 创建展馆服务Provider实例 func NewGalleryProvider( galleryService service.GalleryService, slotService service.SlotService, exhibitionService service.ExhibitionService, ) *GalleryProvider { return &GalleryProvider{ galleryService: galleryService, slotService: slotService, exhibitionService: exhibitionService, } } // GetMyGallery 获取我的展馆 func (p *GalleryProvider) GetMyGallery(ctx context.Context, req *pb.GetMyGalleryRequest) (*pb.GetMyGalleryResponse, error) { logger.Logger.Info("Received GetMyGallery request") // 从 Dubbo attachments 获取用户信息(网关已验证并传递) userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { logger.Logger.Error("Failed to extract user info from attachments", zap.Error(err), ) return &pb.GetMyGalleryResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } // 调用Service层 galleryData, err := p.galleryService.GetMyGallery(userID, starID) if err != nil { logger.Logger.Error("GetMyGallery failed", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Error(err), ) return &pb.GetMyGalleryResponse{ Base: &pbCommon.BaseResponse{ Code: appErrors.ToStatusCode(err), Message: err.Error(), Timestamp: 0, }, }, err } logger.Logger.Debug("GetMyGallery successful", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int32("slot_total", galleryData.SlotTotal), ) return &pb.GetMyGalleryResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: 0, }, Data: galleryData, }, nil } // GetUserGallery 获取他人展馆 func (p *GalleryProvider) GetUserGallery(ctx context.Context, req *pb.GetUserGalleryRequest) (*pb.GetUserGalleryResponse, error) { logger.Logger.Info("Received GetUserGallery request", zap.Int64("target_uid", req.TargetUid), ) // 从 Dubbo attachments 获取用户信息(网关已验证并传递) userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { logger.Logger.Error("Failed to extract user info from attachments", zap.Error(err), ) return &pb.GetUserGalleryResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } // 调用Service层 galleryData, err := p.galleryService.GetUserGallery(userID, starID, req.TargetUid) if err != nil { logger.Logger.Error("GetUserGallery failed", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("target_uid", req.TargetUid), zap.Error(err), ) return &pb.GetUserGalleryResponse{ Base: &pbCommon.BaseResponse{ Code: appErrors.ToStatusCode(err), Message: err.Error(), Timestamp: 0, }, }, err } logger.Logger.Debug("GetUserGallery successful", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("target_uid", req.TargetUid), zap.Int32("slot_total", galleryData.SlotTotal), ) return &pb.GetUserGalleryResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: 0, }, Data: galleryData, }, nil } // PlaceAsset 在展位展示藏品 func (p *GalleryProvider) PlaceAsset(ctx context.Context, req *pb.PlaceAssetRequest) (*pb.PlaceAssetResponse, error) { logger.Logger.Info("Received PlaceAsset request", zap.Int64("asset_id", req.AssetId), zap.Int64("gallery_owner_id", req.GalleryOwnerId), zap.Int64("slot_id", req.SlotId), ) // 从 Dubbo attachments 获取用户信息(网关已验证并传递) userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { logger.Logger.Error("Failed to extract user info from attachments", zap.Error(err), ) return &pb.PlaceAssetResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } // 调用Service层 placeData, err := p.exhibitionService.PlaceAsset(userID, starID, req) if err != nil { errorMsg := err.Error() if errorMsg == "" { errorMsg = "未知错误" } logger.Logger.Error("PlaceAsset failed", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("asset_id", req.AssetId), zap.Int64("slot_id", req.SlotId), zap.String("error_message", errorMsg), zap.Error(err), ) return &pb.PlaceAssetResponse{ Base: &pbCommon.BaseResponse{ Code: appErrors.ToStatusCode(err), Message: errorMsg, Timestamp: 0, }, }, err } logger.Logger.Info("PlaceAsset successful", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("asset_id", req.AssetId), zap.Int64("slot_id", req.SlotId), ) return &pb.PlaceAssetResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: 0, }, Data: placeData, }, nil } // RemoveFromSlot 从展位移除资产(统一接口,支持展位所有者踢走和占位者下架) func (p *GalleryProvider) RemoveFromSlot(ctx context.Context, req *pb.RemoveFromSlotRequest) (*pb.RemoveFromSlotResponse, error) { logger.Logger.Info("Received RemoveFromSlot request", zap.Int64("slot_id", req.SlotId), ) // 从 Dubbo attachments 获取用户信息(网关已验证并传递) userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { logger.Logger.Error("Failed to extract user info from attachments", zap.Error(err), ) return &pb.RemoveFromSlotResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "用户认证失败", Timestamp: 0, }, }, err } // 调用Service层 err = p.exhibitionService.RemoveFromSlot(userID, starID, req) if err != nil { logger.Logger.Error("RemoveFromSlot failed", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("slot_id", req.SlotId), zap.Error(err), ) return &pb.RemoveFromSlotResponse{ Base: &pbCommon.BaseResponse{ Code: appErrors.ToStatusCode(err), Message: err.Error(), Timestamp: 0, }, }, err } logger.Logger.Info("RemoveFromSlot successful", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int64("slot_id", req.SlotId), ) return &pb.RemoveFromSlotResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "移除成功", Timestamp: 0, }, }, nil } // UnlockSlot 解锁/购买新展位 func (p *GalleryProvider) UnlockSlot(ctx context.Context, req *pb.UnlockSlotRequest) (*pb.UnlockSlotResponse, error) { logger.Logger.Info("Received UnlockSlot request") // 从 Dubbo attachments 获取用户信息(网关已验证并传递) userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { logger.Logger.Error("Failed to extract user info from attachments", zap.Error(err), ) return &pb.UnlockSlotResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } // 调用Service层 unlockData, err := p.slotService.UnlockSlot(userID, starID) if err != nil { logger.Logger.Error("UnlockSlot failed", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Error(err), ) return &pb.UnlockSlotResponse{ Base: &pbCommon.BaseResponse{ Code: appErrors.ToStatusCode(err), Message: err.Error(), Timestamp: 0, }, }, err } logger.Logger.Info("UnlockSlot successful", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Int32("slot_total", unlockData.SlotTotal), ) return &pb.UnlockSlotResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: 0, }, Data: unlockData, }, nil } // GetMyExhibitedAssets 获取我展出的作品列表 func (p *GalleryProvider) GetMyExhibitedAssets(ctx context.Context, req *pb.GetMyExhibitedAssetsRequest) (*pb.GetMyExhibitedAssetsResponse, error) { logger.Logger.Info("Received GetMyExhibitedAssets request") // 从 Dubbo attachments 获取用户信息(网关已验证并传递) userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { logger.Logger.Error("Failed to extract user info from attachments", zap.Error(err), ) return &pb.GetMyExhibitedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } // 调用Service层 return p.exhibitionService.GetMyExhibitedAssets(ctx, userID, starID, req) } // ==================== 辅助函数 ==================== // extractUserInfoFromDubboAttachments 从Dubbo attachments提取用户信息 func extractUserInfoFromDubboAttachments(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("failed to extract user info from Dubbo attachments") } // extractUserInfoFromMap 从map中提取用户信息 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 解析各种类型的值为 int64 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: var result int64 fmt.Sscanf(val, "%d", &result) return result case []string: if len(val) > 0 { var result int64 fmt.Sscanf(val[0], "%d", &result) return result } } return 0 }