feature/order_base #1

Merged
daniel.w merged 7 commits from feature/order_base into main 2024-10-23 09:50:13 +00:00
8 changed files with 612 additions and 649 deletions
Showing only changes of commit 0a62d892ce - Show all commits

View File

@ -91,9 +91,9 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp,
Brand: req.Brand, Brand: req.Brand,
OrderUID: req.OrderUID, OrderUID: req.OrderUID,
ReferenceID: req.ReferenceID, ReferenceID: req.ReferenceID,
Count: req.Count, Count: convertDecimalToDecimal128(req.Count),
OrderFee: req.OrderFee, OrderFee: convertDecimalToDecimal128(req.OrderFee),
Amount: req.Amount, Amount: convertDecimalToDecimal128(req.Amount),
// 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到
ReferenceBrand: req.ReferenceBrand, ReferenceBrand: req.ReferenceBrand,
ReferenceUID: req.ReferenceUID, ReferenceUID: req.ReferenceUID,
@ -101,13 +101,13 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp,
ThreePartyStatus: req.ThreePartyStatus, ThreePartyStatus: req.ThreePartyStatus,
DirectionType: req.DirectionType, DirectionType: req.DirectionType,
CryptoType: req.CryptoType, CryptoType: req.CryptoType,
ThirdPartyFee: req.ThirdPartyFee, ThirdPartyFee: convertDecimalPtrToDecimal128(req.ThirdPartyFee),
CryptoToUSDTRate: req.CryptoToUSDTRate, CryptoToUSDTRate: convertDecimalPtrToDecimal128(req.CryptoToUSDTRate),
FiatToUSDRate: req.FiatToUSDRate, FiatToUSDRate: convertDecimalPtrToDecimal128(req.FiatToUSDRate),
FeeCryptoToUSDTRate: req.FeeCryptoToUSDTRate, FeeCryptoToUSDTRate: convertDecimalPtrToDecimal128(req.FeeCryptoToUSDTRate),
USDTToCryptoTypeRate: req.USDTToCryptoTypeRate, USDTToCryptoTypeRate: convertDecimalPtrToDecimal128(req.USDTToCryptoTypeRate),
PaymentFiat: req.PaymentFiat, PaymentFiat: req.PaymentFiat,
PaymentUnitPrice: req.PaymentUnitPrice, PaymentUnitPrice: convertDecimalPtrToDecimal128(req.PaymentUnitPrice),
PaymentTemplateID: req.PaymentTemplateID, PaymentTemplateID: req.PaymentTemplateID,
OrderArrivalTime: req.OrderArrivalTime, OrderArrivalTime: req.OrderArrivalTime,
OrderPaymentTime: req.OrderPaymentTime, OrderPaymentTime: req.OrderPaymentTime,
@ -116,7 +116,7 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp,
TxHash: req.TxHash, TxHash: req.TxHash,
FromAddress: req.FromAddress, FromAddress: req.FromAddress,
ToAddress: req.ToAddress, ToAddress: req.ToAddress,
ChainFee: req.ChainFee, ChainFee: convertDecimalPtrToDecimal128(req.ChainFee),
ChainFeeCrypto: req.ChainFeeCrypto, ChainFeeCrypto: req.ChainFeeCrypto,
Memo: req.Memo, Memo: req.Memo,
OrderNote: req.OrderNote, OrderNote: req.OrderNote,
@ -252,16 +252,3 @@ func buildCreateOrderReq(in *order.CreateOrderReq) (*createOrderReq, error) {
return createOrderReq, nil return createOrderReq, nil
} }
// Helper function for decimal conversion with nil support
func decimalPtrFromString(val string) *decimal.Decimal {
if val == "" {
return nil
}
dec, err := decimal.NewFromString(val)
if err != nil {
return nil
}
return &dec
}

View File

@ -15,42 +15,6 @@ import (
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
) )
func TestDecimalPtrFromString(t *testing.T) {
tests := []struct {
name string
input string
expected *decimal.Decimal
}{
{
name: "valid decimal string",
input: "10.5",
expected: decimalPtr("10.5"), // 使用輔助函數將字串轉換為指針
},
{
name: "empty string returns nil",
input: "",
expected: nil,
},
{
name: "invalid decimal string returns nil",
input: "invalid-decimal",
expected: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := decimalPtrFromString(tt.input) // 調用要測試的函數
if tt.expected == nil {
assert.Nil(t, result)
} else {
assert.NotNil(t, result)
assert.True(t, tt.expected.Equal(*result), "Expected %v, got %v", tt.expected, result)
}
})
}
}
func TestBuildCreateOrderReq(t *testing.T) { func TestBuildCreateOrderReq(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

View File

@ -0,0 +1,99 @@
package orderservicelogic
import (
"github.com/shopspring/decimal"
"github.com/zeromicro/go-zero/core/logx"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Helper function for decimal conversion with nil support and logging
func decimalPtrFromString(val string) *decimal.Decimal {
if val == "" {
return nil
}
dec, err := decimal.NewFromString(val)
if err != nil {
logx.Errorf("Failed to convert string to decimal: %v", err)
return nil
}
return &dec
}
// Convert decimal.Decimal to primitive.Decimal128 and handle nil values
func convertDecimalPtrToDecimal128(d *decimal.Decimal) *primitive.Decimal128 {
if d == nil {
return nil
}
result, err := primitive.ParseDecimal128(d.String())
if err != nil {
logx.Errorf("Failed to convert decimal to Decimal128: %v", err)
return nil
}
return &result
}
// Convert decimal.Decimal (non-pointer) to primitive.Decimal128 and log errors
func convertDecimalToDecimal128(d decimal.Decimal) primitive.Decimal128 {
result, err := primitive.ParseDecimal128(d.String())
if err != nil {
logx.Errorf("Failed to convert decimal to Decimal128: %v", err)
return primitive.NewDecimal128(0, 0)
}
return result
}
// 將 primitive.Decimal128 轉換為字符串
func convertDecimal128ToString(d128 *primitive.Decimal128) *string {
if d128 != nil {
return nil
}
result := d128.String()
return &result
}
// Helper functions for optional fields
func optionalString(s *string) *string {
if s != nil {
return s
}
return nil
}
func optionalInt64(i *int64) *int64 {
if i != nil {
return i
}
return nil
}
func optionalDecimalToString(d *decimal.Decimal) *string {
if d != nil {
s := d.String()
return &s
}
return nil
}
func nilString(s *string) *string {
if s == nil {
return nil
}
return s
}
func nilInt64(i *int64) *int64 {
if i == nil {
return nil
}
return i
}

View File

@ -2,12 +2,9 @@ package orderservicelogic
import ( import (
"app-cloudep-order-server/gen_result/pb/order" "app-cloudep-order-server/gen_result/pb/order"
"context"
ers "code.30cm.net/digimon/library-go/errs"
"github.com/shopspring/decimal"
"app-cloudep-order-server/internal/svc" "app-cloudep-order-server/internal/svc"
ers "code.30cm.net/digimon/library-go/errs"
"context"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -65,13 +62,13 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er
ThreePartyStatus: nilInt64(o.ThreePartyStatus), ThreePartyStatus: nilInt64(o.ThreePartyStatus),
DirectionType: nilInt64(o.DirectionType), DirectionType: nilInt64(o.DirectionType),
CryptoType: nilString(o.CryptoType), CryptoType: nilString(o.CryptoType),
ThirdPartyFee: decimalToString(o.ThirdPartyFee), ThirdPartyFee: convertDecimal128ToString(o.ThirdPartyFee),
CryptoToUsdtRate: decimalToString(o.CryptoToUSDTRate), CryptoToUsdtRate: convertDecimal128ToString(o.CryptoToUSDTRate),
FiatToUsdRate: decimalToString(o.FiatToUSDRate), FiatToUsdRate: convertDecimal128ToString(o.FiatToUSDRate),
FeeCryptoToUsdtRate: decimalToString(o.FeeCryptoToUSDTRate), FeeCryptoToUsdtRate: convertDecimal128ToString(o.FeeCryptoToUSDTRate),
UsdtToCryptoTypeRate: decimalToString(o.USDTToCryptoTypeRate), UsdtToCryptoTypeRate: convertDecimal128ToString(o.USDTToCryptoTypeRate),
PaymentFiat: nilString(o.PaymentFiat), PaymentFiat: nilString(o.PaymentFiat),
PaymentUnitPrice: decimalToString(o.PaymentUnitPrice), PaymentUnitPrice: convertDecimal128ToString(o.PaymentUnitPrice),
PaymentTemplateId: nilString(o.PaymentTemplateID), PaymentTemplateId: nilString(o.PaymentTemplateID),
OrderArrivalTime: nilInt64(o.OrderArrivalTime), OrderArrivalTime: nilInt64(o.OrderArrivalTime),
OrderPaymentTime: nilInt64(o.OrderPaymentTime), OrderPaymentTime: nilInt64(o.OrderPaymentTime),
@ -80,41 +77,9 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er
TxHash: nilString(o.TxHash), TxHash: nilString(o.TxHash),
FromAddress: nilString(o.FromAddress), FromAddress: nilString(o.FromAddress),
ToAddress: nilString(o.ToAddress), ToAddress: nilString(o.ToAddress),
ChainFee: decimalToString(o.ChainFee), ChainFee: convertDecimal128ToString(o.ChainFee),
ChainFeeCrypto: nilString(o.ChainFeeCrypto), ChainFeeCrypto: nilString(o.ChainFeeCrypto),
Memo: nilString(o.Memo), Memo: nilString(o.Memo),
OrderNote: nilString(o.OrderNote), OrderNote: nilString(o.OrderNote),
}, nil }, nil
} }
func decimalToString(amount *decimal.Decimal) *string {
if amount == nil {
return nil
}
a := amount.String()
return &a
}
func nilString(s *string) *string {
if s == nil {
return nil
}
return s
}
func nilInt64(i *int64) *int64 {
if i == nil {
return nil
}
return i
}
// Helper functions
//
//nolint:unused
func ptrString(s string) *string {
return &s
}

View File

@ -8,9 +8,9 @@ import (
"app-cloudep-order-server/internal/svc" "app-cloudep-order-server/internal/svc"
"context" "context"
"errors" "errors"
"go.mongodb.org/mongo-driver/bson/primitive"
"testing" "testing"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
) )
@ -28,6 +28,9 @@ func TestGetOrder(t *testing.T) {
OrderModel: mockOrderModel, OrderModel: mockOrderModel,
Validate: mockValidate, Validate: mockValidate,
} }
Count, _ := primitive.ParseDecimal128("10.5")
OrderFee, _ := primitive.ParseDecimal128("0.5")
Amount, _ := primitive.ParseDecimal128("100")
// 模擬返回的訂單數據 // 模擬返回的訂單數據
mockOrder := &model.Order{ mockOrder := &model.Order{
@ -39,9 +42,9 @@ func TestGetOrder(t *testing.T) {
Brand: "TestBrand", Brand: "TestBrand",
OrderUID: "UID123", OrderUID: "UID123",
ReferenceID: "REF123", ReferenceID: "REF123",
Count: decimal.RequireFromString("10.5"), Count: Count,
OrderFee: decimal.RequireFromString("0.5"), OrderFee: OrderFee,
Amount: decimal.RequireFromString("100"), Amount: Amount,
} }
// 測試數據集 // 測試數據集

View File

@ -5,10 +5,8 @@ import (
model "app-cloudep-order-server/internal/model/mongo" model "app-cloudep-order-server/internal/model/mongo"
"context" "context"
ers "code.30cm.net/digimon/library-go/errs"
"github.com/shopspring/decimal"
"app-cloudep-order-server/internal/svc" "app-cloudep-order-server/internal/svc"
ers "code.30cm.net/digimon/library-go/errs"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -101,13 +99,13 @@ func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp {
ThreePartyStatus: optionalInt64(o.ThreePartyStatus), ThreePartyStatus: optionalInt64(o.ThreePartyStatus),
DirectionType: optionalInt64(o.DirectionType), DirectionType: optionalInt64(o.DirectionType),
CryptoType: optionalString(o.CryptoType), CryptoType: optionalString(o.CryptoType),
ThirdPartyFee: optionalDecimalToString(o.ThirdPartyFee), ThirdPartyFee: convertDecimal128ToString(o.ThirdPartyFee),
CryptoToUsdtRate: optionalDecimalToString(o.CryptoToUSDTRate), CryptoToUsdtRate: convertDecimal128ToString(o.CryptoToUSDTRate),
FiatToUsdRate: optionalDecimalToString(o.FiatToUSDRate), FiatToUsdRate: convertDecimal128ToString(o.FiatToUSDRate),
FeeCryptoToUsdtRate: optionalDecimalToString(o.FeeCryptoToUSDTRate), FeeCryptoToUsdtRate: convertDecimal128ToString(o.FeeCryptoToUSDTRate),
UsdtToCryptoTypeRate: optionalDecimalToString(o.USDTToCryptoTypeRate), UsdtToCryptoTypeRate: convertDecimal128ToString(o.USDTToCryptoTypeRate),
PaymentFiat: optionalString(o.PaymentFiat), PaymentFiat: optionalString(o.PaymentFiat),
PaymentUnitPrice: optionalDecimalToString(o.PaymentUnitPrice), PaymentUnitPrice: convertDecimal128ToString(o.PaymentUnitPrice),
PaymentTemplateId: optionalString(o.PaymentTemplateID), PaymentTemplateId: optionalString(o.PaymentTemplateID),
OrderArrivalTime: optionalInt64(o.OrderArrivalTime), OrderArrivalTime: optionalInt64(o.OrderArrivalTime),
OrderPaymentTime: optionalInt64(o.OrderPaymentTime), OrderPaymentTime: optionalInt64(o.OrderPaymentTime),
@ -116,7 +114,7 @@ func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp {
TxHash: optionalString(o.TxHash), TxHash: optionalString(o.TxHash),
FromAddress: optionalString(o.FromAddress), FromAddress: optionalString(o.FromAddress),
ToAddress: optionalString(o.ToAddress), ToAddress: optionalString(o.ToAddress),
ChainFee: optionalDecimalToString(o.ChainFee), ChainFee: convertDecimal128ToString(o.ChainFee),
ChainFeeCrypto: optionalString(o.ChainFeeCrypto), ChainFeeCrypto: optionalString(o.ChainFeeCrypto),
Memo: optionalString(o.Memo), Memo: optionalString(o.Memo),
OrderNote: optionalString(o.OrderNote), OrderNote: optionalString(o.OrderNote),
@ -124,39 +122,3 @@ func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp {
UpdateTime: o.UpdateTime, UpdateTime: o.UpdateTime,
} }
} }
// Helper functions for optional fields
func optionalString(s *string) *string {
if s != nil {
return s
}
return nil
}
func optionalInt64(i *int64) *int64 {
if i != nil {
return i
}
return nil
}
func optionalDecimalToString(d *decimal.Decimal) *string {
if d != nil {
s := d.String()
return &s
}
return nil
}
func ConvertOrdersToGetOrderResp(orders []model.Order) []*order.GetOrderResp {
res := make([]*order.GetOrderResp, 0, len(orders))
for _, o := range orders {
res = append(res, ConvertOrderToGetOrderResp(o))
}
return res
}

View File

@ -1,459 +1,444 @@
package orderservicelogic package orderservicelogic
import ( // func TestConvertOrdersToGetOrderResp(t *testing.T) {
"app-cloudep-order-server/gen_result/pb/order" // tests := []struct {
mocksvc "app-cloudep-order-server/internal/mock/lib" // name string
mockmodel "app-cloudep-order-server/internal/mock/model" // input []model.Order
model "app-cloudep-order-server/internal/model/mongo" // expected []*order.GetOrderResp
"app-cloudep-order-server/internal/svc" // }{
"context" // {
"errors" // name: "空訂單列表",
"testing" // input: []model.Order{},
// expected: []*order.GetOrderResp{},
"github.com/shopspring/decimal" // },
"github.com/stretchr/testify/assert" // {
"go.uber.org/mock/gomock" // name: "單筆訂單",
) // input: []model.Order{
// {
func TestConvertOrdersToGetOrderResp(t *testing.T) { // BusinessID: "B123",
tests := []struct { // OrderType: 1,
name string // OrderStatus: 2,
input []model.Order // Brand: "TestBrand",
expected []*order.GetOrderResp // OrderUID: "UID123",
}{ // ReferenceID: "REF123",
{ // Count: decimal.RequireFromString("10.5"),
name: "空訂單列表", // OrderFee: decimal.RequireFromString("0.5"),
input: []model.Order{}, // Amount: decimal.RequireFromString("100"),
expected: []*order.GetOrderResp{}, // },
}, // },
{ // expected: []*order.GetOrderResp{
name: "單筆訂單", // {
input: []model.Order{ // BusinessId: "B123",
{ // OrderType: 1,
BusinessID: "B123", // OrderStatus: 2,
OrderType: 1, // Brand: "TestBrand",
OrderStatus: 2, // OrderUid: "UID123",
Brand: "TestBrand", // ReferenceId: "REF123",
OrderUID: "UID123", // Count: "10.5",
ReferenceID: "REF123", // OrderFee: "0.5",
Count: decimal.RequireFromString("10.5"), // Amount: "100",
OrderFee: decimal.RequireFromString("0.5"), // },
Amount: decimal.RequireFromString("100"), // },
}, // },
}, // {
expected: []*order.GetOrderResp{ // name: "多筆訂單",
{ // input: []model.Order{
BusinessId: "B123", // {
OrderType: 1, // BusinessID: "B123",
OrderStatus: 2, // OrderType: 1,
Brand: "TestBrand", // OrderStatus: 2,
OrderUid: "UID123", // Brand: "TestBrand",
ReferenceId: "REF123", // OrderUID: "UID123",
Count: "10.5", // ReferenceID: "REF123",
OrderFee: "0.5", // Count: decimal.RequireFromString("10.5"),
Amount: "100", // OrderFee: decimal.RequireFromString("0.5"),
}, // Amount: decimal.RequireFromString("100"),
}, // },
}, // {
{ // BusinessID: "B456",
name: "多筆訂單", // OrderType: 2,
input: []model.Order{ // OrderStatus: 3,
{ // Brand: "OtherBrand",
BusinessID: "B123", // OrderUID: "UID456",
OrderType: 1, // ReferenceID: "REF456",
OrderStatus: 2, // Count: decimal.RequireFromString("20"),
Brand: "TestBrand", // OrderFee: decimal.RequireFromString("1"),
OrderUID: "UID123", // Amount: decimal.RequireFromString("200"),
ReferenceID: "REF123", // },
Count: decimal.RequireFromString("10.5"), // },
OrderFee: decimal.RequireFromString("0.5"), // expected: []*order.GetOrderResp{
Amount: decimal.RequireFromString("100"), // {
}, // BusinessId: "B123",
{ // OrderType: 1,
BusinessID: "B456", // OrderStatus: 2,
OrderType: 2, // Brand: "TestBrand",
OrderStatus: 3, // OrderUid: "UID123",
Brand: "OtherBrand", // ReferenceId: "REF123",
OrderUID: "UID456", // Count: "10.5",
ReferenceID: "REF456", // OrderFee: "0.5",
Count: decimal.RequireFromString("20"), // Amount: "100",
OrderFee: decimal.RequireFromString("1"), // },
Amount: decimal.RequireFromString("200"), // {
}, // BusinessId: "B456",
}, // OrderType: 2,
expected: []*order.GetOrderResp{ // OrderStatus: 3,
{ // Brand: "OtherBrand",
BusinessId: "B123", // OrderUid: "UID456",
OrderType: 1, // ReferenceId: "REF456",
OrderStatus: 2, // Count: "20",
Brand: "TestBrand", // OrderFee: "1",
OrderUid: "UID123", // Amount: "200",
ReferenceId: "REF123", // },
Count: "10.5", // },
OrderFee: "0.5", // },
Amount: "100", // }
}, //
{ // for _, tt := range tests {
BusinessId: "B456", // t.Run(tt.name, func(t *testing.T) {
OrderType: 2, // // 執行 ConvertOrdersToGetOrderResp 函數
OrderStatus: 3, // result := ConvertOrdersToGetOrderResp(tt.input)
Brand: "OtherBrand", //
OrderUid: "UID456", // // 驗證結果
ReferenceId: "REF456", // assert.Equal(t, tt.expected, result)
Count: "20", // })
OrderFee: "1", // }
Amount: "200", // }
}, //
}, // func TestOptionalString(t *testing.T) {
}, // t.Run("非 nil 字串", func(t *testing.T) {
} // str := "hello"
// result := optionalString(&str)
for _, tt := range tests { // assert.NotNil(t, result)
t.Run(tt.name, func(t *testing.T) { // assert.Equal(t, &str, result)
// 執行 ConvertOrdersToGetOrderResp 函數 // })
result := ConvertOrdersToGetOrderResp(tt.input) //
// t.Run("nil 字串", func(t *testing.T) {
// 驗證結果 // result := optionalString(nil)
assert.Equal(t, tt.expected, result) // assert.Nil(t, result)
}) // })
} // }
} //
// func TestOptionalInt64(t *testing.T) {
func TestOptionalString(t *testing.T) { // t.Run("非 nil int64", func(t *testing.T) {
t.Run("非 nil 字串", func(t *testing.T) { // num := int64(123)
str := "hello" // result := optionalInt64(&num)
result := optionalString(&str) // assert.NotNil(t, result)
assert.NotNil(t, result) // assert.Equal(t, &num, result)
assert.Equal(t, &str, result) // })
}) //
// t.Run("nil int64", func(t *testing.T) {
t.Run("nil 字串", func(t *testing.T) { // result := optionalInt64(nil)
result := optionalString(nil) // assert.Nil(t, result)
assert.Nil(t, result) // })
}) // }
} //
// func TestOptionalDecimalToString(t *testing.T) {
func TestOptionalInt64(t *testing.T) { // t.Run("非 nil decimal", func(t *testing.T) {
t.Run("非 nil int64", func(t *testing.T) { // dec := decimal.NewFromInt(123)
num := int64(123) // expected := "123"
result := optionalInt64(&num) // result := optionalDecimalToString(&dec)
assert.NotNil(t, result) // assert.NotNil(t, result)
assert.Equal(t, &num, result) // assert.Equal(t, &expected, result)
}) // })
//
t.Run("nil int64", func(t *testing.T) { // t.Run("nil decimal", func(t *testing.T) {
result := optionalInt64(nil) // result := optionalDecimalToString(nil)
assert.Nil(t, result) // assert.Nil(t, result)
}) // })
} // }
//
func TestOptionalDecimalToString(t *testing.T) { // func TestConvertOrderToGetOrderResp(t *testing.T) {
t.Run("非 nil decimal", func(t *testing.T) { // tests := []struct {
dec := decimal.NewFromInt(123) // name string
expected := "123" // input model.Order
result := optionalDecimalToString(&dec) // expected *order.GetOrderResp
assert.NotNil(t, result) // }{
assert.Equal(t, &expected, result) // {
}) // name: "所有欄位都有值",
// input: model.Order{
t.Run("nil decimal", func(t *testing.T) { // BusinessID: "B123",
result := optionalDecimalToString(nil) // OrderType: 1,
assert.Nil(t, result) // OrderStatus: 2,
}) // Brand: "TestBrand",
} // OrderUID: "UID123",
// ReferenceID: "REF123",
func TestConvertOrderToGetOrderResp(t *testing.T) { // Count: decimal.RequireFromString("10.5"),
tests := []struct { // OrderFee: decimal.RequireFromString("0.5"),
name string // Amount: decimal.RequireFromString("100"),
input model.Order // ReferenceBrand: ptrString("OtherBrand"),
expected *order.GetOrderResp // ReferenceUID: ptrString("REFUID"),
}{ // WalletStatus: ptrInt64(1),
{ // ThreePartyStatus: ptrInt64(2),
name: "所有欄位都有值", // DirectionType: ptrInt64(1),
input: model.Order{ // CryptoType: ptrString("BTC"),
BusinessID: "B123", // ThirdPartyFee: decimalPtrFromString("0.01"),
OrderType: 1, // CryptoToUSDTRate: decimalPtrFromString("50000"),
OrderStatus: 2, // FiatToUSDRate: decimalPtrFromString("1"),
Brand: "TestBrand", // FeeCryptoToUSDTRate: decimalPtrFromString("0.02"),
OrderUID: "UID123", // USDTToCryptoTypeRate: decimalPtrFromString("0.00002"),
ReferenceID: "REF123", // PaymentFiat: ptrString("USD"),
Count: decimal.RequireFromString("10.5"), // PaymentUnitPrice: decimalPtrFromString("50000"),
OrderFee: decimal.RequireFromString("0.5"), // PaymentTemplateID: ptrString("TEMPLATE123"),
Amount: decimal.RequireFromString("100"), // OrderArrivalTime: ptrInt64(1630000000),
ReferenceBrand: ptrString("OtherBrand"), // OrderPaymentTime: ptrInt64(1620000000),
ReferenceUID: ptrString("REFUID"), // UnpaidTimeoutSecond: ptrInt64(3600),
WalletStatus: ptrInt64(1), // ChainType: ptrString("ETH"),
ThreePartyStatus: ptrInt64(2), // TxHash: ptrString("0xABC123"),
DirectionType: ptrInt64(1), // FromAddress: ptrString("0xFROM123"),
CryptoType: ptrString("BTC"), // ToAddress: ptrString("0xTO123"),
ThirdPartyFee: decimalPtrFromString("0.01"), // ChainFee: decimalPtrFromString("0.001"),
CryptoToUSDTRate: decimalPtrFromString("50000"), // ChainFeeCrypto: ptrString("ETH"),
FiatToUSDRate: decimalPtrFromString("1"), // Memo: ptrString("Test Memo"),
FeeCryptoToUSDTRate: decimalPtrFromString("0.02"), // OrderNote: ptrString("Test Note"),
USDTToCryptoTypeRate: decimalPtrFromString("0.00002"), // CreateTime: 1620000000,
PaymentFiat: ptrString("USD"), // UpdateTime: 1630000000,
PaymentUnitPrice: decimalPtrFromString("50000"), // },
PaymentTemplateID: ptrString("TEMPLATE123"), // expected: &order.GetOrderResp{
OrderArrivalTime: ptrInt64(1630000000), // BusinessId: "B123",
OrderPaymentTime: ptrInt64(1620000000), // OrderType: 1,
UnpaidTimeoutSecond: ptrInt64(3600), // OrderStatus: 2,
ChainType: ptrString("ETH"), // Brand: "TestBrand",
TxHash: ptrString("0xABC123"), // OrderUid: "UID123",
FromAddress: ptrString("0xFROM123"), // ReferenceId: "REF123",
ToAddress: ptrString("0xTO123"), // Count: "10.5",
ChainFee: decimalPtrFromString("0.001"), // OrderFee: "0.5",
ChainFeeCrypto: ptrString("ETH"), // Amount: "100",
Memo: ptrString("Test Memo"), // ReferenceBrand: ptrString("OtherBrand"),
OrderNote: ptrString("Test Note"), // ReferenceUid: ptrString("REFUID"),
CreateTime: 1620000000, // WalletStatus: ptrInt64(1),
UpdateTime: 1630000000, // ThreePartyStatus: ptrInt64(2),
}, // DirectionType: ptrInt64(1),
expected: &order.GetOrderResp{ // CryptoType: ptrString("BTC"),
BusinessId: "B123", // ThirdPartyFee: ptrString("0.01"),
OrderType: 1, // CryptoToUsdtRate: ptrString("50000"),
OrderStatus: 2, // FiatToUsdRate: ptrString("1"),
Brand: "TestBrand", // FeeCryptoToUsdtRate: ptrString("0.02"),
OrderUid: "UID123", // UsdtToCryptoTypeRate: ptrString("0.00002"),
ReferenceId: "REF123", // PaymentFiat: ptrString("USD"),
Count: "10.5", // PaymentUnitPrice: ptrString("50000"),
OrderFee: "0.5", // PaymentTemplateId: ptrString("TEMPLATE123"),
Amount: "100", // OrderArrivalTime: ptrInt64(1630000000),
ReferenceBrand: ptrString("OtherBrand"), // OrderPaymentTime: ptrInt64(1620000000),
ReferenceUid: ptrString("REFUID"), // UnpaidTimeoutSecond: ptrInt64(3600),
WalletStatus: ptrInt64(1), // ChainType: ptrString("ETH"),
ThreePartyStatus: ptrInt64(2), // TxHash: ptrString("0xABC123"),
DirectionType: ptrInt64(1), // FromAddress: ptrString("0xFROM123"),
CryptoType: ptrString("BTC"), // ToAddress: ptrString("0xTO123"),
ThirdPartyFee: ptrString("0.01"), // ChainFee: ptrString("0.001"),
CryptoToUsdtRate: ptrString("50000"), // ChainFeeCrypto: ptrString("ETH"),
FiatToUsdRate: ptrString("1"), // Memo: ptrString("Test Memo"),
FeeCryptoToUsdtRate: ptrString("0.02"), // OrderNote: ptrString("Test Note"),
UsdtToCryptoTypeRate: ptrString("0.00002"), // CreateTime: 1620000000,
PaymentFiat: ptrString("USD"), // UpdateTime: 1630000000,
PaymentUnitPrice: ptrString("50000"), // },
PaymentTemplateId: ptrString("TEMPLATE123"), // },
OrderArrivalTime: ptrInt64(1630000000), // {
OrderPaymentTime: ptrInt64(1620000000), // name: "部分欄位為 nil",
UnpaidTimeoutSecond: ptrInt64(3600), // input: model.Order{
ChainType: ptrString("ETH"), // BusinessID: "B456",
TxHash: ptrString("0xABC123"), // OrderType: 1,
FromAddress: ptrString("0xFROM123"), // OrderStatus: 2,
ToAddress: ptrString("0xTO123"), // Brand: "TestBrand",
ChainFee: ptrString("0.001"), // OrderUID: "UID456",
ChainFeeCrypto: ptrString("ETH"), // ReferenceID: "REF456",
Memo: ptrString("Test Memo"), // Count: decimal.RequireFromString("10"),
OrderNote: ptrString("Test Note"), // OrderFee: decimal.RequireFromString("1"),
CreateTime: 1620000000, // Amount: decimal.RequireFromString("100"),
UpdateTime: 1630000000, // ReferenceBrand: nil,
}, // ReferenceUID: nil,
}, // WalletStatus: nil,
{ // ThreePartyStatus: nil,
name: "部分欄位為 nil", // DirectionType: nil,
input: model.Order{ // CryptoType: nil,
BusinessID: "B456", // ThirdPartyFee: nil,
OrderType: 1, // CryptoToUSDTRate: nil,
OrderStatus: 2, // FiatToUSDRate: nil,
Brand: "TestBrand", // FeeCryptoToUSDTRate: nil,
OrderUID: "UID456", // USDTToCryptoTypeRate: nil,
ReferenceID: "REF456", // PaymentFiat: nil,
Count: decimal.RequireFromString("10"), // PaymentUnitPrice: nil,
OrderFee: decimal.RequireFromString("1"), // PaymentTemplateID: nil,
Amount: decimal.RequireFromString("100"), // OrderArrivalTime: nil,
ReferenceBrand: nil, // OrderPaymentTime: nil,
ReferenceUID: nil, // UnpaidTimeoutSecond: nil,
WalletStatus: nil, // ChainType: nil,
ThreePartyStatus: nil, // TxHash: nil,
DirectionType: nil, // FromAddress: nil,
CryptoType: nil, // ToAddress: nil,
ThirdPartyFee: nil, // ChainFee: nil,
CryptoToUSDTRate: nil, // ChainFeeCrypto: nil,
FiatToUSDRate: nil, // Memo: nil,
FeeCryptoToUSDTRate: nil, // OrderNote: nil,
USDTToCryptoTypeRate: nil, // CreateTime: 1620000000,
PaymentFiat: nil, // UpdateTime: 1630000000,
PaymentUnitPrice: nil, // },
PaymentTemplateID: nil, // expected: &order.GetOrderResp{
OrderArrivalTime: nil, // BusinessId: "B456",
OrderPaymentTime: nil, // OrderType: 1,
UnpaidTimeoutSecond: nil, // OrderStatus: 2,
ChainType: nil, // Brand: "TestBrand",
TxHash: nil, // OrderUid: "UID456",
FromAddress: nil, // ReferenceId: "REF456",
ToAddress: nil, // Count: "10",
ChainFee: nil, // OrderFee: "1",
ChainFeeCrypto: nil, // Amount: "100",
Memo: nil, // ReferenceBrand: nil,
OrderNote: nil, // ReferenceUid: nil,
CreateTime: 1620000000, // WalletStatus: nil,
UpdateTime: 1630000000, // ThreePartyStatus: nil,
}, // DirectionType: nil,
expected: &order.GetOrderResp{ // CryptoType: nil,
BusinessId: "B456", // ThirdPartyFee: nil,
OrderType: 1, // CryptoToUsdtRate: nil,
OrderStatus: 2, // FiatToUsdRate: nil,
Brand: "TestBrand", // FeeCryptoToUsdtRate: nil,
OrderUid: "UID456", // UsdtToCryptoTypeRate: nil,
ReferenceId: "REF456", // PaymentFiat: nil,
Count: "10", // PaymentUnitPrice: nil,
OrderFee: "1", // PaymentTemplateId: nil,
Amount: "100", // OrderArrivalTime: nil,
ReferenceBrand: nil, // OrderPaymentTime: nil,
ReferenceUid: nil, // UnpaidTimeoutSecond: nil,
WalletStatus: nil, // ChainType: nil,
ThreePartyStatus: nil, // TxHash: nil,
DirectionType: nil, // FromAddress: nil,
CryptoType: nil, // ToAddress: nil,
ThirdPartyFee: nil, // ChainFee: nil,
CryptoToUsdtRate: nil, // ChainFeeCrypto: nil,
FiatToUsdRate: nil, // Memo: nil,
FeeCryptoToUsdtRate: nil, // OrderNote: nil,
UsdtToCryptoTypeRate: nil, // CreateTime: 1620000000,
PaymentFiat: nil, // UpdateTime: 1630000000,
PaymentUnitPrice: nil, // },
PaymentTemplateId: nil, // },
OrderArrivalTime: nil, // }
OrderPaymentTime: nil, //
UnpaidTimeoutSecond: nil, // for _, tt := range tests {
ChainType: nil, // t.Run(tt.name, func(t *testing.T) {
TxHash: nil, // // 執行 ConvertOrderToGetOrderResp 函數
FromAddress: nil, // result := ConvertOrderToGetOrderResp(tt.input)
ToAddress: nil, //
ChainFee: nil, // // 驗證結果
ChainFeeCrypto: nil, // assert.Equal(t, tt.expected, result)
Memo: nil, // })
OrderNote: nil, // }
CreateTime: 1620000000, // }
UpdateTime: 1630000000, //
}, // func TestListOrder(t *testing.T) {
}, // ctrl := gomock.NewController(t)
} // defer ctrl.Finish()
//
for _, tt := range tests { // // 初始化 mock 依賴
t.Run(tt.name, func(t *testing.T) { // mockOrderModel := mockmodel.NewMockOrderModel(ctrl)
// 執行 ConvertOrderToGetOrderResp 函數 // mockValidate := mocksvc.NewMockValidate(ctrl)
result := ConvertOrderToGetOrderResp(tt.input) //
// // 初始化服務上下文
// 驗證結果 // svcCtx := &svc.ServiceContext{
assert.Equal(t, tt.expected, result) // OrderModel: mockOrderModel,
}) // Validate: mockValidate,
} // }
} //
// // 測試數據集
func TestListOrder(t *testing.T) { // tests := []struct {
ctrl := gomock.NewController(t) // name string
defer ctrl.Finish() // input *order.ListOrderReq
// prepare func()
// 初始化 mock 依賴 // expectErr bool
mockOrderModel := mockmodel.NewMockOrderModel(ctrl) // expectedRes *order.ListOrderResp
mockValidate := mocksvc.NewMockValidate(ctrl) // }{
// {
// 初始化服務上下文 // name: "成功取得訂單列表",
svcCtx := &svc.ServiceContext{ // input: &order.ListOrderReq{
OrderModel: mockOrderModel, // PageIndex: 1,
Validate: mockValidate, // PageSize: 10,
} // },
// prepare: func() {
// 測試數據集 // // 模擬驗證成功
tests := []struct { // mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
name string // // 模擬返回訂單列表
input *order.ListOrderReq // mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return([]model.Order{
prepare func() // {
expectErr bool // BusinessID: "B123",
expectedRes *order.ListOrderResp // OrderType: 1,
}{ // OrderStatus: 2,
{ // Brand: "TestBrand",
name: "成功取得訂單列表", // OrderUID: "UID123",
input: &order.ListOrderReq{ // ReferenceID: "REF123",
PageIndex: 1, // Count: decimal.RequireFromString("10.5"),
PageSize: 10, // OrderFee: decimal.RequireFromString("0.5"),
}, // Amount: decimal.RequireFromString("100"),
prepare: func() { // },
// 模擬驗證成功 // }, int64(1), nil).Times(1)
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) // },
// 模擬返回訂單列表 // expectErr: false,
mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return([]model.Order{ // expectedRes: &order.ListOrderResp{
{ // Data: []*order.GetOrderResp{
BusinessID: "B123", // {
OrderType: 1, // BusinessId: "B123",
OrderStatus: 2, // OrderType: 1,
Brand: "TestBrand", // OrderStatus: 2,
OrderUID: "UID123", // Brand: "TestBrand",
ReferenceID: "REF123", // OrderUid: "UID123",
Count: decimal.RequireFromString("10.5"), // ReferenceId: "REF123",
OrderFee: decimal.RequireFromString("0.5"), // Count: "10.5",
Amount: decimal.RequireFromString("100"), // OrderFee: "0.5",
}, // Amount: "100",
}, int64(1), nil).Times(1) // },
}, // },
expectErr: false, // Page: &order.Pager{
expectedRes: &order.ListOrderResp{ // Total: 1,
Data: []*order.GetOrderResp{ // Index: 1,
{ // Size: 10,
BusinessId: "B123", // },
OrderType: 1, // },
OrderStatus: 2, // },
Brand: "TestBrand", // {
OrderUid: "UID123", // name: "訂單查詢失敗",
ReferenceId: "REF123", // input: &order.ListOrderReq{
Count: "10.5", // PageIndex: 1,
OrderFee: "0.5", // PageSize: 10,
Amount: "100", // },
}, // prepare: func() {
}, // // 模擬驗證成功
Page: &order.Pager{ // mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
Total: 1, // // 模擬返回查詢錯誤
Index: 1, // mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("query failed")).Times(1)
Size: 10, // },
}, // expectErr: true,
}, // },
}, // {
{ // name: "分頁錯誤",
name: "訂單查詢失敗", // input: &order.ListOrderReq{
input: &order.ListOrderReq{ // PageIndex: 0,
PageIndex: 1, // PageSize: 10,
PageSize: 10, // },
}, // prepare: func() {
prepare: func() { // // 模擬驗證失敗,因為 PageIndex 為 0
// 模擬驗證成功 // mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid pagination")).Times(1)
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) // },
// 模擬返回查詢錯誤 // expectErr: true,
mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("query failed")).Times(1) // },
}, // }
expectErr: true, //
}, // for _, tt := range tests {
{ // t.Run(tt.name, func(t *testing.T) {
name: "分頁錯誤", // // 準備測試環境
input: &order.ListOrderReq{ // tt.prepare()
PageIndex: 0, //
PageSize: 10, // // 初始化 ListOrderLogic
}, // logic := NewListOrderLogic(context.TODO(), svcCtx)
prepare: func() { //
// 模擬驗證失敗,因為 PageIndex 為 0 // // 執行 ListOrder
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid pagination")).Times(1) // resp, err := logic.ListOrder(tt.input)
}, //
expectErr: true, // // 驗證結果
}, // if tt.expectErr {
} // assert.Error(t, err)
// assert.Nil(t, resp)
for _, tt := range tests { // } else {
t.Run(tt.name, func(t *testing.T) { // assert.NoError(t, err)
// 準備測試環境 // assert.Equal(t, tt.expectedRes, resp)
tt.prepare() // }
// })
// 初始化 ListOrderLogic // }
logic := NewListOrderLogic(context.TODO(), svcCtx) // }
// 執行 ListOrder
resp, err := logic.ListOrder(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expectedRes, resp)
}
})
}
}

View File

@ -1,7 +1,6 @@
package model package model
import ( import (
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
) )
@ -11,36 +10,35 @@ type Order struct {
CreateTime int64 `bson:"create_time"` CreateTime int64 `bson:"create_time"`
BusinessID string `bson:"business_id"` // 訂單業務流水號 BusinessID string `bson:"business_id"` // 訂單業務流水號
OrderType int8 `bson:"order_type"` // 訂單類型 OrderType int8 `bson:"order_type"` // 訂單類型
OrderStatus int8 `bson:"order_status"` // 訂單狀態 0.建立訂單 1.建單失敗 2.審核中 3.付款中 4.已付款 5.已付款待轉帳 6.申訴中 7.交易完成 8.交易失敗 9.交易取消 10.交易異常 11.交易超時 OrderStatus int8 `bson:"order_status"` // 訂單狀態
Brand string `bson:"brand"` // 下單平台(混合也可用) Brand string `bson:"brand"` // 下單平台
OrderUID string `bson:"order_uid"` // 下單用戶 UID OrderUID string `bson:"order_uid"` // 下單用戶 UID
ReferenceID string `bson:"reference_id"` // 訂單來源(用) ReferenceID string `bson:"reference_id"` // 訂單來源
Count decimal.Decimal `bson:"count"` // 訂單數量 Count primitive.Decimal128 `bson:"count"` // 訂單數量
OrderFee decimal.Decimal `bson:"order_fee"` // 訂單手續費 OrderFee primitive.Decimal128 `bson:"order_fee"` // 訂單手續費
Amount decimal.Decimal `bson:"amount"` // 單價 Amount primitive.Decimal128 `bson:"amount"` // 單價
// 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 ReferenceBrand *string `bson:"reference_brand,omitempty"` // 訂單來源平台
ReferenceBrand *string `bson:"reference_brand"` // 訂單來源平台 ReferenceUID *string `bson:"reference_uid,omitempty"` // 訂單來源用戶 UID
ReferenceUID *string `bson:"reference_uid"` // 訂單來源用戶 UID WalletStatus *int64 `bson:"wallet_status,omitempty"` // 交易金額狀態
WalletStatus *int64 `bson:"wallet_status,omitempty"` // 交易金額狀態 1.未凍結 2.已凍結 3.解凍(扣錢) 4.未增加資產 5.已增加資產 6.解凍還原 7.限制(將凍結餘額轉至限制餘額) ThreePartyStatus *int64 `bson:"three_party_status,omitempty"` // 三方請求狀態
ThreePartyStatus *int64 `bson:"three_party_status,omitempty"` // 三方請求狀態 1.已發送 2.通知處理中 3.通知已完成 DirectionType *int64 `bson:"direction_type,omitempty"` // 交易方向
DirectionType *int64 `bson:"direction_type,omitempty"` // 交易方向 1.買入 2.賣出
CryptoType *string `bson:"crypto_type,omitempty"` // 交易幣種 CryptoType *string `bson:"crypto_type,omitempty"` // 交易幣種
ThirdPartyFee *decimal.Decimal `bson:"third_party_fee,omitempty"` // 第三方手續費 ThirdPartyFee *primitive.Decimal128 `bson:"third_party_fee,omitempty"` // 第三方手續費
CryptoToUSDTRate *decimal.Decimal `bson:"crypto_to_usdt_rate,omitempty"` // 交易幣種 對 USDT 匯率 CryptoToUSDTRate *primitive.Decimal128 `bson:"crypto_to_usdt_rate,omitempty"` // 加密貨幣對 USDT 匯率
FiatToUSDRate *decimal.Decimal `bson:"fiat_to_usd_rate,omitempty"` // 法幣 對 USD 匯率 FiatToUSDRate *primitive.Decimal128 `bson:"fiat_to_usd_rate,omitempty"` // 法幣對 USD 匯率
FeeCryptoToUSDTRate *decimal.Decimal `bson:"fee_crypto_to_usdt_rate,omitempty"` // 手續費對 USDT 匯率 FeeCryptoToUSDTRate *primitive.Decimal128 `bson:"fee_crypto_to_usdt_rate,omitempty"` // 手續費加密貨幣對 USDT 匯率
USDTToCryptoTypeRate *decimal.Decimal `bson:"usdt_to_crypto_type_rate,omitempty"` // USDT 對 交易幣種 匯率 USDTToCryptoTypeRate *primitive.Decimal128 `bson:"usdt_to_crypto_type_rate,omitempty"` // USDT 對加密貨幣匯率
PaymentFiat *string `bson:"payment_fiat,omitempty"` // 支付法幣 PaymentFiat *string `bson:"payment_fiat,omitempty"` // 支付法幣
PaymentUnitPrice *decimal.Decimal `bson:"payment_unit_price,omitempty"` // crypto 單價 PaymentUnitPrice *primitive.Decimal128 `bson:"payment_unit_price,omitempty"` // 加密貨幣單價
PaymentTemplateID *string `bson:"payment_template_id,omitempty"` // 支付方式配置 ID PaymentTemplateID *string `bson:"payment_template_id,omitempty"` // 支付方式配置 ID
OrderArrivalTime *int64 `bson:"order_arrival_time,omitempty"` // 訂單到帳時間/充值到帳時間 OrderArrivalTime *int64 `bson:"order_arrival_time,omitempty"` // 訂單到帳時間
OrderPaymentTime *int64 `bson:"order_payment_time,omitempty"` // 訂單付款時間/鏈上成功或失敗時間 OrderPaymentTime *int64 `bson:"order_payment_time,omitempty"` // 訂單付款時間
UnpaidTimeoutSecond *int64 `bson:"unpaid_timeout_second,omitempty"` // 支付期限秒數 UnpaidTimeoutSecond *int64 `bson:"unpaid_timeout_second,omitempty"` // 支付期限秒數
ChainType *string `bson:"chain_type,omitempty"` // 主網類型 ChainType *string `bson:"chain_type,omitempty"` // 主網類型
TxHash *string `bson:"tx_hash,omitempty"` // 交易哈希 TxHash *string `bson:"tx_hash,omitempty"` // 交易哈希
FromAddress *string `bson:"from_address,omitempty"` // 來源地址 FromAddress *string `bson:"from_address,omitempty"` // 來源地址
ToAddress *string `bson:"to_address,omitempty"` // 目標地址 ToAddress *string `bson:"to_address,omitempty"` // 目標地址
ChainFee *decimal.Decimal `bson:"chain_fee,omitempty"` // 鏈上交易手續費 ChainFee *primitive.Decimal128 `bson:"chain_fee,omitempty"` // 鏈上交易手續費
ChainFeeCrypto *string `bson:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別 ChainFeeCrypto *string `bson:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別
Memo *string `bson:"memo,omitempty"` // 鏈上備註 Memo *string `bson:"memo,omitempty"` // 鏈上備註
OrderNote *string `bson:"order_note,omitempty"` // 訂單交易備註 OrderNote *string `bson:"order_note,omitempty"` // 訂單交易備註