package client import ( "context" "errors" "fmt" "strconv" "dubbo.apache.org/dubbo-go/v3/common/constant" "github.com/topfans/backend/pkg/logger" pbAsset "github.com/topfans/backend/pkg/proto/asset" pbCommon "github.com/topfans/backend/pkg/proto/common" "go.uber.org/zap" ) // AssetInfo 资产信息 type AssetInfo struct { AssetId int64 OwnerUid int64 StarId int64 Name string CoverUrl string LikeCount int32 } // AssetRPCClient Asset Service RPC客户端接口 type AssetRPCClient interface { // GetAssetForRPC 验证资产是否存在且获取资产信息(用于RPC调用) GetAssetForRPC(assetID, userID, starID int64) (*AssetInfo, error) // GetAssetInfo 获取资产信息(简化版,用于展示) GetAssetInfo(assetID, userID, starID int64) (*AssetInfo, error) // ClearAssetLikeRecords 清除资产点赞记录(不修改 like_count),在藏品下架时调用 ClearAssetLikeRecords(assetID int64) error } // assetRPCClient Asset Service RPC客户端实现 type assetRPCClient struct { client pbAsset.AssetService } // NewAssetRPCClient 创建Asset Service RPC客户端 func NewAssetRPCClient(client pbAsset.AssetService) AssetRPCClient { return &assetRPCClient{ client: client, } } // GetAssetForRPC 验证资产是否存在且获取资产信息(用于RPC调用) func (c *assetRPCClient) GetAssetForRPC(assetID, userID, starID int64) (*AssetInfo, error) { logger.Logger.Debug("Calling AssetService.GetAssetForRPC", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) ctx := context.Background() resp, err := c.client.GetAssetForRPC(ctx, &pbAsset.GetAssetForRPCRequest{ AssetId: assetID, }) if err != nil { errorMsg := fmt.Sprintf("RPC调用失败: %v", err) logger.Logger.Error("Failed to call AssetService.GetAssetForRPC", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.String("error", errorMsg), zap.Error(err), ) return nil, errors.New(errorMsg) } if resp == nil { errorMsg := "AssetService.GetAssetForRPC 返回空响应" logger.Logger.Error("AssetService.GetAssetForRPC returned nil response", zap.Int64("asset_id", assetID), ) return nil, errors.New(errorMsg) } // StatusCode_STATUS_OK = 200,不是 0 if resp.Base.Code != pbCommon.StatusCode_STATUS_OK { errorMsg := resp.Base.Message if errorMsg == "" { errorMsg = fmt.Sprintf("AssetService返回错误码: %d", resp.Base.Code) } logger.Logger.Warn("AssetService.GetAssetForRPC returned error", zap.Int64("asset_id", assetID), zap.Int32("code", int32(resp.Base.Code)), zap.String("message", errorMsg), ) return nil, errors.New(errorMsg) } // 验证资产是否属于当前用户 if resp.OwnerUid != userID { logger.Logger.Warn("Asset does not belong to user", zap.Int64("asset_id", assetID), zap.Int64("owner_uid", resp.OwnerUid), zap.Int64("expected_user_id", userID), ) return nil, errors.New("资产不属于当前用户") } // 验证资产是否属于同一明星 if resp.StarId != starID { logger.Logger.Warn("Asset does not belong to the same star", zap.Int64("asset_id", assetID), zap.Int64("star_id", resp.StarId), zap.Int64("expected_star_id", starID), ) return nil, errors.New("资产不属于同一明星") } logger.Logger.Debug("AssetService.GetAssetForRPC successful", zap.Int64("asset_id", assetID), zap.Int64("owner_uid", resp.OwnerUid), ) // 注意:GetAssetForRPCResponse 不包含 Name, CoverUrl, LikeCount // 这些信息需要通过 GetAsset 获取 return &AssetInfo{ AssetId: resp.AssetId, OwnerUid: resp.OwnerUid, StarId: resp.StarId, Name: "", // 需要调用 GetAsset 获取 CoverUrl: "", // 需要调用 GetAsset 获取 LikeCount: 0, // 需要调用 GetAsset 获取 }, nil } // GetAssetInfo 获取资产信息(简化版,用于展示) func (c *assetRPCClient) GetAssetInfo(assetID, userID, starID int64) (*AssetInfo, error) { logger.Logger.Debug("Calling AssetService.GetAsset", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) // 创建带有用户信息的context(Triple协议要求attachments值为string类型) ctx := context.WithValue(context.Background(), constant.AttachmentKey, map[string]interface{}{ "user_id": strconv.FormatInt(userID, 10), "star_id": strconv.FormatInt(starID, 10), }) resp, err := c.client.GetAsset(ctx, &pbAsset.GetAssetRequest{ AssetId: assetID, }) if err != nil { logger.Logger.Error("Failed to call AssetService.GetAsset", zap.Int64("asset_id", assetID), zap.Error(err), ) return nil, err } // StatusCode_STATUS_OK = 200,不是 0 if resp.Base.Code != pbCommon.StatusCode_STATUS_OK { errorMsg := resp.Base.Message if errorMsg == "" { errorMsg = fmt.Sprintf("AssetService返回错误码: %d", resp.Base.Code) } logger.Logger.Warn("AssetService.GetAsset returned error", zap.Int64("asset_id", assetID), zap.Int32("code", int32(resp.Base.Code)), zap.String("message", errorMsg), ) return nil, errors.New(errorMsg) } logger.Logger.Debug("AssetService.GetAsset successful", zap.Int64("asset_id", assetID), ) return &AssetInfo{ AssetId: resp.Asset.AssetId, OwnerUid: resp.Asset.OwnerUid, StarId: resp.Asset.StarId, Name: resp.Asset.Name, CoverUrl: resp.Asset.CoverUrl, LikeCount: resp.Asset.LikeCount, }, nil } // ClearAssetLikeRecords 清除资产点赞记录(不修改 like_count) func (c *assetRPCClient) ClearAssetLikeRecords(assetID int64) error { logger.Logger.Debug("Calling AssetService.ClearAssetLikeRecords", zap.Int64("asset_id", assetID), ) resp, err := c.client.ClearAssetLikeRecords(context.Background(), &pbAsset.ClearAssetLikeRecordsRequest{ AssetId: assetID, }) if err != nil { logger.Logger.Error("Failed to call AssetService.ClearAssetLikeRecords", zap.Int64("asset_id", assetID), zap.Error(err), ) return fmt.Errorf("清除点赞记录RPC失败: %w", err) } if resp == nil || resp.Base == nil { return fmt.Errorf("AssetService.ClearAssetLikeRecords 返回空响应") } if resp.Base.Code != pbCommon.StatusCode_STATUS_OK { errMsg := resp.Base.Message if errMsg == "" { errMsg = fmt.Sprintf("AssetService返回错误码: %d", resp.Base.Code) } return errors.New(errMsg) } logger.Logger.Debug("AssetService.ClearAssetLikeRecords successful", zap.Int64("asset_id", assetID), ) return nil }