- 新增 GET /api/v1/me/liked-assets 接口 - 新增 GET /api/v1/me/exhibited-assets 接口 - 新增 GetMyLikedAssets 和 GetMyExhibitedAssets RPC 方法 - 新增 ExhibitedAssetItemDTO 和 GetMyExhibitedAssetsResponseDTO - 前端新增 getUserLikedAssetsApi 和 getUserExhibitedAssetsApi(暂不实现) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
417 lines
12 KiB
Go
417 lines
12 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)
|
|
}
|
|
|
|
// ==================== 辅助函数 ====================
|
|
|
|
// 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
|
|
}
|