topfans/backend/services/galleryService/provider/gallery_provider.go
2026-05-06 10:51:08 +08:00

475 lines
13 KiB
Go

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)
}
// GetInspirationFlow 获取灵感瀑布藏品列表
func (p *GalleryProvider) GetInspirationFlow(ctx context.Context, req *pb.GetInspirationFlowRequest) (*pb.GetInspirationFlowResponse, error) {
logger.Logger.Info("Received GetInspirationFlow request",
zap.String("cursor", req.Cursor),
zap.String("direction", req.Direction),
zap.Int32("limit", req.Limit),
zap.String("type", req.Type),
)
// 从 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.GetInspirationFlowResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
data, _, err := p.galleryService.GetInspirationFlow(userID, starID, req.Cursor, req.Direction, req.Limit, req.Type, req.SessionId)
if err != nil {
logger.Logger.Error("GetInspirationFlow failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.String("direction", req.Direction),
zap.Error(err),
)
return &pb.GetInspirationFlowResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}, err
}
logger.Logger.Info("GetInspirationFlow successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Int("item_count", len(data.Items)),
)
return &pb.GetInspirationFlowResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "success",
Timestamp: 0,
},
Data: data,
}, nil
}
// ==================== 辅助函数 ====================
// 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
}