topfans/backend/services/assetService/provider/asset_provider.go
2026-04-20 19:31:42 +08:00

817 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package provider
import (
"context"
"fmt"
"time"
"dubbo.apache.org/dubbo-go/v3/common/constant"
appErrors "github.com/topfans/backend/pkg/errors"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/pkg/models"
pb "github.com/topfans/backend/pkg/proto/asset"
pbCommon "github.com/topfans/backend/pkg/proto/common"
"github.com/topfans/backend/services/assetService/service"
"go.uber.org/zap"
)
// AssetProvider 资产服务Provider实现
// 实现 Triple 协议生成的 AssetServiceHandler 接口
type AssetProvider struct {
assetService service.AssetService
mintService service.MintService
assetLikeService *service.AssetLikeService
}
// 确保 AssetProvider 实现了 AssetServiceHandler 接口
var _ pb.AssetServiceHandler = (*AssetProvider)(nil)
// NewAssetProvider 创建资产服务Provider实例
func NewAssetProvider(assetService service.AssetService, mintService service.MintService, assetLikeService *service.AssetLikeService) *AssetProvider {
return &AssetProvider{
assetService: assetService,
mintService: mintService,
assetLikeService: assetLikeService,
}
}
// InitMintOrder 阶段一:初始化订单(仅落库 order_id
func (p *AssetProvider) InitMintOrder(ctx context.Context, req *pb.InitMintOrderRequest) (*pb.InitMintOrderResponse, error) {
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
return &pb.InitMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
resp, err := p.mintService.InitMintOrder(req.OrderId, userID, starID)
if err != nil {
if resp == nil {
resp = &pb.InitMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
return resp, nil
}
// PreCreateMintOrder 阶段一:预创建铸造订单(生成 order_id
func (p *AssetProvider) PreCreateMintOrder(ctx context.Context, req *pb.PreCreateMintOrderRequest) (*pb.PreCreateMintOrderResponse, error) {
logger.Logger.Info("Received PreCreateMintOrder request",
zap.String("material_url", req.MaterialUrl),
)
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
return &pb.PreCreateMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
resp, err := p.mintService.PreCreateMintOrder(req, userID, starID)
if err != nil {
logger.Logger.Error("PreCreateMintOrder failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
if resp == nil {
resp = &pb.PreCreateMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("PreCreateMintOrder successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.String("order_id", resp.Order.OrderId),
)
return resp, nil
}
// CreateMintOrder 创建铸造订单
func (p *AssetProvider) CreateMintOrder(ctx context.Context, req *pb.CreateMintOrderRequest) (*pb.CreateMintOrderResponse, error) {
// 记录请求日志
logger.Logger.Info("Received CreateMintOrder request",
zap.String("name", req.Name),
)
// 从 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.CreateMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.mintService.CreateMintOrder(req, userID, starID)
if err != nil {
logger.Logger.Error("CreateMintOrder failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.CreateMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("CreateMintOrder successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.String("order_id", resp.Order.OrderId),
)
return resp, nil
}
// GetMyAssets 获取我的藏品列表
func (p *AssetProvider) GetMyAssets(ctx context.Context, req *pb.GetMyAssetsRequest) (*pb.GetMyAssetsResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetMyAssets request",
zap.Int32("page", req.Page),
zap.Int32("page_size", req.PageSize),
)
// 从 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.GetMyAssetsResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.assetService.GetMyAssets(req, userID, starID)
if err != nil {
logger.Logger.Error("GetMyAssets failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetMyAssetsResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetMyAssets successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Int("count", len(resp.Data.Groups)),
)
return resp, nil
}
// GetAsset 获取资产详情
func (p *AssetProvider) GetAsset(ctx context.Context, req *pb.GetAssetRequest) (*pb.GetAssetResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetAsset request",
zap.Int64("asset_id", req.AssetId),
)
// 从 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.GetAssetResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.assetService.GetAsset(req, userID, starID)
if err != nil {
logger.Logger.Error("GetAsset failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetAssetResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetAsset successful",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
)
return resp, nil
}
// GetAssetStatus 查询上链状态
func (p *AssetProvider) GetAssetStatus(ctx context.Context, req *pb.GetAssetStatusRequest) (*pb.GetAssetStatusResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetAssetStatus request",
zap.Int64("asset_id", req.AssetId),
)
// 从 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.GetAssetStatusResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.assetService.GetAssetStatus(req, userID, starID)
if err != nil {
logger.Logger.Error("GetAssetStatus failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetAssetStatusResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetAssetStatus successful",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
)
return resp, nil
}
// GetAssetForRPC 获取资产信息内部RPC调用
func (p *AssetProvider) GetAssetForRPC(ctx context.Context, req *pb.GetAssetForRPCRequest) (*pb.GetAssetForRPCResponse, error) {
logger.Logger.Info("Received GetAssetForRPC request",
zap.Int64("asset_id", req.AssetId),
)
// 调用Service层
resp, err := p.assetService.GetAssetForRPC(req)
if err != nil {
logger.Logger.Error("GetAssetForRPC failed",
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetAssetForRPCResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("GetAssetForRPC successful",
zap.Int64("asset_id", req.AssetId),
)
return resp, nil
}
// LikeAsset 点赞资产内部RPC调用
func (p *AssetProvider) LikeAsset(ctx context.Context, req *pb.LikeAssetRequest) (*pb.LikeAssetResponse, error) {
logger.Logger.Info("Received LikeAsset request",
zap.Int64("asset_id", req.AssetId),
)
// 从 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.LikeAssetResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
likeCount, err := p.assetLikeService.LikeAsset(ctx, req.AssetId, userID, starID)
if err != nil {
logger.Logger.Error("LikeAsset failed",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
return &pb.LikeAssetResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}, err
}
logger.Logger.Info("LikeAsset successful",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
zap.Int32("like_count", likeCount),
)
return &pb.LikeAssetResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "Asset liked successfully",
Timestamp: 0,
},
LikeCount: likeCount,
}, nil
}
// UnlikeAsset 取消点赞资产内部RPC调用
func (p *AssetProvider) UnlikeAsset(ctx context.Context, req *pb.UnlikeAssetRequest) (*pb.UnlikeAssetResponse, error) {
logger.Logger.Info("Received UnlikeAsset request",
zap.Int64("asset_id", req.AssetId),
)
// 从 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.UnlikeAssetResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
likeCount, err := p.assetLikeService.UnlikeAsset(ctx, req.AssetId, userID, starID)
if err != nil {
logger.Logger.Error("UnlikeAsset failed",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
return &pb.UnlikeAssetResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}, err
}
logger.Logger.Info("UnlikeAsset successful",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
zap.Int32("like_count", likeCount),
)
return &pb.UnlikeAssetResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "Asset unliked successfully",
Timestamp: 0,
},
LikeCount: likeCount,
}, nil
}
// CheckAssetLike 检查是否已点赞内部RPC调用
func (p *AssetProvider) CheckAssetLike(ctx context.Context, req *pb.CheckAssetLikeRequest) (*pb.CheckAssetLikeResponse, error) {
logger.Logger.Info("Received CheckAssetLike request",
zap.Int64("asset_id", req.AssetId),
)
// 从 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.CheckAssetLikeResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
isLiked, err := p.assetLikeService.CheckAssetLike(ctx, req.AssetId, userID, starID)
if err != nil {
logger.Logger.Error("CheckAssetLike failed",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
return &pb.CheckAssetLikeResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}, err
}
logger.Logger.Info("CheckAssetLike successful",
zap.Int64("user_id", userID),
zap.Int64("asset_id", req.AssetId),
zap.Bool("is_liked", isLiked),
)
return &pb.CheckAssetLikeResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "Success",
Timestamp: 0,
},
IsLiked: isLiked,
}, nil
}
// GetAssetLikes 获取资产点赞列表内部RPC调用
func (p *AssetProvider) GetAssetLikes(ctx context.Context, req *pb.GetAssetLikesRequest) (*pb.GetAssetLikesResponse, error) {
logger.Logger.Info("Received GetAssetLikes request",
zap.Int64("asset_id", req.AssetId),
zap.Int32("page", req.Page),
zap.Int32("page_size", req.PageSize),
)
// 调用Service层
likes, total, err := p.assetLikeService.GetAssetLikes(ctx, req.AssetId, req.Page, req.PageSize)
if err != nil {
logger.Logger.Error("GetAssetLikes failed",
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
return &pb.GetAssetLikesResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}, err
}
// 转换为 pb.AssetLike
pbLikes := make([]*pb.AssetLike, 0, len(likes))
for _, like := range likes {
pbLikes = append(pbLikes, &pb.AssetLike{
Id: like.ID,
AssetId: like.AssetID,
UserId: like.UserID,
StarId: like.StarID,
CreatedAt: like.CreatedAt,
})
}
// 计算分页信息
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
hasMore := total > int64(req.Page*pageSize)
logger.Logger.Info("GetAssetLikes successful",
zap.Int64("asset_id", req.AssetId),
zap.Int("count", len(pbLikes)),
zap.Int64("total", total),
)
return &pb.GetAssetLikesResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "Success",
Timestamp: 0,
},
Likes: pbLikes,
Total: total,
Page: req.Page,
PageSize: pageSize,
HasMore: hasMore,
}, nil
}
// extractUserInfoFromDubboAttachments 从 Dubbo attachments 中提取用户信息
func extractUserInfoFromDubboAttachments(ctx context.Context) (int64, int64, error) {
logger.Logger.Info("Extracting user info from Dubbo attachments")
// 使用正确的 constant.AttachmentKey 获取 Dubbo attachments
if attachments := ctx.Value(constant.AttachmentKey); attachments != nil {
logger.Logger.Info("Found attachments in context",
zap.Any("attachments", attachments),
zap.String("key", fmt.Sprintf("%v", constant.AttachmentKey)),
)
if attMap, ok := attachments.(map[string]interface{}); ok {
logger.Logger.Info("Attachments is a map", zap.Any("map", attMap))
userID, starID := extractUserInfoFromMap(attMap)
if userID > 0 && starID > 0 {
logger.Logger.Info("Successfully extracted user info",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
return userID, starID, nil
} else {
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",
zap.String("key", fmt.Sprintf("%v", constant.AttachmentKey)),
)
}
logger.Logger.Error("Failed to extract user info from Dubbo attachments")
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
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
}
case []interface{}:
if len(val) > 0 {
return parseIntValue(val[0])
}
}
return 0
}
// ClearAssetLikeRecords 清除资产点赞记录内部RPC调用供Gallery Service在下架时调用
func (p *AssetProvider) ClearAssetLikeRecords(ctx context.Context, req *pb.ClearAssetLikeRecordsRequest) (*pb.ClearAssetLikeRecordsResponse, error) {
logger.Logger.Info("Received ClearAssetLikeRecords request",
zap.Int64("asset_id", req.AssetId),
)
if err := p.assetLikeService.ClearAssetLikeRecords(ctx, req.AssetId); err != nil {
logger.Logger.Error("ClearAssetLikeRecords failed",
zap.Int64("asset_id", req.AssetId),
zap.Error(err),
)
return &pb.ClearAssetLikeRecordsResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
},
}, err
}
logger.Logger.Info("ClearAssetLikeRecords successful",
zap.Int64("asset_id", req.AssetId),
)
return &pb.ClearAssetLikeRecordsResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "点赞记录已清除",
},
}, nil
}
// GetMintOrder 查询铸造订单状态
func (p *AssetProvider) GetMintOrder(ctx context.Context, req *pb.GetMintOrderRequest) (*pb.GetMintOrderResponse, error) {
logger.Logger.Info("Received GetMintOrder request",
zap.String("order_id", req.OrderId),
)
// 从 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.GetMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.mintService.GetMintOrder(req.OrderId, userID, starID)
if err != nil {
logger.Logger.Error("GetMintOrder failed",
zap.String("order_id", req.OrderId),
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("GetMintOrder successful",
zap.String("order_id", req.OrderId),
zap.Int64("user_id", userID),
zap.String("status", resp.Order.Status),
)
return resp, nil
}
// CancelMintOrder 取消铸造订单
func (p *AssetProvider) CancelMintOrder(ctx context.Context, req *pb.CancelMintOrderRequest) (*pb.CancelMintOrderResponse, error) {
// 记录请求日志
logger.Logger.Info("Received CancelMintOrder request",
zap.String("order_id", req.OrderId),
)
// 从 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.CancelMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "未授权",
Timestamp: time.Now().UnixMilli(),
},
}, err
}
// 调用服务层取消订单
err = p.mintService.CancelMintOrder(req.OrderId, userID, starID)
// 构建响应
resp := &pb.CancelMintOrderResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_OK,
Message: "",
Timestamp: time.Now().UnixMilli(),
},
OrderId: req.OrderId,
Status: models.MintOrderStatusCancelled,
}
if err != nil {
logger.Logger.Error("CancelMintOrder failed",
zap.String("order_id", req.OrderId),
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
resp.Base.Code = appErrors.ToStatusCode(err)
resp.Base.Message = err.Error()
return resp, err
}
logger.Logger.Debug("CancelMintOrder successful",
zap.String("order_id", req.OrderId),
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
return resp, nil
}