Compare commits
No commits in common. "feature/wallet" and "main" have entirely different histories.
feature/wa
...
main
11
Makefile
11
Makefile
|
@ -62,14 +62,3 @@ mock-gen: # 建立 mock 資料
|
||||||
.PHONY: migrate-database
|
.PHONY: migrate-database
|
||||||
migrate-database:
|
migrate-database:
|
||||||
migrate -source file://generate/database/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
|
||||||
|
|
||||||
.PHONY: gen-my-sql-model
|
|
||||||
gen-my-sql-model: # 建立 rpc 資料庫
|
|
||||||
goctl model mysql ddl -s ./generate/database/mysql/20230626063528_create_wallet_table.up.sql --style $(GO_ZERO_STYLE) -d ./internal/model -i ''
|
|
||||||
goctl model mysql ddl -s ./generate/database/mysql/20230626065705_create_transaction_table.up.sql --style $(GO_ZERO_STYLE) -d ./internal/model -i ''
|
|
||||||
goctl model mysql ddl -s ./generate/database/mysql/20230913072305_create_wallet_transaction.up.sql --style $(GO_ZERO_STYLE) -d ./internal/model -i ''
|
|
||||||
@echo "Generate mysql model files successfully"
|
|
||||||
|
|
||||||
.PHONY: migrate-database
|
|
||||||
migrate-database:
|
|
||||||
migrate -source file://generate/database/mysql -database 'mysql://root:yytt@tcp(127.0.0.1:3306)/digimon_wallet' up
|
|
||||||
|
|
|
@ -27,15 +27,3 @@ Mongo:
|
||||||
MaxPoolSize: 30
|
MaxPoolSize: 30
|
||||||
MinPoolSize: 10
|
MinPoolSize: 10
|
||||||
MaxConnIdleTime: 30m
|
MaxConnIdleTime: 30m
|
||||||
|
|
||||||
DB:
|
|
||||||
Host: 127.0.0.1
|
|
||||||
Port: 3306
|
|
||||||
User: root
|
|
||||||
Password: yytt
|
|
||||||
name: permission
|
|
||||||
MaxIdleConns: 10
|
|
||||||
MaxOpenConns: 200
|
|
||||||
ConnMaxLifetime: 10s
|
|
||||||
InterpolateParams: false
|
|
||||||
Database: digimon_wallet
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
DROP TABLE IF EXISTS `wallet`;
|
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE TABLE `wallet` (
|
|
||||||
`id` BIGINT(20) AUTO_INCREMENT NOT NULL COMMENT '錢包流水號',
|
|
||||||
`uid` VARCHAR(50) NOT NULL COMMENT '用戶ID',
|
|
||||||
`brand` VARCHAR(50) DEFAULT '' NOT NULL COMMENT '品牌名稱',
|
|
||||||
`currency` VARCHAR(20) NOT NULL COMMENT '幣別(或平台點數)',
|
|
||||||
`balance` DECIMAL(30, 18) UNSIGNED DEFAULT 0 NOT NULL COMMENT '錢包餘額',
|
|
||||||
`wallet_type` TINYINT NOT NULL COMMENT '錢包種類: 1=可用, 2=凍結, 3=限制(僅出金)',
|
|
||||||
`created_at` BIGINT NOT NULL COMMENT '創建時間',
|
|
||||||
`updated_at` BIGINT NOT NULL COMMENT '更新時間',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE INDEX `uq_user_currency_type` (`uid`, `currency`, `wallet_type`),
|
|
||||||
INDEX `idx_user_currency` (`uid`, `currency`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶錢包';
|
|
|
@ -1 +0,0 @@
|
||||||
DROP TABLE IF EXISTS `transaction`;
|
|
|
@ -1,20 +0,0 @@
|
||||||
CREATE TABLE `transaction` (
|
|
||||||
`id` BIGINT(20) AUTO_INCREMENT NOT NULL COMMENT '交易流水號',
|
|
||||||
`tx_id` VARCHAR(200) NOT NULL COMMENT '交易 ID',
|
|
||||||
`order_id` VARCHAR(200) NOT NULL COMMENT '訂單 ID',
|
|
||||||
`brand` VARCHAR(50) NOT NULL COMMENT '品牌名稱',
|
|
||||||
`uid` VARCHAR(50) NOT NULL COMMENT '發起 UID',
|
|
||||||
`to_uid` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '接收方UID',
|
|
||||||
`type` TINYINT NOT NULL COMMENT '交易類型: 1=提現, 2=充值',
|
|
||||||
`business_type` TINYINT NOT NULL COMMENT '業務類型',
|
|
||||||
`currency` VARCHAR(20) NOT NULL COMMENT '幣種',
|
|
||||||
`amount` DECIMAL(30, 18) UNSIGNED DEFAULT 0 NOT NULL COMMENT '交易金額',
|
|
||||||
`balance` DECIMAL(30, 18) UNSIGNED DEFAULT 0 NOT NULL COMMENT '交易後餘額',
|
|
||||||
`before_balance` DECIMAL(30, 18) UNSIGNED DEFAULT 0 NOT NULL COMMENT '交易前餘額',
|
|
||||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '結算狀態',
|
|
||||||
`create_time` BIGINT NOT NULL COMMENT '創建時間(Unix 時間戳,毫秒)',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE INDEX `uk_order_id_type` (`order_id`, `type`),
|
|
||||||
INDEX `idx_uid` (`uid`),
|
|
||||||
INDEX `idx_order_id` (`order_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶錢包交異動紀錄';
|
|
|
@ -1 +0,0 @@
|
||||||
DROP TABLE IF EXISTS `wallet_transaction`;
|
|
|
@ -1,19 +0,0 @@
|
||||||
CREATE TABLE `wallet_journal` (
|
|
||||||
`id` BIGINT(20) AUTO_INCREMENT NOT NULL COMMENT '錢包賬本流水號',
|
|
||||||
`transaction_id` BIGINT(20) NOT NULL COMMENT '交易 ID,對應 transaction 表',
|
|
||||||
`order_id` VARCHAR(200) NOT NULL COMMENT '訂單 ID,對應 order 表',
|
|
||||||
`brand` VARCHAR(50) NOT NULL COMMENT '品牌名稱',
|
|
||||||
`uid` VARCHAR(50) NOT NULL COMMENT '用戶 ID',
|
|
||||||
`wallet_type` TINYINT NOT NULL COMMENT '餘額種類: 1=可用, 2=凍結, 3=限制(僅出金)',
|
|
||||||
`currency` VARCHAR(20) NOT NULL COMMENT '幣種或平台點數',
|
|
||||||
`transaction_amount` DECIMAL(30, 18) UNSIGNED DEFAULT 0 NOT NULL COMMENT '交易金額',
|
|
||||||
`post_transaction_balance` DECIMAL(30, 18) UNSIGNED DEFAULT 0 NOT NULL COMMENT '交易後餘額',
|
|
||||||
`business_type` TINYINT NOT NULL COMMENT '業務類型',
|
|
||||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '狀態',
|
|
||||||
`due_time` BIGINT NOT NULL DEFAULT 0 COMMENT 'T+N 執行時間',
|
|
||||||
`created_at` BIGINT NOT NULL COMMENT '創建時間(Unix 時間戳,毫秒)',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `idx_user_brand` (`uid`, `brand`),
|
|
||||||
KEY `idx_order_id` (`order_id`),
|
|
||||||
KEY `idx_status` (`status`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶錢包變動';
|
|
|
@ -1 +0,0 @@
|
||||||
DROP DATABASE IF EXISTS `digimon_wallet`;
|
|
|
@ -1 +0,0 @@
|
||||||
CREATE DATABASE IF NOT EXISTS `digimon_wallet`;
|
|
7
go.mod
7
go.mod
|
@ -13,12 +13,9 @@ require (
|
||||||
go.uber.org/mock v0.5.0
|
go.uber.org/mock v0.5.0
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.67.1
|
||||||
google.golang.org/protobuf v1.35.1
|
google.golang.org/protobuf v1.35.1
|
||||||
gorm.io/driver/sqlite v1.5.6
|
|
||||||
gorm.io/gorm v1.25.12
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
@ -36,7 +33,6 @@ require (
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
@ -46,8 +42,6 @@ require (
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
|
@ -55,7 +49,6 @@ require (
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
|
|
|
@ -24,16 +24,4 @@ type Config struct {
|
||||||
// Compressors []string
|
// Compressors []string
|
||||||
// EnableStandardReadWriteSplitMode bool
|
// EnableStandardReadWriteSplitMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
DB struct {
|
|
||||||
User string
|
|
||||||
Password string
|
|
||||||
Host string
|
|
||||||
Port int64
|
|
||||||
Database string
|
|
||||||
MaxIdleConns int
|
|
||||||
MaxOpenConns int
|
|
||||||
ConnMaxLifetime time.Duration
|
|
||||||
InterpolateParams bool
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,6 @@ const (
|
||||||
TimeoutOrderErrorCode
|
TimeoutOrderErrorCode
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error Code 統一這邊改
|
|
||||||
const (
|
|
||||||
_ ErrorCode = 20 + iota
|
|
||||||
CreateWalletErrorCode
|
|
||||||
WalletTxErrorCode
|
|
||||||
WalletBalanceNotFound
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_ ErrorCode = 10 + iota
|
_ ErrorCode = 10 + iota
|
||||||
DataNotFoundErrorCode
|
DataNotFoundErrorCode
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type OrderStatus int64
|
||||||
|
|
||||||
|
func (o *OrderStatus) ToInt64() int64 {
|
||||||
|
return int64(*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 // 交易超時
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderType int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
OrderTypeTest OrderType = 0 // 測試訂單
|
||||||
|
)
|
||||||
|
|
||||||
|
func (o *OrderType) ToInt() int {
|
||||||
|
return int(*o)
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package order
|
|
||||||
|
|
||||||
// Status 表示訂單狀態
|
|
||||||
type Status int64
|
|
||||||
|
|
||||||
// ToInt64 將訂單狀態轉為 int64
|
|
||||||
func (s Status) ToInt64() int64 {
|
|
||||||
return int64(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 訂單狀態常量
|
|
||||||
const (
|
|
||||||
Created Status = iota // 訂單已建立
|
|
||||||
Failed // 建單失敗
|
|
||||||
UnderReview // 審核中
|
|
||||||
Processing // 付款中
|
|
||||||
Paid // 已付款
|
|
||||||
AwaitingTransfer // 待轉帳
|
|
||||||
InDispute // 申訴中
|
|
||||||
Completed // 交易完成
|
|
||||||
FailedTrade // 交易失敗
|
|
||||||
Cancelled // 交易取消
|
|
||||||
Abnormal // 交易異常
|
|
||||||
TimedOut // 交易超時
|
|
||||||
)
|
|
|
@ -1,15 +0,0 @@
|
||||||
package order
|
|
||||||
|
|
||||||
// Type 表示訂單類型
|
|
||||||
type Type int64
|
|
||||||
|
|
||||||
// ToInt 將訂單類型轉為 int
|
|
||||||
func (t Type) ToInt() int {
|
|
||||||
return int(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 訂單類型常量
|
|
||||||
|
|
||||||
const (
|
|
||||||
TestType Type = iota // 測試訂單
|
|
||||||
)
|
|
|
@ -1,21 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"app-cloudep-trade-service/internal/model"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 如果有需要Select for update 的話,請在 Option 當中加上鎖並且傳入 sqlx.conn)
|
|
||||||
|
|
||||||
// UserWalletOperator 針對使用者的錢包基本操作接口
|
|
||||||
type UserWalletOperator interface {
|
|
||||||
// Balances 取得多種類別餘額
|
|
||||||
Balances(ctx context.Context, balanceType []wallet.BalanceType, opts ...WalletOperatorOption) ([]model.Wallet, error)
|
|
||||||
// LocalBalance 取得本地錢包的數額
|
|
||||||
LocalBalance(balanceType wallet.BalanceType) decimal.Decimal
|
|
||||||
// GetBalancesByID 取得錢包的數額 ByID
|
|
||||||
GetBalancesByID(ctx context.Context, ids []int64, opts ...WalletOperatorOption) ([]model.Wallet, error)
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"app-cloudep-trade-service/internal/model"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WalletRepository 錢包基礎操作(可能有平台,使用者等多元的錢包)
|
|
||||||
type WalletRepository interface {
|
|
||||||
// Create 建立錢包(某 id 的可用,凍結,限制三種)
|
|
||||||
Create(ctx context.Context, uid, currency, brand string) ([]*model.Wallet, error)
|
|
||||||
// Balances 取得某個錢包的餘額
|
|
||||||
Balances(ctx context.Context, req BalanceReq) ([]model.Wallet, error)
|
|
||||||
// GetUserWalletOperator 取得使用者錢包操作看使否需要使用 transaction
|
|
||||||
GetUserWalletOperator(uid, currency string, opts ...Option) UserWalletOperator
|
|
||||||
// Transaction 把 tx 暴露出來
|
|
||||||
Transaction(ctx context.Context, fn func(tx sqlx.Session) error) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalanceReq 取得全部的,因為一個人錢包種類的不會太多,故全撈
|
|
||||||
type BalanceReq struct {
|
|
||||||
UID []string
|
|
||||||
Currency []string
|
|
||||||
BalanceType []wallet.BalanceType
|
|
||||||
}
|
|
||||||
|
|
||||||
type Option func() sqlx.SqlConn
|
|
|
@ -1,67 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WalletOperatorOption 選項模式
|
|
||||||
type WalletOperatorOption func(*WalletOptions)
|
|
||||||
|
|
||||||
type WalletOptions struct {
|
|
||||||
WithLock bool
|
|
||||||
OrderID string
|
|
||||||
Amount decimal.Decimal
|
|
||||||
BalanceType wallet.BalanceType
|
|
||||||
Business wallet.BusinessLogic
|
|
||||||
Tx *sqlx.SqlConn
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyOptions 將多個 WalletOperatorOption 應用到一個 walletOptions 中
|
|
||||||
func ApplyOptions(opts ...WalletOperatorOption) WalletOptions {
|
|
||||||
options := WalletOptions{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithLock() WalletOperatorOption {
|
|
||||||
return func(opts *WalletOptions) {
|
|
||||||
opts.WithLock = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithOrderID(orderID string) WalletOperatorOption {
|
|
||||||
return func(opts *WalletOptions) {
|
|
||||||
opts.OrderID = orderID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithAmount(amount decimal.Decimal) WalletOperatorOption {
|
|
||||||
return func(opts *WalletOptions) {
|
|
||||||
opts.Amount = amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithKind(balanceType wallet.BalanceType) WalletOperatorOption {
|
|
||||||
return func(opts *WalletOptions) {
|
|
||||||
opts.BalanceType = balanceType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithBusiness(business wallet.BusinessLogic) WalletOperatorOption {
|
|
||||||
return func(opts *WalletOptions) {
|
|
||||||
opts.Business = business
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithSession(session *sqlx.SqlConn) WalletOperatorOption {
|
|
||||||
return func(opts *WalletOptions) {
|
|
||||||
opts.Tx = session
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package usecase
|
package usecase
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
|
@ -38,7 +38,7 @@ type ModifyOrderQuery struct {
|
||||||
// CancelOrderQuery 1.建單失敗 9.交易取消 10.交易異常
|
// CancelOrderQuery 1.建單失敗 9.交易取消 10.交易異常
|
||||||
type CancelOrderQuery struct {
|
type CancelOrderQuery struct {
|
||||||
BusinessID string
|
BusinessID string
|
||||||
Status order.Status
|
Status domain.OrderStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOrderQuery 刪除訂單(軟刪除)
|
// DeleteOrderQuery 刪除訂單(軟刪除)
|
||||||
|
@ -52,42 +52,42 @@ type GetOrderQuery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetOrderResp struct {
|
type GetOrderResp struct {
|
||||||
BusinessID string // 訂單業務流水號
|
BusinessID string // 訂單業務流水號
|
||||||
OrderType order.Type `json:"order_type"` // 訂單類型
|
OrderType domain.OrderType `json:"order_type"` // 訂單類型
|
||||||
OrderStatus order.Status `json:"order_status"` // 訂單狀態
|
OrderStatus domain.OrderStatus `json:"order_status"` // 訂單狀態
|
||||||
Brand string `json:"brand"` // 下單平台
|
Brand string `json:"brand"` // 下單平台
|
||||||
OrderUID string `json:"order_uid"` // 下單用戶 UID
|
OrderUID string `json:"order_uid"` // 下單用戶 UID
|
||||||
ReferenceID string `json:"reference_id"` // 訂單來源
|
ReferenceID string `json:"reference_id"` // 訂單來源
|
||||||
Count string `json:"count"` // 訂單數量 (decimal to string)
|
Count string `json:"count"` // 訂單數量 (decimal to string)
|
||||||
OrderFee string `json:"order_fee"` // 訂單手續費 (decimal to string)
|
OrderFee string `json:"order_fee"` // 訂單手續費 (decimal to string)
|
||||||
Amount string `json:"amount"` // 單價 (decimal to string)
|
Amount string `json:"amount"` // 單價 (decimal to string)
|
||||||
ReferenceBrand *string `json:"reference_brand,omitempty"` // 訂單來源平台
|
ReferenceBrand *string `json:"reference_brand,omitempty"` // 訂單來源平台
|
||||||
ReferenceUID *string `json:"reference_uid,omitempty"` // 訂單來源用戶 UID
|
ReferenceUID *string `json:"reference_uid,omitempty"` // 訂單來源用戶 UID
|
||||||
WalletStatus *int64 `json:"wallet_status,omitempty"` // 交易金額狀態
|
WalletStatus *int64 `json:"wallet_status,omitempty"` // 交易金額狀態
|
||||||
ThreePartyStatus *int64 `json:"three_party_status,omitempty"` // 三方請求狀態
|
ThreePartyStatus *int64 `json:"three_party_status,omitempty"` // 三方請求狀態
|
||||||
DirectionType *int64 `json:"direction_type,omitempty"` // 交易方向
|
DirectionType *int64 `json:"direction_type,omitempty"` // 交易方向
|
||||||
CryptoType *string `json:"crypto_type,omitempty"` // 交易幣種
|
CryptoType *string `json:"crypto_type,omitempty"` // 交易幣種
|
||||||
ThirdPartyFee *string `json:"third_party_fee,omitempty"` // 第三方手續費 (decimal to string)
|
ThirdPartyFee *string `json:"third_party_fee,omitempty"` // 第三方手續費 (decimal to string)
|
||||||
CryptoToUsdtRate *string `json:"crypto_to_usdt_rate,omitempty"` // 交易幣種對 USDT 匯率 (decimal to string)
|
CryptoToUsdtRate *string `json:"crypto_to_usdt_rate,omitempty"` // 交易幣種對 USDT 匯率 (decimal to string)
|
||||||
FiatToUsdRate *string `json:"fiat_to_usd_rate,omitempty"` // 法幣對 USD 匯率 (decimal to string)
|
FiatToUsdRate *string `json:"fiat_to_usd_rate,omitempty"` // 法幣對 USD 匯率 (decimal to string)
|
||||||
FeeCryptoToUsdtRate *string `json:"fee_crypto_to_usdt_rate,omitempty"` // 手續費幣種對 USDT 匯率 (decimal to string)
|
FeeCryptoToUsdtRate *string `json:"fee_crypto_to_usdt_rate,omitempty"` // 手續費幣種對 USDT 匯率 (decimal to string)
|
||||||
UsdtToCryptoTypeRate *string `json:"usdt_to_crypto_type_rate,omitempty"` // USDT 對交易幣種匯率 (decimal to string)
|
UsdtToCryptoTypeRate *string `json:"usdt_to_crypto_type_rate,omitempty"` // USDT 對交易幣種匯率 (decimal to string)
|
||||||
PaymentFiat *string `json:"payment_fiat,omitempty"` // 支付法幣
|
PaymentFiat *string `json:"payment_fiat,omitempty"` // 支付法幣
|
||||||
PaymentUnitPrice *string `json:"payment_unit_price,omitempty"` // crypto 單價 (decimal to string)
|
PaymentUnitPrice *string `json:"payment_unit_price,omitempty"` // crypto 單價 (decimal to string)
|
||||||
PaymentTemplateID *string `json:"payment_template_id,omitempty"` // 支付方式配置 ID
|
PaymentTemplateID *string `json:"payment_template_id,omitempty"` // 支付方式配置 ID
|
||||||
OrderArrivalTime *int64 `json:"order_arrival_time,omitempty"` // 訂單到帳時間
|
OrderArrivalTime *int64 `json:"order_arrival_time,omitempty"` // 訂單到帳時間
|
||||||
OrderPaymentTime *int64 `json:"order_payment_time,omitempty"` // 訂單付款時間
|
OrderPaymentTime *int64 `json:"order_payment_time,omitempty"` // 訂單付款時間
|
||||||
UnpaidTimeoutSecond *int64 `json:"unpaid_timeout_second,omitempty"` // 支付期限秒數
|
UnpaidTimeoutSecond *int64 `json:"unpaid_timeout_second,omitempty"` // 支付期限秒數
|
||||||
ChainType *string `json:"chain_type,omitempty"` // 主網類型
|
ChainType *string `json:"chain_type,omitempty"` // 主網類型
|
||||||
TxHash *string `json:"tx_hash,omitempty,omitempty"` // 交易哈希
|
TxHash *string `json:"tx_hash,omitempty,omitempty"` // 交易哈希
|
||||||
FromAddress *string `json:"from_address,omitempty,omitempty"` // 來源地址
|
FromAddress *string `json:"from_address,omitempty,omitempty"` // 來源地址
|
||||||
ToAddress *string `json:"to_address,omitempty,omitempty"` // 目標地址
|
ToAddress *string `json:"to_address,omitempty,omitempty"` // 目標地址
|
||||||
ChainFee *string `json:"chain_fee,omitempty"` // 鏈上交易手續費 (decimal to string)
|
ChainFee *string `json:"chain_fee,omitempty"` // 鏈上交易手續費 (decimal to string)
|
||||||
ChainFeeCrypto *string `json:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別
|
ChainFeeCrypto *string `json:"chain_fee_crypto,omitempty"` // 鏈上手續費使用幣別
|
||||||
Memo *string `json:"memo,omitempty"` // 鏈上備註
|
Memo *string `json:"memo,omitempty"` // 鏈上備註
|
||||||
OrderNote *string `json:"order_note,omitempty"` // 訂單交易備註
|
OrderNote *string `json:"order_note,omitempty"` // 訂單交易備註
|
||||||
CreateTime int64 `json:"create_time,omitempty"` // 建立時間
|
CreateTime int64 `json:"create_time,omitempty"` // 建立時間
|
||||||
UpdateTime int64 `json:"update_time,omitempty"` // 更新時間
|
UpdateTime int64 `json:"update_time,omitempty"` // 更新時間
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetOrderListReq struct {
|
type GetOrderListReq struct {
|
||||||
|
@ -98,7 +98,7 @@ type GetOrderListReq struct {
|
||||||
ReferenceUID string
|
ReferenceUID string
|
||||||
BusinessID string
|
BusinessID string
|
||||||
UID string
|
UID string
|
||||||
OrderType order.Type
|
OrderType domain.OrderType
|
||||||
DirectionType []int64
|
DirectionType []int64
|
||||||
OrderStatus []int64
|
OrderStatus []int64
|
||||||
|
|
||||||
|
@ -128,16 +128,16 @@ type ListOrderResp struct {
|
||||||
|
|
||||||
type CreateOrderReq struct {
|
type CreateOrderReq struct {
|
||||||
BusinessID string
|
BusinessID string
|
||||||
OrderType order.Type // 訂單類型
|
OrderType domain.OrderType // 訂單類型
|
||||||
OrderStatus order.Status // 訂單狀態
|
OrderStatus domain.OrderStatus // 訂單狀態
|
||||||
Brand string // 下單平台
|
Brand string // 下單平台
|
||||||
OrderUID string // 下單用戶 UID
|
OrderUID string // 下單用戶 UID
|
||||||
ReferenceID string // 訂單來源
|
ReferenceID string // 訂單來源
|
||||||
Count decimal.Decimal // 訂單數量
|
Count decimal.Decimal // 訂單數量
|
||||||
OrderFee decimal.Decimal // 訂單手續費
|
OrderFee decimal.Decimal // 訂單手續費
|
||||||
Amount decimal.Decimal // 單價
|
Amount decimal.Decimal // 單價
|
||||||
WalletStatus int64 // 交易金額狀態
|
WalletStatus int64 // 交易金額狀態
|
||||||
DirectionType int64 // 交易方向
|
DirectionType int64 // 交易方向
|
||||||
// 以上為必要欄位,下面是區塊鏈時才需要
|
// 以上為必要欄位,下面是區塊鏈時才需要
|
||||||
ReferenceBrand *string // 訂單來源平台
|
ReferenceBrand *string // 訂單來源平台
|
||||||
ReferenceUID *string // 訂單來源用戶 UID
|
ReferenceUID *string // 訂單來源用戶 UID
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
package usecase
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WalletQueryUseCase 定義所有查詢行為(餘額查詢、檢查、歷史記錄)的行為
|
|
||||||
type WalletQueryUseCase interface {
|
|
||||||
// Balance 用戶餘額
|
|
||||||
Balance(ctx context.Context, req BalanceReq) ([]Balance, error)
|
|
||||||
// CheckBalance 根據tx檢查用戶餘額是否足夠
|
|
||||||
CheckBalance(ctx context.Context, tx Transaction) error
|
|
||||||
// HistoryBalance 歷史餘額變化
|
|
||||||
HistoryBalance(ctx context.Context, req BalanceReq) ([]Balance, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalletOperationUseCase 定義所有錢包操作(提款、充值、凍結等)的行為
|
|
||||||
type WalletOperationUseCase interface {
|
|
||||||
// Withdraw 提款
|
|
||||||
Withdraw(ctx context.Context, tx Transaction) error
|
|
||||||
// Deposit 充值
|
|
||||||
Deposit(ctx context.Context, tx Transaction) error
|
|
||||||
// DepositUnconfirmed 增加限制餘額
|
|
||||||
DepositUnconfirmed(ctx context.Context, tx Transaction) error
|
|
||||||
// Freeze 凍結
|
|
||||||
Freeze(ctx context.Context, tx Transaction) error
|
|
||||||
// AppendFreeze 追加凍結金額
|
|
||||||
AppendFreeze(ctx context.Context, tx Transaction) error
|
|
||||||
// UnFreeze 解凍
|
|
||||||
UnFreeze(ctx context.Context, tx Transaction) error
|
|
||||||
// RollbackFreeze Rollback 凍結,不可指定金額(剩餘order凍結金額)
|
|
||||||
RollbackFreeze(ctx context.Context, tx Transaction) error
|
|
||||||
// RollbackFreezeAddAvailable Rollback剩餘凍結,可指定金額(剩餘order凍結金額)
|
|
||||||
RollbackFreezeAddAvailable(ctx context.Context, tx Transaction) error
|
|
||||||
// CancelFreeze 取消凍結,可指定金額
|
|
||||||
CancelFreeze(ctx context.Context, tx Transaction) error
|
|
||||||
// Unconfirmed 增加限制
|
|
||||||
Unconfirmed(ctx context.Context, tx Transaction) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalletUseCase 基礎操作類別
|
|
||||||
type WalletUseCase interface {
|
|
||||||
WalletOperationUseCase
|
|
||||||
WalletQueryUseCase
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction 交易
|
|
||||||
type Transaction struct {
|
|
||||||
OrderID string // 交易訂單
|
|
||||||
UID string // 交易發起人
|
|
||||||
ToUID string // 交易接收人
|
|
||||||
Currency string // 幣別
|
|
||||||
Amount decimal.Decimal // 交易金額
|
|
||||||
BeforeBalance decimal.Decimal // 交易前餘額
|
|
||||||
Type wallet.TransactionType // 交易種類
|
|
||||||
BusinessType wallet.BusinessLogic // 商業種類
|
|
||||||
Brand string // 轉帳平台
|
|
||||||
From wallet.BalanceType // 從哪種錢包類型
|
|
||||||
To wallet.BalanceType // 到哪種錢包類型
|
|
||||||
}
|
|
||||||
|
|
||||||
type BalanceReq struct {
|
|
||||||
UID string // 用戶 UID
|
|
||||||
Currency string // 幣值
|
|
||||||
BeforeHour int // 在某個時段之前
|
|
||||||
}
|
|
||||||
|
|
||||||
type Balance struct {
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
Available decimal.Decimal `json:"available"`
|
|
||||||
Unavailable UnavailableBalance `json:"unavailable"`
|
|
||||||
UpdateTime int64 `json:"update_time"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnavailableBalance struct {
|
|
||||||
Freeze decimal.Decimal `json:"freeze"`
|
|
||||||
Unconfirmed decimal.Decimal `json:"unconfirmed"`
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
package wallet
|
package domain
|
||||||
|
|
||||||
type Status int64
|
type WalletStatus int64
|
||||||
|
|
||||||
func (o *Status) ToInt() int {
|
func (o *WalletStatus) ToInt() int {
|
||||||
return int(*o)
|
return int(*o)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
package wallet
|
|
||||||
|
|
||||||
// ===================商業邏輯類型===================
|
|
||||||
|
|
||||||
type BusinessLogic string
|
|
||||||
|
|
||||||
// 定義商業邏輯名稱
|
|
||||||
const (
|
|
||||||
// NoLogic 無商業邏輯
|
|
||||||
NoLogic BusinessLogic = ""
|
|
||||||
// OrderLogic 訂單相關業務邏輯
|
|
||||||
OrderLogic BusinessLogic = "order"
|
|
||||||
// SystemTransferLogic 系統劃轉邏輯
|
|
||||||
SystemTransferLogic BusinessLogic = "system_transfer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 定義商業邏輯類型
|
|
||||||
const (
|
|
||||||
// NoLogicType 無商業邏輯類型
|
|
||||||
NoLogicType int8 = iota
|
|
||||||
// OrderLogicType 訂單業務邏輯類型
|
|
||||||
OrderLogicType
|
|
||||||
// SystemTransferLogicType 系統劃轉業務邏輯類型
|
|
||||||
SystemTransferLogicType
|
|
||||||
)
|
|
||||||
|
|
||||||
// 定義名稱和類型之間的映射
|
|
||||||
var logicNameToType = map[BusinessLogic]int8{
|
|
||||||
OrderLogic: OrderLogicType,
|
|
||||||
SystemTransferLogic: SystemTransferLogicType,
|
|
||||||
}
|
|
||||||
|
|
||||||
var logicTypeToName = map[int8]BusinessLogic{
|
|
||||||
OrderLogicType: OrderLogic,
|
|
||||||
SystemTransferLogicType: SystemTransferLogic,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToInt8 將 BusinessLogic 轉換為對應的 int8 類型
|
|
||||||
func (b BusinessLogic) ToInt8() int8 {
|
|
||||||
if val, ok := logicNameToType[b]; ok {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
return NoLogicType
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogicTypeToName 將 int8 類型轉換為對應的 BusinessLogic 名稱
|
|
||||||
func LogicTypeToName(b int8) BusinessLogic {
|
|
||||||
if val, ok := logicTypeToName[b]; ok {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
return NoLogic
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package wallet
|
|
||||||
|
|
||||||
// ===================交易類型===================
|
|
||||||
|
|
||||||
type TransactionType int64
|
|
||||||
|
|
||||||
// 定義交易類型
|
|
||||||
const (
|
|
||||||
// Deposit 增加可用餘額的充值交易
|
|
||||||
Deposit TransactionType = iota + 1
|
|
||||||
|
|
||||||
// Withdraw 減少可用餘額的提現交易
|
|
||||||
Withdraw
|
|
||||||
|
|
||||||
// Freeze 將可用餘額轉入凍結餘額的凍結交易
|
|
||||||
Freeze
|
|
||||||
|
|
||||||
// Unfreeze 減少凍結餘額的解凍交易
|
|
||||||
Unfreeze
|
|
||||||
|
|
||||||
// RollbackFreeze 回滾凍結:減少凍結餘額並恢復至可用餘額,不指定金額
|
|
||||||
RollbackFreeze
|
|
||||||
|
|
||||||
// Unconfirmed 限制交易:減少凍結餘額並增加他人限制餘額
|
|
||||||
Unconfirmed
|
|
||||||
|
|
||||||
// CancelFreeze 取消凍結:減少凍結餘額並恢復至可用餘額,允許指定金額
|
|
||||||
CancelFreeze
|
|
||||||
|
|
||||||
// DepositToUnconfirmed 增加限制餘額的充值交易
|
|
||||||
DepositToUnconfirmed
|
|
||||||
|
|
||||||
// AppendFreeze 追加凍結:減少可用餘額並增加凍結餘額
|
|
||||||
AppendFreeze
|
|
||||||
|
|
||||||
// RollbackFreezeToAvailable 回滾凍結:指定金額回滾凍結餘額並增加至可用餘額
|
|
||||||
RollbackFreezeToAvailable
|
|
||||||
|
|
||||||
// PlatformDistribution 平台分發交易
|
|
||||||
PlatformDistribution
|
|
||||||
|
|
||||||
// SystemTransfer 系統劃轉交易
|
|
||||||
SystemTransfer
|
|
||||||
)
|
|
||||||
|
|
||||||
// ToInt 將交易類型轉換為 int64
|
|
||||||
func (t TransactionType) ToInt() int64 {
|
|
||||||
return int64(t)
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package wallet
|
|
||||||
|
|
||||||
// ===================錢包餘額類型===================
|
|
||||||
|
|
||||||
// BalanceType 表示錢包餘額的類型
|
|
||||||
type BalanceType int64
|
|
||||||
|
|
||||||
// 錢包餘額類型
|
|
||||||
const (
|
|
||||||
// AvailableBalanceType 表示可動用的餘額
|
|
||||||
AvailableBalanceType BalanceType = iota + 1
|
|
||||||
|
|
||||||
// FrozenBalanceType 表示在交易過程中凍結的餘額
|
|
||||||
FrozenBalanceType
|
|
||||||
|
|
||||||
// PendingBalanceType 表示已提交但尚未確認完成的交易餘額
|
|
||||||
PendingBalanceType
|
|
||||||
|
|
||||||
// ContractAvailableBalanceType 表示合約或交易可用的餘額
|
|
||||||
ContractAvailableBalanceType
|
|
||||||
|
|
||||||
// ContractFrozenBalanceType 表示合約或交易凍結的餘額
|
|
||||||
ContractFrozenBalanceType
|
|
||||||
)
|
|
||||||
|
|
||||||
// balanceTypeToName 將餘額類型映射到其名稱
|
|
||||||
var balanceTypeToName = map[BalanceType]string{
|
|
||||||
AvailableBalanceType: "available",
|
|
||||||
FrozenBalanceType: "frozen",
|
|
||||||
PendingBalanceType: "pending",
|
|
||||||
ContractAvailableBalanceType: "contract_available",
|
|
||||||
ContractFrozenBalanceType: "contract_frozen",
|
|
||||||
}
|
|
||||||
|
|
||||||
// nameToBalanceType 將名稱映射到餘額類型
|
|
||||||
var nameToBalanceType = map[string]BalanceType{
|
|
||||||
"available": AvailableBalanceType,
|
|
||||||
"frozen": FrozenBalanceType,
|
|
||||||
"pending": PendingBalanceType,
|
|
||||||
"contract_available": ContractAvailableBalanceType,
|
|
||||||
"contract_frozen": ContractFrozenBalanceType,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name 返回餘額類型的名稱
|
|
||||||
func (b BalanceType) Name() string {
|
|
||||||
return balanceTypeToName[b]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBalanceTypeByName 根據名稱返回對應的餘額類型
|
|
||||||
func GetBalanceTypeByName(name string) BalanceType {
|
|
||||||
return nameToBalanceType[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllBalanceTypes 包含目前所有的錢包餘額類型
|
|
||||||
var AllBalanceTypes = []BalanceType{
|
|
||||||
AvailableBalanceType, FrozenBalanceType, PendingBalanceType,
|
|
||||||
ContractAvailableBalanceType, ContractFrozenBalanceType,
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package orderservicelogic
|
package orderservicelogic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"app-cloudep-trade-service/internal/domain/usecase"
|
"app-cloudep-trade-service/internal/domain/usecase"
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ func (l *CancelOrderLogic) CancelOrder(in *trade.CancelOrderReq) (*trade.OKResp,
|
||||||
|
|
||||||
err := l.svcCtx.OrderUseCase.CancelOrder(l.ctx, usecase.CancelOrderQuery{
|
err := l.svcCtx.OrderUseCase.CancelOrder(l.ctx, usecase.CancelOrderQuery{
|
||||||
BusinessID: in.GetBusinessId(),
|
BusinessID: in.GetBusinessId(),
|
||||||
Status: order.Status(in.GetStatus()),
|
Status: domain.OrderStatus(in.GetStatus()),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -2,7 +2,7 @@ package orderservicelogic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/gen_result/pb/trade"
|
"app-cloudep-trade-service/gen_result/pb/trade"
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"app-cloudep-trade-service/internal/domain/usecase"
|
"app-cloudep-trade-service/internal/domain/usecase"
|
||||||
"app-cloudep-trade-service/internal/svc"
|
"app-cloudep-trade-service/internal/svc"
|
||||||
"context"
|
"context"
|
||||||
|
@ -169,8 +169,8 @@ func buildCreateOrderReq(in *trade.CreateOrderReq) *createOrderReq {
|
||||||
func toCreateOrderUseCase(req *createOrderReq) usecase.CreateOrderReq {
|
func toCreateOrderUseCase(req *createOrderReq) usecase.CreateOrderReq {
|
||||||
return usecase.CreateOrderReq{
|
return usecase.CreateOrderReq{
|
||||||
BusinessID: req.BusinessID,
|
BusinessID: req.BusinessID,
|
||||||
OrderType: order.Type(req.OrderType),
|
OrderType: domain.OrderType(req.OrderType),
|
||||||
OrderStatus: order.Status(req.OrderStatus),
|
OrderStatus: domain.OrderStatus(req.OrderStatus),
|
||||||
Brand: req.Brand,
|
Brand: req.Brand,
|
||||||
OrderUID: req.OrderUID,
|
OrderUID: req.OrderUID,
|
||||||
ReferenceID: req.ReferenceID,
|
ReferenceID: req.ReferenceID,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package orderservicelogic
|
package orderservicelogic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"app-cloudep-trade-service/internal/domain/usecase"
|
"app-cloudep-trade-service/internal/domain/usecase"
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ func toGetOrderListReq(req *trade.ListOrderReq) usecase.GetOrderListReq {
|
||||||
ReferenceUID: req.ReferenceUid,
|
ReferenceUID: req.ReferenceUid,
|
||||||
BusinessID: req.BusinessId,
|
BusinessID: req.BusinessId,
|
||||||
UID: req.Uid,
|
UID: req.Uid,
|
||||||
OrderType: order.Type(req.OrderType),
|
OrderType: domain.OrderType(req.OrderType),
|
||||||
DirectionType: i32To64(req.DirectionType),
|
DirectionType: i32To64(req.DirectionType),
|
||||||
OrderStatus: i32To64(req.OrderStatus),
|
OrderStatus: i32To64(req.OrderStatus),
|
||||||
StartCreateTime: req.StartCreateTime,
|
StartCreateTime: req.StartCreateTime,
|
||||||
|
|
|
@ -2,8 +2,23 @@ package orderservicelogic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// getInt64Value 將 *int64 的值返回,如果為 nil 則返回 0
|
// getInt64Value 將 *int64 的值返回,如果為 nil 則返回 0
|
||||||
func getInt64Value(val *int64) int64 {
|
func getInt64Value(val *int64) int64 {
|
||||||
if val == nil {
|
if val == nil {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
@ -99,7 +99,7 @@ func (m *customOrderModel) UpdateTimeoutOrder(ctx context.Context, req UpdateTim
|
||||||
// 構建過濾條件,選擇創建時間在指定時間之前且狀態為 0 (創建) 的項目
|
// 構建過濾條件,選擇創建時間在指定時間之前且狀態為 0 (創建) 的項目
|
||||||
filter := bson.M{
|
filter := bson.M{
|
||||||
"create_time": bson.M{"$lt": req.CreateTimeBefore},
|
"create_time": bson.M{"$lt": req.CreateTimeBefore},
|
||||||
"status": order.Cancelled,
|
"status": domain.OrderStatusCreated,
|
||||||
"$or": []bson.M{
|
"$or": []bson.M{
|
||||||
{"delete_time": bson.M{"$exists": false}},
|
{"delete_time": bson.M{"$exists": false}},
|
||||||
{"delete_time": 0},
|
{"delete_time": 0},
|
||||||
|
@ -109,7 +109,7 @@ func (m *customOrderModel) UpdateTimeoutOrder(ctx context.Context, req UpdateTim
|
||||||
// 更新內容,將狀態設置為 11,並更新 update_time
|
// 更新內容,將狀態設置為 11,並更新 update_time
|
||||||
updates := bson.M{
|
updates := bson.M{
|
||||||
"$set": bson.M{
|
"$set": bson.M{
|
||||||
"status": order.TimedOut,
|
"status": domain.OrderStatusTimeout,
|
||||||
"update_time": time.Now().UTC().UnixNano(),
|
"update_time": time.Now().UTC().UnixNano(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
@ -12,8 +12,8 @@ type Order struct {
|
||||||
UpdateTime int64 `bson:"update_time"`
|
UpdateTime int64 `bson:"update_time"`
|
||||||
CreateTime int64 `bson:"create_time"`
|
CreateTime int64 `bson:"create_time"`
|
||||||
BusinessID string `bson:"business_id"` // 訂單業務流水號
|
BusinessID string `bson:"business_id"` // 訂單業務流水號
|
||||||
OrderType order.Type `bson:"order_type"` // 訂單類型
|
OrderType domain.OrderType `bson:"order_type"` // 訂單類型
|
||||||
OrderStatus order.Status `bson:"order_status"` // 訂單狀態
|
OrderStatus domain.OrderStatus `bson:"order_status"` // 訂單狀態
|
||||||
Brand string `bson:"brand"` // 下單平台
|
Brand string `bson:"brand"` // 下單平台
|
||||||
OrderUID string `bson:"order_uid"` // 下單用戶 UID
|
OrderUID string `bson:"order_uid"` // 下單用戶 UID
|
||||||
ReferenceID string `bson:"reference_id"` // 訂單來源
|
ReferenceID string `bson:"reference_id"` // 訂單來源
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// queryBindINBuilder 用來動態生成 WHERE 條件和參數
|
|
||||||
func queryBindINBuilder(baseQuery string, conditions map[string][]any) (string, []any) {
|
|
||||||
args := make([]any, 0)
|
|
||||||
whereClause := make([]string, 0)
|
|
||||||
|
|
||||||
for column, values := range conditions {
|
|
||||||
if len(values) > 0 {
|
|
||||||
placeholders := strings.Repeat("?,", len(values))
|
|
||||||
placeholders = placeholders[:len(placeholders)-1]
|
|
||||||
whereClause = append(whereClause, fmt.Sprintf("%s IN (%s)", column, placeholders))
|
|
||||||
args = append(args, values...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 將 WHERE 條件添加到查詢中
|
|
||||||
if len(whereClause) > 0 {
|
|
||||||
baseQuery += " WHERE " + strings.Join(whereClause, " AND ")
|
|
||||||
}
|
|
||||||
return baseQuery, args
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertSliceToInterface[T any](slice []T) []any {
|
|
||||||
result := make([]any, 0, len(slice))
|
|
||||||
for _, v := range slice {
|
|
||||||
|
|
||||||
result = append(result, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import "github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
|
|
||||||
var _ TransactionModel = (*customTransactionModel)(nil)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// TransactionModel is an interface to be customized, add more methods here,
|
|
||||||
// and implement the added methods in customTransactionModel.
|
|
||||||
TransactionModel interface {
|
|
||||||
transactionModel
|
|
||||||
}
|
|
||||||
|
|
||||||
customTransactionModel struct {
|
|
||||||
*defaultTransactionModel
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewTransactionModel returns a model for the database table.
|
|
||||||
func NewTransactionModel(conn sqlx.SqlConn) TransactionModel {
|
|
||||||
return &customTransactionModel{
|
|
||||||
defaultTransactionModel: newTransactionModel(conn),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
|
||||||
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
transactionFieldNames = builder.RawFieldNames(&Transaction{})
|
|
||||||
transactionRows = strings.Join(transactionFieldNames, ",")
|
|
||||||
transactionRowsExpectAutoSet = strings.Join(stringx.Remove(transactionFieldNames, "`id`"), ",")
|
|
||||||
transactionRowsWithPlaceHolder = strings.Join(stringx.Remove(transactionFieldNames, "`id`"), "=?,") + "=?"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
transactionModel interface {
|
|
||||||
Insert(ctx context.Context, data *Transaction) (sql.Result, error)
|
|
||||||
FindOne(ctx context.Context, id int64) (*Transaction, error)
|
|
||||||
FindOneByOrderIdType(ctx context.Context, orderId string, tp int64) (*Transaction, error)
|
|
||||||
Update(ctx context.Context, data *Transaction) error
|
|
||||||
Delete(ctx context.Context, id int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultTransactionModel struct {
|
|
||||||
conn sqlx.SqlConn
|
|
||||||
table string
|
|
||||||
}
|
|
||||||
|
|
||||||
Transaction struct {
|
|
||||||
Id int64 `db:"id"` // 交易流水號
|
|
||||||
TxId string `db:"tx_id"` // 交易 ID
|
|
||||||
OrderId string `db:"order_id"` // 訂單 ID
|
|
||||||
Brand string `db:"brand"` // 品牌名稱
|
|
||||||
Uid string `db:"uid"` // 發起 UID
|
|
||||||
ToUid string `db:"to_uid"` // 接收方UID
|
|
||||||
Type int64 `db:"type"` // 交易類型: 1=提現, 2=充值
|
|
||||||
BusinessType int64 `db:"business_type"` // 業務類型
|
|
||||||
Currency string `db:"currency"` // 幣種
|
|
||||||
Amount float64 `db:"amount"` // 交易金額
|
|
||||||
Balance float64 `db:"balance"` // 交易後餘額
|
|
||||||
BeforeBalance float64 `db:"before_balance"` // 交易前餘額
|
|
||||||
Status int64 `db:"status"` // 結算狀態
|
|
||||||
CreateTime int64 `db:"create_time"` // 創建時間(Unix 時間戳,毫秒)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newTransactionModel(conn sqlx.SqlConn) *defaultTransactionModel {
|
|
||||||
return &defaultTransactionModel{
|
|
||||||
conn: conn,
|
|
||||||
table: "`transaction`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) withSession(session sqlx.Session) *defaultTransactionModel {
|
|
||||||
return &defaultTransactionModel{
|
|
||||||
conn: sqlx.NewSqlConnFromSession(session),
|
|
||||||
table: "`transaction`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) Delete(ctx context.Context, id int64) error {
|
|
||||||
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
|
||||||
_, err := m.conn.ExecCtx(ctx, query, id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) FindOne(ctx context.Context, id int64) (*Transaction, error) {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", transactionRows, m.table)
|
|
||||||
var resp Transaction
|
|
||||||
err := m.conn.QueryRowCtx(ctx, &resp, query, id)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return &resp, nil
|
|
||||||
case sqlc.ErrNotFound:
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) FindOneByOrderIdType(ctx context.Context, orderId string, tp int64) (*Transaction, error) {
|
|
||||||
var resp Transaction
|
|
||||||
query := fmt.Sprintf("select %s from %s where `order_id` = ? and `type` = ? limit 1", transactionRows, m.table)
|
|
||||||
err := m.conn.QueryRowCtx(ctx, &resp, query, orderId, tp)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return &resp, nil
|
|
||||||
case sqlc.ErrNotFound:
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) Insert(ctx context.Context, data *Transaction) (sql.Result, error) {
|
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, transactionRowsExpectAutoSet)
|
|
||||||
ret, err := m.conn.ExecCtx(ctx, query, data.TxId, data.OrderId, data.Brand, data.Uid, data.ToUid, data.Type, data.BusinessType, data.Currency, data.Amount, data.Balance, data.BeforeBalance, data.Status, data.CreateTime)
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) Update(ctx context.Context, newData *Transaction) error {
|
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, transactionRowsWithPlaceHolder)
|
|
||||||
_, err := m.conn.ExecCtx(ctx, query, newData.TxId, newData.OrderId, newData.Brand, newData.Uid, newData.ToUid, newData.Type, newData.BusinessType, newData.Currency, newData.Amount, newData.Balance, newData.BeforeBalance, newData.Status, newData.CreateTime, newData.Id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultTransactionModel) tableName() string {
|
|
||||||
return m.table
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import "github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
|
|
||||||
var ErrNotFound = sqlx.ErrNotFound
|
|
|
@ -1,37 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ WalletJournalModel = (*customWalletJournalModel)(nil)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// WalletJournalModel is an interface to be customized, add more methods here,
|
|
||||||
// and implement the added methods in customWalletJournalModel.
|
|
||||||
WalletJournalModel interface {
|
|
||||||
walletJournalModel
|
|
||||||
InsertWithSession(ctx context.Context, tx sqlx.Session, data *WalletJournal) (sql.Result, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
customWalletJournalModel struct {
|
|
||||||
*defaultWalletJournalModel
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewWalletJournalModel returns a model for the database table.
|
|
||||||
func NewWalletJournalModel(conn sqlx.SqlConn) WalletJournalModel {
|
|
||||||
return &customWalletJournalModel{
|
|
||||||
defaultWalletJournalModel: newWalletJournalModel(conn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *customWalletJournalModel) InsertWithSession(ctx context.Context, tx sqlx.Session, data *WalletJournal) (sql.Result, error) {
|
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, walletJournalRowsExpectAutoSet)
|
|
||||||
ret, err := tx.ExecCtx(ctx, query, data.TransactionId, data.OrderId, data.Brand, data.Uid, data.WalletType, data.Currency, data.TransactionAmount, data.PostTransactionBalance, data.BusinessType, data.Status, data.DueTime, data.CreatedAt)
|
|
||||||
return ret, err
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
|
||||||
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
walletJournalFieldNames = builder.RawFieldNames(&WalletJournal{})
|
|
||||||
walletJournalRows = strings.Join(walletJournalFieldNames, ",")
|
|
||||||
walletJournalRowsExpectAutoSet = strings.Join(stringx.Remove(walletJournalFieldNames, "`id`"), ",")
|
|
||||||
walletJournalRowsWithPlaceHolder = strings.Join(stringx.Remove(walletJournalFieldNames, "`id`"), "=?,") + "=?"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
walletJournalModel interface {
|
|
||||||
Insert(ctx context.Context, data *WalletJournal) (sql.Result, error)
|
|
||||||
FindOne(ctx context.Context, id int64) (*WalletJournal, error)
|
|
||||||
Update(ctx context.Context, data *WalletJournal) error
|
|
||||||
Delete(ctx context.Context, id int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultWalletJournalModel struct {
|
|
||||||
conn sqlx.SqlConn
|
|
||||||
table string
|
|
||||||
}
|
|
||||||
|
|
||||||
WalletJournal struct {
|
|
||||||
Id int64 `db:"id"` // 錢包賬本流水號
|
|
||||||
TransactionId int64 `db:"transaction_id"` // 交易 ID,對應 transaction 表
|
|
||||||
OrderId string `db:"order_id"` // 訂單 ID,對應 order 表
|
|
||||||
Brand string `db:"brand"` // 品牌名稱
|
|
||||||
Uid string `db:"uid"` // 用戶 ID
|
|
||||||
WalletType int64 `db:"wallet_type"` // 餘額種類: 1=可用, 2=凍結, 3=限制(僅出金)
|
|
||||||
Currency string `db:"currency"` // 幣種或平台點數
|
|
||||||
TransactionAmount float64 `db:"transaction_amount"` // 交易金額
|
|
||||||
PostTransactionBalance float64 `db:"post_transaction_balance"` // 交易後餘額
|
|
||||||
BusinessType int64 `db:"business_type"` // 業務類型
|
|
||||||
Status int64 `db:"status"` // 狀態
|
|
||||||
DueTime int64 `db:"due_time"` // T+N 執行時間
|
|
||||||
CreatedAt int64 `db:"created_at"` // 創建時間(Unix 時間戳,毫秒)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newWalletJournalModel(conn sqlx.SqlConn) *defaultWalletJournalModel {
|
|
||||||
return &defaultWalletJournalModel{
|
|
||||||
conn: conn,
|
|
||||||
table: "`wallet_journal`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletJournalModel) withSession(session sqlx.Session) *defaultWalletJournalModel {
|
|
||||||
return &defaultWalletJournalModel{
|
|
||||||
conn: sqlx.NewSqlConnFromSession(session),
|
|
||||||
table: "`wallet_journal`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletJournalModel) Delete(ctx context.Context, id int64) error {
|
|
||||||
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
|
||||||
_, err := m.conn.ExecCtx(ctx, query, id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletJournalModel) FindOne(ctx context.Context, id int64) (*WalletJournal, error) {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", walletJournalRows, m.table)
|
|
||||||
var resp WalletJournal
|
|
||||||
err := m.conn.QueryRowCtx(ctx, &resp, query, id)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return &resp, nil
|
|
||||||
case sqlc.ErrNotFound:
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletJournalModel) Insert(ctx context.Context, data *WalletJournal) (sql.Result, error) {
|
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, walletJournalRowsExpectAutoSet)
|
|
||||||
ret, err := m.conn.ExecCtx(ctx, query, data.TransactionId, data.OrderId, data.Brand, data.Uid, data.WalletType, data.Currency, data.TransactionAmount, data.PostTransactionBalance, data.BusinessType, data.Status, data.DueTime, data.CreatedAt)
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletJournalModel) Update(ctx context.Context, data *WalletJournal) error {
|
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, walletJournalRowsWithPlaceHolder)
|
|
||||||
_, err := m.conn.ExecCtx(ctx, query, data.TransactionId, data.OrderId, data.Brand, data.Uid, data.WalletType, data.Currency, data.TransactionAmount, data.PostTransactionBalance, data.BusinessType, data.Status, data.DueTime, data.CreatedAt, data.Id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletJournalModel) tableName() string {
|
|
||||||
return m.table
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ WalletModel = (*customWalletModel)(nil)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// WalletModel is an interface to be customized, add more methods here,
|
|
||||||
// and implement the added methods in customWalletModel.
|
|
||||||
WalletModel interface {
|
|
||||||
walletModel
|
|
||||||
InsertMany(ctx context.Context, wallets []*Wallet) (sql.Result, error)
|
|
||||||
// Balances 給他 select for update 的鎖先加上去
|
|
||||||
Balances(ctx context.Context, req BalanceReq, withLock bool, tx sqlx.Session) ([]Wallet, error)
|
|
||||||
// BalancesByIDs 給他 select for update 的鎖先加上去
|
|
||||||
BalancesByIDs(ctx context.Context, ids []int64, withLock bool, tx sqlx.Session) ([]Wallet, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
customWalletModel struct {
|
|
||||||
*defaultWalletModel
|
|
||||||
}
|
|
||||||
|
|
||||||
BalanceReq struct {
|
|
||||||
UID []string
|
|
||||||
Currency []string
|
|
||||||
Kind []wallet.BalanceType
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewWalletModel returns a model for the database table.
|
|
||||||
func NewWalletModel(conn sqlx.SqlConn) WalletModel {
|
|
||||||
return &customWalletModel{
|
|
||||||
defaultWalletModel: newWalletModel(conn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *customWalletModel) InsertMany(ctx context.Context, wallets []*Wallet) (sql.Result, error) {
|
|
||||||
if len(wallets) == 0 {
|
|
||||||
return nil, fmt.Errorf("no data to insert")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 構建多條記錄的佔位符,例如: (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), ...
|
|
||||||
valueStrings := make([]string, 0, len(wallets))
|
|
||||||
valueArgs := make([]interface{}, 0, len(wallets)*7) // 每條記錄有7個值
|
|
||||||
|
|
||||||
for _, wallet := range wallets {
|
|
||||||
valueStrings = append(valueStrings, "(?, ?, ?, ?, ?, ?, ?)")
|
|
||||||
valueArgs = append(valueArgs, wallet.Uid, wallet.Brand, wallet.Currency, wallet.Balance, wallet.WalletType, wallet.CreatedAt, wallet.UpdatedAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 構建批量插入的 SQL 語句
|
|
||||||
query := fmt.Sprintf("insert into %s (%s) values %s", m.table, walletRowsExpectAutoSet, strings.Join(valueStrings, ","))
|
|
||||||
|
|
||||||
// 使用單一連線執行批量插入
|
|
||||||
return m.conn.ExecCtx(ctx, query, valueArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *customWalletModel) Balances(ctx context.Context, req BalanceReq, isLock bool, tx sqlx.Session) ([]Wallet, error) {
|
|
||||||
baseQuery := fmt.Sprintf("SELECT `id`, `currency`, `balance`, `wallet_type` FROM %s", m.table)
|
|
||||||
|
|
||||||
// 構建條件字典
|
|
||||||
conditions := map[string][]any{
|
|
||||||
"`uid`": convertSliceToInterface(req.UID),
|
|
||||||
"`currency`": convertSliceToInterface(req.Currency),
|
|
||||||
"`wallet_type`": convertSliceToInterface(req.Kind),
|
|
||||||
}
|
|
||||||
// 使用 queryBuilder 構建完整查詢
|
|
||||||
query, args := queryBindINBuilder(baseQuery, conditions)
|
|
||||||
|
|
||||||
if isLock {
|
|
||||||
// 加上排他鎖
|
|
||||||
query += " FOR UPDATE"
|
|
||||||
}
|
|
||||||
|
|
||||||
// 執行查詢
|
|
||||||
var wallets []Wallet
|
|
||||||
err := tx.QueryRowsPartialCtx(ctx, &wallets, query, args...)
|
|
||||||
switch {
|
|
||||||
case err == nil:
|
|
||||||
return wallets, nil
|
|
||||||
case errors.Is(err, sqlc.ErrNotFound):
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *customWalletModel) BalancesByIDs(ctx context.Context, ids []int64, withLock bool, tx sqlx.Session) ([]Wallet, error) {
|
|
||||||
baseQuery := fmt.Sprintf("SELECT `id`, `currency`, `balance`, `wallet_type` FROM %s", m.table)
|
|
||||||
|
|
||||||
// 構建條件字典
|
|
||||||
conditions := map[string][]any{
|
|
||||||
"`id`": convertSliceToInterface(ids),
|
|
||||||
}
|
|
||||||
// 使用 queryBuilder 構建完整查詢
|
|
||||||
query, args := queryBindINBuilder(baseQuery, conditions)
|
|
||||||
|
|
||||||
if withLock {
|
|
||||||
// 加上排他鎖
|
|
||||||
query += " FOR UPDATE"
|
|
||||||
}
|
|
||||||
|
|
||||||
// 執行查詢
|
|
||||||
var wallets []Wallet
|
|
||||||
err := tx.QueryRowsPartialCtx(ctx, &wallets, query, args...)
|
|
||||||
switch {
|
|
||||||
case err == nil:
|
|
||||||
return wallets, nil
|
|
||||||
case errors.Is(err, sqlc.ErrNotFound):
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
|
||||||
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
walletFieldNames = builder.RawFieldNames(&Wallet{})
|
|
||||||
walletRows = strings.Join(walletFieldNames, ",")
|
|
||||||
walletRowsExpectAutoSet = strings.Join(stringx.Remove(walletFieldNames, "`id`"), ",")
|
|
||||||
walletRowsWithPlaceHolder = strings.Join(stringx.Remove(walletFieldNames, "`id`"), "=?,") + "=?"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
walletModel interface {
|
|
||||||
Insert(ctx context.Context, data *Wallet) (sql.Result, error)
|
|
||||||
FindOne(ctx context.Context, id int64) (*Wallet, error)
|
|
||||||
FindOneByUidCurrencyWalletType(ctx context.Context, uid string, currency string, walletType int64) (*Wallet, error)
|
|
||||||
Update(ctx context.Context, data *Wallet) error
|
|
||||||
Delete(ctx context.Context, id int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultWalletModel struct {
|
|
||||||
conn sqlx.SqlConn
|
|
||||||
table string
|
|
||||||
}
|
|
||||||
|
|
||||||
Wallet struct {
|
|
||||||
Id int64 `db:"id"` // 錢包流水號
|
|
||||||
Uid string `db:"uid"` // 用戶ID
|
|
||||||
Brand string `db:"brand"` // 品牌名稱
|
|
||||||
Currency string `db:"currency"` // 幣別(或平台點數)
|
|
||||||
Balance decimal.Decimal `db:"balance"` // 錢包餘額
|
|
||||||
WalletType wallet.BalanceType `db:"wallet_type"` // 錢包種類: 1=可用, 2=凍結, 3=限制(僅出金)
|
|
||||||
CreatedAt int64 `db:"created_at"` // 創建時間
|
|
||||||
UpdatedAt int64 `db:"updated_at"` // 更新時間
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newWalletModel(conn sqlx.SqlConn) *defaultWalletModel {
|
|
||||||
return &defaultWalletModel{
|
|
||||||
conn: conn,
|
|
||||||
table: "`wallet`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) withSession(session sqlx.Session) *defaultWalletModel {
|
|
||||||
return &defaultWalletModel{
|
|
||||||
conn: sqlx.NewSqlConnFromSession(session),
|
|
||||||
table: "`wallet`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) Delete(ctx context.Context, id int64) error {
|
|
||||||
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
|
||||||
_, err := m.conn.ExecCtx(ctx, query, id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) FindOne(ctx context.Context, id int64) (*Wallet, error) {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", walletRows, m.table)
|
|
||||||
var resp Wallet
|
|
||||||
err := m.conn.QueryRowCtx(ctx, &resp, query, id)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return &resp, nil
|
|
||||||
case sqlc.ErrNotFound:
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) FindOneByUidCurrencyWalletType(ctx context.Context, uid string, currency string, walletType int64) (*Wallet, error) {
|
|
||||||
var resp Wallet
|
|
||||||
query := fmt.Sprintf("select %s from %s where `uid` = ? and `currency` = ? and `wallet_type` = ? limit 1", walletRows, m.table)
|
|
||||||
err := m.conn.QueryRowCtx(ctx, &resp, query, uid, currency, walletType)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return &resp, nil
|
|
||||||
case sqlc.ErrNotFound:
|
|
||||||
return nil, ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) Insert(ctx context.Context, data *Wallet) (sql.Result, error) {
|
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?)", m.table, walletRowsExpectAutoSet)
|
|
||||||
ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.Brand, data.Currency, data.Balance, data.WalletType, data.CreatedAt, data.UpdatedAt)
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) Update(ctx context.Context, newData *Wallet) error {
|
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, walletRowsWithPlaceHolder)
|
|
||||||
_, err := m.conn.ExecCtx(ctx, query, newData.Uid, newData.Brand, newData.Currency, newData.Balance, newData.WalletType, newData.CreatedAt, newData.UpdatedAt, newData.Id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultWalletModel) tableName() string {
|
|
||||||
return m.table
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/repository"
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"app-cloudep-trade-service/internal/model"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 用戶某個幣種餘額
|
|
||||||
type userLocalWallet struct {
|
|
||||||
wm model.WalletModel
|
|
||||||
txConn sqlx.SqlConn
|
|
||||||
|
|
||||||
uid string
|
|
||||||
currency string
|
|
||||||
|
|
||||||
// local wallet 相關計算的餘額存在這裡
|
|
||||||
walletBalance map[wallet.BalanceType]model.Wallet
|
|
||||||
|
|
||||||
// local order wallet 相關計算的餘額存在這裡
|
|
||||||
localOrderBalance map[int64]decimal.Decimal
|
|
||||||
|
|
||||||
// local wallet 內所有餘額變化紀錄
|
|
||||||
transactions []model.WalletJournal
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBalancesByID 使用 Wallet ID 取得餘額,如果需要上鎖,可以在 option 內加上 tx 以及 lock,可以參考 TestBalanceUseCase
|
|
||||||
func (use *userLocalWallet) GetBalancesByID(ctx context.Context, ids []int64, opts ...repository.WalletOperatorOption) ([]model.Wallet, error) {
|
|
||||||
o := repository.ApplyOptions(opts...)
|
|
||||||
tx := use.txConn
|
|
||||||
if o.Tx != nil {
|
|
||||||
tx = *o.Tx
|
|
||||||
}
|
|
||||||
|
|
||||||
wallets, err := use.wm.BalancesByIDs(ctx, ids, o.WithLock, tx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, w := range wallets {
|
|
||||||
use.walletBalance[w.WalletType] = w
|
|
||||||
}
|
|
||||||
|
|
||||||
return wallets, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalBalance 內存餘額
|
|
||||||
func (use *userLocalWallet) LocalBalance(BalanceType wallet.BalanceType) decimal.Decimal {
|
|
||||||
w, ok := use.walletBalance[BalanceType]
|
|
||||||
if !ok {
|
|
||||||
return decimal.Zero
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Balance
|
|
||||||
}
|
|
||||||
|
|
||||||
// Balances 取得餘額,如果需要上鎖,可以在option 內加上 tx 以及 lock,可以參考 TestBalanceUseCase
|
|
||||||
func (use *userLocalWallet) Balances(ctx context.Context, balanceType []wallet.BalanceType, opts ...repository.WalletOperatorOption) ([]model.Wallet, error) {
|
|
||||||
o := repository.ApplyOptions(opts...)
|
|
||||||
tx := use.txConn
|
|
||||||
if o.Tx != nil {
|
|
||||||
tx = *o.Tx
|
|
||||||
}
|
|
||||||
|
|
||||||
wallets, err := use.wm.Balances(ctx, model.BalanceReq{
|
|
||||||
UID: []string{use.uid},
|
|
||||||
Currency: []string{use.currency},
|
|
||||||
Kind: balanceType,
|
|
||||||
}, o.WithLock, tx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, wallet := range wallets {
|
|
||||||
use.walletBalance[wallet.WalletType] = wallet
|
|
||||||
}
|
|
||||||
|
|
||||||
return wallets, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUserWalletOperator(uid, currency string, wm model.WalletModel, txConn sqlx.SqlConn) repository.UserWalletOperator {
|
|
||||||
return &userLocalWallet{
|
|
||||||
wm: wm,
|
|
||||||
txConn: txConn,
|
|
||||||
uid: uid,
|
|
||||||
currency: currency,
|
|
||||||
|
|
||||||
walletBalance: make(map[wallet.BalanceType]model.Wallet, len(wallet.AllBalanceTypes)),
|
|
||||||
localOrderBalance: make(map[int64]decimal.Decimal, len(wallet.AllBalanceTypes)),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain"
|
|
||||||
"app-cloudep-trade-service/internal/domain/repository"
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"app-cloudep-trade-service/internal/model"
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type WalletRepositoryParam struct {
|
|
||||||
WalletModel model.WalletModel
|
|
||||||
TxConn sqlx.SqlConn
|
|
||||||
}
|
|
||||||
|
|
||||||
type WalletRepository struct {
|
|
||||||
WalletRepositoryParam
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *WalletRepository) Transaction(ctx context.Context, fn func(tx sqlx.Session) error) error {
|
|
||||||
// 取得原生的 *sql.DB
|
|
||||||
db, err := repo.TxConn.RawDB()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定義事務選項
|
|
||||||
txOptions := &sql.TxOptions{
|
|
||||||
Isolation: sql.LevelReadCommitted,
|
|
||||||
ReadOnly: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 開啟事務
|
|
||||||
tx, err := db.BeginTx(ctx, txOptions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 sqlx.Session 包裹事務
|
|
||||||
session := sqlx.NewSessionFromTx(tx)
|
|
||||||
|
|
||||||
// 執行傳入的操作
|
|
||||||
if err := fn(session); err != nil {
|
|
||||||
// 若有錯誤則回滾
|
|
||||||
err := tx.Rollback()
|
|
||||||
if err != nil {
|
|
||||||
// 錯誤代碼 06-021-22
|
|
||||||
e := domain.CommentErrorL(
|
|
||||||
domain.WalletTxErrorCode,
|
|
||||||
logx.WithContext(ctx),
|
|
||||||
[]logx.LogField{
|
|
||||||
{Key: "func", Value: "WalletModel.Transaction.Rollback"},
|
|
||||||
{Key: "err", Value: err},
|
|
||||||
},
|
|
||||||
"failed to find balance into mongo:").Wrap(err)
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// 裡面那一層會紀錄
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交事務
|
|
||||||
return tx.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserWalletOperator 取得本地操作使用者錢包的操作運算元
|
|
||||||
func (repo *WalletRepository) GetUserWalletOperator(uid, currency string, opts ...repository.Option) repository.UserWalletOperator {
|
|
||||||
db := repo.TxConn
|
|
||||||
|
|
||||||
// 看是否有最新的DB 連線要傳入,做 tx
|
|
||||||
for _, fn := range opts {
|
|
||||||
db = fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewUserWalletOperator(uid, currency, repo.WalletModel, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 創建錢包,如果有相同的就跳過不建立
|
|
||||||
func (repo *WalletRepository) Create(ctx context.Context, uid, currency, brand string) ([]*model.Wallet, error) {
|
|
||||||
wallets := make([]*model.Wallet, 0, len(wallet.AllBalanceTypes))
|
|
||||||
// 建立個人所有種類的錢包
|
|
||||||
now := time.Now().UTC().UnixNano()
|
|
||||||
for _, item := range wallet.AllBalanceTypes {
|
|
||||||
balance := decimal.Zero
|
|
||||||
wallets = append(wallets, &model.Wallet{
|
|
||||||
Brand: brand,
|
|
||||||
Currency: currency,
|
|
||||||
Uid: uid,
|
|
||||||
Balance: balance,
|
|
||||||
WalletType: item,
|
|
||||||
CreatedAt: now,
|
|
||||||
UpdatedAt: now,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := repo.WalletModel.InsertMany(ctx, wallets)
|
|
||||||
if err != nil {
|
|
||||||
// 錯誤代碼 06-021-20
|
|
||||||
e := domain.CommentErrorL(
|
|
||||||
domain.CreateWalletErrorCode,
|
|
||||||
logx.WithContext(ctx),
|
|
||||||
[]logx.LogField{
|
|
||||||
{Key: "param", Value: fmt.Sprintf("uid: %s, currency:%s, brand:%s", uid, currency, brand)},
|
|
||||||
{Key: "func", Value: "WalletModel.InsertMany"},
|
|
||||||
{Key: "err", Value: err},
|
|
||||||
},
|
|
||||||
"failed to insert wallet into mysql:").Wrap(err)
|
|
||||||
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
|
|
||||||
return wallets, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *WalletRepository) Balances(ctx context.Context, req repository.BalanceReq) ([]model.Wallet, error) {
|
|
||||||
data, err := repo.WalletModel.Balances(ctx, model.BalanceReq{
|
|
||||||
UID: req.UID,
|
|
||||||
Currency: req.Currency,
|
|
||||||
Kind: req.BalanceType,
|
|
||||||
}, false, repo.TxConn)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(sqlc.ErrNotFound, err) {
|
|
||||||
// 錯誤代碼 06-031-23
|
|
||||||
return nil, domain.NotFoundError(domain.WalletBalanceNotFound, "balance not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 錯誤代碼 06-021-20
|
|
||||||
e := domain.CommentErrorL(
|
|
||||||
domain.CreateWalletErrorCode,
|
|
||||||
logx.WithContext(ctx),
|
|
||||||
[]logx.LogField{
|
|
||||||
{Key: "param", Value: req},
|
|
||||||
{Key: "func", Value: "WalletModel.Balances"},
|
|
||||||
{Key: "err", Value: err},
|
|
||||||
},
|
|
||||||
"failed to get balance")
|
|
||||||
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWalletRepository(param WalletRepositoryParam) repository.WalletRepository {
|
|
||||||
return &WalletRepository{
|
|
||||||
WalletRepositoryParam: param,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/domain/wallet"
|
|
||||||
"app-cloudep-trade-service/internal/model"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestByMe(t *testing.T) {
|
|
||||||
sqlConn := sqlx.NewSqlConn("mysql",
|
|
||||||
"root:yytt@tcp(127.0.0.1:3306)/digimon_wallet?parseTime=true&interpolateParams=false")
|
|
||||||
|
|
||||||
WalletRepo := NewWalletRepository(WalletRepositoryParam{
|
|
||||||
model.NewWalletModel(sqlConn),
|
|
||||||
sqlConn,
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
wo := WalletRepo.GetUserWalletOperator("OOOOOOOK", "USD")
|
|
||||||
balances, err := wo.Balances(ctx, wallet.AllBalanceTypes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(balances)
|
|
||||||
|
|
||||||
// create, err := WalletRepo.Create(ctx, "OOOOOOOK", "USD", "Digimon")
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fmt.Println(create)
|
|
||||||
|
|
||||||
// balances, err := WalletRepo.Balances(ctx, repository.BalanceReq{
|
|
||||||
// UID: []string{"OOOOOOOK"},
|
|
||||||
// Currency: []string{"USD"},
|
|
||||||
// // Kind: make([]domain.WalletType, 0),
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// err := WalletRepo.Transaction(ctx, func(tx sqlx.Session) error {
|
|
||||||
// wm := model.NewWalletJournalModel(sqlConn)
|
|
||||||
// _, err := wm.InsertWithSession(ctx, tx, &model.WalletJournal{
|
|
||||||
// Id: 1,
|
|
||||||
// TransactionId: 10001,
|
|
||||||
// OrderId: "ORD123456789",
|
|
||||||
// Brand: "BrandX",
|
|
||||||
// Uid: "user123",
|
|
||||||
// WalletType: 1, // 1=可用
|
|
||||||
// Currency: "USD",
|
|
||||||
// TransactionAmount: 500.00,
|
|
||||||
// PostTransactionBalance: 1500.00,
|
|
||||||
// BusinessType: 2, // 假設 2 表示特定業務類型
|
|
||||||
// Status: 1, // 假設 1 表示成功狀態
|
|
||||||
// DueTime: 1698289200000, // T+N 執行時間
|
|
||||||
// CreatedAt: 1698192800000, // 創建時間 (Unix 時間戳,毫秒)
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return nil
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fmt.Println(balances)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBalanceUseCase(t *testing.T) {}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package svc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"app-cloudep-trade-service/internal/config"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
)
|
|
||||||
|
|
||||||
func mustDSN(c config.Config) string {
|
|
||||||
// 構建 DSN 字符串
|
|
||||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&interpolateParams=%t",
|
|
||||||
c.DB.User,
|
|
||||||
c.DB.Password,
|
|
||||||
c.DB.Host,
|
|
||||||
c.DB.Port,
|
|
||||||
c.DB.Database,
|
|
||||||
c.DB.InterpolateParams,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDatabase(c config.Config) (*sql.DB, error) {
|
|
||||||
// 創建基礎的 *sql.DB 連接
|
|
||||||
db, err := sql.Open("mysql", mustDSN(c))
|
|
||||||
if err != nil {
|
|
||||||
logx.Error(fmt.Errorf("failed to connect to database: %w", err))
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 設置數據庫連接池參數
|
|
||||||
db.SetMaxIdleConns(c.DB.MaxIdleConns)
|
|
||||||
db.SetMaxOpenConns(c.DB.MaxOpenConns)
|
|
||||||
db.SetConnMaxLifetime(c.DB.ConnMaxLifetime)
|
|
||||||
|
|
||||||
return db, nil
|
|
||||||
}
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
duc "app-cloudep-trade-service/internal/domain/usecase"
|
duc "app-cloudep-trade-service/internal/domain/usecase"
|
||||||
"app-cloudep-trade-service/internal/usecase"
|
"app-cloudep-trade-service/internal/usecase"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
|
|
||||||
ers "code.30cm.net/digimon/library-go/errs"
|
ers "code.30cm.net/digimon/library-go/errs"
|
||||||
"code.30cm.net/digimon/library-go/errs/code"
|
"code.30cm.net/digimon/library-go/errs/code"
|
||||||
vi "code.30cm.net/digimon/library-go/validator"
|
vi "code.30cm.net/digimon/library-go/validator"
|
||||||
|
@ -17,7 +15,6 @@ type ServiceContext struct {
|
||||||
Validate vi.Validate
|
Validate vi.Validate
|
||||||
|
|
||||||
OrderUseCase duc.OrderUseCase
|
OrderUseCase duc.OrderUseCase
|
||||||
SQLConn sqlx.SqlConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
@ -29,20 +26,13 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
OrderModel: om,
|
OrderModel: om,
|
||||||
})
|
})
|
||||||
|
|
||||||
mysql, err := newDatabase(c)
|
|
||||||
if err != nil {
|
|
||||||
panic("failed to connect to wallet")
|
|
||||||
}
|
|
||||||
// 創建 SQL 連線並返回
|
|
||||||
sqlConn := sqlx.NewSqlConnFromDB(mysql)
|
|
||||||
|
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
Validate: vi.MustValidator(
|
Validate: vi.MustValidator(
|
||||||
WithDecimalGt(),
|
WithDecimalGt(),
|
||||||
WithDecimalGte(),
|
WithDecimalGte(),
|
||||||
),
|
),
|
||||||
SQLConn: sqlConn,
|
|
||||||
OrderUseCase: orderUseCase,
|
OrderUseCase: orderUseCase,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package usecase
|
package usecase
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/domain/order"
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"app-cloudep-trade-service/internal/domain/usecase"
|
"app-cloudep-trade-service/internal/domain/usecase"
|
||||||
mockmodel "app-cloudep-trade-service/internal/mock/model"
|
mockmodel "app-cloudep-trade-service/internal/mock/model"
|
||||||
model "app-cloudep-trade-service/internal/model/mongo"
|
model "app-cloudep-trade-service/internal/model/mongo"
|
||||||
|
@ -30,7 +30,7 @@ func TestOrderUseCase_CancelOrder(t *testing.T) {
|
||||||
|
|
||||||
cancelOrderQuery := usecase.CancelOrderQuery{
|
cancelOrderQuery := usecase.CancelOrderQuery{
|
||||||
BusinessID: "business_123",
|
BusinessID: "business_123",
|
||||||
Status: order.Cancelled, // 使用模擬的狀態
|
Status: domain.OrderStatusCancelled, // 使用模擬的狀態
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -89,8 +89,8 @@ func TestOrderUseCase_CreateOrder(t *testing.T) {
|
||||||
// 構建測試參數
|
// 構建測試參數
|
||||||
createOrderReq := usecase.CreateOrderReq{
|
createOrderReq := usecase.CreateOrderReq{
|
||||||
BusinessID: "business_123",
|
BusinessID: "business_123",
|
||||||
OrderType: order.TestType,
|
OrderType: domain.OrderTypeTest,
|
||||||
OrderStatus: order.Created,
|
OrderStatus: domain.OrderStatusCreated,
|
||||||
Brand: "test_brand",
|
Brand: "test_brand",
|
||||||
OrderUID: "user_123",
|
OrderUID: "user_123",
|
||||||
ReferenceID: "reference_123",
|
ReferenceID: "reference_123",
|
||||||
|
@ -222,8 +222,8 @@ func TestOrderUseCase_GetOrder(t *testing.T) {
|
||||||
UpdateTime: 123456789,
|
UpdateTime: 123456789,
|
||||||
CreateTime: 123456789,
|
CreateTime: 123456789,
|
||||||
BusinessID: "business_123",
|
BusinessID: "business_123",
|
||||||
OrderType: order.TestType,
|
OrderType: domain.OrderTypeTest,
|
||||||
OrderStatus: order.Created,
|
OrderStatus: domain.OrderStatusCreated,
|
||||||
Brand: "test_brand",
|
Brand: "test_brand",
|
||||||
OrderUID: "user_123",
|
OrderUID: "user_123",
|
||||||
ReferenceID: "reference_123",
|
ReferenceID: "reference_123",
|
||||||
|
@ -343,14 +343,14 @@ func TestOrderUseCase_ListOrder(t *testing.T) {
|
||||||
PageIndex: 1,
|
PageIndex: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
BusinessID: "business_123",
|
BusinessID: "business_123",
|
||||||
OrderStatus: []int64{int64(order.Created)},
|
OrderStatus: []int64{int64(domain.OrderStatusCreated)},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockOrders := []model.Order{
|
mockOrders := []model.Order{
|
||||||
{
|
{
|
||||||
BusinessID: "business_123",
|
BusinessID: "business_123",
|
||||||
OrderUID: "user_123",
|
OrderUID: "user_123",
|
||||||
OrderStatus: order.Created,
|
OrderStatus: domain.OrderStatusCreated,
|
||||||
// 其他欄位根據需要填充
|
// 其他欄位根據需要填充
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -414,7 +414,7 @@ func TestOrderUseCase_ModifyOrderStatus(t *testing.T) {
|
||||||
|
|
||||||
modifyOrderQuery := &usecase.ModifyOrderQuery{
|
modifyOrderQuery := &usecase.ModifyOrderQuery{
|
||||||
BusinessID: "business_123",
|
BusinessID: "business_123",
|
||||||
Status: int64(order.Cancelled),
|
Status: int64(domain.OrderStatusCancelled),
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
Loading…
Reference in New Issue