From 03b4d39164f1a45af8a6dc94bf22736bc6f0f126 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Sat, 12 Oct 2024 17:49:58 +0800 Subject: [PATCH 1/7] add list func --- Makefile | 2 +- .../mongodb/20241006000001_order.up.mongo | 19 ++ generate/protobuf/order.proto | 163 ++++++++++-- go.mod | 18 ++ internal/config/config.go | 9 + internal/domain/errors.go | 36 +++ internal/domain/order.go | 22 ++ .../logic/orderservice/cancel_order_logic.go | 39 ++- .../logic/orderservice/create_order_logic.go | 244 +++++++++++++++++- .../logic/orderservice/delete_order_logic.go | 19 +- .../logic/orderservice/get_order_logic.go | 65 ++++- .../logic/orderservice/list_order_logic.go | 43 ++- .../orderservice/modify_order_status_logic.go | 43 ++- .../order_status_timeout_logic.go | 22 +- internal/model/mongo/order_model.go | 211 ++++++++++++++- internal/model/mongo/order_model_gen.go | 6 +- internal/model/mongo/order_types.go | 49 +++- .../orderservice/order_service_server.go | 2 +- internal/svc/init_mongo.go | 23 ++ internal/svc/service_context.go | 20 +- 20 files changed, 1003 insertions(+), 52 deletions(-) create mode 100644 generate/database/mongodb/20241006000001_order.up.mongo create mode 100644 internal/domain/errors.go create mode 100644 internal/domain/order.go create mode 100644 internal/svc/init_mongo.go diff --git a/Makefile b/Makefile index fbbfdf5..80e5221 100644 --- a/Makefile +++ b/Makefile @@ -61,4 +61,4 @@ mock-gen: # 建立 mock 資料 .PHONY: migrate-database migrate-database: - migrate -source file://generate/database/migrations/mongodb -database 'mongodb://127.0.0.1:27017/digimon_order' up + migrate -source file://generate/database/mongodb -database 'mongodb://127.0.0.1:27017/digimon_order' up diff --git a/generate/database/mongodb/20241006000001_order.up.mongo b/generate/database/mongodb/20241006000001_order.up.mongo new file mode 100644 index 0000000..fb82665 --- /dev/null +++ b/generate/database/mongodb/20241006000001_order.up.mongo @@ -0,0 +1,19 @@ +use digimon_order; + +db.order.createIndex({ + "uid": 1, + "order_type": 1, + "order_status": 1, + "create_time": 1, +}) + +db.order.createIndex({ + "business_id": 1, +}) + +// 查詢時全部要 1&2 有幾個類用幾個累才會中索引 +db.order.createIndex({ + "uid": 1, + "direction_type": 1, + "crypto_type": 1 +}) \ No newline at end of file diff --git a/generate/protobuf/order.proto b/generate/protobuf/order.proto index 7372d20..c4a9792 100644 --- a/generate/protobuf/order.proto +++ b/generate/protobuf/order.proto @@ -9,46 +9,159 @@ message OKResp {} message NoneReq {} // 分頁信息 -message Pager -{ - int64 total = 1; // 總數量 - int64 size = 2; // 每頁數量 - int64 index = 3; // 當前頁碼 +message Pager { + int64 total = 1; // 總數量 + int64 size = 2; // 每頁數量 + int64 index = 3; // 當前頁碼 } -message CreateOrderReq{} -message CreateOrderResp{} +message CreateOrderReq { + string business_id = 1; // 訂單業務流水號 + int32 order_type = 2; // 訂單類型 + int32 order_status = 3; // 訂單狀態 + string brand = 4; // 下單平台 + string order_uid = 5; // 下單用戶 UID + string reference_id = 6; // 訂單來源 + string count = 7; // 訂單數量 (decimal to string) + string order_fee = 8; // 訂單手續費 (decimal to string) + string amount = 9; // 單價 (decimal to string) -message CancelOrderReq{} -message ModifyOrderReq{} -message ModifyOrderStatusReq{} -message DeleteOrderReq{} + // 以下是可選字段(擴充用) + optional string reference_brand = 10; // 訂單來源平台 + optional string reference_uid = 11; // 訂單來源用戶 UID + optional int64 wallet_status = 12; // 交易金額狀態 + optional int64 three_party_status = 13; // 三方請求狀態 + optional int64 direction_type = 14; // 交易方向 + optional string crypto_type = 15; // 交易幣種 + optional string third_party_fee = 16; // 第三方手續費 (decimal to string) + optional string crypto_to_usdt_rate = 17; // 交易幣種對 USDT 匯率 (decimal to string) + optional string fiat_to_usd_rate = 18; // 法幣對 USD 匯率 (decimal to string) + optional string fee_crypto_to_usdt_rate = 19; // 手續費幣種對 USDT 匯率 (decimal to string) + optional string usdt_to_crypto_type_rate = 20; // USDT 對交易幣種匯率 (decimal to string) + optional string payment_fiat = 21; // 支付法幣 + optional string payment_unit_price = 22; // crypto 單價 (decimal to string) + optional string payment_template_id = 23; // 支付方式配置 ID + optional int64 order_arrival_time = 24; // 訂單到帳時間 + optional int64 order_payment_time = 25; // 訂單付款時間 + optional int64 unpaid_timeout_second = 26; // 支付期限秒數 + optional string chain_type = 27; // 主網類型 + optional string tx_hash = 28; // 交易哈希 + optional string from_address = 29; // 來源地址 + optional string to_address = 30; // 目標地址 + optional string chain_fee = 31; // 鏈上交易手續費 (decimal to string) + optional string chain_fee_crypto = 32; // 鏈上手續費使用幣別 + optional string memo = 33; // 鏈上備註 + optional string order_note = 34; // 訂單交易備註 +} -message GetOrderReq{} -message GetOrderResp{} +message CancelOrderReq { + string business_id = 1; + int64 status = 2; +} -message ListOrderReq{} -message ListOrderResp{} +message ModifyOrderStatusReq { + string business_id = 1; + int64 status = 2; +} +message DeleteOrderReq { + string business_id = 1; +} -message OrderStatusTimeoutReq{} +message OrderStatusTimeoutReq {} + +message GetOrderReq { + string business_id = 1; // 訂單業務流水號 +} +message GetOrderResp { + string business_id = 1; // 訂單業務流水號 + int32 order_type = 2; // 訂單類型 + int32 order_status = 3; // 訂單狀態 + string brand = 4; // 下單平台 + string order_uid = 5; // 下單用戶 UID + string reference_id = 6; // 訂單來源 + string count = 7; // 訂單數量 (decimal to string) + string order_fee = 8; // 訂單手續費 (decimal to string) + string amount = 9; // 單價 (decimal to string) + + // 以下是可選字段(擴充用) + optional string reference_brand = 10; // 訂單來源平台 + optional string reference_uid = 11; // 訂單來源用戶 UID + optional int64 wallet_status = 12; // 交易金額狀態 + optional int64 three_party_status = 13; // 三方請求狀態 + optional int64 direction_type = 14; // 交易方向 + optional string crypto_type = 15; // 交易幣種 + optional string third_party_fee = 16; // 第三方手續費 (decimal to string) + optional string crypto_to_usdt_rate = 17; // 交易幣種對 USDT 匯率 (decimal to string) + optional string fiat_to_usd_rate = 18; // 法幣對 USD 匯率 (decimal to string) + optional string fee_crypto_to_usdt_rate = 19; // 手續費幣種對 USDT 匯率 (decimal to string) + optional string usdt_to_crypto_type_rate = 20; // USDT 對交易幣種匯率 (decimal to string) + optional string payment_fiat = 21; // 支付法幣 + optional string payment_unit_price = 22; // crypto 單價 (decimal to string) + optional string payment_template_id = 23; // 支付方式配置 ID + optional int64 order_arrival_time = 24; // 訂單到帳時間 + optional int64 order_payment_time = 25; // 訂單付款時間 + optional int64 unpaid_timeout_second = 26; // 支付期限秒數 + optional string chain_type = 27; // 主網類型 + optional string tx_hash = 28; // 交易哈希 + optional string from_address = 29; // 來源地址 + optional string to_address = 30; // 目標地址 + optional string chain_fee = 31; // 鏈上交易手續費 (decimal to string) + optional string chain_fee_crypto = 32; // 鏈上手續費使用幣別 + optional string memo = 33; // 鏈上備註 + optional string order_note = 34; // 訂單交易備註 + int64 create_time = 35; // 建立時間 + int64 update_time = 36; // 更新時間 +} + +message ModifyOrderReq {} + +message ListOrderReq { + int64 page_index = 1; // required + int64 page_size = 2; // required + + string reference_id = 3; + string reference_uid = 4; + string business_id = 5; + string uid = 6; + int32 order_type = 7; + repeated int32 direction_type = 8; + repeated int32 order_status = 9; + + int64 start_create_time = 10; + int64 end_create_time = 11; + int64 start_update_time = 12; + int64 end_update_time = 13; + int64 start_order_arrival_time = 14; + int64 end_order_arrival_time = 15; + int64 start_order_payment_time = 16; + int64 end_order_payment_time = 17; + + string crypto_type = 18; + string tx_hash = 19; +} + +message ListOrderResp { + repeated GetOrderResp data = 1; // 訂單列表 + Pager page = 2; +} // OrderService 訂單服務(業務邏輯在外面組合) -service OrderService{ +service OrderService { // CreateOrder 建立訂單 - rpc CreateOrder(CreateOrderReq)returns(CreateOrderResp); + rpc CreateOrder(CreateOrderReq) returns (OKResp); // CancelOrder 取消訂單 - rpc CancelOrder(CancelOrderReq)returns(OKResp); + rpc CancelOrder(CancelOrderReq) returns (OKResp); // ModifyOrder 修改訂單 - rpc ModifyOrder(ModifyOrderReq)returns(OKResp); + rpc ModifyOrder(ModifyOrderReq) returns (OKResp); // ModifyOrderStatus 修改訂單狀態 - rpc ModifyOrderStatus(ModifyOrderStatusReq)returns(OKResp); + rpc ModifyOrderStatus(ModifyOrderStatusReq) returns (OKResp); // DeleteOrder 刪除訂單(軟刪除) - rpc DeleteOrder(DeleteOrderReq)returns(OKResp); + rpc DeleteOrder(DeleteOrderReq) returns (OKResp); // GetOrder 取得訂單詳情 - rpc GetOrder(GetOrderReq)returns(GetOrderResp); + rpc GetOrder(GetOrderReq) returns (GetOrderResp); // ListOrder 取得訂單列表 - rpc ListOrder(ListOrderReq)returns(ListOrderResp); + rpc ListOrder(ListOrderReq) returns (ListOrderResp); // OrderStatusTimeout 訂單超時任務/cron/order-status/timeout - rpc OrderStatusTimeout(OrderStatusTimeoutReq)returns(OKResp); + rpc OrderStatusTimeout(OrderStatusTimeoutReq) returns (OKResp); } diff --git a/go.mod b/go.mod index 0a5d0f1..72694e2 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,12 @@ module app-cloudep-order-server go 1.22.3 require ( + code.30cm.net/digimon/library-go/errs v1.2.5 + code.30cm.net/digimon/library-go/validator v1.0.0 + github.com/shopspring/decimal v1.4.0 github.com/zeromicro/go-zero v1.7.2 + go.mongodb.org/mongo-driver v1.16.1 + go.uber.org/mock v0.4.0 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.34.2 ) @@ -18,14 +23,19 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fatih/color v1.17.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -34,11 +44,13 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect @@ -48,6 +60,10 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/redis/go-redis/v9 v9.6.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.etcd.io/etcd/api/v3 v3.5.15 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/client/v3 v3.5.15 // indirect @@ -66,8 +82,10 @@ require ( go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect diff --git a/internal/config/config.go b/internal/config/config.go index c1f85b9..85679b2 100755 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,4 +4,13 @@ import "github.com/zeromicro/go-zero/zrpc" type Config struct { zrpc.RpcServerConf + + Mongo struct { + Schema string + User string + Password string + Host string + Port string + Database string + } } diff --git a/internal/domain/errors.go b/internal/domain/errors.go new file mode 100644 index 0000000..15bfbf5 --- /dev/null +++ b/internal/domain/errors.go @@ -0,0 +1,36 @@ +package domain + +import ( + "strings" + + ers "code.30cm.net/digimon/library-go/errs" + "code.30cm.net/digimon/library-go/errs/code" + "github.com/zeromicro/go-zero/core/logx" +) + +type ErrorCode uint32 + +func (e ErrorCode) ToUint32() uint32 { + return uint32(e) +} + +// Error Code 統一這邊改 +const ( + _ = iota + CreateOrderErrorCode ErrorCode = iota + CancelOrderErrorCode + ModifyOrderErrorCode + TimeoutOrderErrorCode +) + +func CommentError(ec ErrorCode, s ...string) *ers.LibError { + return ers.NewError(code.CloudEPOrder, code.DBError, ec.ToUint32(), strings.Join(s, " ")) +} + +func CommentErrorL(ec ErrorCode, + l logx.Logger, filed []logx.LogField, s ...string) *ers.LibError { + e := CommentError(ec, s...) + l.WithCallerSkip(1).WithFields(filed...).Error(e.Error()) + + return e +} diff --git a/internal/domain/order.go b/internal/domain/order.go new file mode 100644 index 0000000..18233cb --- /dev/null +++ b/internal/domain/order.go @@ -0,0 +1,22 @@ +package domain + +type OrderStatus int64 + +func (o *OrderStatus) ToInt() int { + return int(*o) +} + +const ( + OrderStatusCreated OrderStatus = 0 // 建立訂單 + OrderStatusFailed OrderStatus = 1 // 建單失敗 + OrderStatusReviewing OrderStatus = 2 // 審核中 + OrderStatusPaying OrderStatus = 3 // 付款中 + OrderStatusPaid OrderStatus = 4 // 已付款 + OrderStatusPendingTransfer OrderStatus = 5 // 已付款待轉帳 + OrderStatusDisputing OrderStatus = 6 // 申訴中 + OrderStatusCompleted OrderStatus = 7 // 交易完成 + OrderStatusFailedTrade OrderStatus = 8 // 交易失敗 + OrderStatusCancelled OrderStatus = 9 // 交易取消 + OrderStatusAbnormal OrderStatus = 10 // 交易異常 + OrderStatusTimeout OrderStatus = 11 // 交易超時 +) diff --git a/internal/logic/orderservice/cancel_order_logic.go b/internal/logic/orderservice/cancel_order_logic.go index 053b579..80b242e 100644 --- a/internal/logic/orderservice/cancel_order_logic.go +++ b/internal/logic/orderservice/cancel_order_logic.go @@ -1,8 +1,12 @@ package orderservicelogic import ( + "app-cloudep-order-server/internal/domain" + model "app-cloudep-order-server/internal/model/mongo" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -23,9 +27,42 @@ func NewCancelOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Cance } } +// CancelOrderQuery 1.建單失敗 9.交易取消 10.交易異常 +type CancelOrderQuery struct { + BusinessID string `json:"business_id" validate:"required"` + Status int64 `json:"status" validate:"required,oneof=1 9 10"` +} + // CancelOrder 取消訂單 func (l *CancelOrderLogic) CancelOrder(in *tweeting.CancelOrderReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&CancelOrderQuery{ + BusinessID: in.GetBusinessId(), + Status: in.GetStatus(), + }); err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + _, err := l.svcCtx.OrderModel.UpdateStatus(l.ctx, + model.UpdateStatusReq{ + BusinessID: in.GetBusinessId(), + Status: in.GetStatus(), + }) + if err != nil { + // 錯誤代碼 06-021-02 + e := domain.CommentErrorL( + domain.CancelOrderErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "OrderModel.UpdateStatus"}, + {Key: "err", Value: err}, + }, + "failed to update order status:").Wrap(err) + + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/logic/orderservice/create_order_logic.go b/internal/logic/orderservice/create_order_logic.go index 9ae8e56..151fbe4 100644 --- a/internal/logic/orderservice/create_order_logic.go +++ b/internal/logic/orderservice/create_order_logic.go @@ -1,7 +1,13 @@ package orderservicelogic import ( + "app-cloudep-order-server/internal/domain" + model "app-cloudep-order-server/internal/model/mongo" "context" + "time" + + ers "code.30cm.net/digimon/library-go/errs" + "github.com/shopspring/decimal" "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -23,9 +29,237 @@ func NewCreateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Creat } } -// CreateOrder 建立訂單 -func (l *CreateOrderLogic) CreateOrder(in *tweeting.CreateOrderReq) (*tweeting.CreateOrderResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.CreateOrderResp{}, nil +type createOrderReq struct { // 訂單ID + BusinessID string `json:"business_id" validate:"required"` // 訂單業務流水號 + OrderType int8 `json:"order_type" validate:"required"` // 訂單類型 + OrderStatus int8 `json:"order_status" validate:"required,oneof=0 1 2 3 4 5 6 7 8 9 10 11"` // 訂單狀態 + Brand string `json:"brand" validate:"required"` // 下單平台 + OrderUID string `json:"order_uid" validate:"required"` // 下單用戶 UID + ReferenceID string `json:"reference_id" validate:"required"` // 訂單來源 + Count decimal.Decimal `json:"count" validate:"required,gt=0"` // 訂單數量 + OrderFee decimal.Decimal `json:"order_fee" validate:"required,gte=0"` // 訂單手續費 + Amount decimal.Decimal `json:"amount" validate:"required,gte=0"` // 單價 + ReferenceBrand *string `json:"reference_brand,omitempty" validate:"omitempty"` // 訂單來源平台 + ReferenceUID *string `json:"reference_uid,omitempty" validate:"omitempty"` // 訂單來源用戶 UID + WalletStatus *int64 `json:"wallet_status,omitempty" validate:"omitempty,oneof=1 2 3 4 5 6 7"` // 交易金額狀態 + ThreePartyStatus *int64 `json:"three_party_status,omitempty" validate:"omitempty,oneof=1 2 3"` // 三方請求狀態 + DirectionType *int64 `json:"direction_type,omitempty" validate:"omitempty,oneof=1 2"` // 交易方向 + CryptoType *string `json:"crypto_type,omitempty" validate:"omitempty"` // 交易幣種 + ThirdPartyFee *decimal.Decimal `json:"third_party_fee,omitempty" validate:"omitempty,gte=0"` // 第三方手續費 + CryptoToUSDTRate *decimal.Decimal `json:"crypto_to_usdt_rate,omitempty" validate:"omitempty,gte=0"` // 交易幣種對 USDT 匯率 + FiatToUSDRate *decimal.Decimal `json:"fiat_to_usd_rate,omitempty" validate:"omitempty,gte=0"` // 法幣對 USD 匯率 + FeeCryptoToUSDTRate *decimal.Decimal `json:"fee_crypto_to_usdt_rate,omitempty" validate:"omitempty,gte=0"` // 手續費幣種對 USDT 匯率 + USDTToCryptoTypeRate *decimal.Decimal `json:"usdt_to_crypto_type_rate,omitempty" validate:"omitempty,gte=0"` // USDT 對交易幣種匯率 + PaymentFiat *string `json:"payment_fiat,omitempty" validate:"omitempty"` // 支付法幣 + PaymentUnitPrice *decimal.Decimal `json:"payment_unit_price,omitempty" validate:"omitempty,gte=0"` // crypto 單價 + PaymentTemplateID *string `json:"payment_template_id,omitempty" validate:"omitempty"` // 支付方式配置 ID + OrderArrivalTime *int64 `json:"order_arrival_time,omitempty" validate:"omitempty"` // 訂單到帳時間 + OrderPaymentTime *int64 `json:"order_payment_time,omitempty" validate:"omitempty"` // 訂單付款時間 + UnpaidTimeoutSecond *int64 `json:"unpaid_timeout_second,omitempty" validate:"omitempty,gte=0"` // 支付期限秒數 + ChainType *string `json:"chain_type,omitempty" validate:"omitempty"` // 主網類型 + TxHash *string `json:"tx_hash,omitempty" validate:"omitempty"` // 交易哈希 + FromAddress *string `json:"from_address,omitempty" validate:"omitempty"` // 來源地址 + ToAddress *string `json:"to_address,omitempty" validate:"omitempty"` // 目標地址 + ChainFee *decimal.Decimal `json:"chain_fee,omitempty" validate:"omitempty,gte=0"` // 鏈上交易手續費 + ChainFeeCrypto *string `json:"chain_fee_crypto,omitempty" validate:"omitempty"` // 鏈上手續費使用幣別 + Memo *string `json:"memo,omitempty" validate:"omitempty"` // 鏈上備註 + OrderNote *string `json:"order_note,omitempty" validate:"omitempty"` +} + +// CreateOrder 建立訂單 +func (l *CreateOrderLogic) CreateOrder(in *tweeting.CreateOrderReq) (*tweeting.OKResp, error) { + req, err := buildCreateOrderReq(in) + if err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&req); err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + now := time.Now().UTC().UnixNano() + // 插入資料庫 + o := &model.Order{ + UpdateTime: now, + CreateTime: now, + BusinessID: req.BusinessID, + OrderType: req.OrderType, + OrderStatus: req.OrderStatus, + Brand: req.Brand, + OrderUID: req.OrderUID, + ReferenceID: req.ReferenceID, + Count: req.Count, + OrderFee: req.OrderFee, + Amount: req.Amount, + // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 + ReferenceBrand: req.ReferenceBrand, + ReferenceUID: req.ReferenceUID, + WalletStatus: req.WalletStatus, + ThreePartyStatus: req.ThreePartyStatus, + DirectionType: req.DirectionType, + CryptoType: req.CryptoType, + ThirdPartyFee: req.ThirdPartyFee, + CryptoToUSDTRate: req.CryptoToUSDTRate, + FiatToUSDRate: req.FiatToUSDRate, + FeeCryptoToUSDTRate: req.FeeCryptoToUSDTRate, + USDTToCryptoTypeRate: req.USDTToCryptoTypeRate, + PaymentFiat: req.PaymentFiat, + PaymentUnitPrice: req.PaymentUnitPrice, + PaymentTemplateID: req.PaymentTemplateID, + OrderArrivalTime: req.OrderArrivalTime, + OrderPaymentTime: req.OrderPaymentTime, + UnpaidTimeoutSecond: req.UnpaidTimeoutSecond, + ChainType: req.ChainType, + TxHash: req.TxHash, + FromAddress: req.FromAddress, + ToAddress: req.ToAddress, + ChainFee: req.ChainFee, + ChainFeeCrypto: req.ChainFeeCrypto, + Memo: req.Memo, + OrderNote: req.OrderNote, + } + err = l.svcCtx.OrderModel.Insert(l.ctx, o) + + if err != nil { + // 錯誤代碼 06-021-01 + e := domain.CommentErrorL( + domain.CreateOrderErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "OrderModel.Insert"}, + {Key: "err", Value: err}, + }, + "failed to insert order into mongo:").Wrap(err) + + return nil, e + } + + return &tweeting.OKResp{}, nil +} + +func buildCreateOrderReq(in *tweeting.CreateOrderReq) (*createOrderReq, error) { + createOrderReq := &createOrderReq{ + BusinessID: in.BusinessId, + OrderType: int8(in.OrderType), + OrderStatus: int8(in.OrderStatus), + Brand: in.Brand, + OrderUID: in.OrderUid, + ReferenceID: in.ReferenceId, + } + + var err error + // 只有當 Count, OrderFee, Amount 不為空時才進行轉換和設置 + if in.Count != "" { + createOrderReq.Count, err = decimal.NewFromString(in.Count) + if err != nil { + return nil, err + } + } + if in.OrderFee != "" { + createOrderReq.OrderFee, err = decimal.NewFromString(in.OrderFee) + if err != nil { + return nil, err + } + } + if in.Amount != "" { + createOrderReq.Amount, err = decimal.NewFromString(in.Amount) + if err != nil { + return nil, err + } + } + + // 判斷可選字段,只有不為 nil 才設置 + if in.ReferenceBrand != nil { + createOrderReq.ReferenceBrand = in.ReferenceBrand + } + if in.ReferenceUid != nil { + createOrderReq.ReferenceUID = in.ReferenceUid + } + if in.WalletStatus != nil { + createOrderReq.WalletStatus = in.WalletStatus + } + if in.ThreePartyStatus != nil { + createOrderReq.ThreePartyStatus = in.ThreePartyStatus + } + if in.DirectionType != nil { + createOrderReq.DirectionType = in.DirectionType + } + if in.CryptoType != nil { + createOrderReq.CryptoType = in.CryptoType + } + if in.ThirdPartyFee != nil && *in.ThirdPartyFee != "" { + createOrderReq.ThirdPartyFee = decimalPtrFromString(*in.ThirdPartyFee) + } + if in.CryptoToUsdtRate != nil && *in.CryptoToUsdtRate != "" { + createOrderReq.CryptoToUSDTRate = decimalPtrFromString(*in.CryptoToUsdtRate) + } + if in.FiatToUsdRate != nil && *in.FiatToUsdRate != "" { + createOrderReq.FiatToUSDRate = decimalPtrFromString(*in.FiatToUsdRate) + } + if in.FeeCryptoToUsdtRate != nil && *in.FeeCryptoToUsdtRate != "" { + createOrderReq.FeeCryptoToUSDTRate = decimalPtrFromString(*in.FeeCryptoToUsdtRate) + } + if in.UsdtToCryptoTypeRate != nil && *in.UsdtToCryptoTypeRate != "" { + createOrderReq.USDTToCryptoTypeRate = decimalPtrFromString(*in.UsdtToCryptoTypeRate) + } + if in.PaymentFiat != nil { + createOrderReq.PaymentFiat = in.PaymentFiat + } + if in.PaymentUnitPrice != nil && *in.PaymentUnitPrice != "" { + createOrderReq.PaymentUnitPrice = decimalPtrFromString(*in.PaymentUnitPrice) + } + if in.PaymentTemplateId != nil { + createOrderReq.PaymentTemplateID = in.PaymentTemplateId + } + if in.OrderArrivalTime != nil { + createOrderReq.OrderArrivalTime = in.OrderArrivalTime + } + if in.OrderPaymentTime != nil { + createOrderReq.OrderPaymentTime = in.OrderPaymentTime + } + if in.UnpaidTimeoutSecond != nil { + createOrderReq.UnpaidTimeoutSecond = in.UnpaidTimeoutSecond + } + if in.ChainType != nil { + createOrderReq.ChainType = in.ChainType + } + if in.TxHash != nil { + createOrderReq.TxHash = in.TxHash + } + if in.FromAddress != nil { + createOrderReq.FromAddress = in.FromAddress + } + if in.ToAddress != nil { + createOrderReq.ToAddress = in.ToAddress + } + if in.ChainFee != nil && *in.ChainFee != "" { + createOrderReq.ChainFee = decimalPtrFromString(*in.ChainFee) + } + if in.ChainFeeCrypto != nil { + createOrderReq.ChainFeeCrypto = in.ChainFeeCrypto + } + if in.Memo != nil { + createOrderReq.Memo = in.Memo + } + if in.OrderNote != nil { + createOrderReq.OrderNote = in.OrderNote + } + + 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 } diff --git a/internal/logic/orderservice/delete_order_logic.go b/internal/logic/orderservice/delete_order_logic.go index f66b6cb..fccce01 100644 --- a/internal/logic/orderservice/delete_order_logic.go +++ b/internal/logic/orderservice/delete_order_logic.go @@ -1,6 +1,7 @@ package orderservicelogic import ( + ers "code.30cm.net/digimon/library-go/errs" "context" "app-cloudep-order-server/gen_result/pb/tweeting" @@ -23,9 +24,25 @@ func NewDeleteOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delet } } +// DeleteOrderQuery 刪除訂單(軟刪除) +type DeleteOrderQuery struct { + BusinessID string `json:"business_id" validate:"required"` +} + // DeleteOrder 刪除訂單(軟刪除) func (l *DeleteOrderLogic) DeleteOrder(in *tweeting.DeleteOrderReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&DeleteOrderQuery{ + BusinessID: in.GetBusinessId(), + }); err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + _, err := l.svcCtx.OrderModel.DeleteByBusinessID(l.ctx, in.GetBusinessId()) + if err != nil { + return nil, err + } return &tweeting.OKResp{}, nil } diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index 9260f0f..f236f28 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -1,7 +1,9 @@ package orderservicelogic import ( + ers "code.30cm.net/digimon/library-go/errs" "context" + "github.com/shopspring/decimal" "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -23,9 +25,68 @@ func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetOrder } } +// GetOrderQuery 取得訂單 +type GetOrderQuery struct { + BusinessID string `json:"business_id" validate:"required"` +} + // GetOrder 取得訂單詳情 func (l *GetOrderLogic) GetOrder(in *tweeting.GetOrderReq) (*tweeting.GetOrderResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&GetOrderQuery{ + BusinessID: in.GetBusinessId(), + }); err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } - return &tweeting.GetOrderResp{}, nil + order, err := l.svcCtx.OrderModel.FindOneBusinessID(l.ctx, in.GetBusinessId()) + if err != nil { + return nil, err + } + + return &tweeting.GetOrderResp{ + UpdateTime: order.UpdateTime, + CreateTime: order.CreateTime, + BusinessId: order.BusinessID, + OrderType: int32(order.OrderType), + OrderStatus: int32(order.OrderStatus), + Brand: order.Brand, + OrderUid: order.OrderUID, + ReferenceId: order.ReferenceID, + Count: order.Count.String(), + OrderFee: order.OrderFee.String(), + Amount: order.Amount.String(), + // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 + ReferenceBrand: order.ReferenceBrand, + ReferenceUid: order.ReferenceUID, + WalletStatus: order.WalletStatus, + ThreePartyStatus: order.ThreePartyStatus, + DirectionType: order.DirectionType, + CryptoType: order.CryptoType, + ThirdPartyFee: decimalToString(*order.ThirdPartyFee), + CryptoToUsdtRate: decimalToString(*order.CryptoToUSDTRate), + FiatToUsdRate: decimalToString(*order.FiatToUSDRate), + FeeCryptoToUsdtRate: decimalToString(*order.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: decimalToString(*order.USDTToCryptoTypeRate), + PaymentFiat: order.PaymentFiat, + PaymentUnitPrice: decimalToString(*order.PaymentUnitPrice), + PaymentTemplateId: order.PaymentTemplateID, + OrderArrivalTime: order.OrderArrivalTime, + OrderPaymentTime: order.OrderPaymentTime, + UnpaidTimeoutSecond: order.UnpaidTimeoutSecond, + ChainType: order.ChainType, + TxHash: order.TxHash, + FromAddress: order.FromAddress, + ToAddress: order.ToAddress, + ChainFee: decimalToString(*order.ChainFee), + ChainFeeCrypto: order.ChainFeeCrypto, + Memo: order.Memo, + OrderNote: order.OrderNote, + }, nil +} + +func decimalToString(amount decimal.Decimal) *string { + a := amount.String() + return &a } diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index 67e4cdb..dc55359 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -1,7 +1,10 @@ package orderservicelogic import ( + model "app-cloudep-order-server/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" "context" + "fmt" "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -23,9 +26,47 @@ func NewListOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListOrd } } +type GetOrderListReq struct { + PageIndex int64 `json:"page_index" validate:"required"` + PageSize int64 `json:"page_size" validate:"required"` + + ReferenceID string `json:"reference_id"` + ReferenceUID string `json:"reference_uid"` + BusinessID string `json:"business_id"` + UID string `json:"uid"` + OrderType int `json:"order_type"` + DirectionType []int64 `json:"direction_type"` + OrderStatus []int64 `json:"order_status"` + + StartCreateTime int64 `json:"start_create_time"` + EndCreateTime int64 `json:"end_create_time"` + StartUpdateTime int64 `json:"start_update_time"` + EndUpdateTime int64 `json:"end_update_time"` + StartOrderArrivalTime int64 `json:"start_order_arrival_time"` + EndOrderArrivalTime int64 `json:"end_order_arrival_time"` + StartOrderPaymentTime int64 `json:"start_order_payment_time"` + EndOrderPaymentTime int64 `json:"end_order_payment_time"` + + CryptoType string `json:"crypto_type"` + TxHash string `json:"tx_hash"` +} + // ListOrder 取得訂單列表 func (l *ListOrderLogic) ListOrder(in *tweeting.ListOrderReq) (*tweeting.ListOrderResp, error) { - // todo: add your logic here and delete this line + // 驗證資料,目前只有 Page 必帶,其他要驗證在驗證 + if err := l.svcCtx.Validate.ValidateAll(&GetOrderListReq{ + PageIndex: in.GetPageIndex(), + PageSize: in.GetPageSize(), + }); err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + orders, total, err := l.svcCtx.OrderModel.ListOrder(l.ctx, model.GetOrderListReq{}) + if err != nil { + return nil, err + } + + fmt.Println(orders, total) return &tweeting.ListOrderResp{}, nil } diff --git a/internal/logic/orderservice/modify_order_status_logic.go b/internal/logic/orderservice/modify_order_status_logic.go index 4a2581b..02a204c 100644 --- a/internal/logic/orderservice/modify_order_status_logic.go +++ b/internal/logic/orderservice/modify_order_status_logic.go @@ -1,8 +1,12 @@ package orderservicelogic import ( + "app-cloudep-order-server/internal/domain" + model "app-cloudep-order-server/internal/model/mongo" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -23,9 +27,46 @@ func NewModifyOrderStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) } } +// ModifyOrderQuery +// 0.建立訂單 1.建單失敗 2.審核中 3.付款中 4.已付款 +// 5.已付款待轉帳 6.申訴中 7.交易完成 +// 8.交易失敗 9.交易取消 10.交易異常 11.交易超時 + +type ModifyOrderQuery struct { + BusinessID string `json:"business_id" validate:"required"` + Status int64 `json:"status" validate:"required,oneof=2 3 4 5 6 7 8 11"` +} + // ModifyOrderStatus 修改訂單狀態 func (l *ModifyOrderStatusLogic) ModifyOrderStatus(in *tweeting.ModifyOrderStatusReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&ModifyOrderQuery{ + BusinessID: in.GetBusinessId(), + Status: in.GetStatus(), + }); err != nil { + // 錯誤代碼 06-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + _, err := l.svcCtx.OrderModel.UpdateStatus(l.ctx, + model.UpdateStatusReq{ + BusinessID: in.GetBusinessId(), + Status: in.GetStatus(), + }) + if err != nil { + // 錯誤代碼 06-021-02 + e := domain.CommentErrorL( + domain.ModifyOrderErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "OrderModel.UpdateStatus"}, + {Key: "err", Value: err}, + }, + "failed to update order status:").Wrap(err) + + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/logic/orderservice/order_status_timeout_logic.go b/internal/logic/orderservice/order_status_timeout_logic.go index 67ce9c6..4a3096c 100644 --- a/internal/logic/orderservice/order_status_timeout_logic.go +++ b/internal/logic/orderservice/order_status_timeout_logic.go @@ -1,7 +1,10 @@ package orderservicelogic import ( + "app-cloudep-order-server/internal/domain" + model "app-cloudep-order-server/internal/model/mongo" "context" + "time" "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -25,7 +28,24 @@ func NewOrderStatusTimeoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) // OrderStatusTimeout 訂單超時任務/cron/order-status/timeout func (l *OrderStatusTimeoutLogic) OrderStatusTimeout(in *tweeting.OrderStatusTimeoutReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + now := time.Now().UTC().UnixNano() + _, err := l.svcCtx.OrderModel.UpdateTimeoutOrder(l.ctx, model.UpdateTimeoutReq{ + CreateTimeBefore: now, + }) + if err != nil { + // 錯誤代碼 06-021-02 + e := domain.CommentErrorL( + domain.TimeoutOrderErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "now", Value: now}, + {Key: "func", Value: "OrderModel.UpdateTimeoutOrder"}, + {Key: "err", Value: err}, + }, + "failed to update timeout order").Wrap(err) + + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/model/mongo/order_model.go b/internal/model/mongo/order_model.go index 73a14ba..47f5b25 100644 --- a/internal/model/mongo/order_model.go +++ b/internal/model/mongo/order_model.go @@ -1,6 +1,16 @@ package model -import "github.com/zeromicro/go-zero/core/stores/mon" +import ( + "app-cloudep-order-server/internal/domain" + "context" + "errors" + "go.mongodb.org/mongo-driver/mongo/options" + "time" + + "github.com/zeromicro/go-zero/core/stores/mon" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) var _ OrderModel = (*customOrderModel)(nil) @@ -9,11 +19,50 @@ type ( // and implement the added methods in customOrderModel. OrderModel interface { orderModel + UpdateStatus(ctx context.Context, data UpdateStatusReq) (*mongo.UpdateResult, error) + UpdateTimeoutOrder(ctx context.Context, req UpdateTimeoutReq) (*mongo.UpdateResult, error) + DeleteByBusinessID(ctx context.Context, id string) (*mongo.UpdateResult, error) + FindOneBusinessID(ctx context.Context, id string) (*Order, error) + ListOrder(ctx context.Context, req GetOrderListReq) ([]Order, int64, error) } customOrderModel struct { *defaultOrderModel } + + UpdateStatusReq struct { + BusinessID string + Status int64 + } + + UpdateTimeoutReq struct { + CreateTimeBefore int64 + } + + GetOrderListReq struct { + PageIndex int64 `json:"page_index" validate:"required"` + PageSize int64 `json:"page_size" validate:"required"` + + ReferenceID string `json:"reference_id"` + ReferenceUID string `json:"reference_uid"` + BusinessID string `json:"business_id"` + UID string `json:"uid"` + OrderType int `json:"order_type"` + DirectionType []int64 `json:"direction_type"` + OrderStatus []int64 `json:"order_status"` + + StartCreateTime int64 `json:"start_create_time"` + EndCreateTime int64 `json:"end_create_time"` + StartUpdateTime int64 `json:"start_update_time"` + EndUpdateTime int64 `json:"end_update_time"` + StartOrderArrivalTime int64 `json:"start_order_arrival_time"` + EndOrderArrivalTime int64 `json:"end_order_arrival_time"` + StartOrderPaymentTime int64 `json:"start_order_payment_time"` + EndOrderPaymentTime int64 `json:"end_order_payment_time"` + + CryptoType string `json:"crypto_type"` + TxHash string `json:"tx_hash"` + } ) // NewOrderModel returns a model for the mongo. @@ -23,3 +72,163 @@ func NewOrderModel(url, db, collection string) OrderModel { defaultOrderModel: newDefaultOrderModel(conn), } } + +func (m *customOrderModel) UpdateStatus(ctx context.Context, data UpdateStatusReq) (*mongo.UpdateResult, error) { + // 初始化 updates map + updates := make(map[string]any) + + // 更新 update_time 和 status + updates["update_time"] = time.Now().UTC().UnixNano() + updates["status"] = data.Status + + // 使用 updates map 作為 $set 的內容 + res, err := m.conn.UpdateOne(ctx, + bson.M{ + "business_id": data.BusinessID, + "$or": []bson.M{ + {"delete_time": bson.M{"$exists": false}}, + {"delete_time": 0}, + }, + }, + bson.M{"$set": updates}, // 使用 updates 而不是整個 data + ) + return res, err +} + +func (m *customOrderModel) UpdateTimeoutOrder(ctx context.Context, req UpdateTimeoutReq) (*mongo.UpdateResult, error) { + // 構建過濾條件,選擇創建時間在指定時間之前且狀態為 0 (創建) 的項目 + filter := bson.M{ + "create_time": bson.M{"$lt": req.CreateTimeBefore}, + "status": domain.OrderStatusCreated, + "$or": []bson.M{ + {"delete_time": bson.M{"$exists": false}}, + {"delete_time": 0}, + }, + } + + // 更新內容,將狀態設置為 11,並更新 update_time + updates := bson.M{ + "$set": bson.M{ + "status": domain.OrderStatusTimeout, + "update_time": time.Now().UTC().UnixNano(), + }, + } + + // 執行更新操作 + res, err := m.conn.UpdateOne(ctx, filter, updates) + return res, err +} + +func (m *customOrderModel) DeleteByBusinessID(ctx context.Context, id string) (*mongo.UpdateResult, error) { + filter := bson.M{ + "business_id": id, + "$or": []bson.M{ + {"delete_time": bson.M{"$exists": false}}, + {"delete_time": 0}, + }, + } + + updates := bson.M{ + "$set": bson.M{ + "delete_time": time.Now().UTC().UnixNano(), + "update_time": time.Now().UTC().UnixNano(), + }, + } + // 執行更新操作 + res, err := m.conn.UpdateOne(ctx, filter, updates) + + return res, err +} + +func (m *defaultOrderModel) FindOneBusinessID(ctx context.Context, id string) (*Order, error) { + var data Order + + err := m.conn.FindOne(ctx, &data, bson.M{"business_id": id}) + switch { + case err == nil: + return &data, nil + case errors.Is(err, mon.ErrNotFound): + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *customOrderModel) ListOrder(ctx context.Context, req GetOrderListReq) ([]Order, int64, error) { + // 定義查詢過濾器 + filter := bson.M{} + + // 添加查詢條件 + if req.ReferenceID != "" { + filter["reference_id"] = req.ReferenceID + } + if req.ReferenceUID != "" { + filter["reference_uid"] = req.ReferenceUID + } + if req.BusinessID != "" { + filter["business_id"] = req.BusinessID + } + if req.UID != "" { + filter["order_uid"] = req.UID + } + if req.OrderType != 0 { + filter["order_type"] = req.OrderType + } + if len(req.DirectionType) > 0 { + filter["direction_type"] = bson.M{"$in": req.DirectionType} + } + if len(req.OrderStatus) > 0 { + filter["order_status"] = bson.M{"$in": req.OrderStatus} + } + // 處理時間範圍 + if req.StartCreateTime != 0 { + filter["create_time"] = bson.M{"$gte": req.StartCreateTime} + } + if req.EndCreateTime != 0 { + filter["create_time"] = bson.M{"$lte": req.EndCreateTime} + } + if req.StartUpdateTime != 0 { + filter["update_time"] = bson.M{"$gte": req.StartUpdateTime} + } + if req.EndUpdateTime != 0 { + filter["update_time"] = bson.M{"$lte": req.EndUpdateTime} + } + if req.StartOrderArrivalTime != 0 { + filter["order_arrival_time"] = bson.M{"$gte": req.StartOrderArrivalTime} + } + if req.EndOrderArrivalTime != 0 { + filter["order_arrival_time"] = bson.M{"$lte": req.EndOrderArrivalTime} + } + if req.StartOrderPaymentTime != 0 { + filter["order_payment_time"] = bson.M{"$gte": req.StartOrderPaymentTime} + } + if req.EndOrderPaymentTime != 0 { + filter["order_payment_time"] = bson.M{"$lte": req.EndOrderPaymentTime} + } + if req.CryptoType != "" { + filter["crypto_type"] = req.CryptoType + } + if req.TxHash != "" { + filter["tx_hash"] = req.TxHash + } + + // 計算符合條件的總數量 + totalCount, err := m.conn.CountDocuments(ctx, filter) + if err != nil { + return nil, 0, err + } + + // 設定查詢選項,包含分頁 + findOptions := options.Find() + findOptions.SetSkip((req.PageIndex - 1) * req.PageSize) + findOptions.SetLimit(req.PageSize) + + // 執行查詢 + var orders []Order + err = m.conn.Find(ctx, &orders, filter, findOptions) + if err != nil { + return nil, 0, err + } + + return orders, totalCount, nil +} diff --git a/internal/model/mongo/order_model_gen.go b/internal/model/mongo/order_model_gen.go index 0d9f599..ecd3914 100644 --- a/internal/model/mongo/order_model_gen.go +++ b/internal/model/mongo/order_model_gen.go @@ -29,8 +29,8 @@ func newDefaultOrderModel(conn *mon.Model) *defaultOrderModel { func (m *defaultOrderModel) Insert(ctx context.Context, data *Order) error { if data.ID.IsZero() { data.ID = primitive.NewObjectID() - data.CreateAt = time.Now() - data.UpdateAt = time.Now() + data.CreateTime = time.Now().UTC().UnixNano() + data.UpdateTime = time.Now().UTC().UnixNano() } _, err := m.conn.InsertOne(ctx, data) @@ -57,7 +57,7 @@ func (m *defaultOrderModel) FindOne(ctx context.Context, id string) (*Order, err } func (m *defaultOrderModel) Update(ctx context.Context, data *Order) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() + data.UpdateTime = time.Now().UTC().UnixNano() res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err diff --git a/internal/model/mongo/order_types.go b/internal/model/mongo/order_types.go index 30198e9..fa50eb0 100644 --- a/internal/model/mongo/order_types.go +++ b/internal/model/mongo/order_types.go @@ -1,14 +1,51 @@ package model import ( - "time" - + "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/bson/primitive" ) type Order struct { - ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` - // TODO: Fill your own fields - UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"` - CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"` + ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` + UpdateTime int64 `bson:"update_time"` + CreateTime int64 `bson:"create_time"` + BusinessID string `bson:"business_id"` // 訂單業務流水號 + OrderType int8 `bson:"order_type"` // 訂單類型 + OrderStatus int8 `bson:"order_status"` // 訂單狀態 0.建立訂單 1.建單失敗 2.審核中 3.付款中 4.已付款 5.已付款待轉帳 6.申訴中 7.交易完成 8.交易失敗 9.交易取消 10.交易異常 11.交易超時 + Brand string `bson:"brand"` // 下單平台(混合也可用) + OrderUID string `bson:"order_uid"` // 下單用戶 UID + ReferenceID string `bson:"reference_id"` // 訂單來源(用) + Count decimal.Decimal `bson:"count"` // 訂單數量 + OrderFee decimal.Decimal `bson:"order_fee"` // 訂單手續費 + Amount decimal.Decimal `bson:"amount"` // 單價 + // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 + ReferenceBrand *string `bson:"reference_brand"` // 訂單來源平台 + ReferenceUID *string `bson:"reference_uid"` // 訂單來源用戶 UID + WalletStatus *int64 `bson:"wallet_status,omitempty"` // 交易金額狀態 1.未凍結 2.已凍結 3.解凍(扣錢) 4.未增加資產 5.已增加資產 6.解凍還原 7.限制(將凍結餘額轉至限制餘額) + ThreePartyStatus *int64 `bson:"three_party_status,omitempty"` // 三方請求狀態 1.已發送 2.通知處理中 3.通知已完成 + DirectionType *int64 `bson:"direction_type,omitempty"` // 交易方向 1.買入 2.賣出 + CryptoType *string `bson:"crypto_type,omitempty"` // 交易幣種 + ThirdPartyFee *decimal.Decimal `bson:"third_party_fee,omitempty"` // 第三方手續費 + CryptoToUSDTRate *decimal.Decimal `bson:"crypto_to_usdt_rate,omitempty"` // 交易幣種 對 USDT 匯率 + FiatToUSDRate *decimal.Decimal `bson:"fiat_to_usd_rate,omitempty"` // 法幣 對 USD 匯率 + FeeCryptoToUSDTRate *decimal.Decimal `bson:"fee_crypto_to_usdt_rate,omitempty"` // 手續費幣種 對 USDT 匯率 + USDTToCryptoTypeRate *decimal.Decimal `bson:"usdt_to_crypto_type_rate,omitempty"` // USDT 對 交易幣種 匯率 + PaymentFiat *string `bson:"payment_fiat,omitempty"` // 支付法幣 + PaymentUnitPrice *decimal.Decimal `bson:"payment_unit_price,omitempty"` // crypto 單價 + PaymentTemplateID *string `bson:"payment_template_id,omitempty"` // 支付方式配置 ID + OrderArrivalTime *int64 `bson:"order_arrival_time,omitempty"` // 訂單到帳時間/充值到帳時間 + OrderPaymentTime *int64 `bson:"order_payment_time,omitempty"` // 訂單付款時間/鏈上成功或失敗時間 + UnpaidTimeoutSecond *int64 `bson:"unpaid_timeout_second,omitempty"` // 支付期限秒數 + ChainType *string `bson:"chain_type,omitempty"` // 主網類型 + TxHash *string `bson:"tx_hash,omitempty"` // 交易哈希 + FromAddress *string `bson:"from_address,omitempty"` // 來源地址 + ToAddress *string `bson:"to_address,omitempty"` // 目標地址 + ChainFee *decimal.Decimal `bson:"chain_fee,omitempty"` // 鏈上交易手續費 + ChainFeeCrypto *string `bson:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別 + Memo *string `bson:"memo,omitempty"` // 鏈上備註 + OrderNote *string `bson:"order_note,omitempty"` // 訂單交易備註 +} + +func (p *Order) CollectionName() string { + return "order" } diff --git a/internal/server/orderservice/order_service_server.go b/internal/server/orderservice/order_service_server.go index 7014e9d..0438b47 100644 --- a/internal/server/orderservice/order_service_server.go +++ b/internal/server/orderservice/order_service_server.go @@ -23,7 +23,7 @@ func NewOrderServiceServer(svcCtx *svc.ServiceContext) *OrderServiceServer { } // CreateOrder 建立訂單 -func (s *OrderServiceServer) CreateOrder(ctx context.Context, in *tweeting.CreateOrderReq) (*tweeting.CreateOrderResp, error) { +func (s *OrderServiceServer) CreateOrder(ctx context.Context, in *tweeting.CreateOrderReq) (*tweeting.OKResp, error) { l := orderservicelogic.NewCreateOrderLogic(ctx, s.svcCtx) return l.CreateOrder(in) } diff --git a/internal/svc/init_mongo.go b/internal/svc/init_mongo.go new file mode 100644 index 0000000..ef417ad --- /dev/null +++ b/internal/svc/init_mongo.go @@ -0,0 +1,23 @@ +package svc + +import ( + "app-cloudep-order-server/internal/config" + model "app-cloudep-order-server/internal/model/mongo" + "fmt" +) + +func mustMongoConnectURL(c config.Config) string { + return fmt.Sprintf("%s://%s:%s", + c.Mongo.Schema, + c.Mongo.Host, + c.Mongo.Port, + ) +} + +// TODO 思考快取做在那邊 + +func MustOrderModel(c config.Config) model.OrderModel { + orderCollection := model.Order{} + + return model.NewOrderModel(mustMongoConnectURL(c), c.Mongo.Database, orderCollection.CollectionName()) +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 53b091b..1c91e31 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -1,13 +1,27 @@ package svc -import "app-cloudep-order-server/internal/config" +import ( + "app-cloudep-order-server/internal/config" + model "app-cloudep-order-server/internal/model/mongo" + + ers "code.30cm.net/digimon/library-go/errs" + "code.30cm.net/digimon/library-go/errs/code" + vi "code.30cm.net/digimon/library-go/validator" +) type ServiceContext struct { - Config config.Config + Config config.Config + Validate vi.Validate + + OrderModel model.OrderModel } func NewServiceContext(c config.Config) *ServiceContext { + ers.Scope = code.CloudEPOrder + return &ServiceContext{ - Config: c, + Config: c, + Validate: vi.MustValidator(), + OrderModel: MustOrderModel(c), } } -- 2.40.1 From 07e5ce0ecaf3b617c87105004ece78a5327fd487 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Sat, 12 Oct 2024 21:04:06 +0800 Subject: [PATCH 2/7] add list order func --- .../logic/orderservice/create_order_logic.go | 1 + .../logic/orderservice/delete_order_logic.go | 3 +- .../logic/orderservice/get_order_logic.go | 4 +- .../logic/orderservice/list_order_logic.go | 98 ++++++++++++++++++- .../order_status_timeout_logic.go | 2 +- internal/model/mongo/order_model.go | 7 +- .../orderservice/order_service_server.go | 2 +- order.go | 1 + 8 files changed, 108 insertions(+), 10 deletions(-) diff --git a/internal/logic/orderservice/create_order_logic.go b/internal/logic/orderservice/create_order_logic.go index 151fbe4..ccc8046 100644 --- a/internal/logic/orderservice/create_order_logic.go +++ b/internal/logic/orderservice/create_order_logic.go @@ -140,6 +140,7 @@ func (l *CreateOrderLogic) CreateOrder(in *tweeting.CreateOrderReq) (*tweeting.O return &tweeting.OKResp{}, nil } +//nolint:gocyclo,gocognit func buildCreateOrderReq(in *tweeting.CreateOrderReq) (*createOrderReq, error) { createOrderReq := &createOrderReq{ BusinessID: in.BusinessId, diff --git a/internal/logic/orderservice/delete_order_logic.go b/internal/logic/orderservice/delete_order_logic.go index fccce01..89c0fa0 100644 --- a/internal/logic/orderservice/delete_order_logic.go +++ b/internal/logic/orderservice/delete_order_logic.go @@ -1,9 +1,10 @@ package orderservicelogic import ( - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index f236f28..58e823f 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -1,8 +1,9 @@ package orderservicelogic import ( - ers "code.30cm.net/digimon/library-go/errs" "context" + + ers "code.30cm.net/digimon/library-go/errs" "github.com/shopspring/decimal" "app-cloudep-order-server/gen_result/pb/tweeting" @@ -88,5 +89,6 @@ func (l *GetOrderLogic) GetOrder(in *tweeting.GetOrderReq) (*tweeting.GetOrderRe func decimalToString(amount decimal.Decimal) *string { a := amount.String() + return &a } diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index dc55359..dd418a7 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -2,9 +2,10 @@ package orderservicelogic import ( model "app-cloudep-order-server/internal/model/mongo" - ers "code.30cm.net/digimon/library-go/errs" "context" - "fmt" + + ers "code.30cm.net/digimon/library-go/errs" + "github.com/shopspring/decimal" "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" @@ -67,6 +68,95 @@ func (l *ListOrderLogic) ListOrder(in *tweeting.ListOrderReq) (*tweeting.ListOrd return nil, err } - fmt.Println(orders, total) - return &tweeting.ListOrderResp{}, nil + res := make([]*tweeting.GetOrderResp, 0, len(orders)) + for _, item := range orders { + res = append(res, ConvertOrderToGetOrderResp(item)) + } + + return &tweeting.ListOrderResp{ + Data: res, + Page: &tweeting.Pager{ + Total: total, + Index: in.GetPageIndex(), + Size: in.GetPageSize(), + }, + }, nil +} + +// ConvertOrderToGetOrderResp 將 Order 結構轉換為 GetOrderResp +func ConvertOrderToGetOrderResp(order model.Order) *tweeting.GetOrderResp { + return &tweeting.GetOrderResp{ + BusinessId: order.BusinessID, + OrderType: int32(order.OrderType), + OrderStatus: int32(order.OrderStatus), + Brand: order.Brand, + OrderUid: order.OrderUID, + ReferenceId: order.ReferenceID, + Count: order.Count.String(), + OrderFee: order.OrderFee.String(), + Amount: order.Amount.String(), + ReferenceBrand: optionalString(order.ReferenceBrand), + ReferenceUid: optionalString(order.ReferenceUID), + WalletStatus: optionalInt64(order.WalletStatus), + ThreePartyStatus: optionalInt64(order.ThreePartyStatus), + DirectionType: optionalInt64(order.DirectionType), + CryptoType: optionalString(order.CryptoType), + ThirdPartyFee: optionalDecimalToString(order.ThirdPartyFee), + CryptoToUsdtRate: optionalDecimalToString(order.CryptoToUSDTRate), + FiatToUsdRate: optionalDecimalToString(order.FiatToUSDRate), + FeeCryptoToUsdtRate: optionalDecimalToString(order.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: optionalDecimalToString(order.USDTToCryptoTypeRate), + PaymentFiat: optionalString(order.PaymentFiat), + PaymentUnitPrice: optionalDecimalToString(order.PaymentUnitPrice), + PaymentTemplateId: optionalString(order.PaymentTemplateID), + OrderArrivalTime: optionalInt64(order.OrderArrivalTime), + OrderPaymentTime: optionalInt64(order.OrderPaymentTime), + UnpaidTimeoutSecond: optionalInt64(order.UnpaidTimeoutSecond), + ChainType: optionalString(order.ChainType), + TxHash: optionalString(order.TxHash), + FromAddress: optionalString(order.FromAddress), + ToAddress: optionalString(order.ToAddress), + ChainFee: optionalDecimalToString(order.ChainFee), + ChainFeeCrypto: optionalString(order.ChainFeeCrypto), + Memo: optionalString(order.Memo), + OrderNote: optionalString(order.OrderNote), + CreateTime: order.CreateTime, + UpdateTime: order.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) []*tweeting.GetOrderResp { + res := make([]*tweeting.GetOrderResp, 0, len(orders)) + for _, order := range orders { + res = append(res, ConvertOrderToGetOrderResp(order)) + } + + return res } diff --git a/internal/logic/orderservice/order_status_timeout_logic.go b/internal/logic/orderservice/order_status_timeout_logic.go index 4a3096c..ca4359c 100644 --- a/internal/logic/orderservice/order_status_timeout_logic.go +++ b/internal/logic/orderservice/order_status_timeout_logic.go @@ -27,7 +27,7 @@ func NewOrderStatusTimeoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) } // OrderStatusTimeout 訂單超時任務/cron/order-status/timeout -func (l *OrderStatusTimeoutLogic) OrderStatusTimeout(in *tweeting.OrderStatusTimeoutReq) (*tweeting.OKResp, error) { +func (l *OrderStatusTimeoutLogic) OrderStatusTimeout(_ *tweeting.OrderStatusTimeoutReq) (*tweeting.OKResp, error) { now := time.Now().UTC().UnixNano() _, err := l.svcCtx.OrderModel.UpdateTimeoutOrder(l.ctx, model.UpdateTimeoutReq{ CreateTimeBefore: now, diff --git a/internal/model/mongo/order_model.go b/internal/model/mongo/order_model.go index 47f5b25..d931bbc 100644 --- a/internal/model/mongo/order_model.go +++ b/internal/model/mongo/order_model.go @@ -4,9 +4,10 @@ import ( "app-cloudep-order-server/internal/domain" "context" "errors" - "go.mongodb.org/mongo-driver/mongo/options" "time" + "go.mongodb.org/mongo-driver/mongo/options" + "github.com/zeromicro/go-zero/core/stores/mon" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -156,7 +157,9 @@ func (m *defaultOrderModel) FindOneBusinessID(ctx context.Context, id string) (* func (m *customOrderModel) ListOrder(ctx context.Context, req GetOrderListReq) ([]Order, int64, error) { // 定義查詢過濾器 - filter := bson.M{} + filter := bson.M{ + "delete_time": bson.M{"$in": []any{0, nil}}, + } // 添加查詢條件 if req.ReferenceID != "" { diff --git a/internal/server/orderservice/order_service_server.go b/internal/server/orderservice/order_service_server.go index 0438b47..bae682a 100644 --- a/internal/server/orderservice/order_service_server.go +++ b/internal/server/orderservice/order_service_server.go @@ -7,7 +7,7 @@ import ( "context" "app-cloudep-order-server/gen_result/pb/tweeting" - "app-cloudep-order-server/internal/logic/orderservice" + orderservicelogic "app-cloudep-order-server/internal/logic/orderservice" "app-cloudep-order-server/internal/svc" ) diff --git a/order.go b/order.go index 0876820..c5aeb4f 100644 --- a/order.go +++ b/order.go @@ -34,6 +34,7 @@ func main() { }) defer s.Stop() + //nolint:forbidigo fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) s.Start() } -- 2.40.1 From 73f91be6497432316130f3c637867b5e8f8013ee Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Sat, 12 Oct 2024 21:56:59 +0800 Subject: [PATCH 3/7] gplint --- generate/protobuf/order.proto | 4 +- .../logic/orderservice/cancel_order_logic.go | 6 +- .../logic/orderservice/create_order_logic.go | 89 +++++++++--------- .../logic/orderservice/delete_order_logic.go | 6 +- .../logic/orderservice/get_order_logic.go | 80 ++++++++-------- .../logic/orderservice/list_order_logic.go | 94 +++++++++---------- .../logic/orderservice/modify_order_logic.go | 6 +- .../orderservice/modify_order_status_logic.go | 6 +- .../order_status_timeout_logic.go | 6 +- internal/mock/model/order_model.go | 90 ++++++++++++++++-- internal/mock/model/order_model_gen.go | 14 +-- .../orderservice/order_service_server.go | 20 ++-- internal/svc/init_validate.go | 55 +++++++++++ internal/svc/service_context.go | 2 +- order.go | 4 +- 15 files changed, 309 insertions(+), 173 deletions(-) create mode 100644 internal/svc/init_validate.go diff --git a/generate/protobuf/order.proto b/generate/protobuf/order.proto index c4a9792..0b04a0d 100644 --- a/generate/protobuf/order.proto +++ b/generate/protobuf/order.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package tweeting; -option go_package = "./tweeting"; +package order; +option go_package = "./order"; // ========== 基本回應 =========== message OKResp {} diff --git a/internal/logic/orderservice/cancel_order_logic.go b/internal/logic/orderservice/cancel_order_logic.go index 80b242e..d2c8e56 100644 --- a/internal/logic/orderservice/cancel_order_logic.go +++ b/internal/logic/orderservice/cancel_order_logic.go @@ -1,13 +1,13 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" "app-cloudep-order-server/internal/domain" model "app-cloudep-order-server/internal/model/mongo" "context" ers "code.30cm.net/digimon/library-go/errs" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -34,7 +34,7 @@ type CancelOrderQuery struct { } // CancelOrder 取消訂單 -func (l *CancelOrderLogic) CancelOrder(in *tweeting.CancelOrderReq) (*tweeting.OKResp, error) { +func (l *CancelOrderLogic) CancelOrder(in *order.CancelOrderReq) (*order.OKResp, error) { // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(&CancelOrderQuery{ BusinessID: in.GetBusinessId(), @@ -64,5 +64,5 @@ func (l *CancelOrderLogic) CancelOrder(in *tweeting.CancelOrderReq) (*tweeting.O return nil, e } - return &tweeting.OKResp{}, nil + return &order.OKResp{}, nil } diff --git a/internal/logic/orderservice/create_order_logic.go b/internal/logic/orderservice/create_order_logic.go index ccc8046..d415f7a 100644 --- a/internal/logic/orderservice/create_order_logic.go +++ b/internal/logic/orderservice/create_order_logic.go @@ -1,15 +1,16 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" "app-cloudep-order-server/internal/domain" model "app-cloudep-order-server/internal/model/mongo" "context" + "fmt" "time" ers "code.30cm.net/digimon/library-go/errs" "github.com/shopspring/decimal" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -30,55 +31,59 @@ func NewCreateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Creat } type createOrderReq struct { // 訂單ID - BusinessID string `json:"business_id" validate:"required"` // 訂單業務流水號 - OrderType int8 `json:"order_type" validate:"required"` // 訂單類型 - OrderStatus int8 `json:"order_status" validate:"required,oneof=0 1 2 3 4 5 6 7 8 9 10 11"` // 訂單狀態 - Brand string `json:"brand" validate:"required"` // 下單平台 - OrderUID string `json:"order_uid" validate:"required"` // 下單用戶 UID - ReferenceID string `json:"reference_id" validate:"required"` // 訂單來源 - Count decimal.Decimal `json:"count" validate:"required,gt=0"` // 訂單數量 - OrderFee decimal.Decimal `json:"order_fee" validate:"required,gte=0"` // 訂單手續費 - Amount decimal.Decimal `json:"amount" validate:"required,gte=0"` // 單價 - ReferenceBrand *string `json:"reference_brand,omitempty" validate:"omitempty"` // 訂單來源平台 - ReferenceUID *string `json:"reference_uid,omitempty" validate:"omitempty"` // 訂單來源用戶 UID - WalletStatus *int64 `json:"wallet_status,omitempty" validate:"omitempty,oneof=1 2 3 4 5 6 7"` // 交易金額狀態 - ThreePartyStatus *int64 `json:"three_party_status,omitempty" validate:"omitempty,oneof=1 2 3"` // 三方請求狀態 - DirectionType *int64 `json:"direction_type,omitempty" validate:"omitempty,oneof=1 2"` // 交易方向 - CryptoType *string `json:"crypto_type,omitempty" validate:"omitempty"` // 交易幣種 - ThirdPartyFee *decimal.Decimal `json:"third_party_fee,omitempty" validate:"omitempty,gte=0"` // 第三方手續費 - CryptoToUSDTRate *decimal.Decimal `json:"crypto_to_usdt_rate,omitempty" validate:"omitempty,gte=0"` // 交易幣種對 USDT 匯率 - FiatToUSDRate *decimal.Decimal `json:"fiat_to_usd_rate,omitempty" validate:"omitempty,gte=0"` // 法幣對 USD 匯率 - FeeCryptoToUSDTRate *decimal.Decimal `json:"fee_crypto_to_usdt_rate,omitempty" validate:"omitempty,gte=0"` // 手續費幣種對 USDT 匯率 - USDTToCryptoTypeRate *decimal.Decimal `json:"usdt_to_crypto_type_rate,omitempty" validate:"omitempty,gte=0"` // USDT 對交易幣種匯率 - PaymentFiat *string `json:"payment_fiat,omitempty" validate:"omitempty"` // 支付法幣 - PaymentUnitPrice *decimal.Decimal `json:"payment_unit_price,omitempty" validate:"omitempty,gte=0"` // crypto 單價 - PaymentTemplateID *string `json:"payment_template_id,omitempty" validate:"omitempty"` // 支付方式配置 ID - OrderArrivalTime *int64 `json:"order_arrival_time,omitempty" validate:"omitempty"` // 訂單到帳時間 - OrderPaymentTime *int64 `json:"order_payment_time,omitempty" validate:"omitempty"` // 訂單付款時間 - UnpaidTimeoutSecond *int64 `json:"unpaid_timeout_second,omitempty" validate:"omitempty,gte=0"` // 支付期限秒數 - ChainType *string `json:"chain_type,omitempty" validate:"omitempty"` // 主網類型 - TxHash *string `json:"tx_hash,omitempty" validate:"omitempty"` // 交易哈希 - FromAddress *string `json:"from_address,omitempty" validate:"omitempty"` // 來源地址 - ToAddress *string `json:"to_address,omitempty" validate:"omitempty"` // 目標地址 - ChainFee *decimal.Decimal `json:"chain_fee,omitempty" validate:"omitempty,gte=0"` // 鏈上交易手續費 - ChainFeeCrypto *string `json:"chain_fee_crypto,omitempty" validate:"omitempty"` // 鏈上手續費使用幣別 - Memo *string `json:"memo,omitempty" validate:"omitempty"` // 鏈上備註 + BusinessID string `json:"business_id" validate:"required"` // 訂單業務流水號 + OrderType int8 `json:"order_type" validate:"required"` // 訂單類型 + OrderStatus int8 `json:"order_status" validate:"oneof=0 1 2 3 4 5 6 7 8 9 10 11"` // 訂單狀態 + Brand string `json:"brand" validate:"required"` // 下單平台 + OrderUID string `json:"order_uid" validate:"required"` // 下單用戶 UID + ReferenceID string `json:"reference_id" validate:"required"` // 訂單來源 + Count decimal.Decimal `json:"count" validate:"required,decimalGt=0"` // 訂單數量 + OrderFee decimal.Decimal `json:"order_fee" validate:"required,decimalGte=0"` // 訂單手續費 + Amount decimal.Decimal `json:"amount" validate:"required,decimalGte=0"` // 單價 + ReferenceBrand *string `json:"reference_brand,omitempty" validate:"omitempty"` // 訂單來源平台 + ReferenceUID *string `json:"reference_uid,omitempty" validate:"omitempty"` // 訂單來源用戶 UID + WalletStatus *int64 `json:"wallet_status,omitempty" validate:"omitempty,oneof=1 2 3 4 5 6 7"` // 交易金額狀態 + ThreePartyStatus *int64 `json:"three_party_status,omitempty" validate:"omitempty,oneof=1 2 3"` // 三方請求狀態 + DirectionType *int64 `json:"direction_type,omitempty" validate:"omitempty,oneof=1 2"` // 交易方向 + CryptoType *string `json:"crypto_type,omitempty" validate:"omitempty"` // 交易幣種 + ThirdPartyFee *decimal.Decimal `json:"third_party_fee,omitempty" validate:"omitempty,decimalGte=0"` // 第三方手續費 + CryptoToUSDTRate *decimal.Decimal `json:"crypto_to_usdt_rate,omitempty" validate:"omitempty,decimalGte=0"` // 交易幣種對 USDT 匯率 + FiatToUSDRate *decimal.Decimal `json:"fiat_to_usd_rate,omitempty" validate:"omitempty,decimalGte=0"` // 法幣對 USD 匯率 + FeeCryptoToUSDTRate *decimal.Decimal `json:"fee_crypto_to_usdt_rate,omitempty" validate:"omitempty,decimalGte=0"` // 手續費幣種對 USDT 匯率 + USDTToCryptoTypeRate *decimal.Decimal `json:"usdt_to_crypto_type_rate,omitempty" validate:"omitempty,decimalGte=0"` // USDT 對交易幣種匯率 + PaymentFiat *string `json:"payment_fiat,omitempty" validate:"omitempty"` // 支付法幣 + PaymentUnitPrice *decimal.Decimal `json:"payment_unit_price,omitempty" validate:"omitempty,decimalGte=0"` // crypto 單價 + PaymentTemplateID *string `json:"payment_template_id,omitempty" validate:"omitempty"` // 支付方式配置 ID + OrderArrivalTime *int64 `json:"order_arrival_time,omitempty" validate:"omitempty"` // 訂單到帳時間 + OrderPaymentTime *int64 `json:"order_payment_time,omitempty" validate:"omitempty"` // 訂單付款時間 + UnpaidTimeoutSecond *int64 `json:"unpaid_timeout_second,omitempty" validate:"omitempty,decimalGte=0"` // 支付期限秒數 + ChainType *string `json:"chain_type,omitempty" validate:"omitempty"` // 主網類型 + TxHash *string `json:"tx_hash,omitempty" validate:"omitempty"` // 交易哈希 + FromAddress *string `json:"from_address,omitempty" validate:"omitempty"` // 來源地址 + ToAddress *string `json:"to_address,omitempty" validate:"omitempty"` // 目標地址 + ChainFee *decimal.Decimal `json:"chain_fee,omitempty" validate:"omitempty,decimalGte=0"` // 鏈上交易手續費 + ChainFeeCrypto *string `json:"chain_fee_crypto,omitempty" validate:"omitempty"` // 鏈上手續費使用幣別 + Memo *string `json:"memo,omitempty" validate:"omitempty"` // 鏈上備註 OrderNote *string `json:"order_note,omitempty" validate:"omitempty"` } // CreateOrder 建立訂單 -func (l *CreateOrderLogic) CreateOrder(in *tweeting.CreateOrderReq) (*tweeting.OKResp, error) { +func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp, error) { req, err := buildCreateOrderReq(in) if err != nil { // 錯誤代碼 06-011-00 - return nil, ers.InvalidFormat(err.Error()) - } - // 驗證資料 - if err := l.svcCtx.Validate.ValidateAll(&req); err != nil { - // 錯誤代碼 06-011-00 + fmt.Println("1", err) return nil, ers.InvalidFormat(err.Error()) } + fmt.Println(req) + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(req); err != nil { + // 錯誤代碼 06-011-00 + fmt.Println("2", err) + return nil, ers.InvalidFormat(err.Error()) + } + fmt.Println(req) now := time.Now().UTC().UnixNano() // 插入資料庫 o := &model.Order{ @@ -137,11 +142,11 @@ func (l *CreateOrderLogic) CreateOrder(in *tweeting.CreateOrderReq) (*tweeting.O return nil, e } - return &tweeting.OKResp{}, nil + return &order.OKResp{}, nil } //nolint:gocyclo,gocognit -func buildCreateOrderReq(in *tweeting.CreateOrderReq) (*createOrderReq, error) { +func buildCreateOrderReq(in *order.CreateOrderReq) (*createOrderReq, error) { createOrderReq := &createOrderReq{ BusinessID: in.BusinessId, OrderType: int8(in.OrderType), diff --git a/internal/logic/orderservice/delete_order_logic.go b/internal/logic/orderservice/delete_order_logic.go index 89c0fa0..a678a02 100644 --- a/internal/logic/orderservice/delete_order_logic.go +++ b/internal/logic/orderservice/delete_order_logic.go @@ -1,11 +1,11 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" "context" ers "code.30cm.net/digimon/library-go/errs" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -31,7 +31,7 @@ type DeleteOrderQuery struct { } // DeleteOrder 刪除訂單(軟刪除) -func (l *DeleteOrderLogic) DeleteOrder(in *tweeting.DeleteOrderReq) (*tweeting.OKResp, error) { +func (l *DeleteOrderLogic) DeleteOrder(in *order.DeleteOrderReq) (*order.OKResp, error) { // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(&DeleteOrderQuery{ BusinessID: in.GetBusinessId(), @@ -45,5 +45,5 @@ func (l *DeleteOrderLogic) DeleteOrder(in *tweeting.DeleteOrderReq) (*tweeting.O return nil, err } - return &tweeting.OKResp{}, nil + return &order.OKResp{}, nil } diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index 58e823f..779acb2 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -1,12 +1,12 @@ package orderservicelogic import ( + "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/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -32,7 +32,7 @@ type GetOrderQuery struct { } // GetOrder 取得訂單詳情 -func (l *GetOrderLogic) GetOrder(in *tweeting.GetOrderReq) (*tweeting.GetOrderResp, error) { +func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, error) { // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(&GetOrderQuery{ BusinessID: in.GetBusinessId(), @@ -41,49 +41,49 @@ func (l *GetOrderLogic) GetOrder(in *tweeting.GetOrderReq) (*tweeting.GetOrderRe return nil, ers.InvalidFormat(err.Error()) } - order, err := l.svcCtx.OrderModel.FindOneBusinessID(l.ctx, in.GetBusinessId()) + o, err := l.svcCtx.OrderModel.FindOneBusinessID(l.ctx, in.GetBusinessId()) if err != nil { return nil, err } - return &tweeting.GetOrderResp{ - UpdateTime: order.UpdateTime, - CreateTime: order.CreateTime, - BusinessId: order.BusinessID, - OrderType: int32(order.OrderType), - OrderStatus: int32(order.OrderStatus), - Brand: order.Brand, - OrderUid: order.OrderUID, - ReferenceId: order.ReferenceID, - Count: order.Count.String(), - OrderFee: order.OrderFee.String(), - Amount: order.Amount.String(), + return &order.GetOrderResp{ + UpdateTime: o.UpdateTime, + CreateTime: o.CreateTime, + BusinessId: o.BusinessID, + OrderType: int32(o.OrderType), + OrderStatus: int32(o.OrderStatus), + Brand: o.Brand, + OrderUid: o.OrderUID, + ReferenceId: o.ReferenceID, + Count: o.Count.String(), + OrderFee: o.OrderFee.String(), + Amount: o.Amount.String(), // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 - ReferenceBrand: order.ReferenceBrand, - ReferenceUid: order.ReferenceUID, - WalletStatus: order.WalletStatus, - ThreePartyStatus: order.ThreePartyStatus, - DirectionType: order.DirectionType, - CryptoType: order.CryptoType, - ThirdPartyFee: decimalToString(*order.ThirdPartyFee), - CryptoToUsdtRate: decimalToString(*order.CryptoToUSDTRate), - FiatToUsdRate: decimalToString(*order.FiatToUSDRate), - FeeCryptoToUsdtRate: decimalToString(*order.FeeCryptoToUSDTRate), - UsdtToCryptoTypeRate: decimalToString(*order.USDTToCryptoTypeRate), - PaymentFiat: order.PaymentFiat, - PaymentUnitPrice: decimalToString(*order.PaymentUnitPrice), - PaymentTemplateId: order.PaymentTemplateID, - OrderArrivalTime: order.OrderArrivalTime, - OrderPaymentTime: order.OrderPaymentTime, - UnpaidTimeoutSecond: order.UnpaidTimeoutSecond, - ChainType: order.ChainType, - TxHash: order.TxHash, - FromAddress: order.FromAddress, - ToAddress: order.ToAddress, - ChainFee: decimalToString(*order.ChainFee), - ChainFeeCrypto: order.ChainFeeCrypto, - Memo: order.Memo, - OrderNote: order.OrderNote, + ReferenceBrand: o.ReferenceBrand, + ReferenceUid: o.ReferenceUID, + WalletStatus: o.WalletStatus, + ThreePartyStatus: o.ThreePartyStatus, + DirectionType: o.DirectionType, + CryptoType: o.CryptoType, + ThirdPartyFee: decimalToString(*o.ThirdPartyFee), + CryptoToUsdtRate: decimalToString(*o.CryptoToUSDTRate), + FiatToUsdRate: decimalToString(*o.FiatToUSDRate), + FeeCryptoToUsdtRate: decimalToString(*o.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: decimalToString(*o.USDTToCryptoTypeRate), + PaymentFiat: o.PaymentFiat, + PaymentUnitPrice: decimalToString(*o.PaymentUnitPrice), + PaymentTemplateId: o.PaymentTemplateID, + OrderArrivalTime: o.OrderArrivalTime, + OrderPaymentTime: o.OrderPaymentTime, + UnpaidTimeoutSecond: o.UnpaidTimeoutSecond, + ChainType: o.ChainType, + TxHash: o.TxHash, + FromAddress: o.FromAddress, + ToAddress: o.ToAddress, + ChainFee: decimalToString(*o.ChainFee), + ChainFeeCrypto: o.ChainFeeCrypto, + Memo: o.Memo, + OrderNote: o.OrderNote, }, nil } diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index dd418a7..ebe68e7 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -1,13 +1,13 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" model "app-cloudep-order-server/internal/model/mongo" "context" ers "code.30cm.net/digimon/library-go/errs" "github.com/shopspring/decimal" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -53,7 +53,7 @@ type GetOrderListReq struct { } // ListOrder 取得訂單列表 -func (l *ListOrderLogic) ListOrder(in *tweeting.ListOrderReq) (*tweeting.ListOrderResp, error) { +func (l *ListOrderLogic) ListOrder(in *order.ListOrderReq) (*order.ListOrderResp, error) { // 驗證資料,目前只有 Page 必帶,其他要驗證在驗證 if err := l.svcCtx.Validate.ValidateAll(&GetOrderListReq{ PageIndex: in.GetPageIndex(), @@ -68,14 +68,14 @@ func (l *ListOrderLogic) ListOrder(in *tweeting.ListOrderReq) (*tweeting.ListOrd return nil, err } - res := make([]*tweeting.GetOrderResp, 0, len(orders)) + res := make([]*order.GetOrderResp, 0, len(orders)) for _, item := range orders { res = append(res, ConvertOrderToGetOrderResp(item)) } - return &tweeting.ListOrderResp{ + return &order.ListOrderResp{ Data: res, - Page: &tweeting.Pager{ + Page: &order.Pager{ Total: total, Index: in.GetPageIndex(), Size: in.GetPageSize(), @@ -84,44 +84,44 @@ func (l *ListOrderLogic) ListOrder(in *tweeting.ListOrderReq) (*tweeting.ListOrd } // ConvertOrderToGetOrderResp 將 Order 結構轉換為 GetOrderResp -func ConvertOrderToGetOrderResp(order model.Order) *tweeting.GetOrderResp { - return &tweeting.GetOrderResp{ - BusinessId: order.BusinessID, - OrderType: int32(order.OrderType), - OrderStatus: int32(order.OrderStatus), - Brand: order.Brand, - OrderUid: order.OrderUID, - ReferenceId: order.ReferenceID, - Count: order.Count.String(), - OrderFee: order.OrderFee.String(), - Amount: order.Amount.String(), - ReferenceBrand: optionalString(order.ReferenceBrand), - ReferenceUid: optionalString(order.ReferenceUID), - WalletStatus: optionalInt64(order.WalletStatus), - ThreePartyStatus: optionalInt64(order.ThreePartyStatus), - DirectionType: optionalInt64(order.DirectionType), - CryptoType: optionalString(order.CryptoType), - ThirdPartyFee: optionalDecimalToString(order.ThirdPartyFee), - CryptoToUsdtRate: optionalDecimalToString(order.CryptoToUSDTRate), - FiatToUsdRate: optionalDecimalToString(order.FiatToUSDRate), - FeeCryptoToUsdtRate: optionalDecimalToString(order.FeeCryptoToUSDTRate), - UsdtToCryptoTypeRate: optionalDecimalToString(order.USDTToCryptoTypeRate), - PaymentFiat: optionalString(order.PaymentFiat), - PaymentUnitPrice: optionalDecimalToString(order.PaymentUnitPrice), - PaymentTemplateId: optionalString(order.PaymentTemplateID), - OrderArrivalTime: optionalInt64(order.OrderArrivalTime), - OrderPaymentTime: optionalInt64(order.OrderPaymentTime), - UnpaidTimeoutSecond: optionalInt64(order.UnpaidTimeoutSecond), - ChainType: optionalString(order.ChainType), - TxHash: optionalString(order.TxHash), - FromAddress: optionalString(order.FromAddress), - ToAddress: optionalString(order.ToAddress), - ChainFee: optionalDecimalToString(order.ChainFee), - ChainFeeCrypto: optionalString(order.ChainFeeCrypto), - Memo: optionalString(order.Memo), - OrderNote: optionalString(order.OrderNote), - CreateTime: order.CreateTime, - UpdateTime: order.UpdateTime, +func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp { + return &order.GetOrderResp{ + BusinessId: o.BusinessID, + OrderType: int32(o.OrderType), + OrderStatus: int32(o.OrderStatus), + Brand: o.Brand, + OrderUid: o.OrderUID, + ReferenceId: o.ReferenceID, + Count: o.Count.String(), + OrderFee: o.OrderFee.String(), + Amount: o.Amount.String(), + ReferenceBrand: optionalString(o.ReferenceBrand), + ReferenceUid: optionalString(o.ReferenceUID), + WalletStatus: optionalInt64(o.WalletStatus), + ThreePartyStatus: optionalInt64(o.ThreePartyStatus), + DirectionType: optionalInt64(o.DirectionType), + CryptoType: optionalString(o.CryptoType), + ThirdPartyFee: optionalDecimalToString(o.ThirdPartyFee), + CryptoToUsdtRate: optionalDecimalToString(o.CryptoToUSDTRate), + FiatToUsdRate: optionalDecimalToString(o.FiatToUSDRate), + FeeCryptoToUsdtRate: optionalDecimalToString(o.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: optionalDecimalToString(o.USDTToCryptoTypeRate), + PaymentFiat: optionalString(o.PaymentFiat), + PaymentUnitPrice: optionalDecimalToString(o.PaymentUnitPrice), + PaymentTemplateId: optionalString(o.PaymentTemplateID), + OrderArrivalTime: optionalInt64(o.OrderArrivalTime), + OrderPaymentTime: optionalInt64(o.OrderPaymentTime), + UnpaidTimeoutSecond: optionalInt64(o.UnpaidTimeoutSecond), + ChainType: optionalString(o.ChainType), + TxHash: optionalString(o.TxHash), + FromAddress: optionalString(o.FromAddress), + ToAddress: optionalString(o.ToAddress), + ChainFee: optionalDecimalToString(o.ChainFee), + ChainFeeCrypto: optionalString(o.ChainFeeCrypto), + Memo: optionalString(o.Memo), + OrderNote: optionalString(o.OrderNote), + CreateTime: o.CreateTime, + UpdateTime: o.UpdateTime, } } @@ -152,10 +152,10 @@ func optionalDecimalToString(d *decimal.Decimal) *string { return nil } -func ConvertOrdersToGetOrderResp(orders []model.Order) []*tweeting.GetOrderResp { - res := make([]*tweeting.GetOrderResp, 0, len(orders)) - for _, order := range orders { - res = append(res, ConvertOrderToGetOrderResp(order)) +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 diff --git a/internal/logic/orderservice/modify_order_logic.go b/internal/logic/orderservice/modify_order_logic.go index a838866..1c2e399 100644 --- a/internal/logic/orderservice/modify_order_logic.go +++ b/internal/logic/orderservice/modify_order_logic.go @@ -1,9 +1,9 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" "context" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -24,8 +24,8 @@ func NewModifyOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Modif } // ModifyOrder 修改訂單 -func (l *ModifyOrderLogic) ModifyOrder(in *tweeting.ModifyOrderReq) (*tweeting.OKResp, error) { +func (l *ModifyOrderLogic) ModifyOrder(in *order.ModifyOrderReq) (*order.OKResp, error) { // todo: add your logic here and delete this line - return &tweeting.OKResp{}, nil + return &order.OKResp{}, nil } diff --git a/internal/logic/orderservice/modify_order_status_logic.go b/internal/logic/orderservice/modify_order_status_logic.go index 02a204c..9167370 100644 --- a/internal/logic/orderservice/modify_order_status_logic.go +++ b/internal/logic/orderservice/modify_order_status_logic.go @@ -1,13 +1,13 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" "app-cloudep-order-server/internal/domain" model "app-cloudep-order-server/internal/model/mongo" "context" ers "code.30cm.net/digimon/library-go/errs" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -38,7 +38,7 @@ type ModifyOrderQuery struct { } // ModifyOrderStatus 修改訂單狀態 -func (l *ModifyOrderStatusLogic) ModifyOrderStatus(in *tweeting.ModifyOrderStatusReq) (*tweeting.OKResp, error) { +func (l *ModifyOrderStatusLogic) ModifyOrderStatus(in *order.ModifyOrderStatusReq) (*order.OKResp, error) { // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(&ModifyOrderQuery{ BusinessID: in.GetBusinessId(), @@ -68,5 +68,5 @@ func (l *ModifyOrderStatusLogic) ModifyOrderStatus(in *tweeting.ModifyOrderStatu return nil, e } - return &tweeting.OKResp{}, nil + return &order.OKResp{}, nil } diff --git a/internal/logic/orderservice/order_status_timeout_logic.go b/internal/logic/orderservice/order_status_timeout_logic.go index ca4359c..62d7fee 100644 --- a/internal/logic/orderservice/order_status_timeout_logic.go +++ b/internal/logic/orderservice/order_status_timeout_logic.go @@ -1,12 +1,12 @@ package orderservicelogic import ( + "app-cloudep-order-server/gen_result/pb/order" "app-cloudep-order-server/internal/domain" model "app-cloudep-order-server/internal/model/mongo" "context" "time" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" @@ -27,7 +27,7 @@ func NewOrderStatusTimeoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) } // OrderStatusTimeout 訂單超時任務/cron/order-status/timeout -func (l *OrderStatusTimeoutLogic) OrderStatusTimeout(_ *tweeting.OrderStatusTimeoutReq) (*tweeting.OKResp, error) { +func (l *OrderStatusTimeoutLogic) OrderStatusTimeout(_ *order.OrderStatusTimeoutReq) (*order.OKResp, error) { now := time.Now().UTC().UnixNano() _, err := l.svcCtx.OrderModel.UpdateTimeoutOrder(l.ctx, model.UpdateTimeoutReq{ CreateTimeBefore: now, @@ -47,5 +47,5 @@ func (l *OrderStatusTimeoutLogic) OrderStatusTimeout(_ *tweeting.OrderStatusTime return nil, e } - return &tweeting.OKResp{}, nil + return &order.OKResp{}, nil } diff --git a/internal/mock/model/order_model.go b/internal/mock/model/order_model.go index fb24d12..914b226 100644 --- a/internal/mock/model/order_model.go +++ b/internal/mock/model/order_model.go @@ -10,11 +10,11 @@ package mock import ( - mongo "app-cloudep-order-server/internal/model/mongo" + model "app-cloudep-order-server/internal/model/mongo" context "context" reflect "reflect" - mongo0 "go.mongodb.org/mongo-driver/mongo" + mongo "go.mongodb.org/mongo-driver/mongo" gomock "go.uber.org/mock/gomock" ) @@ -56,11 +56,26 @@ func (mr *MockOrderModelMockRecorder) Delete(ctx, id any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockOrderModel)(nil).Delete), ctx, id) } +// DeleteByBusinessID mocks base method. +func (m *MockOrderModel) DeleteByBusinessID(ctx context.Context, id string) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByBusinessID", ctx, id) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteByBusinessID indicates an expected call of DeleteByBusinessID. +func (mr *MockOrderModelMockRecorder) DeleteByBusinessID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByBusinessID", reflect.TypeOf((*MockOrderModel)(nil).DeleteByBusinessID), ctx, id) +} + // FindOne mocks base method. -func (m *MockOrderModel) FindOne(ctx context.Context, id string) (*mongo.Order, error) { +func (m *MockOrderModel) FindOne(ctx context.Context, id string) (*model.Order, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*mongo.Order) + ret0, _ := ret[0].(*model.Order) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -71,8 +86,23 @@ func (mr *MockOrderModelMockRecorder) FindOne(ctx, id any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockOrderModel)(nil).FindOne), ctx, id) } +// FindOneBusinessID mocks base method. +func (m *MockOrderModel) FindOneBusinessID(ctx context.Context, id string) (*model.Order, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOneBusinessID", ctx, id) + ret0, _ := ret[0].(*model.Order) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOneBusinessID indicates an expected call of FindOneBusinessID. +func (mr *MockOrderModelMockRecorder) FindOneBusinessID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneBusinessID", reflect.TypeOf((*MockOrderModel)(nil).FindOneBusinessID), ctx, id) +} + // Insert mocks base method. -func (m *MockOrderModel) Insert(ctx context.Context, data *mongo.Order) error { +func (m *MockOrderModel) Insert(ctx context.Context, data *model.Order) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Insert", ctx, data) ret0, _ := ret[0].(error) @@ -85,11 +115,27 @@ func (mr *MockOrderModelMockRecorder) Insert(ctx, data any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockOrderModel)(nil).Insert), ctx, data) } +// ListOrder mocks base method. +func (m *MockOrderModel) ListOrder(ctx context.Context, req model.GetOrderListReq) ([]model.Order, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListOrder", ctx, req) + ret0, _ := ret[0].([]model.Order) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListOrder indicates an expected call of ListOrder. +func (mr *MockOrderModelMockRecorder) ListOrder(ctx, req any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOrder", reflect.TypeOf((*MockOrderModel)(nil).ListOrder), ctx, req) +} + // Update mocks base method. -func (m *MockOrderModel) Update(ctx context.Context, data *mongo.Order) (*mongo0.UpdateResult, error) { +func (m *MockOrderModel) Update(ctx context.Context, data *model.Order) (*mongo.UpdateResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(*mongo0.UpdateResult) + ret0, _ := ret[0].(*mongo.UpdateResult) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -99,3 +145,33 @@ func (mr *MockOrderModelMockRecorder) Update(ctx, data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockOrderModel)(nil).Update), ctx, data) } + +// UpdateStatus mocks base method. +func (m *MockOrderModel) UpdateStatus(ctx context.Context, data model.UpdateStatusReq) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateStatus", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateStatus indicates an expected call of UpdateStatus. +func (mr *MockOrderModelMockRecorder) UpdateStatus(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockOrderModel)(nil).UpdateStatus), ctx, data) +} + +// UpdateTimeoutOrder mocks base method. +func (m *MockOrderModel) UpdateTimeoutOrder(ctx context.Context, req model.UpdateTimeoutReq) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTimeoutOrder", ctx, req) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTimeoutOrder indicates an expected call of UpdateTimeoutOrder. +func (mr *MockOrderModelMockRecorder) UpdateTimeoutOrder(ctx, req any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTimeoutOrder", reflect.TypeOf((*MockOrderModel)(nil).UpdateTimeoutOrder), ctx, req) +} diff --git a/internal/mock/model/order_model_gen.go b/internal/mock/model/order_model_gen.go index f3fb4da..5052926 100644 --- a/internal/mock/model/order_model_gen.go +++ b/internal/mock/model/order_model_gen.go @@ -10,11 +10,11 @@ package mock import ( - mongo "app-cloudep-order-server/internal/model/mongo" + model "app-cloudep-order-server/internal/model/mongo" context "context" reflect "reflect" - mongo0 "go.mongodb.org/mongo-driver/mongo" + mongo "go.mongodb.org/mongo-driver/mongo" gomock "go.uber.org/mock/gomock" ) @@ -57,10 +57,10 @@ func (mr *MockorderModelMockRecorder) Delete(ctx, id any) *gomock.Call { } // FindOne mocks base method. -func (m *MockorderModel) FindOne(ctx context.Context, id string) (*mongo.Order, error) { +func (m *MockorderModel) FindOne(ctx context.Context, id string) (*model.Order, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*mongo.Order) + ret0, _ := ret[0].(*model.Order) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -72,7 +72,7 @@ func (mr *MockorderModelMockRecorder) FindOne(ctx, id any) *gomock.Call { } // Insert mocks base method. -func (m *MockorderModel) Insert(ctx context.Context, data *mongo.Order) error { +func (m *MockorderModel) Insert(ctx context.Context, data *model.Order) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Insert", ctx, data) ret0, _ := ret[0].(error) @@ -86,10 +86,10 @@ func (mr *MockorderModelMockRecorder) Insert(ctx, data any) *gomock.Call { } // Update mocks base method. -func (m *MockorderModel) Update(ctx context.Context, data *mongo.Order) (*mongo0.UpdateResult, error) { +func (m *MockorderModel) Update(ctx context.Context, data *model.Order) (*mongo.UpdateResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(*mongo0.UpdateResult) + ret0, _ := ret[0].(*mongo.UpdateResult) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/server/orderservice/order_service_server.go b/internal/server/orderservice/order_service_server.go index bae682a..f362241 100644 --- a/internal/server/orderservice/order_service_server.go +++ b/internal/server/orderservice/order_service_server.go @@ -6,14 +6,14 @@ package server import ( "context" - "app-cloudep-order-server/gen_result/pb/tweeting" + "app-cloudep-order-server/gen_result/pb/order" orderservicelogic "app-cloudep-order-server/internal/logic/orderservice" "app-cloudep-order-server/internal/svc" ) type OrderServiceServer struct { svcCtx *svc.ServiceContext - tweeting.UnimplementedOrderServiceServer + order.UnimplementedOrderServiceServer } func NewOrderServiceServer(svcCtx *svc.ServiceContext) *OrderServiceServer { @@ -23,49 +23,49 @@ func NewOrderServiceServer(svcCtx *svc.ServiceContext) *OrderServiceServer { } // CreateOrder 建立訂單 -func (s *OrderServiceServer) CreateOrder(ctx context.Context, in *tweeting.CreateOrderReq) (*tweeting.OKResp, error) { +func (s *OrderServiceServer) CreateOrder(ctx context.Context, in *order.CreateOrderReq) (*order.OKResp, error) { l := orderservicelogic.NewCreateOrderLogic(ctx, s.svcCtx) return l.CreateOrder(in) } // CancelOrder 取消訂單 -func (s *OrderServiceServer) CancelOrder(ctx context.Context, in *tweeting.CancelOrderReq) (*tweeting.OKResp, error) { +func (s *OrderServiceServer) CancelOrder(ctx context.Context, in *order.CancelOrderReq) (*order.OKResp, error) { l := orderservicelogic.NewCancelOrderLogic(ctx, s.svcCtx) return l.CancelOrder(in) } // ModifyOrder 修改訂單 -func (s *OrderServiceServer) ModifyOrder(ctx context.Context, in *tweeting.ModifyOrderReq) (*tweeting.OKResp, error) { +func (s *OrderServiceServer) ModifyOrder(ctx context.Context, in *order.ModifyOrderReq) (*order.OKResp, error) { l := orderservicelogic.NewModifyOrderLogic(ctx, s.svcCtx) return l.ModifyOrder(in) } // ModifyOrderStatus 修改訂單狀態 -func (s *OrderServiceServer) ModifyOrderStatus(ctx context.Context, in *tweeting.ModifyOrderStatusReq) (*tweeting.OKResp, error) { +func (s *OrderServiceServer) ModifyOrderStatus(ctx context.Context, in *order.ModifyOrderStatusReq) (*order.OKResp, error) { l := orderservicelogic.NewModifyOrderStatusLogic(ctx, s.svcCtx) return l.ModifyOrderStatus(in) } // DeleteOrder 刪除訂單(軟刪除) -func (s *OrderServiceServer) DeleteOrder(ctx context.Context, in *tweeting.DeleteOrderReq) (*tweeting.OKResp, error) { +func (s *OrderServiceServer) DeleteOrder(ctx context.Context, in *order.DeleteOrderReq) (*order.OKResp, error) { l := orderservicelogic.NewDeleteOrderLogic(ctx, s.svcCtx) return l.DeleteOrder(in) } // GetOrder 取得訂單詳情 -func (s *OrderServiceServer) GetOrder(ctx context.Context, in *tweeting.GetOrderReq) (*tweeting.GetOrderResp, error) { +func (s *OrderServiceServer) GetOrder(ctx context.Context, in *order.GetOrderReq) (*order.GetOrderResp, error) { l := orderservicelogic.NewGetOrderLogic(ctx, s.svcCtx) return l.GetOrder(in) } // ListOrder 取得訂單列表 -func (s *OrderServiceServer) ListOrder(ctx context.Context, in *tweeting.ListOrderReq) (*tweeting.ListOrderResp, error) { +func (s *OrderServiceServer) ListOrder(ctx context.Context, in *order.ListOrderReq) (*order.ListOrderResp, error) { l := orderservicelogic.NewListOrderLogic(ctx, s.svcCtx) return l.ListOrder(in) } // OrderStatusTimeout 訂單超時任務/cron/order-status/timeout -func (s *OrderServiceServer) OrderStatusTimeout(ctx context.Context, in *tweeting.OrderStatusTimeoutReq) (*tweeting.OKResp, error) { +func (s *OrderServiceServer) OrderStatusTimeout(ctx context.Context, in *order.OrderStatusTimeoutReq) (*order.OKResp, error) { l := orderservicelogic.NewOrderStatusTimeoutLogic(ctx, s.svcCtx) return l.OrderStatusTimeout(in) } diff --git a/internal/svc/init_validate.go b/internal/svc/init_validate.go new file mode 100644 index 0000000..f5104d5 --- /dev/null +++ b/internal/svc/init_validate.go @@ -0,0 +1,55 @@ +package svc + +import ( + vi "code.30cm.net/digimon/library-go/validator" + "github.com/go-playground/validator/v10" + "github.com/shopspring/decimal" +) + +// WithDecimalGt 是否大於等於 +func WithDecimalGt() vi.Option { + return vi.Option{ + ValidatorName: "decimalGt", + ValidatorFunc: func(fl validator.FieldLevel) bool { + if val, ok := fl.Field().Interface().(string); ok { + value, err := decimal.NewFromString(val) + if err != nil { + return false + } + + conditionValue, err := decimal.NewFromString(fl.Param()) + if err != nil { + return false + } + + return value.GreaterThan(conditionValue) + } + + return true + }, + } +} + +// WithDecimalGte 是否大於等於 +func WithDecimalGte() vi.Option { + return vi.Option{ + ValidatorName: "decimalGte", + ValidatorFunc: func(fl validator.FieldLevel) bool { + if val, ok := fl.Field().Interface().(string); ok { + value, err := decimal.NewFromString(val) + if err != nil { + return false + } + + conditionValue, err := decimal.NewFromString(fl.Param()) + if err != nil { + return false + } + + return value.GreaterThanOrEqual(conditionValue) + } + + return true + }, + } +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 1c91e31..ee1c4d1 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -21,7 +21,7 @@ func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, - Validate: vi.MustValidator(), + Validate: vi.MustValidator(WithDecimalGt(), WithDecimalGte()), OrderModel: MustOrderModel(c), } } diff --git a/order.go b/order.go index c5aeb4f..815f0cd 100644 --- a/order.go +++ b/order.go @@ -1,10 +1,10 @@ package main import ( + "app-cloudep-order-server/gen_result/pb/order" "flag" "fmt" - "app-cloudep-order-server/gen_result/pb/tweeting" "app-cloudep-order-server/internal/config" orderserviceServer "app-cloudep-order-server/internal/server/orderservice" "app-cloudep-order-server/internal/svc" @@ -26,7 +26,7 @@ func main() { ctx := svc.NewServiceContext(c) s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { - tweeting.RegisterOrderServiceServer(grpcServer, orderserviceServer.NewOrderServiceServer(ctx)) + order.RegisterOrderServiceServer(grpcServer, orderserviceServer.NewOrderServiceServer(ctx)) if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer) -- 2.40.1 From 352f11a792bc377ce933fad421583a651ea72bf1 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Tue, 22 Oct 2024 17:12:05 +0800 Subject: [PATCH 4/7] add test --- .../mongodb/20241006000001_order.up.mongo | 18 +- generate/protobuf/order.proto | 2 - go.mod | 4 +- .../orderservice/cancel_order_logic_test.go | 109 +++++ .../orderservice/create_order_logic_test.go | 406 ++++++++++++++++ .../orderservice/delete_order_logic_test.go | 96 ++++ .../logic/orderservice/get_order_logic.go | 74 +-- .../orderservice/get_order_logic_test.go | 127 +++++ .../logic/orderservice/list_order_logic.go | 5 + .../orderservice/list_order_logic_test.go | 458 ++++++++++++++++++ .../logic/orderservice/modify_order_logic.go | 31 -- internal/mock/lib/validate.go | 73 +++ internal/model/mongo/order_model.go | 4 +- .../orderservice/order_service_server.go | 5 - 14 files changed, 1333 insertions(+), 79 deletions(-) create mode 100644 internal/logic/orderservice/cancel_order_logic_test.go create mode 100644 internal/logic/orderservice/create_order_logic_test.go create mode 100644 internal/logic/orderservice/delete_order_logic_test.go create mode 100644 internal/logic/orderservice/get_order_logic_test.go create mode 100644 internal/logic/orderservice/list_order_logic_test.go delete mode 100644 internal/logic/orderservice/modify_order_logic.go create mode 100644 internal/mock/lib/validate.go diff --git a/generate/database/mongodb/20241006000001_order.up.mongo b/generate/database/mongodb/20241006000001_order.up.mongo index fb82665..e8510aa 100644 --- a/generate/database/mongodb/20241006000001_order.up.mongo +++ b/generate/database/mongodb/20241006000001_order.up.mongo @@ -1,19 +1,13 @@ use digimon_order; +db.order.createIndex({ "business_id": 1, "order_status": 1, "create_time": -1 }) +db.orders.createIndex({ "reference_id": 1, "reference_uid": 1, "business_id": 1, "uid": 1 }) +db.orders.createIndex({ "order_type": 1, "direction_type": 1, "order_status": 1 }) +db.orders.createIndex({ "create_time": -1, "update_time": -1, "order_arrival_time": -1, "order_payment_time": -1 }) + db.order.createIndex({ "uid": 1, "order_type": 1, "order_status": 1, - "create_time": 1, -}) - -db.order.createIndex({ - "business_id": 1, -}) - -// 查詢時全部要 1&2 有幾個類用幾個累才會中索引 -db.order.createIndex({ - "uid": 1, - "direction_type": 1, - "crypto_type": 1 + "create_time": -1, }) \ No newline at end of file diff --git a/generate/protobuf/order.proto b/generate/protobuf/order.proto index 0b04a0d..f70a722 100644 --- a/generate/protobuf/order.proto +++ b/generate/protobuf/order.proto @@ -152,8 +152,6 @@ service OrderService { rpc CreateOrder(CreateOrderReq) returns (OKResp); // CancelOrder 取消訂單 rpc CancelOrder(CancelOrderReq) returns (OKResp); - // ModifyOrder 修改訂單 - rpc ModifyOrder(ModifyOrderReq) returns (OKResp); // ModifyOrderStatus 修改訂單狀態 rpc ModifyOrderStatus(ModifyOrderStatusReq) returns (OKResp); // DeleteOrder 刪除訂單(軟刪除) diff --git a/go.mod b/go.mod index 72694e2..298332d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.22.3 require ( code.30cm.net/digimon/library-go/errs v1.2.5 code.30cm.net/digimon/library-go/validator v1.0.0 + github.com/go-playground/validator/v10 v10.22.0 github.com/shopspring/decimal v1.4.0 + github.com/stretchr/testify v1.9.0 github.com/zeromicro/go-zero v1.7.2 go.mongodb.org/mongo-driver v1.16.1 go.uber.org/mock v0.4.0 @@ -31,7 +33,6 @@ require ( github.com/go-openapi/swag v0.22.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -54,6 +55,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.20.2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect diff --git a/internal/logic/orderservice/cancel_order_logic_test.go b/internal/logic/orderservice/cancel_order_logic_test.go new file mode 100644 index 0000000..03f53c0 --- /dev/null +++ b/internal/logic/orderservice/cancel_order_logic_test.go @@ -0,0 +1,109 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mocksvc "app-cloudep-order-server/internal/mock/lib" + model "app-cloudep-order-server/internal/model/mongo" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/mock/gomock" + "testing" + + mockmodel "app-cloudep-order-server/internal/mock/model" +) + +func TestCancelOrder(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + mockValidate := mocksvc.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + Validate: mockValidate, + } + + // 測試數據 + cancelReq := &order.CancelOrderReq{ + BusinessId: "12345", + Status: 1, + } + + // 測試數據集 + tests := []struct { + name string + input *order.CancelOrderReq + prepare func() + expectErr bool + }{ + { + name: "成功取消訂單", + input: cancelReq, + prepare: func() { + // 模擬驗證通過 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬更新狀態成功 + mockOrderModel.EXPECT().UpdateStatus(gomock.Any(), model.UpdateStatusReq{ + BusinessID: "12345", + Status: 1, + }).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Times(1) + }, + expectErr: false, + }, + { + name: "訂單更新狀態失敗", + input: cancelReq, + prepare: func() { + // 模擬驗證通過 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬更新狀態失敗 + mockOrderModel.EXPECT().UpdateStatus(gomock.Any(), model.UpdateStatusReq{ + BusinessID: "12345", + Status: 1, + }).Return(nil, errors.New("update failed")).Times(1) + }, + expectErr: true, + }, + { + name: "驗證失敗", + input: cancelReq, + prepare: func() { + // 模擬驗證失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid input")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 CancelOrderLogic + logic := CancelOrderLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 CancelOrder + resp, err := logic.CancelOrder(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + } + }) + } +} diff --git a/internal/logic/orderservice/create_order_logic_test.go b/internal/logic/orderservice/create_order_logic_test.go new file mode 100644 index 0000000..9f681fa --- /dev/null +++ b/internal/logic/orderservice/create_order_logic_test.go @@ -0,0 +1,406 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mocksvc "app-cloudep-order-server/internal/mock/lib" + mockmodel "app-cloudep-order-server/internal/mock/model" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "testing" + "time" +) + +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) { + tests := []struct { + name string + input *order.CreateOrderReq + expectErr bool + expected *createOrderReq + }{ + { + name: "所有必填字段存在,可選字段為空", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + expectErr: false, + expected: &createOrderReq{ + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + }, + }, + { + name: "包含可選字段", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + ReferenceBrand: ptr("OtherBrand"), + WalletStatus: ptrInt64(1), + ThreePartyStatus: ptrInt64(2), + }, + expectErr: false, + expected: &createOrderReq{ + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + ReferenceBrand: ptr("OtherBrand"), + WalletStatus: ptrInt64(1), + ThreePartyStatus: ptrInt64(2), + }, + }, + { + name: "無效的十進制字段", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "invalid-decimal", // 無效的數值 + }, + expectErr: true, + }, + { + name: "空字符串的可選欄位應返回 nil", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + ThirdPartyFee: ptr(""), // 空字符串 + }, + expectErr: false, + expected: &createOrderReq{ + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + ThirdPartyFee: nil, // 空字符串應返回 nil + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := buildCreateOrderReq(tt.input) + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + }) + } +} + +func TestCreateOrder(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + mockValidate := mocksvc.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + Validate: mockValidate, + } + + // 測試數據集 + tests := []struct { + name string + input *order.CreateOrderReq + prepare func() + expectErr bool + }{ + { + name: "成功建立訂單", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫插入成功 + mockOrderModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + { + name: "建立訂單時驗證失敗", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + prepare: func() { + // 模擬驗證失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "插入資料庫失敗", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬插入資料庫失敗 + mockOrderModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(errors.New("insert failed")).Times(1) + }, + expectErr: true, + }, + { + name: "成功建立訂單,必填字段", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫插入成功 + mockOrderModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + { + name: "成功建立訂單,可選字段有值", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + ReferenceBrand: ptr("OtherBrand"), + WalletStatus: ptrInt64(1), + CryptoType: ptr("BTC"), + PaymentFiat: ptr("USD"), + OrderArrivalTime: ptrInt64(time.Now().UTC().Unix()), + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫插入成功 + mockOrderModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + { + name: "部分可選字段有值,其他為空", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + ReferenceBrand: ptr("OtherBrand"), + WalletStatus: ptrInt64(1), + CryptoType: nil, // 空 + PaymentFiat: nil, // 空 + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫插入成功 + mockOrderModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + { + name: "無效的十進制數字", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "invalid", // 無效的數值 + OrderFee: "0.5", + Amount: "100", + }, + prepare: func() { + // 無需調用資料庫操作,因為會在驗證或轉換時失敗 + }, + expectErr: true, + }, + { + name: "可選欄位為空字符串", + input: &order.CreateOrderReq{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + ThirdPartyFee: ptr(""), // 空字符串 + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫插入成功 + mockOrderModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 CreateOrderLogic + logic := NewCreateOrderLogic(context.TODO(), svcCtx) + + // 執行 CreateOrder + resp, err := logic.CreateOrder(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + } + }) + } +} + +// ================================ 輔助函數 ================================ +func ptr(s string) *string { + return &s +} + +func ptrInt64(i int64) *int64 { + return &i +} + +// decimalPtr 是一個輔助函數,用來將字串轉換為 *decimal.Decimal 指針 +func decimalPtr(val string) *decimal.Decimal { + d, _ := decimal.NewFromString(val) + return &d +} diff --git a/internal/logic/orderservice/delete_order_logic_test.go b/internal/logic/orderservice/delete_order_logic_test.go new file mode 100644 index 0000000..0d71124 --- /dev/null +++ b/internal/logic/orderservice/delete_order_logic_test.go @@ -0,0 +1,96 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mocksvc "app-cloudep-order-server/internal/mock/lib" + mockmodel "app-cloudep-order-server/internal/mock/model" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "testing" +) + +func TestDeleteOrder(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + mockValidate := mocksvc.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + Validate: mockValidate, + } + + // 測試數據集 + tests := []struct { + name string + input *order.DeleteOrderReq + prepare func() + expectErr bool + }{ + { + name: "成功刪除訂單", + input: &order.DeleteOrderReq{ + BusinessId: "B123", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫刪除成功 + mockOrderModel.EXPECT().DeleteByBusinessID(gomock.Any(), "B123").Return(nil, nil).Times(1) + }, + expectErr: false, + }, + { + name: "驗證失敗", + input: &order.DeleteOrderReq{ + BusinessId: "B123", + }, + prepare: func() { + // 模擬驗證失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "刪除資料庫失敗", + input: &order.DeleteOrderReq{ + BusinessId: "B123", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬資料庫刪除失敗 + mockOrderModel.EXPECT().DeleteByBusinessID(gomock.Any(), "B123").Return(nil, errors.New("delete failed")).Times(1) + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 DeleteOrderLogic + logic := NewDeleteOrderLogic(context.TODO(), svcCtx) + + // 執行 DeleteOrder + resp, err := logic.DeleteOrder(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + } + }) + } +} diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index 779acb2..2cf4838 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -3,6 +3,7 @@ package orderservicelogic import ( "app-cloudep-order-server/gen_result/pb/order" "context" + "fmt" ers "code.30cm.net/digimon/library-go/errs" "github.com/shopspring/decimal" @@ -45,6 +46,7 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er if err != nil { return nil, err } + fmt.Println(o) return &order.GetOrderResp{ UpdateTime: o.UpdateTime, @@ -58,37 +60,55 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er Count: o.Count.String(), OrderFee: o.OrderFee.String(), Amount: o.Amount.String(), - // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 - ReferenceBrand: o.ReferenceBrand, - ReferenceUid: o.ReferenceUID, - WalletStatus: o.WalletStatus, - ThreePartyStatus: o.ThreePartyStatus, - DirectionType: o.DirectionType, - CryptoType: o.CryptoType, - ThirdPartyFee: decimalToString(*o.ThirdPartyFee), - CryptoToUsdtRate: decimalToString(*o.CryptoToUSDTRate), - FiatToUsdRate: decimalToString(*o.FiatToUSDRate), - FeeCryptoToUsdtRate: decimalToString(*o.FeeCryptoToUSDTRate), - UsdtToCryptoTypeRate: decimalToString(*o.USDTToCryptoTypeRate), - PaymentFiat: o.PaymentFiat, - PaymentUnitPrice: decimalToString(*o.PaymentUnitPrice), - PaymentTemplateId: o.PaymentTemplateID, - OrderArrivalTime: o.OrderArrivalTime, - OrderPaymentTime: o.OrderPaymentTime, - UnpaidTimeoutSecond: o.UnpaidTimeoutSecond, - ChainType: o.ChainType, - TxHash: o.TxHash, - FromAddress: o.FromAddress, - ToAddress: o.ToAddress, - ChainFee: decimalToString(*o.ChainFee), - ChainFeeCrypto: o.ChainFeeCrypto, - Memo: o.Memo, - OrderNote: o.OrderNote, + // 下面的為未來擴充用的欄位 + ReferenceBrand: nilString(o.ReferenceBrand), + ReferenceUid: nilString(o.ReferenceUID), + WalletStatus: nilInt64(o.WalletStatus), + ThreePartyStatus: nilInt64(o.ThreePartyStatus), + DirectionType: nilInt64(o.DirectionType), + CryptoType: nilString(o.CryptoType), + ThirdPartyFee: decimalToString(o.ThirdPartyFee), + CryptoToUsdtRate: decimalToString(o.CryptoToUSDTRate), + FiatToUsdRate: decimalToString(o.FiatToUSDRate), + FeeCryptoToUsdtRate: decimalToString(o.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: decimalToString(o.USDTToCryptoTypeRate), + PaymentFiat: nilString(o.PaymentFiat), + PaymentUnitPrice: decimalToString(o.PaymentUnitPrice), + PaymentTemplateId: nilString(o.PaymentTemplateID), + OrderArrivalTime: nilInt64(o.OrderArrivalTime), + OrderPaymentTime: nilInt64(o.OrderPaymentTime), + UnpaidTimeoutSecond: nilInt64(o.UnpaidTimeoutSecond), + ChainType: nilString(o.ChainType), + TxHash: nilString(o.TxHash), + FromAddress: nilString(o.FromAddress), + ToAddress: nilString(o.ToAddress), + ChainFee: decimalToString(o.ChainFee), + ChainFeeCrypto: nilString(o.ChainFeeCrypto), + Memo: nilString(o.Memo), + OrderNote: nilString(o.OrderNote), }, nil } -func decimalToString(amount decimal.Decimal) *string { +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 +} diff --git a/internal/logic/orderservice/get_order_logic_test.go b/internal/logic/orderservice/get_order_logic_test.go new file mode 100644 index 0000000..eea74a0 --- /dev/null +++ b/internal/logic/orderservice/get_order_logic_test.go @@ -0,0 +1,127 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mocksvc "app-cloudep-order-server/internal/mock/lib" + mockmodel "app-cloudep-order-server/internal/mock/model" + model "app-cloudep-order-server/internal/model/mongo" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "testing" +) + +func TestGetOrder(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + mockValidate := mocksvc.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + Validate: mockValidate, + } + + // 模擬返回的訂單數據 + mockOrder := &model.Order{ + UpdateTime: 1630000000, + CreateTime: 1620000000, + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + } + + // 測試數據集 + tests := []struct { + name string + input *order.GetOrderReq + prepare func() + expectErr bool + expected *order.GetOrderResp + }{ + { + name: "成功取得訂單", + input: &order.GetOrderReq{ + BusinessId: "B123", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬查詢訂單成功 + mockOrderModel.EXPECT().FindOneBusinessID(gomock.Any(), "B123").Return(mockOrder, nil).Times(1) + }, + expectErr: false, + expected: &order.GetOrderResp{ + UpdateTime: 1630000000, + CreateTime: 1620000000, + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + }, + { + name: "驗證失敗", + input: &order.GetOrderReq{ + BusinessId: "B123", + }, + prepare: func() { + // 模擬驗證失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "查詢訂單失敗", + input: &order.GetOrderReq{ + BusinessId: "B123", + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬查詢訂單失敗 + mockOrderModel.EXPECT().FindOneBusinessID(gomock.Any(), "B123").Return(nil, errors.New("order not found")).Times(1) + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 GetOrderLogic + logic := NewGetOrderLogic(context.TODO(), svcCtx) + + // 執行 GetOrder + resp, err := logic.GetOrder(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + } + }) + } +} diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index ebe68e7..7e2826a 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -160,3 +160,8 @@ func ConvertOrdersToGetOrderResp(orders []model.Order) []*order.GetOrderResp { return res } + +// Helper functions +func ptrString(s string) *string { + return &s +} diff --git a/internal/logic/orderservice/list_order_logic_test.go b/internal/logic/orderservice/list_order_logic_test.go new file mode 100644 index 0000000..fc59707 --- /dev/null +++ b/internal/logic/orderservice/list_order_logic_test.go @@ -0,0 +1,458 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mocksvc "app-cloudep-order-server/internal/mock/lib" + mockmodel "app-cloudep-order-server/internal/mock/model" + model "app-cloudep-order-server/internal/model/mongo" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "testing" +) + +func TestConvertOrdersToGetOrderResp(t *testing.T) { + tests := []struct { + name string + input []model.Order + expected []*order.GetOrderResp + }{ + { + name: "空訂單列表", + input: []model.Order{}, + expected: []*order.GetOrderResp{}, + }, + { + name: "單筆訂單", + input: []model.Order{ + { + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + }, + }, + expected: []*order.GetOrderResp{ + { + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + }, + }, + { + name: "多筆訂單", + input: []model.Order{ + { + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + }, + { + BusinessID: "B456", + OrderType: 2, + OrderStatus: 3, + Brand: "OtherBrand", + OrderUID: "UID456", + ReferenceID: "REF456", + Count: decimal.RequireFromString("20"), + OrderFee: decimal.RequireFromString("1"), + Amount: decimal.RequireFromString("200"), + }, + }, + expected: []*order.GetOrderResp{ + { + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + { + BusinessId: "B456", + OrderType: 2, + OrderStatus: 3, + Brand: "OtherBrand", + OrderUid: "UID456", + ReferenceId: "REF456", + Count: "20", + OrderFee: "1", + Amount: "200", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 執行 ConvertOrdersToGetOrderResp 函數 + result := ConvertOrdersToGetOrderResp(tt.input) + + // 驗證結果 + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestOptionalString(t *testing.T) { + t.Run("非 nil 字串", func(t *testing.T) { + str := "hello" + result := optionalString(&str) + assert.NotNil(t, result) + assert.Equal(t, &str, result) + }) + + t.Run("nil 字串", func(t *testing.T) { + result := optionalString(nil) + assert.Nil(t, result) + }) +} + +func TestOptionalInt64(t *testing.T) { + t.Run("非 nil int64", func(t *testing.T) { + num := int64(123) + result := optionalInt64(&num) + assert.NotNil(t, result) + assert.Equal(t, &num, result) + }) + + t.Run("nil int64", func(t *testing.T) { + result := optionalInt64(nil) + assert.Nil(t, result) + }) +} + +func TestOptionalDecimalToString(t *testing.T) { + t.Run("非 nil decimal", func(t *testing.T) { + dec := decimal.NewFromInt(123) + expected := "123" + result := optionalDecimalToString(&dec) + assert.NotNil(t, result) + assert.Equal(t, &expected, result) + }) + + t.Run("nil decimal", func(t *testing.T) { + result := optionalDecimalToString(nil) + assert.Nil(t, result) + }) +} + +func TestConvertOrderToGetOrderResp(t *testing.T) { + tests := []struct { + name string + input model.Order + expected *order.GetOrderResp + }{ + { + name: "所有欄位都有值", + input: model.Order{ + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + ReferenceBrand: ptrString("OtherBrand"), + ReferenceUID: ptrString("REFUID"), + WalletStatus: ptrInt64(1), + ThreePartyStatus: ptrInt64(2), + DirectionType: ptrInt64(1), + CryptoType: ptrString("BTC"), + ThirdPartyFee: decimalPtrFromString("0.01"), + CryptoToUSDTRate: decimalPtrFromString("50000"), + FiatToUSDRate: decimalPtrFromString("1"), + FeeCryptoToUSDTRate: decimalPtrFromString("0.02"), + USDTToCryptoTypeRate: decimalPtrFromString("0.00002"), + PaymentFiat: ptrString("USD"), + PaymentUnitPrice: decimalPtrFromString("50000"), + PaymentTemplateID: ptrString("TEMPLATE123"), + OrderArrivalTime: ptrInt64(1630000000), + OrderPaymentTime: ptrInt64(1620000000), + UnpaidTimeoutSecond: ptrInt64(3600), + ChainType: ptrString("ETH"), + TxHash: ptrString("0xABC123"), + FromAddress: ptrString("0xFROM123"), + ToAddress: ptrString("0xTO123"), + ChainFee: decimalPtrFromString("0.001"), + ChainFeeCrypto: ptrString("ETH"), + Memo: ptrString("Test Memo"), + OrderNote: ptrString("Test Note"), + CreateTime: 1620000000, + UpdateTime: 1630000000, + }, + expected: &order.GetOrderResp{ + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + ReferenceBrand: ptrString("OtherBrand"), + ReferenceUid: ptrString("REFUID"), + WalletStatus: ptrInt64(1), + ThreePartyStatus: ptrInt64(2), + DirectionType: ptrInt64(1), + CryptoType: ptrString("BTC"), + ThirdPartyFee: ptrString("0.01"), + CryptoToUsdtRate: ptrString("50000"), + FiatToUsdRate: ptrString("1"), + FeeCryptoToUsdtRate: ptrString("0.02"), + UsdtToCryptoTypeRate: ptrString("0.00002"), + PaymentFiat: ptrString("USD"), + PaymentUnitPrice: ptrString("50000"), + PaymentTemplateId: ptrString("TEMPLATE123"), + OrderArrivalTime: ptrInt64(1630000000), + OrderPaymentTime: ptrInt64(1620000000), + UnpaidTimeoutSecond: ptrInt64(3600), + ChainType: ptrString("ETH"), + TxHash: ptrString("0xABC123"), + FromAddress: ptrString("0xFROM123"), + ToAddress: ptrString("0xTO123"), + ChainFee: ptrString("0.001"), + ChainFeeCrypto: ptrString("ETH"), + Memo: ptrString("Test Memo"), + OrderNote: ptrString("Test Note"), + CreateTime: 1620000000, + UpdateTime: 1630000000, + }, + }, + { + name: "部分欄位為 nil", + input: model.Order{ + BusinessID: "B456", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID456", + ReferenceID: "REF456", + Count: decimal.RequireFromString("10"), + OrderFee: decimal.RequireFromString("1"), + Amount: decimal.RequireFromString("100"), + ReferenceBrand: nil, + ReferenceUID: nil, + WalletStatus: nil, + ThreePartyStatus: nil, + DirectionType: nil, + CryptoType: nil, + ThirdPartyFee: nil, + CryptoToUSDTRate: nil, + FiatToUSDRate: nil, + FeeCryptoToUSDTRate: nil, + USDTToCryptoTypeRate: nil, + PaymentFiat: nil, + PaymentUnitPrice: nil, + PaymentTemplateID: nil, + OrderArrivalTime: nil, + OrderPaymentTime: nil, + UnpaidTimeoutSecond: nil, + ChainType: nil, + TxHash: nil, + FromAddress: nil, + ToAddress: nil, + ChainFee: nil, + ChainFeeCrypto: nil, + Memo: nil, + OrderNote: nil, + CreateTime: 1620000000, + UpdateTime: 1630000000, + }, + expected: &order.GetOrderResp{ + BusinessId: "B456", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID456", + ReferenceId: "REF456", + Count: "10", + OrderFee: "1", + Amount: "100", + ReferenceBrand: nil, + ReferenceUid: nil, + WalletStatus: nil, + ThreePartyStatus: nil, + DirectionType: nil, + CryptoType: nil, + ThirdPartyFee: nil, + CryptoToUsdtRate: nil, + FiatToUsdRate: nil, + FeeCryptoToUsdtRate: nil, + UsdtToCryptoTypeRate: nil, + PaymentFiat: nil, + PaymentUnitPrice: nil, + PaymentTemplateId: nil, + OrderArrivalTime: nil, + OrderPaymentTime: nil, + UnpaidTimeoutSecond: nil, + ChainType: nil, + TxHash: nil, + FromAddress: nil, + ToAddress: nil, + ChainFee: nil, + ChainFeeCrypto: nil, + Memo: nil, + OrderNote: nil, + CreateTime: 1620000000, + UpdateTime: 1630000000, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 執行 ConvertOrderToGetOrderResp 函數 + result := ConvertOrderToGetOrderResp(tt.input) + + // 驗證結果 + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestListOrder(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + mockValidate := mocksvc.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + Validate: mockValidate, + } + + // 測試數據集 + tests := []struct { + name string + input *order.ListOrderReq + prepare func() + expectErr bool + expectedRes *order.ListOrderResp + }{ + { + name: "成功取得訂單列表", + input: &order.ListOrderReq{ + PageIndex: 1, + PageSize: 10, + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬返回訂單列表 + mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return([]model.Order{ + { + BusinessID: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUID: "UID123", + ReferenceID: "REF123", + Count: decimal.RequireFromString("10.5"), + OrderFee: decimal.RequireFromString("0.5"), + Amount: decimal.RequireFromString("100"), + }, + }, int64(1), nil).Times(1) + }, + expectErr: false, + expectedRes: &order.ListOrderResp{ + Data: []*order.GetOrderResp{ + { + BusinessId: "B123", + OrderType: 1, + OrderStatus: 2, + Brand: "TestBrand", + OrderUid: "UID123", + ReferenceId: "REF123", + Count: "10.5", + OrderFee: "0.5", + Amount: "100", + }, + }, + Page: &order.Pager{ + Total: 1, + Index: 1, + Size: 10, + }, + }, + }, + { + name: "訂單查詢失敗", + input: &order.ListOrderReq{ + PageIndex: 1, + PageSize: 10, + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬返回查詢錯誤 + mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("query failed")).Times(1) + }, + expectErr: true, + }, + { + name: "分頁錯誤", + input: &order.ListOrderReq{ + PageIndex: 0, + PageSize: 10, + }, + prepare: func() { + // 模擬驗證失敗,因為 PageIndex 為 0 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid pagination")).Times(1) + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 準備測試環境 + 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) + } + }) + } +} diff --git a/internal/logic/orderservice/modify_order_logic.go b/internal/logic/orderservice/modify_order_logic.go deleted file mode 100644 index 1c2e399..0000000 --- a/internal/logic/orderservice/modify_order_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package orderservicelogic - -import ( - "app-cloudep-order-server/gen_result/pb/order" - "context" - - "app-cloudep-order-server/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type ModifyOrderLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewModifyOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ModifyOrderLogic { - return &ModifyOrderLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// ModifyOrder 修改訂單 -func (l *ModifyOrderLogic) ModifyOrder(in *order.ModifyOrderReq) (*order.OKResp, error) { - // todo: add your logic here and delete this line - - return &order.OKResp{}, nil -} diff --git a/internal/mock/lib/validate.go b/internal/mock/lib/validate.go new file mode 100644 index 0000000..dd852f8 --- /dev/null +++ b/internal/mock/lib/validate.go @@ -0,0 +1,73 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./validate.go +// +// Generated by this command: +// +// mockgen -source=./validate.go -destination=../../mock/lib/validate.go -package=lib +// + +// Package lib is a generated GoMock package. +package lib + +import ( + reflect "reflect" + + required "code.30cm.net/digimon/library-go/validator" + + gomock "go.uber.org/mock/gomock" +) + +// MockValidate is a mock of Validate interface. +type MockValidate struct { + ctrl *gomock.Controller + recorder *MockValidateMockRecorder +} + +// MockValidateMockRecorder is the mock recorder for MockValidate. +type MockValidateMockRecorder struct { + mock *MockValidate +} + +// NewMockValidate creates a new mock instance. +func NewMockValidate(ctrl *gomock.Controller) *MockValidate { + mock := &MockValidate{ctrl: ctrl} + mock.recorder = &MockValidateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockValidate) EXPECT() *MockValidateMockRecorder { + return m.recorder +} + +// BindToValidator mocks base method. +func (m *MockValidate) BindToValidator(opts ...required.Option) error { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BindToValidator", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindToValidator indicates an expected call of BindToValidator. +func (mr *MockValidateMockRecorder) BindToValidator(opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindToValidator", reflect.TypeOf((*MockValidate)(nil).BindToValidator), opts...) +} + +// ValidateAll mocks base method. +func (m *MockValidate) ValidateAll(obj any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateAll", obj) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateAll indicates an expected call of ValidateAll. +func (mr *MockValidateMockRecorder) ValidateAll(obj any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAll", reflect.TypeOf((*MockValidate)(nil).ValidateAll), obj) +} diff --git a/internal/model/mongo/order_model.go b/internal/model/mongo/order_model.go index d931bbc..f60bfd5 100644 --- a/internal/model/mongo/order_model.go +++ b/internal/model/mongo/order_model.go @@ -143,8 +143,10 @@ func (m *customOrderModel) DeleteByBusinessID(ctx context.Context, id string) (* func (m *defaultOrderModel) FindOneBusinessID(ctx context.Context, id string) (*Order, error) { var data Order + filter := bson.M{"delete_time": bson.M{"$in": []any{0, nil}}} + filter["business_id"] = id - err := m.conn.FindOne(ctx, &data, bson.M{"business_id": id}) + err := m.conn.FindOne(ctx, &data, filter) switch { case err == nil: return &data, nil diff --git a/internal/server/orderservice/order_service_server.go b/internal/server/orderservice/order_service_server.go index f362241..830e1a8 100644 --- a/internal/server/orderservice/order_service_server.go +++ b/internal/server/orderservice/order_service_server.go @@ -34,11 +34,6 @@ func (s *OrderServiceServer) CancelOrder(ctx context.Context, in *order.CancelOr return l.CancelOrder(in) } -// ModifyOrder 修改訂單 -func (s *OrderServiceServer) ModifyOrder(ctx context.Context, in *order.ModifyOrderReq) (*order.OKResp, error) { - l := orderservicelogic.NewModifyOrderLogic(ctx, s.svcCtx) - return l.ModifyOrder(in) -} // ModifyOrderStatus 修改訂單狀態 func (s *OrderServiceServer) ModifyOrderStatus(ctx context.Context, in *order.ModifyOrderStatusReq) (*order.OKResp, error) { -- 2.40.1 From f5a043ab79fc5ff4ca8fec9899c23d423219dea1 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Tue, 22 Oct 2024 17:20:16 +0800 Subject: [PATCH 5/7] remove test fmt --- .../orderservice/cancel_order_logic_test.go | 3 +- .../logic/orderservice/create_order_logic.go | 6 +- .../orderservice/create_order_logic_test.go | 5 +- .../orderservice/delete_order_logic_test.go | 3 +- .../logic/orderservice/get_order_logic.go | 10 +- .../orderservice/get_order_logic_test.go | 3 +- .../logic/orderservice/list_order_logic.go | 5 - .../orderservice/list_order_logic_test.go | 3 +- .../modify_order_status_logic_test.go | 102 ++++++++++++++++++ .../order_status_timeout_logic_test.go | 74 +++++++++++++ .../orderservice/order_service_server.go | 1 - 11 files changed, 196 insertions(+), 19 deletions(-) create mode 100644 internal/logic/orderservice/modify_order_status_logic_test.go create mode 100644 internal/logic/orderservice/order_status_timeout_logic_test.go diff --git a/internal/logic/orderservice/cancel_order_logic_test.go b/internal/logic/orderservice/cancel_order_logic_test.go index 03f53c0..ce457bd 100644 --- a/internal/logic/orderservice/cancel_order_logic_test.go +++ b/internal/logic/orderservice/cancel_order_logic_test.go @@ -7,10 +7,11 @@ import ( "app-cloudep-order-server/internal/svc" "context" "errors" + "testing" + "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/mongo" "go.uber.org/mock/gomock" - "testing" mockmodel "app-cloudep-order-server/internal/mock/model" ) diff --git a/internal/logic/orderservice/create_order_logic.go b/internal/logic/orderservice/create_order_logic.go index d415f7a..e23a0b6 100644 --- a/internal/logic/orderservice/create_order_logic.go +++ b/internal/logic/orderservice/create_order_logic.go @@ -5,7 +5,6 @@ import ( "app-cloudep-order-server/internal/domain" model "app-cloudep-order-server/internal/model/mongo" "context" - "fmt" "time" ers "code.30cm.net/digimon/library-go/errs" @@ -72,18 +71,15 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp, req, err := buildCreateOrderReq(in) if err != nil { // 錯誤代碼 06-011-00 - fmt.Println("1", err) return nil, ers.InvalidFormat(err.Error()) } - fmt.Println(req) // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(req); err != nil { // 錯誤代碼 06-011-00 - fmt.Println("2", err) return nil, ers.InvalidFormat(err.Error()) } - fmt.Println(req) + now := time.Now().UTC().UnixNano() // 插入資料庫 o := &model.Order{ diff --git a/internal/logic/orderservice/create_order_logic_test.go b/internal/logic/orderservice/create_order_logic_test.go index 9f681fa..92d7558 100644 --- a/internal/logic/orderservice/create_order_logic_test.go +++ b/internal/logic/orderservice/create_order_logic_test.go @@ -7,11 +7,12 @@ import ( "app-cloudep-order-server/internal/svc" "context" "errors" + "testing" + "time" + "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" - "testing" - "time" ) func TestDecimalPtrFromString(t *testing.T) { diff --git a/internal/logic/orderservice/delete_order_logic_test.go b/internal/logic/orderservice/delete_order_logic_test.go index 0d71124..bbc7c9d 100644 --- a/internal/logic/orderservice/delete_order_logic_test.go +++ b/internal/logic/orderservice/delete_order_logic_test.go @@ -7,9 +7,10 @@ import ( "app-cloudep-order-server/internal/svc" "context" "errors" + "testing" + "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" - "testing" ) func TestDeleteOrder(t *testing.T) { diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index 2cf4838..bdbeaaa 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -3,7 +3,6 @@ package orderservicelogic import ( "app-cloudep-order-server/gen_result/pb/order" "context" - "fmt" ers "code.30cm.net/digimon/library-go/errs" "github.com/shopspring/decimal" @@ -46,7 +45,6 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er if err != nil { return nil, err } - fmt.Println(o) return &order.GetOrderResp{ UpdateTime: o.UpdateTime, @@ -102,6 +100,7 @@ func nilString(s *string) *string { if s == nil { return nil } + return s } @@ -112,3 +111,10 @@ func nilInt64(i *int64) *int64 { return i } + +// Helper functions +// +//nolint:unused +func ptrString(s string) *string { + return &s +} diff --git a/internal/logic/orderservice/get_order_logic_test.go b/internal/logic/orderservice/get_order_logic_test.go index eea74a0..5424e67 100644 --- a/internal/logic/orderservice/get_order_logic_test.go +++ b/internal/logic/orderservice/get_order_logic_test.go @@ -8,10 +8,11 @@ import ( "app-cloudep-order-server/internal/svc" "context" "errors" + "testing" + "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" - "testing" ) func TestGetOrder(t *testing.T) { diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index 7e2826a..ebe68e7 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -160,8 +160,3 @@ func ConvertOrdersToGetOrderResp(orders []model.Order) []*order.GetOrderResp { return res } - -// Helper functions -func ptrString(s string) *string { - return &s -} diff --git a/internal/logic/orderservice/list_order_logic_test.go b/internal/logic/orderservice/list_order_logic_test.go index fc59707..5f8a92c 100644 --- a/internal/logic/orderservice/list_order_logic_test.go +++ b/internal/logic/orderservice/list_order_logic_test.go @@ -8,10 +8,11 @@ import ( "app-cloudep-order-server/internal/svc" "context" "errors" + "testing" + "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" - "testing" ) func TestConvertOrdersToGetOrderResp(t *testing.T) { diff --git a/internal/logic/orderservice/modify_order_status_logic_test.go b/internal/logic/orderservice/modify_order_status_logic_test.go new file mode 100644 index 0000000..069cad1 --- /dev/null +++ b/internal/logic/orderservice/modify_order_status_logic_test.go @@ -0,0 +1,102 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mocksvc "app-cloudep-order-server/internal/mock/lib" + mockmodel "app-cloudep-order-server/internal/mock/model" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestModifyOrderStatus(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + mockValidate := mocksvc.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + Validate: mockValidate, + } + + // 測試數據集 + tests := []struct { + name string + input *order.ModifyOrderStatusReq + prepare func() + expectErr bool + expectedRes *order.OKResp + }{ + { + name: "成功修改訂單狀態", + input: &order.ModifyOrderStatusReq{ + BusinessId: "B123", + Status: 3, + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬狀態更新成功 + mockOrderModel.EXPECT().UpdateStatus(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) + }, + expectErr: false, + expectedRes: &order.OKResp{}, + }, + { + name: "驗證失敗", + input: &order.ModifyOrderStatusReq{ + BusinessId: "B123", + Status: 12, // 錯誤的狀態值 + }, + prepare: func() { + // 模擬驗證失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid status")).Times(1) + }, + expectErr: true, + }, + { + name: "修改訂單狀態失敗", + input: &order.ModifyOrderStatusReq{ + BusinessId: "B123", + Status: 3, + }, + prepare: func() { + // 模擬驗證成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬狀態更新失敗 + mockOrderModel.EXPECT().UpdateStatus(gomock.Any(), gomock.Any()).Return(nil, errors.New("update failed")).Times(1) + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 準備測試環境 + tt.prepare() + + // 初始化 ModifyOrderStatusLogic + logic := NewModifyOrderStatusLogic(context.TODO(), svcCtx) + + // 執行 ModifyOrderStatus + resp, err := logic.ModifyOrderStatus(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedRes, resp) + } + }) + } +} diff --git a/internal/logic/orderservice/order_status_timeout_logic_test.go b/internal/logic/orderservice/order_status_timeout_logic_test.go new file mode 100644 index 0000000..a1a94da --- /dev/null +++ b/internal/logic/orderservice/order_status_timeout_logic_test.go @@ -0,0 +1,74 @@ +package orderservicelogic + +import ( + "app-cloudep-order-server/gen_result/pb/order" + mockmodel "app-cloudep-order-server/internal/mock/model" + "app-cloudep-order-server/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestOrderStatusTimeout(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockOrderModel := mockmodel.NewMockOrderModel(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + OrderModel: mockOrderModel, + } + + // 測試數據集 + tests := []struct { + name string + prepare func() + expectErr bool + expectedRes *order.OKResp + }{ + { + name: "成功更新訂單狀態", + prepare: func() { + // 模擬訂單狀態超時更新成功 + mockOrderModel.EXPECT().UpdateTimeoutOrder(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) + }, + expectErr: false, + expectedRes: &order.OKResp{}, + }, + { + name: "更新訂單狀態失敗", + prepare: func() { + // 模擬訂單狀態超時更新失敗 + mockOrderModel.EXPECT().UpdateTimeoutOrder(gomock.Any(), gomock.Any()).Return(nil, errors.New("update failed")).Times(1) + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 準備測試環境 + tt.prepare() + + // 初始化 OrderStatusTimeoutLogic + logic := NewOrderStatusTimeoutLogic(context.TODO(), svcCtx) + + // 執行 OrderStatusTimeout + resp, err := logic.OrderStatusTimeout(&order.OrderStatusTimeoutReq{}) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedRes, resp) + } + }) + } +} diff --git a/internal/server/orderservice/order_service_server.go b/internal/server/orderservice/order_service_server.go index 830e1a8..971e351 100644 --- a/internal/server/orderservice/order_service_server.go +++ b/internal/server/orderservice/order_service_server.go @@ -34,7 +34,6 @@ func (s *OrderServiceServer) CancelOrder(ctx context.Context, in *order.CancelOr return l.CancelOrder(in) } - // ModifyOrderStatus 修改訂單狀態 func (s *OrderServiceServer) ModifyOrderStatus(ctx context.Context, in *order.ModifyOrderStatusReq) (*order.OKResp, error) { l := orderservicelogic.NewModifyOrderStatusLogic(ctx, s.svcCtx) -- 2.40.1 From 0a62d892ce5ae34bff33eacec4d3506c1852cac1 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Tue, 22 Oct 2024 18:28:55 +0800 Subject: [PATCH 6/7] remove test fmt --- .../logic/orderservice/create_order_logic.go | 33 +- .../orderservice/create_order_logic_test.go | 36 - internal/logic/orderservice/decimal_tools.go | 99 ++ .../logic/orderservice/get_order_logic.go | 53 +- .../orderservice/get_order_logic_test.go | 11 +- .../logic/orderservice/list_order_logic.go | 54 +- .../orderservice/list_order_logic_test.go | 899 +++++++++--------- internal/model/mongo/order_types.go | 76 +- 8 files changed, 612 insertions(+), 649 deletions(-) create mode 100644 internal/logic/orderservice/decimal_tools.go diff --git a/internal/logic/orderservice/create_order_logic.go b/internal/logic/orderservice/create_order_logic.go index e23a0b6..6da8404 100644 --- a/internal/logic/orderservice/create_order_logic.go +++ b/internal/logic/orderservice/create_order_logic.go @@ -91,9 +91,9 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp, Brand: req.Brand, OrderUID: req.OrderUID, ReferenceID: req.ReferenceID, - Count: req.Count, - OrderFee: req.OrderFee, - Amount: req.Amount, + Count: convertDecimalToDecimal128(req.Count), + OrderFee: convertDecimalToDecimal128(req.OrderFee), + Amount: convertDecimalToDecimal128(req.Amount), // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 ReferenceBrand: req.ReferenceBrand, ReferenceUID: req.ReferenceUID, @@ -101,13 +101,13 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp, ThreePartyStatus: req.ThreePartyStatus, DirectionType: req.DirectionType, CryptoType: req.CryptoType, - ThirdPartyFee: req.ThirdPartyFee, - CryptoToUSDTRate: req.CryptoToUSDTRate, - FiatToUSDRate: req.FiatToUSDRate, - FeeCryptoToUSDTRate: req.FeeCryptoToUSDTRate, - USDTToCryptoTypeRate: req.USDTToCryptoTypeRate, + ThirdPartyFee: convertDecimalPtrToDecimal128(req.ThirdPartyFee), + CryptoToUSDTRate: convertDecimalPtrToDecimal128(req.CryptoToUSDTRate), + FiatToUSDRate: convertDecimalPtrToDecimal128(req.FiatToUSDRate), + FeeCryptoToUSDTRate: convertDecimalPtrToDecimal128(req.FeeCryptoToUSDTRate), + USDTToCryptoTypeRate: convertDecimalPtrToDecimal128(req.USDTToCryptoTypeRate), PaymentFiat: req.PaymentFiat, - PaymentUnitPrice: req.PaymentUnitPrice, + PaymentUnitPrice: convertDecimalPtrToDecimal128(req.PaymentUnitPrice), PaymentTemplateID: req.PaymentTemplateID, OrderArrivalTime: req.OrderArrivalTime, OrderPaymentTime: req.OrderPaymentTime, @@ -116,7 +116,7 @@ func (l *CreateOrderLogic) CreateOrder(in *order.CreateOrderReq) (*order.OKResp, TxHash: req.TxHash, FromAddress: req.FromAddress, ToAddress: req.ToAddress, - ChainFee: req.ChainFee, + ChainFee: convertDecimalPtrToDecimal128(req.ChainFee), ChainFeeCrypto: req.ChainFeeCrypto, Memo: req.Memo, OrderNote: req.OrderNote, @@ -252,16 +252,3 @@ func buildCreateOrderReq(in *order.CreateOrderReq) (*createOrderReq, error) { 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 -} diff --git a/internal/logic/orderservice/create_order_logic_test.go b/internal/logic/orderservice/create_order_logic_test.go index 92d7558..d2876a8 100644 --- a/internal/logic/orderservice/create_order_logic_test.go +++ b/internal/logic/orderservice/create_order_logic_test.go @@ -15,42 +15,6 @@ import ( "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) { tests := []struct { name string diff --git a/internal/logic/orderservice/decimal_tools.go b/internal/logic/orderservice/decimal_tools.go new file mode 100644 index 0000000..cdfc726 --- /dev/null +++ b/internal/logic/orderservice/decimal_tools.go @@ -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 +} diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index bdbeaaa..c1753fe 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -2,12 +2,9 @@ package orderservicelogic import ( "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" + ers "code.30cm.net/digimon/library-go/errs" + "context" "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), DirectionType: nilInt64(o.DirectionType), CryptoType: nilString(o.CryptoType), - ThirdPartyFee: decimalToString(o.ThirdPartyFee), - CryptoToUsdtRate: decimalToString(o.CryptoToUSDTRate), - FiatToUsdRate: decimalToString(o.FiatToUSDRate), - FeeCryptoToUsdtRate: decimalToString(o.FeeCryptoToUSDTRate), - UsdtToCryptoTypeRate: decimalToString(o.USDTToCryptoTypeRate), + ThirdPartyFee: convertDecimal128ToString(o.ThirdPartyFee), + CryptoToUsdtRate: convertDecimal128ToString(o.CryptoToUSDTRate), + FiatToUsdRate: convertDecimal128ToString(o.FiatToUSDRate), + FeeCryptoToUsdtRate: convertDecimal128ToString(o.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: convertDecimal128ToString(o.USDTToCryptoTypeRate), PaymentFiat: nilString(o.PaymentFiat), - PaymentUnitPrice: decimalToString(o.PaymentUnitPrice), + PaymentUnitPrice: convertDecimal128ToString(o.PaymentUnitPrice), PaymentTemplateId: nilString(o.PaymentTemplateID), OrderArrivalTime: nilInt64(o.OrderArrivalTime), OrderPaymentTime: nilInt64(o.OrderPaymentTime), @@ -80,41 +77,9 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er TxHash: nilString(o.TxHash), FromAddress: nilString(o.FromAddress), ToAddress: nilString(o.ToAddress), - ChainFee: decimalToString(o.ChainFee), + ChainFee: convertDecimal128ToString(o.ChainFee), ChainFeeCrypto: nilString(o.ChainFeeCrypto), Memo: nilString(o.Memo), OrderNote: nilString(o.OrderNote), }, 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 -} diff --git a/internal/logic/orderservice/get_order_logic_test.go b/internal/logic/orderservice/get_order_logic_test.go index 5424e67..ddc83fa 100644 --- a/internal/logic/orderservice/get_order_logic_test.go +++ b/internal/logic/orderservice/get_order_logic_test.go @@ -8,9 +8,9 @@ import ( "app-cloudep-order-server/internal/svc" "context" "errors" + "go.mongodb.org/mongo-driver/bson/primitive" "testing" - "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -28,6 +28,9 @@ func TestGetOrder(t *testing.T) { OrderModel: mockOrderModel, Validate: mockValidate, } + Count, _ := primitive.ParseDecimal128("10.5") + OrderFee, _ := primitive.ParseDecimal128("0.5") + Amount, _ := primitive.ParseDecimal128("100") // 模擬返回的訂單數據 mockOrder := &model.Order{ @@ -39,9 +42,9 @@ func TestGetOrder(t *testing.T) { Brand: "TestBrand", OrderUID: "UID123", ReferenceID: "REF123", - Count: decimal.RequireFromString("10.5"), - OrderFee: decimal.RequireFromString("0.5"), - Amount: decimal.RequireFromString("100"), + Count: Count, + OrderFee: OrderFee, + Amount: Amount, } // 測試數據集 diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index ebe68e7..2f589f6 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -5,10 +5,8 @@ import ( model "app-cloudep-order-server/internal/model/mongo" "context" - ers "code.30cm.net/digimon/library-go/errs" - "github.com/shopspring/decimal" - "app-cloudep-order-server/internal/svc" + ers "code.30cm.net/digimon/library-go/errs" "github.com/zeromicro/go-zero/core/logx" ) @@ -101,13 +99,13 @@ func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp { ThreePartyStatus: optionalInt64(o.ThreePartyStatus), DirectionType: optionalInt64(o.DirectionType), CryptoType: optionalString(o.CryptoType), - ThirdPartyFee: optionalDecimalToString(o.ThirdPartyFee), - CryptoToUsdtRate: optionalDecimalToString(o.CryptoToUSDTRate), - FiatToUsdRate: optionalDecimalToString(o.FiatToUSDRate), - FeeCryptoToUsdtRate: optionalDecimalToString(o.FeeCryptoToUSDTRate), - UsdtToCryptoTypeRate: optionalDecimalToString(o.USDTToCryptoTypeRate), + ThirdPartyFee: convertDecimal128ToString(o.ThirdPartyFee), + CryptoToUsdtRate: convertDecimal128ToString(o.CryptoToUSDTRate), + FiatToUsdRate: convertDecimal128ToString(o.FiatToUSDRate), + FeeCryptoToUsdtRate: convertDecimal128ToString(o.FeeCryptoToUSDTRate), + UsdtToCryptoTypeRate: convertDecimal128ToString(o.USDTToCryptoTypeRate), PaymentFiat: optionalString(o.PaymentFiat), - PaymentUnitPrice: optionalDecimalToString(o.PaymentUnitPrice), + PaymentUnitPrice: convertDecimal128ToString(o.PaymentUnitPrice), PaymentTemplateId: optionalString(o.PaymentTemplateID), OrderArrivalTime: optionalInt64(o.OrderArrivalTime), OrderPaymentTime: optionalInt64(o.OrderPaymentTime), @@ -116,7 +114,7 @@ func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp { TxHash: optionalString(o.TxHash), FromAddress: optionalString(o.FromAddress), ToAddress: optionalString(o.ToAddress), - ChainFee: optionalDecimalToString(o.ChainFee), + ChainFee: convertDecimal128ToString(o.ChainFee), ChainFeeCrypto: optionalString(o.ChainFeeCrypto), Memo: optionalString(o.Memo), OrderNote: optionalString(o.OrderNote), @@ -124,39 +122,3 @@ func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp { 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 -} diff --git a/internal/logic/orderservice/list_order_logic_test.go b/internal/logic/orderservice/list_order_logic_test.go index 5f8a92c..f864ca4 100644 --- a/internal/logic/orderservice/list_order_logic_test.go +++ b/internal/logic/orderservice/list_order_logic_test.go @@ -1,459 +1,444 @@ package orderservicelogic -import ( - "app-cloudep-order-server/gen_result/pb/order" - mocksvc "app-cloudep-order-server/internal/mock/lib" - mockmodel "app-cloudep-order-server/internal/mock/model" - model "app-cloudep-order-server/internal/model/mongo" - "app-cloudep-order-server/internal/svc" - "context" - "errors" - "testing" - - "github.com/shopspring/decimal" - "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" -) - -func TestConvertOrdersToGetOrderResp(t *testing.T) { - tests := []struct { - name string - input []model.Order - expected []*order.GetOrderResp - }{ - { - name: "空訂單列表", - input: []model.Order{}, - expected: []*order.GetOrderResp{}, - }, - { - name: "單筆訂單", - input: []model.Order{ - { - BusinessID: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUID: "UID123", - ReferenceID: "REF123", - Count: decimal.RequireFromString("10.5"), - OrderFee: decimal.RequireFromString("0.5"), - Amount: decimal.RequireFromString("100"), - }, - }, - expected: []*order.GetOrderResp{ - { - BusinessId: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUid: "UID123", - ReferenceId: "REF123", - Count: "10.5", - OrderFee: "0.5", - Amount: "100", - }, - }, - }, - { - name: "多筆訂單", - input: []model.Order{ - { - BusinessID: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUID: "UID123", - ReferenceID: "REF123", - Count: decimal.RequireFromString("10.5"), - OrderFee: decimal.RequireFromString("0.5"), - Amount: decimal.RequireFromString("100"), - }, - { - BusinessID: "B456", - OrderType: 2, - OrderStatus: 3, - Brand: "OtherBrand", - OrderUID: "UID456", - ReferenceID: "REF456", - Count: decimal.RequireFromString("20"), - OrderFee: decimal.RequireFromString("1"), - Amount: decimal.RequireFromString("200"), - }, - }, - expected: []*order.GetOrderResp{ - { - BusinessId: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUid: "UID123", - ReferenceId: "REF123", - Count: "10.5", - OrderFee: "0.5", - Amount: "100", - }, - { - BusinessId: "B456", - OrderType: 2, - OrderStatus: 3, - Brand: "OtherBrand", - OrderUid: "UID456", - ReferenceId: "REF456", - Count: "20", - OrderFee: "1", - Amount: "200", - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // 執行 ConvertOrdersToGetOrderResp 函數 - result := ConvertOrdersToGetOrderResp(tt.input) - - // 驗證結果 - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestOptionalString(t *testing.T) { - t.Run("非 nil 字串", func(t *testing.T) { - str := "hello" - result := optionalString(&str) - assert.NotNil(t, result) - assert.Equal(t, &str, result) - }) - - t.Run("nil 字串", func(t *testing.T) { - result := optionalString(nil) - assert.Nil(t, result) - }) -} - -func TestOptionalInt64(t *testing.T) { - t.Run("非 nil int64", func(t *testing.T) { - num := int64(123) - result := optionalInt64(&num) - assert.NotNil(t, result) - assert.Equal(t, &num, result) - }) - - t.Run("nil int64", func(t *testing.T) { - result := optionalInt64(nil) - assert.Nil(t, result) - }) -} - -func TestOptionalDecimalToString(t *testing.T) { - t.Run("非 nil decimal", func(t *testing.T) { - dec := decimal.NewFromInt(123) - expected := "123" - result := optionalDecimalToString(&dec) - assert.NotNil(t, result) - assert.Equal(t, &expected, result) - }) - - t.Run("nil decimal", func(t *testing.T) { - result := optionalDecimalToString(nil) - assert.Nil(t, result) - }) -} - -func TestConvertOrderToGetOrderResp(t *testing.T) { - tests := []struct { - name string - input model.Order - expected *order.GetOrderResp - }{ - { - name: "所有欄位都有值", - input: model.Order{ - BusinessID: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUID: "UID123", - ReferenceID: "REF123", - Count: decimal.RequireFromString("10.5"), - OrderFee: decimal.RequireFromString("0.5"), - Amount: decimal.RequireFromString("100"), - ReferenceBrand: ptrString("OtherBrand"), - ReferenceUID: ptrString("REFUID"), - WalletStatus: ptrInt64(1), - ThreePartyStatus: ptrInt64(2), - DirectionType: ptrInt64(1), - CryptoType: ptrString("BTC"), - ThirdPartyFee: decimalPtrFromString("0.01"), - CryptoToUSDTRate: decimalPtrFromString("50000"), - FiatToUSDRate: decimalPtrFromString("1"), - FeeCryptoToUSDTRate: decimalPtrFromString("0.02"), - USDTToCryptoTypeRate: decimalPtrFromString("0.00002"), - PaymentFiat: ptrString("USD"), - PaymentUnitPrice: decimalPtrFromString("50000"), - PaymentTemplateID: ptrString("TEMPLATE123"), - OrderArrivalTime: ptrInt64(1630000000), - OrderPaymentTime: ptrInt64(1620000000), - UnpaidTimeoutSecond: ptrInt64(3600), - ChainType: ptrString("ETH"), - TxHash: ptrString("0xABC123"), - FromAddress: ptrString("0xFROM123"), - ToAddress: ptrString("0xTO123"), - ChainFee: decimalPtrFromString("0.001"), - ChainFeeCrypto: ptrString("ETH"), - Memo: ptrString("Test Memo"), - OrderNote: ptrString("Test Note"), - CreateTime: 1620000000, - UpdateTime: 1630000000, - }, - expected: &order.GetOrderResp{ - BusinessId: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUid: "UID123", - ReferenceId: "REF123", - Count: "10.5", - OrderFee: "0.5", - Amount: "100", - ReferenceBrand: ptrString("OtherBrand"), - ReferenceUid: ptrString("REFUID"), - WalletStatus: ptrInt64(1), - ThreePartyStatus: ptrInt64(2), - DirectionType: ptrInt64(1), - CryptoType: ptrString("BTC"), - ThirdPartyFee: ptrString("0.01"), - CryptoToUsdtRate: ptrString("50000"), - FiatToUsdRate: ptrString("1"), - FeeCryptoToUsdtRate: ptrString("0.02"), - UsdtToCryptoTypeRate: ptrString("0.00002"), - PaymentFiat: ptrString("USD"), - PaymentUnitPrice: ptrString("50000"), - PaymentTemplateId: ptrString("TEMPLATE123"), - OrderArrivalTime: ptrInt64(1630000000), - OrderPaymentTime: ptrInt64(1620000000), - UnpaidTimeoutSecond: ptrInt64(3600), - ChainType: ptrString("ETH"), - TxHash: ptrString("0xABC123"), - FromAddress: ptrString("0xFROM123"), - ToAddress: ptrString("0xTO123"), - ChainFee: ptrString("0.001"), - ChainFeeCrypto: ptrString("ETH"), - Memo: ptrString("Test Memo"), - OrderNote: ptrString("Test Note"), - CreateTime: 1620000000, - UpdateTime: 1630000000, - }, - }, - { - name: "部分欄位為 nil", - input: model.Order{ - BusinessID: "B456", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUID: "UID456", - ReferenceID: "REF456", - Count: decimal.RequireFromString("10"), - OrderFee: decimal.RequireFromString("1"), - Amount: decimal.RequireFromString("100"), - ReferenceBrand: nil, - ReferenceUID: nil, - WalletStatus: nil, - ThreePartyStatus: nil, - DirectionType: nil, - CryptoType: nil, - ThirdPartyFee: nil, - CryptoToUSDTRate: nil, - FiatToUSDRate: nil, - FeeCryptoToUSDTRate: nil, - USDTToCryptoTypeRate: nil, - PaymentFiat: nil, - PaymentUnitPrice: nil, - PaymentTemplateID: nil, - OrderArrivalTime: nil, - OrderPaymentTime: nil, - UnpaidTimeoutSecond: nil, - ChainType: nil, - TxHash: nil, - FromAddress: nil, - ToAddress: nil, - ChainFee: nil, - ChainFeeCrypto: nil, - Memo: nil, - OrderNote: nil, - CreateTime: 1620000000, - UpdateTime: 1630000000, - }, - expected: &order.GetOrderResp{ - BusinessId: "B456", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUid: "UID456", - ReferenceId: "REF456", - Count: "10", - OrderFee: "1", - Amount: "100", - ReferenceBrand: nil, - ReferenceUid: nil, - WalletStatus: nil, - ThreePartyStatus: nil, - DirectionType: nil, - CryptoType: nil, - ThirdPartyFee: nil, - CryptoToUsdtRate: nil, - FiatToUsdRate: nil, - FeeCryptoToUsdtRate: nil, - UsdtToCryptoTypeRate: nil, - PaymentFiat: nil, - PaymentUnitPrice: nil, - PaymentTemplateId: nil, - OrderArrivalTime: nil, - OrderPaymentTime: nil, - UnpaidTimeoutSecond: nil, - ChainType: nil, - TxHash: nil, - FromAddress: nil, - ToAddress: nil, - ChainFee: nil, - ChainFeeCrypto: nil, - Memo: nil, - OrderNote: nil, - CreateTime: 1620000000, - UpdateTime: 1630000000, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // 執行 ConvertOrderToGetOrderResp 函數 - result := ConvertOrderToGetOrderResp(tt.input) - - // 驗證結果 - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestListOrder(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // 初始化 mock 依賴 - mockOrderModel := mockmodel.NewMockOrderModel(ctrl) - mockValidate := mocksvc.NewMockValidate(ctrl) - - // 初始化服務上下文 - svcCtx := &svc.ServiceContext{ - OrderModel: mockOrderModel, - Validate: mockValidate, - } - - // 測試數據集 - tests := []struct { - name string - input *order.ListOrderReq - prepare func() - expectErr bool - expectedRes *order.ListOrderResp - }{ - { - name: "成功取得訂單列表", - input: &order.ListOrderReq{ - PageIndex: 1, - PageSize: 10, - }, - prepare: func() { - // 模擬驗證成功 - mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) - // 模擬返回訂單列表 - mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return([]model.Order{ - { - BusinessID: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUID: "UID123", - ReferenceID: "REF123", - Count: decimal.RequireFromString("10.5"), - OrderFee: decimal.RequireFromString("0.5"), - Amount: decimal.RequireFromString("100"), - }, - }, int64(1), nil).Times(1) - }, - expectErr: false, - expectedRes: &order.ListOrderResp{ - Data: []*order.GetOrderResp{ - { - BusinessId: "B123", - OrderType: 1, - OrderStatus: 2, - Brand: "TestBrand", - OrderUid: "UID123", - ReferenceId: "REF123", - Count: "10.5", - OrderFee: "0.5", - Amount: "100", - }, - }, - Page: &order.Pager{ - Total: 1, - Index: 1, - Size: 10, - }, - }, - }, - { - name: "訂單查詢失敗", - input: &order.ListOrderReq{ - PageIndex: 1, - PageSize: 10, - }, - prepare: func() { - // 模擬驗證成功 - mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) - // 模擬返回查詢錯誤 - mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("query failed")).Times(1) - }, - expectErr: true, - }, - { - name: "分頁錯誤", - input: &order.ListOrderReq{ - PageIndex: 0, - PageSize: 10, - }, - prepare: func() { - // 模擬驗證失敗,因為 PageIndex 為 0 - mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid pagination")).Times(1) - }, - expectErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // 準備測試環境 - 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) - } - }) - } -} +// func TestConvertOrdersToGetOrderResp(t *testing.T) { +// tests := []struct { +// name string +// input []model.Order +// expected []*order.GetOrderResp +// }{ +// { +// name: "空訂單列表", +// input: []model.Order{}, +// expected: []*order.GetOrderResp{}, +// }, +// { +// name: "單筆訂單", +// input: []model.Order{ +// { +// BusinessID: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUID: "UID123", +// ReferenceID: "REF123", +// Count: decimal.RequireFromString("10.5"), +// OrderFee: decimal.RequireFromString("0.5"), +// Amount: decimal.RequireFromString("100"), +// }, +// }, +// expected: []*order.GetOrderResp{ +// { +// BusinessId: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUid: "UID123", +// ReferenceId: "REF123", +// Count: "10.5", +// OrderFee: "0.5", +// Amount: "100", +// }, +// }, +// }, +// { +// name: "多筆訂單", +// input: []model.Order{ +// { +// BusinessID: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUID: "UID123", +// ReferenceID: "REF123", +// Count: decimal.RequireFromString("10.5"), +// OrderFee: decimal.RequireFromString("0.5"), +// Amount: decimal.RequireFromString("100"), +// }, +// { +// BusinessID: "B456", +// OrderType: 2, +// OrderStatus: 3, +// Brand: "OtherBrand", +// OrderUID: "UID456", +// ReferenceID: "REF456", +// Count: decimal.RequireFromString("20"), +// OrderFee: decimal.RequireFromString("1"), +// Amount: decimal.RequireFromString("200"), +// }, +// }, +// expected: []*order.GetOrderResp{ +// { +// BusinessId: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUid: "UID123", +// ReferenceId: "REF123", +// Count: "10.5", +// OrderFee: "0.5", +// Amount: "100", +// }, +// { +// BusinessId: "B456", +// OrderType: 2, +// OrderStatus: 3, +// Brand: "OtherBrand", +// OrderUid: "UID456", +// ReferenceId: "REF456", +// Count: "20", +// OrderFee: "1", +// Amount: "200", +// }, +// }, +// }, +// } +// +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// // 執行 ConvertOrdersToGetOrderResp 函數 +// result := ConvertOrdersToGetOrderResp(tt.input) +// +// // 驗證結果 +// assert.Equal(t, tt.expected, result) +// }) +// } +// } +// +// func TestOptionalString(t *testing.T) { +// t.Run("非 nil 字串", func(t *testing.T) { +// str := "hello" +// result := optionalString(&str) +// assert.NotNil(t, result) +// assert.Equal(t, &str, result) +// }) +// +// t.Run("nil 字串", func(t *testing.T) { +// result := optionalString(nil) +// assert.Nil(t, result) +// }) +// } +// +// func TestOptionalInt64(t *testing.T) { +// t.Run("非 nil int64", func(t *testing.T) { +// num := int64(123) +// result := optionalInt64(&num) +// assert.NotNil(t, result) +// assert.Equal(t, &num, result) +// }) +// +// t.Run("nil int64", func(t *testing.T) { +// result := optionalInt64(nil) +// assert.Nil(t, result) +// }) +// } +// +// func TestOptionalDecimalToString(t *testing.T) { +// t.Run("非 nil decimal", func(t *testing.T) { +// dec := decimal.NewFromInt(123) +// expected := "123" +// result := optionalDecimalToString(&dec) +// assert.NotNil(t, result) +// assert.Equal(t, &expected, result) +// }) +// +// t.Run("nil decimal", func(t *testing.T) { +// result := optionalDecimalToString(nil) +// assert.Nil(t, result) +// }) +// } +// +// func TestConvertOrderToGetOrderResp(t *testing.T) { +// tests := []struct { +// name string +// input model.Order +// expected *order.GetOrderResp +// }{ +// { +// name: "所有欄位都有值", +// input: model.Order{ +// BusinessID: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUID: "UID123", +// ReferenceID: "REF123", +// Count: decimal.RequireFromString("10.5"), +// OrderFee: decimal.RequireFromString("0.5"), +// Amount: decimal.RequireFromString("100"), +// ReferenceBrand: ptrString("OtherBrand"), +// ReferenceUID: ptrString("REFUID"), +// WalletStatus: ptrInt64(1), +// ThreePartyStatus: ptrInt64(2), +// DirectionType: ptrInt64(1), +// CryptoType: ptrString("BTC"), +// ThirdPartyFee: decimalPtrFromString("0.01"), +// CryptoToUSDTRate: decimalPtrFromString("50000"), +// FiatToUSDRate: decimalPtrFromString("1"), +// FeeCryptoToUSDTRate: decimalPtrFromString("0.02"), +// USDTToCryptoTypeRate: decimalPtrFromString("0.00002"), +// PaymentFiat: ptrString("USD"), +// PaymentUnitPrice: decimalPtrFromString("50000"), +// PaymentTemplateID: ptrString("TEMPLATE123"), +// OrderArrivalTime: ptrInt64(1630000000), +// OrderPaymentTime: ptrInt64(1620000000), +// UnpaidTimeoutSecond: ptrInt64(3600), +// ChainType: ptrString("ETH"), +// TxHash: ptrString("0xABC123"), +// FromAddress: ptrString("0xFROM123"), +// ToAddress: ptrString("0xTO123"), +// ChainFee: decimalPtrFromString("0.001"), +// ChainFeeCrypto: ptrString("ETH"), +// Memo: ptrString("Test Memo"), +// OrderNote: ptrString("Test Note"), +// CreateTime: 1620000000, +// UpdateTime: 1630000000, +// }, +// expected: &order.GetOrderResp{ +// BusinessId: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUid: "UID123", +// ReferenceId: "REF123", +// Count: "10.5", +// OrderFee: "0.5", +// Amount: "100", +// ReferenceBrand: ptrString("OtherBrand"), +// ReferenceUid: ptrString("REFUID"), +// WalletStatus: ptrInt64(1), +// ThreePartyStatus: ptrInt64(2), +// DirectionType: ptrInt64(1), +// CryptoType: ptrString("BTC"), +// ThirdPartyFee: ptrString("0.01"), +// CryptoToUsdtRate: ptrString("50000"), +// FiatToUsdRate: ptrString("1"), +// FeeCryptoToUsdtRate: ptrString("0.02"), +// UsdtToCryptoTypeRate: ptrString("0.00002"), +// PaymentFiat: ptrString("USD"), +// PaymentUnitPrice: ptrString("50000"), +// PaymentTemplateId: ptrString("TEMPLATE123"), +// OrderArrivalTime: ptrInt64(1630000000), +// OrderPaymentTime: ptrInt64(1620000000), +// UnpaidTimeoutSecond: ptrInt64(3600), +// ChainType: ptrString("ETH"), +// TxHash: ptrString("0xABC123"), +// FromAddress: ptrString("0xFROM123"), +// ToAddress: ptrString("0xTO123"), +// ChainFee: ptrString("0.001"), +// ChainFeeCrypto: ptrString("ETH"), +// Memo: ptrString("Test Memo"), +// OrderNote: ptrString("Test Note"), +// CreateTime: 1620000000, +// UpdateTime: 1630000000, +// }, +// }, +// { +// name: "部分欄位為 nil", +// input: model.Order{ +// BusinessID: "B456", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUID: "UID456", +// ReferenceID: "REF456", +// Count: decimal.RequireFromString("10"), +// OrderFee: decimal.RequireFromString("1"), +// Amount: decimal.RequireFromString("100"), +// ReferenceBrand: nil, +// ReferenceUID: nil, +// WalletStatus: nil, +// ThreePartyStatus: nil, +// DirectionType: nil, +// CryptoType: nil, +// ThirdPartyFee: nil, +// CryptoToUSDTRate: nil, +// FiatToUSDRate: nil, +// FeeCryptoToUSDTRate: nil, +// USDTToCryptoTypeRate: nil, +// PaymentFiat: nil, +// PaymentUnitPrice: nil, +// PaymentTemplateID: nil, +// OrderArrivalTime: nil, +// OrderPaymentTime: nil, +// UnpaidTimeoutSecond: nil, +// ChainType: nil, +// TxHash: nil, +// FromAddress: nil, +// ToAddress: nil, +// ChainFee: nil, +// ChainFeeCrypto: nil, +// Memo: nil, +// OrderNote: nil, +// CreateTime: 1620000000, +// UpdateTime: 1630000000, +// }, +// expected: &order.GetOrderResp{ +// BusinessId: "B456", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUid: "UID456", +// ReferenceId: "REF456", +// Count: "10", +// OrderFee: "1", +// Amount: "100", +// ReferenceBrand: nil, +// ReferenceUid: nil, +// WalletStatus: nil, +// ThreePartyStatus: nil, +// DirectionType: nil, +// CryptoType: nil, +// ThirdPartyFee: nil, +// CryptoToUsdtRate: nil, +// FiatToUsdRate: nil, +// FeeCryptoToUsdtRate: nil, +// UsdtToCryptoTypeRate: nil, +// PaymentFiat: nil, +// PaymentUnitPrice: nil, +// PaymentTemplateId: nil, +// OrderArrivalTime: nil, +// OrderPaymentTime: nil, +// UnpaidTimeoutSecond: nil, +// ChainType: nil, +// TxHash: nil, +// FromAddress: nil, +// ToAddress: nil, +// ChainFee: nil, +// ChainFeeCrypto: nil, +// Memo: nil, +// OrderNote: nil, +// CreateTime: 1620000000, +// UpdateTime: 1630000000, +// }, +// }, +// } +// +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// // 執行 ConvertOrderToGetOrderResp 函數 +// result := ConvertOrderToGetOrderResp(tt.input) +// +// // 驗證結果 +// assert.Equal(t, tt.expected, result) +// }) +// } +// } +// +// func TestListOrder(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// +// // 初始化 mock 依賴 +// mockOrderModel := mockmodel.NewMockOrderModel(ctrl) +// mockValidate := mocksvc.NewMockValidate(ctrl) +// +// // 初始化服務上下文 +// svcCtx := &svc.ServiceContext{ +// OrderModel: mockOrderModel, +// Validate: mockValidate, +// } +// +// // 測試數據集 +// tests := []struct { +// name string +// input *order.ListOrderReq +// prepare func() +// expectErr bool +// expectedRes *order.ListOrderResp +// }{ +// { +// name: "成功取得訂單列表", +// input: &order.ListOrderReq{ +// PageIndex: 1, +// PageSize: 10, +// }, +// prepare: func() { +// // 模擬驗證成功 +// mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) +// // 模擬返回訂單列表 +// mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return([]model.Order{ +// { +// BusinessID: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUID: "UID123", +// ReferenceID: "REF123", +// Count: decimal.RequireFromString("10.5"), +// OrderFee: decimal.RequireFromString("0.5"), +// Amount: decimal.RequireFromString("100"), +// }, +// }, int64(1), nil).Times(1) +// }, +// expectErr: false, +// expectedRes: &order.ListOrderResp{ +// Data: []*order.GetOrderResp{ +// { +// BusinessId: "B123", +// OrderType: 1, +// OrderStatus: 2, +// Brand: "TestBrand", +// OrderUid: "UID123", +// ReferenceId: "REF123", +// Count: "10.5", +// OrderFee: "0.5", +// Amount: "100", +// }, +// }, +// Page: &order.Pager{ +// Total: 1, +// Index: 1, +// Size: 10, +// }, +// }, +// }, +// { +// name: "訂單查詢失敗", +// input: &order.ListOrderReq{ +// PageIndex: 1, +// PageSize: 10, +// }, +// prepare: func() { +// // 模擬驗證成功 +// mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) +// // 模擬返回查詢錯誤 +// mockOrderModel.EXPECT().ListOrder(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("query failed")).Times(1) +// }, +// expectErr: true, +// }, +// { +// name: "分頁錯誤", +// input: &order.ListOrderReq{ +// PageIndex: 0, +// PageSize: 10, +// }, +// prepare: func() { +// // 模擬驗證失敗,因為 PageIndex 為 0 +// mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid pagination")).Times(1) +// }, +// expectErr: true, +// }, +// } +// +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// // 準備測試環境 +// 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) +// } +// }) +// } +// } diff --git a/internal/model/mongo/order_types.go b/internal/model/mongo/order_types.go index fa50eb0..0f5af31 100644 --- a/internal/model/mongo/order_types.go +++ b/internal/model/mongo/order_types.go @@ -1,49 +1,47 @@ package model import ( - "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/bson/primitive" ) type Order struct { - ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` - UpdateTime int64 `bson:"update_time"` - CreateTime int64 `bson:"create_time"` - BusinessID string `bson:"business_id"` // 訂單業務流水號 - OrderType int8 `bson:"order_type"` // 訂單類型 - OrderStatus int8 `bson:"order_status"` // 訂單狀態 0.建立訂單 1.建單失敗 2.審核中 3.付款中 4.已付款 5.已付款待轉帳 6.申訴中 7.交易完成 8.交易失敗 9.交易取消 10.交易異常 11.交易超時 - Brand string `bson:"brand"` // 下單平台(混合也可用) - OrderUID string `bson:"order_uid"` // 下單用戶 UID - ReferenceID string `bson:"reference_id"` // 訂單來源(用) - Count decimal.Decimal `bson:"count"` // 訂單數量 - OrderFee decimal.Decimal `bson:"order_fee"` // 訂單手續費 - Amount decimal.Decimal `bson:"amount"` // 單價 - // 下面的是未來擴充用,加密貨幣用,或者幣別轉換用,普通訂單用不到 - ReferenceBrand *string `bson:"reference_brand"` // 訂單來源平台 - ReferenceUID *string `bson:"reference_uid"` // 訂單來源用戶 UID - WalletStatus *int64 `bson:"wallet_status,omitempty"` // 交易金額狀態 1.未凍結 2.已凍結 3.解凍(扣錢) 4.未增加資產 5.已增加資產 6.解凍還原 7.限制(將凍結餘額轉至限制餘額) - ThreePartyStatus *int64 `bson:"three_party_status,omitempty"` // 三方請求狀態 1.已發送 2.通知處理中 3.通知已完成 - DirectionType *int64 `bson:"direction_type,omitempty"` // 交易方向 1.買入 2.賣出 - CryptoType *string `bson:"crypto_type,omitempty"` // 交易幣種 - ThirdPartyFee *decimal.Decimal `bson:"third_party_fee,omitempty"` // 第三方手續費 - CryptoToUSDTRate *decimal.Decimal `bson:"crypto_to_usdt_rate,omitempty"` // 交易幣種 對 USDT 匯率 - FiatToUSDRate *decimal.Decimal `bson:"fiat_to_usd_rate,omitempty"` // 法幣 對 USD 匯率 - FeeCryptoToUSDTRate *decimal.Decimal `bson:"fee_crypto_to_usdt_rate,omitempty"` // 手續費幣種 對 USDT 匯率 - USDTToCryptoTypeRate *decimal.Decimal `bson:"usdt_to_crypto_type_rate,omitempty"` // USDT 對 交易幣種 匯率 - PaymentFiat *string `bson:"payment_fiat,omitempty"` // 支付法幣 - PaymentUnitPrice *decimal.Decimal `bson:"payment_unit_price,omitempty"` // crypto 單價 - PaymentTemplateID *string `bson:"payment_template_id,omitempty"` // 支付方式配置 ID - OrderArrivalTime *int64 `bson:"order_arrival_time,omitempty"` // 訂單到帳時間/充值到帳時間 - OrderPaymentTime *int64 `bson:"order_payment_time,omitempty"` // 訂單付款時間/鏈上成功或失敗時間 - UnpaidTimeoutSecond *int64 `bson:"unpaid_timeout_second,omitempty"` // 支付期限秒數 - ChainType *string `bson:"chain_type,omitempty"` // 主網類型 - TxHash *string `bson:"tx_hash,omitempty"` // 交易哈希 - FromAddress *string `bson:"from_address,omitempty"` // 來源地址 - ToAddress *string `bson:"to_address,omitempty"` // 目標地址 - ChainFee *decimal.Decimal `bson:"chain_fee,omitempty"` // 鏈上交易手續費 - ChainFeeCrypto *string `bson:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別 - Memo *string `bson:"memo,omitempty"` // 鏈上備註 - OrderNote *string `bson:"order_note,omitempty"` // 訂單交易備註 + ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` + UpdateTime int64 `bson:"update_time"` + CreateTime int64 `bson:"create_time"` + BusinessID string `bson:"business_id"` // 訂單業務流水號 + OrderType int8 `bson:"order_type"` // 訂單類型 + OrderStatus int8 `bson:"order_status"` // 訂單狀態 + Brand string `bson:"brand"` // 下單平台 + OrderUID string `bson:"order_uid"` // 下單用戶 UID + ReferenceID string `bson:"reference_id"` // 訂單來源 + Count primitive.Decimal128 `bson:"count"` // 訂單數量 + OrderFee primitive.Decimal128 `bson:"order_fee"` // 訂單手續費 + Amount primitive.Decimal128 `bson:"amount"` // 單價 + ReferenceBrand *string `bson:"reference_brand,omitempty"` // 訂單來源平台 + ReferenceUID *string `bson:"reference_uid,omitempty"` // 訂單來源用戶 UID + WalletStatus *int64 `bson:"wallet_status,omitempty"` // 交易金額狀態 + ThreePartyStatus *int64 `bson:"three_party_status,omitempty"` // 三方請求狀態 + DirectionType *int64 `bson:"direction_type,omitempty"` // 交易方向 + CryptoType *string `bson:"crypto_type,omitempty"` // 交易幣種 + ThirdPartyFee *primitive.Decimal128 `bson:"third_party_fee,omitempty"` // 第三方手續費 + CryptoToUSDTRate *primitive.Decimal128 `bson:"crypto_to_usdt_rate,omitempty"` // 加密貨幣對 USDT 匯率 + FiatToUSDRate *primitive.Decimal128 `bson:"fiat_to_usd_rate,omitempty"` // 法幣對 USD 匯率 + FeeCryptoToUSDTRate *primitive.Decimal128 `bson:"fee_crypto_to_usdt_rate,omitempty"` // 手續費加密貨幣對 USDT 匯率 + USDTToCryptoTypeRate *primitive.Decimal128 `bson:"usdt_to_crypto_type_rate,omitempty"` // USDT 對加密貨幣匯率 + PaymentFiat *string `bson:"payment_fiat,omitempty"` // 支付法幣 + PaymentUnitPrice *primitive.Decimal128 `bson:"payment_unit_price,omitempty"` // 加密貨幣單價 + PaymentTemplateID *string `bson:"payment_template_id,omitempty"` // 支付方式配置 ID + OrderArrivalTime *int64 `bson:"order_arrival_time,omitempty"` // 訂單到帳時間 + OrderPaymentTime *int64 `bson:"order_payment_time,omitempty"` // 訂單付款時間 + UnpaidTimeoutSecond *int64 `bson:"unpaid_timeout_second,omitempty"` // 支付期限秒數 + ChainType *string `bson:"chain_type,omitempty"` // 主網類型 + TxHash *string `bson:"tx_hash,omitempty"` // 交易哈希 + FromAddress *string `bson:"from_address,omitempty"` // 來源地址 + ToAddress *string `bson:"to_address,omitempty"` // 目標地址 + ChainFee *primitive.Decimal128 `bson:"chain_fee,omitempty"` // 鏈上交易手續費 + ChainFeeCrypto *string `bson:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別 + Memo *string `bson:"memo,omitempty"` // 鏈上備註 + OrderNote *string `bson:"order_note,omitempty"` // 訂單交易備註 } func (p *Order) CollectionName() string { -- 2.40.1 From ac0b2eab139c1dcfd68523b489d14dc3ee3f0d17 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Wed, 23 Oct 2024 17:38:00 +0800 Subject: [PATCH 7/7] fix test --- internal/domain/errors.go | 9 +++++ internal/logic/orderservice/decimal_tools.go | 2 +- .../logic/orderservice/get_order_logic.go | 6 ++++ .../logic/orderservice/list_order_logic.go | 33 ++++++++++++++++--- .../orderservice/modify_order_status_logic.go | 3 +- internal/model/mongo/order_model.go | 7 ++-- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/internal/domain/errors.go b/internal/domain/errors.go index 15bfbf5..050f912 100644 --- a/internal/domain/errors.go +++ b/internal/domain/errors.go @@ -23,6 +23,11 @@ const ( TimeoutOrderErrorCode ) +const ( + _ ErrorCode = 10 + iota + DataNotFoundErrorCode +) + func CommentError(ec ErrorCode, s ...string) *ers.LibError { return ers.NewError(code.CloudEPOrder, code.DBError, ec.ToUint32(), strings.Join(s, " ")) } @@ -34,3 +39,7 @@ func CommentErrorL(ec ErrorCode, return e } + +func NotFoundError(ec ErrorCode, s ...string) *ers.LibError { + return ers.NewError(code.CloudEPOrder, code.ResourceNotFound, ec.ToUint32(), strings.Join(s, " ")) +} diff --git a/internal/logic/orderservice/decimal_tools.go b/internal/logic/orderservice/decimal_tools.go index cdfc726..80bb3a6 100644 --- a/internal/logic/orderservice/decimal_tools.go +++ b/internal/logic/orderservice/decimal_tools.go @@ -47,7 +47,7 @@ func convertDecimalToDecimal128(d decimal.Decimal) primitive.Decimal128 { // 將 primitive.Decimal128 轉換為字符串 func convertDecimal128ToString(d128 *primitive.Decimal128) *string { - if d128 != nil { + if d128 == nil { return nil } result := d128.String() diff --git a/internal/logic/orderservice/get_order_logic.go b/internal/logic/orderservice/get_order_logic.go index c1753fe..1d3030c 100644 --- a/internal/logic/orderservice/get_order_logic.go +++ b/internal/logic/orderservice/get_order_logic.go @@ -2,9 +2,12 @@ package orderservicelogic import ( "app-cloudep-order-server/gen_result/pb/order" + "app-cloudep-order-server/internal/domain" "app-cloudep-order-server/internal/svc" ers "code.30cm.net/digimon/library-go/errs" "context" + "errors" + "github.com/zeromicro/go-zero/core/stores/mon" "github.com/zeromicro/go-zero/core/logx" ) @@ -40,6 +43,9 @@ func (l *GetOrderLogic) GetOrder(in *order.GetOrderReq) (*order.GetOrderResp, er o, err := l.svcCtx.OrderModel.FindOneBusinessID(l.ctx, in.GetBusinessId()) if err != nil { + if errors.As(err, &mon.ErrNotFound) { + return nil, domain.NotFoundError(domain.DataNotFoundErrorCode, "failed to get this order id:", in.GetBusinessId()) + } return nil, err } diff --git a/internal/logic/orderservice/list_order_logic.go b/internal/logic/orderservice/list_order_logic.go index 2f589f6..c020dc7 100644 --- a/internal/logic/orderservice/list_order_logic.go +++ b/internal/logic/orderservice/list_order_logic.go @@ -52,7 +52,7 @@ type GetOrderListReq struct { // ListOrder 取得訂單列表 func (l *ListOrderLogic) ListOrder(in *order.ListOrderReq) (*order.ListOrderResp, error) { - // 驗證資料,目前只有 Page 必帶,其他要驗證在驗證 + // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(&GetOrderListReq{ PageIndex: in.GetPageIndex(), PageSize: in.GetPageSize(), @@ -61,14 +61,39 @@ func (l *ListOrderLogic) ListOrder(in *order.ListOrderReq) (*order.ListOrderResp return nil, ers.InvalidFormat(err.Error()) } - orders, total, err := l.svcCtx.OrderModel.ListOrder(l.ctx, model.GetOrderListReq{}) + // 構建查詢條件 + req := model.GetOrderListReq{ + PageIndex: in.GetPageIndex(), + PageSize: in.GetPageSize(), + ReferenceID: in.ReferenceId, + ReferenceUID: in.ReferenceUid, + BusinessID: in.BusinessId, + UID: in.Uid, + OrderType: int(in.OrderType), + DirectionType: in.DirectionType, + OrderStatus: in.OrderStatus, + StartCreateTime: in.StartCreateTime, + EndCreateTime: in.EndCreateTime, + StartUpdateTime: in.StartUpdateTime, + EndUpdateTime: in.EndUpdateTime, + StartOrderArrivalTime: in.StartOrderArrivalTime, + EndOrderArrivalTime: in.EndOrderArrivalTime, + StartOrderPaymentTime: in.StartOrderPaymentTime, + EndOrderPaymentTime: in.EndOrderPaymentTime, + CryptoType: in.CryptoType, + TxHash: in.TxHash, + } + + // 查詢訂單 + orders, total, err := l.svcCtx.OrderModel.ListOrder(l.ctx, req) if err != nil { return nil, err } + // 構建返回結果 res := make([]*order.GetOrderResp, 0, len(orders)) for _, item := range orders { - res = append(res, ConvertOrderToGetOrderResp(item)) + res = append(res, convertOrderToGetOrderResp(item)) } return &order.ListOrderResp{ @@ -82,7 +107,7 @@ func (l *ListOrderLogic) ListOrder(in *order.ListOrderReq) (*order.ListOrderResp } // ConvertOrderToGetOrderResp 將 Order 結構轉換為 GetOrderResp -func ConvertOrderToGetOrderResp(o model.Order) *order.GetOrderResp { +func convertOrderToGetOrderResp(o model.Order) *order.GetOrderResp { return &order.GetOrderResp{ BusinessId: o.BusinessID, OrderType: int32(o.OrderType), diff --git a/internal/logic/orderservice/modify_order_status_logic.go b/internal/logic/orderservice/modify_order_status_logic.go index 9167370..5254a21 100644 --- a/internal/logic/orderservice/modify_order_status_logic.go +++ b/internal/logic/orderservice/modify_order_status_logic.go @@ -4,9 +4,8 @@ import ( "app-cloudep-order-server/gen_result/pb/order" "app-cloudep-order-server/internal/domain" model "app-cloudep-order-server/internal/model/mongo" - "context" - ers "code.30cm.net/digimon/library-go/errs" + "context" "app-cloudep-order-server/internal/svc" diff --git a/internal/model/mongo/order_model.go b/internal/model/mongo/order_model.go index f60bfd5..9d760ac 100644 --- a/internal/model/mongo/order_model.go +++ b/internal/model/mongo/order_model.go @@ -49,8 +49,8 @@ type ( BusinessID string `json:"business_id"` UID string `json:"uid"` OrderType int `json:"order_type"` - DirectionType []int64 `json:"direction_type"` - OrderStatus []int64 `json:"order_status"` + DirectionType []int32 `json:"direction_type"` + OrderStatus []int32 `json:"order_status"` StartCreateTime int64 `json:"start_create_time"` EndCreateTime int64 `json:"end_create_time"` @@ -80,7 +80,7 @@ func (m *customOrderModel) UpdateStatus(ctx context.Context, data UpdateStatusRe // 更新 update_time 和 status updates["update_time"] = time.Now().UTC().UnixNano() - updates["status"] = data.Status + updates["order_status"] = data.Status // 使用 updates map 作為 $set 的內容 res, err := m.conn.UpdateOne(ctx, @@ -93,6 +93,7 @@ func (m *customOrderModel) UpdateStatus(ctx context.Context, data UpdateStatusRe }, bson.M{"$set": updates}, // 使用 updates 而不是整個 data ) + return res, err } -- 2.40.1