add wallet repo
This commit is contained in:
parent
8687e467c9
commit
fc748fb66f
|
@ -23,6 +23,12 @@ const (
|
||||||
TimeoutOrderErrorCode
|
TimeoutOrderErrorCode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Error Code 統一這邊改
|
||||||
|
const (
|
||||||
|
_ ErrorCode = 20 + iota
|
||||||
|
CreateWalletErrorCode
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_ ErrorCode = 10 + iota
|
_ ErrorCode = 10 + iota
|
||||||
DataNotFoundErrorCode
|
DataNotFoundErrorCode
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
// UserWalletOperator 針對使用者的錢包基本操作接口
|
||||||
|
type UserWalletOperator interface{}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app-cloudep-trade-service/internal/domain"
|
||||||
|
"app-cloudep-trade-service/internal/model"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WalletRepository 錢包基礎操作(可能有平台,使用者等多元的錢包)
|
||||||
|
type WalletRepository interface {
|
||||||
|
// Create 建立錢包組合(某人的可用,凍結,限制三種)
|
||||||
|
Create(ctx context.Context, uid, currency, brand string) ([]*model.Wallet, error)
|
||||||
|
// Balances 取得某個人的餘額
|
||||||
|
Balances(ctx context.Context, req BalanceReq) ([]model.Wallet, error)
|
||||||
|
// GetTxDatabaseConn 取得 sql 要做 tx 的連線
|
||||||
|
GetTxDatabaseConn() sqlx.SqlConn
|
||||||
|
// GetUserWalletOperator 取得使用者錢包操作看使否需要使用 transaction
|
||||||
|
GetUserWalletOperator(uid, currency string, opts ...Option) UserWalletOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceReq 取得全部的,因為一個人錢包種類的不會太多,故全撈
|
||||||
|
type BalanceReq struct {
|
||||||
|
UID []string
|
||||||
|
Currency []string
|
||||||
|
Kind []domain.WalletType
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func() sqlx.SqlConn
|
|
@ -1,4 +1,81 @@
|
||||||
package usecase
|
package usecase
|
||||||
|
|
||||||
type WalletUseCase interface {
|
import (
|
||||||
|
"app-cloudep-trade-service/internal/domain"
|
||||||
|
"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 domain.TxType // 交易種類
|
||||||
|
BusinessType domain.BusinessName // 商業種類
|
||||||
|
Brand string // 轉帳平台
|
||||||
|
From domain.WalletType // 從哪種錢包類型
|
||||||
|
To domain.WalletType // 到哪種錢包類型
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
// ===================交易商業邏輯種類===================
|
||||||
|
|
||||||
|
type BusinessName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoBusinessName 非商業邏輯
|
||||||
|
NoBusinessName BusinessName = ""
|
||||||
|
// OrderBusinessTypeBusinessName order業務邏輯
|
||||||
|
OrderBusinessTypeBusinessName BusinessName = "order"
|
||||||
|
// SystemTransferBusinessTypeBusinessName 系統劃轉
|
||||||
|
SystemTransferBusinessTypeBusinessName BusinessName = "system_transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoBusinessType 非商業邏輯
|
||||||
|
NoBusinessType int8 = iota
|
||||||
|
// OrderBusinessTypeBusinessType order 業務邏輯
|
||||||
|
OrderBusinessTypeBusinessType
|
||||||
|
// SystemTransferBusinessTypeBusinessType 系統劃轉
|
||||||
|
SystemTransferBusinessTypeBusinessType
|
||||||
|
)
|
||||||
|
|
||||||
|
// 定義兩個map用於名稱和類型的相互映射
|
||||||
|
var nameToBusinessType = map[BusinessName]int8{
|
||||||
|
OrderBusinessTypeBusinessName: OrderBusinessTypeBusinessType,
|
||||||
|
SystemTransferBusinessTypeBusinessName: SystemTransferBusinessTypeBusinessType,
|
||||||
|
}
|
||||||
|
|
||||||
|
var businessTypeToName = map[int8]BusinessName{
|
||||||
|
OrderBusinessTypeBusinessType: OrderBusinessTypeBusinessName,
|
||||||
|
SystemTransferBusinessTypeBusinessType: SystemTransferBusinessTypeBusinessName,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToINT8 converts BusinessName to its corresponding int8 type
|
||||||
|
func (b BusinessName) ToINT8() int8 {
|
||||||
|
if val, ok := nameToBusinessType[b]; ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoBusinessType
|
||||||
|
}
|
||||||
|
|
||||||
|
// BusinessTypeToString converts an int8 type to its corresponding BusinessName
|
||||||
|
func BusinessTypeToString(b int8) BusinessName {
|
||||||
|
if val, ok := businessTypeToName[b]; ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoBusinessName
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
// ===================交易種類===================
|
||||||
|
|
||||||
|
type TxType int64
|
||||||
|
|
||||||
|
// 交易類型
|
||||||
|
const (
|
||||||
|
// TxDepositType 充值(增加可用餘額)
|
||||||
|
TxDepositType TxType = iota + 1
|
||||||
|
|
||||||
|
// TxWithdrawType 提現(減少可用餘額)
|
||||||
|
TxWithdrawType
|
||||||
|
|
||||||
|
// TxFreezeType 凍結(減少可用餘額,加在凍結餘額)
|
||||||
|
TxFreezeType
|
||||||
|
|
||||||
|
// TxUnFreezeType 解凍(減少凍結餘額)
|
||||||
|
TxUnFreezeType
|
||||||
|
|
||||||
|
// TxRollbackFreezeType rollback凍結(減少凍結餘額,加回可用餘額,不可指定金額)
|
||||||
|
TxRollbackFreezeType
|
||||||
|
|
||||||
|
// TxUnconfirmedType 限制(減少凍結餘額,加別人限制餘額)
|
||||||
|
TxUnconfirmedType
|
||||||
|
|
||||||
|
// TxCancelFreezeType 取消凍結(減少凍結餘額,加回可用餘額,,可指定金額)
|
||||||
|
TxCancelFreezeType
|
||||||
|
|
||||||
|
// TxDepositUnconfirmedType 充值(增加限制餘額)
|
||||||
|
TxDepositUnconfirmedType
|
||||||
|
|
||||||
|
// TxAppendFreezeType 追加凍結(減少可用餘額,加在凍結餘額)
|
||||||
|
TxAppendFreezeType
|
||||||
|
|
||||||
|
// TxRollbackFreezeAddAvailableType rollback凍結(rollback凍結餘額,指定金額加回可用餘額)
|
||||||
|
TxRollbackFreezeAddAvailableType
|
||||||
|
|
||||||
|
// TxDistributionType 平台分發
|
||||||
|
TxDistributionType
|
||||||
|
|
||||||
|
// TxSystemTransfer 系統劃轉
|
||||||
|
TxSystemTransfer
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t TxType) ToInt() int64 {
|
||||||
|
return int64(t)
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
// ===================錢包金額種類===================
|
||||||
|
|
||||||
|
type WalletType int64
|
||||||
|
|
||||||
|
// 錢包種類
|
||||||
|
const (
|
||||||
|
// WalletAvailableType 錢包可動用的金額
|
||||||
|
WalletAvailableType WalletType = iota + 1
|
||||||
|
|
||||||
|
// WalletFreezeType 交易過程,錢包被凍結的金額
|
||||||
|
WalletFreezeType
|
||||||
|
|
||||||
|
// WalletUnconfirmedType 已提交的交易但還未確認完成交易的金額
|
||||||
|
WalletUnconfirmedType
|
||||||
|
|
||||||
|
// WalletContractAvailableType 合約/交易可用餘額
|
||||||
|
WalletContractAvailableType
|
||||||
|
|
||||||
|
// WalletContractFreezeType 合約/交易凍結餘額
|
||||||
|
WalletContractFreezeType
|
||||||
|
)
|
||||||
|
|
||||||
|
var walletTypeToName = map[WalletType]string{
|
||||||
|
WalletAvailableType: "available",
|
||||||
|
WalletFreezeType: "freeze",
|
||||||
|
WalletUnconfirmedType: "unconfirmed",
|
||||||
|
WalletContractAvailableType: "contract_available",
|
||||||
|
WalletContractFreezeType: "contract_freeze",
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameToWalletType = map[string]WalletType{
|
||||||
|
"available": WalletAvailableType,
|
||||||
|
"freeze": WalletFreezeType,
|
||||||
|
"unconfirmed": WalletUnconfirmedType,
|
||||||
|
"contract_available": WalletContractAvailableType,
|
||||||
|
"contract_freeze": WalletContractFreezeType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name associated with the WalletType.
|
||||||
|
func (w WalletType) Name() string {
|
||||||
|
return walletTypeToName[w]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWalletTypeByName returns the WalletType associated with the given name.
|
||||||
|
func GetWalletTypeByName(name string) WalletType {
|
||||||
|
return nameToWalletType[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
var AllWalletType = []WalletType{
|
||||||
|
WalletAvailableType, WalletFreezeType, WalletUnconfirmedType,
|
||||||
|
WalletContractAvailableType, WalletContractFreezeType,
|
||||||
|
}
|
|
@ -1,6 +1,18 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import "github.com/zeromicro/go-zero/core/stores/sqlx"
|
import (
|
||||||
|
"app-cloudep-trade-service/internal/domain"
|
||||||
|
|
||||||
|
"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)
|
var _ WalletModel = (*customWalletModel)(nil)
|
||||||
|
|
||||||
|
@ -9,11 +21,19 @@ type (
|
||||||
// and implement the added methods in customWalletModel.
|
// and implement the added methods in customWalletModel.
|
||||||
WalletModel interface {
|
WalletModel interface {
|
||||||
walletModel
|
walletModel
|
||||||
|
InsertMany(ctx context.Context, wallets []*Wallet) (sql.Result, error)
|
||||||
|
Balances(ctx context.Context, req BalanceReq) ([]Wallet, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
customWalletModel struct {
|
customWalletModel struct {
|
||||||
*defaultWalletModel
|
*defaultWalletModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BalanceReq struct {
|
||||||
|
UID []string
|
||||||
|
Currency []string
|
||||||
|
Kind []domain.WalletType
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWalletModel returns a model for the database table.
|
// NewWalletModel returns a model for the database table.
|
||||||
|
@ -22,3 +42,75 @@ func NewWalletModel(conn sqlx.SqlConn) WalletModel {
|
||||||
defaultWalletModel: newWalletModel(conn),
|
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) ([]Wallet, error) {
|
||||||
|
var data []Wallet
|
||||||
|
query := fmt.Sprintf("select 'id', 'uid', 'currency', 'balance', 'type', 'update_time' from %s", m.table)
|
||||||
|
var conditions []string
|
||||||
|
var args []any
|
||||||
|
|
||||||
|
// 根據條件動態拼接 WHERE 子句
|
||||||
|
if len(req.UID) > 0 {
|
||||||
|
placeholders := strings.Repeat("?,", len(req.UID))
|
||||||
|
placeholders = placeholders[:len(placeholders)-1] // 移除最後一個逗號
|
||||||
|
conditions = append(conditions, fmt.Sprintf("uid IN (%s)", placeholders))
|
||||||
|
args = append(args, convertSliceToInterface(req.UID)...)
|
||||||
|
}
|
||||||
|
if len(req.Currency) > 0 {
|
||||||
|
placeholders := strings.Repeat("?,", len(req.Currency))
|
||||||
|
placeholders = placeholders[:len(placeholders)-1]
|
||||||
|
conditions = append(conditions, fmt.Sprintf("currency IN (%s)", placeholders))
|
||||||
|
args = append(args, convertSliceToInterface(req.Currency)...)
|
||||||
|
}
|
||||||
|
if len(req.Kind) > 0 {
|
||||||
|
placeholders := strings.Repeat("?,", len(req.Kind))
|
||||||
|
placeholders = placeholders[:len(placeholders)-1]
|
||||||
|
conditions = append(conditions, fmt.Sprintf("type IN (%s)", placeholders))
|
||||||
|
args = append(args, convertSliceToInterface(req.Kind)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有條件,則拼接 WHERE 子句
|
||||||
|
if len(conditions) > 0 {
|
||||||
|
query += " WHERE " + strings.Join(conditions, " AND ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 執行查詢
|
||||||
|
err := m.conn.QueryRowCtx(ctx, &data, query, args...)
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
return data, nil
|
||||||
|
case errors.Is(err, sqlc.ErrNotFound):
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSliceToInterface[T any](slice []T) []any {
|
||||||
|
interfaces := make([]any, 0, len(slice))
|
||||||
|
for i, v := range slice {
|
||||||
|
interfaces[i] = v
|
||||||
|
}
|
||||||
|
return interfaces
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"app-cloudep-trade-service/internal/domain"
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
@ -36,14 +39,14 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
Wallet struct {
|
Wallet struct {
|
||||||
Id int64 `db:"id"` // 錢包流水號
|
Id int64 `db:"id"` // 錢包流水號
|
||||||
Uid string `db:"uid"` // 用戶ID
|
Uid string `db:"uid"` // 用戶ID
|
||||||
Brand string `db:"brand"` // 品牌名稱
|
Brand string `db:"brand"` // 品牌名稱
|
||||||
Currency string `db:"currency"` // 幣別(或平台點數)
|
Currency string `db:"currency"` // 幣別(或平台點數)
|
||||||
Balance float64 `db:"balance"` // 錢包餘額
|
Balance decimal.Decimal `db:"balance"` // 錢包餘額
|
||||||
WalletType int64 `db:"wallet_type"` // 錢包種類: 1=可用, 2=凍結, 3=限制(僅出金)
|
WalletType domain.WalletType `db:"wallet_type"` // 錢包種類: 1=可用, 2=凍結, 3=限制(僅出金)
|
||||||
CreatedAt int64 `db:"created_at"` // 創建時間
|
CreatedAt int64 `db:"created_at"` // 創建時間
|
||||||
UpdatedAt int64 `db:"updated_at"` // 更新時間
|
UpdatedAt int64 `db:"updated_at"` // 更新時間
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app-cloudep-trade-service/internal/domain"
|
||||||
|
"app-cloudep-trade-service/internal/domain/repository"
|
||||||
|
"app-cloudep-trade-service/internal/model"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 用戶某個幣種餘額
|
||||||
|
type userLocalWallet struct {
|
||||||
|
wm model.WalletModel
|
||||||
|
txConn sqlx.SqlConn
|
||||||
|
|
||||||
|
uid string
|
||||||
|
crypto string
|
||||||
|
|
||||||
|
// local wallet 相關計算的餘額存在這裡
|
||||||
|
walletBalance map[domain.WalletType]model.Wallet
|
||||||
|
|
||||||
|
// local order wallet 相關計算的餘額存在這裡
|
||||||
|
localOrderBalance map[int64]decimal.Decimal
|
||||||
|
|
||||||
|
// local wallet 內所有餘額變化紀錄
|
||||||
|
transactions []model.WalletJournal
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserWalletOperator(uid, crypto string, wm model.WalletModel, txConn sqlx.SqlConn) repository.UserWalletOperator {
|
||||||
|
return &userLocalWallet{
|
||||||
|
wm: wm,
|
||||||
|
txConn: txConn,
|
||||||
|
uid: uid,
|
||||||
|
crypto: crypto,
|
||||||
|
|
||||||
|
walletBalance: make(map[domain.WalletType]model.Wallet, len(domain.AllWalletType)),
|
||||||
|
localOrderBalance: make(map[int64]decimal.Decimal, len(domain.AllWalletType)),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app-cloudep-trade-service/internal/domain"
|
||||||
|
"app-cloudep-trade-service/internal/domain/repository"
|
||||||
|
"app-cloudep-trade-service/internal/model"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(domain.AllWalletType))
|
||||||
|
// 建立個人所有種類的錢包
|
||||||
|
now := time.Now().UTC().UnixNano()
|
||||||
|
for _, item := range domain.AllWalletType {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// 錯誤代碼 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 find balance into mongo:").Wrap(err)
|
||||||
|
|
||||||
|
return []model.Wallet{}, e
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *WalletRepository) GetTxDatabaseConn() sqlx.SqlConn {
|
||||||
|
return repo.TxConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWalletRepository(param WalletRepositoryParam) repository.WalletRepository {
|
||||||
|
return &WalletRepository{
|
||||||
|
WalletRepositoryParam: param,
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,10 @@ package svc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/config"
|
"app-cloudep-trade-service/internal/config"
|
||||||
model "app-cloudep-trade-service/internal/model"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func mustDSN(c config.Config) string {
|
func mustDSN(c config.Config) string {
|
||||||
|
@ -38,11 +36,3 @@ func newDatabase(c config.Config) (*sql.DB, error) {
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustWalletModel 連線 wallet 時
|
|
||||||
func MustWalletModel(db *sql.DB) model.WalletModel {
|
|
||||||
// 創建並返回 *sqlx.SqlConn
|
|
||||||
sqlConn := sqlx.NewSqlConnFromDB(db)
|
|
||||||
|
|
||||||
return model.NewWalletModel(sqlConn)
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ package svc
|
||||||
import (
|
import (
|
||||||
"app-cloudep-trade-service/internal/config"
|
"app-cloudep-trade-service/internal/config"
|
||||||
duc "app-cloudep-trade-service/internal/domain/usecase"
|
duc "app-cloudep-trade-service/internal/domain/usecase"
|
||||||
"app-cloudep-trade-service/internal/model"
|
|
||||||
"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"
|
||||||
|
@ -16,7 +17,7 @@ type ServiceContext struct {
|
||||||
Validate vi.Validate
|
Validate vi.Validate
|
||||||
|
|
||||||
OrderUseCase duc.OrderUseCase
|
OrderUseCase duc.OrderUseCase
|
||||||
WalletModel model.WalletModel
|
SQLConn sqlx.SqlConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
@ -32,6 +33,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to connect to wallet")
|
panic("failed to connect to wallet")
|
||||||
}
|
}
|
||||||
|
// 創建 SQL 連線並返回
|
||||||
|
sqlConn := sqlx.NewSqlConnFromDB(mysql)
|
||||||
|
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
|
@ -39,8 +42,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
WithDecimalGt(),
|
WithDecimalGt(),
|
||||||
WithDecimalGte(),
|
WithDecimalGte(),
|
||||||
),
|
),
|
||||||
|
SQLConn: sqlConn,
|
||||||
OrderUseCase: orderUseCase,
|
OrderUseCase: orderUseCase,
|
||||||
WalletModel: MustWalletModel(mysql),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue