package usecase import ( "code.30cm.net/digimon/app-cloudep-wallet-service/pkg/domain/repository" "code.30cm.net/digimon/app-cloudep-wallet-service/pkg/domain/usecase" "code.30cm.net/digimon/app-cloudep-wallet-service/pkg/domain/wallet" "context" "errors" "fmt" ) type uidAssetKey struct { uid string asset string } // walletActionOption 表示一個「錢包操作函式」,可在錢包流程中插入自定義動作(例如:扣款、加值、凍結) type walletActionOption func( ctx context.Context, tx *usecase.WalletTransferRequest, wallet repository.UserWalletService, ) error // withLockAvailable 鎖定用戶可用餘額 func (use *WalletUseCase) withLockAvailable() walletActionOption { return func(ctx context.Context, tx *usecase.WalletTransferRequest, w repository.UserWalletService) error { uidAsset := uidAssetKey{ uid: tx.FromUID, asset: tx.Asset, } if !use.checkWalletExistence(uidAsset) { // 找不到錢包存不存在 wStatus, err := w.HasAvailableBalance(ctx) if err != nil { return fmt.Errorf("failed to check wallet: %w", err) } // 錢包不存在要做新增 if !wStatus { //// 是合約模擬交易或帳變且錢包不存在才建立錢包 //if !(tx.Business == wa.ContractSimulationBusinessTypeBusinessName || tx.BusinessType == domain.DistributionBusinessTypeBusinessName) { // // 新增錢包有命中 UK 不需要額外上鎖 // return use.translateError(err) //} if _, err := w.InitializeWallets(ctx, tx.Brand); err != nil { return err } return nil } use.markWalletAsExisting(uidAsset) } _, err := w.GetBalancesForUpdate(ctx, []wallet.Types{wallet.TypeAvailable}) if err != nil { return err } return nil } } // withSubAvailable 減少用戶可用餘額 func (use *WalletUseCase) withSubAvailable() walletActionOption { return func(_ context.Context, tx *usecase.WalletTransferRequest, w repository.UserWalletService) error { if err := w.DecreaseBalance(wallet.TypeAvailable, tx.ReferenceOrderID, tx.Amount); err != nil { if errors.Is(err, repository.ErrBalanceInsufficient) { // todo 錯誤要看怎麼給(餘額不足) return fmt.Errorf("balance insufficient") } return err } return nil } } // withAddAvailable 增加用戶可用餘額 func (use *WalletUseCase) withAddAvailable() walletActionOption { return func(_ context.Context, tx *usecase.WalletTransferRequest, w repository.UserWalletService) error { if err := w.IncreaseBalance(wallet.TypeAvailable, tx.ReferenceOrderID, tx.Amount); err != nil { return err } return nil } } // withAddFreeze 增加用戶凍結餘額 func (use *WalletUseCase) withAddFreeze() walletActionOption { return func(_ context.Context, tx *usecase.WalletTransferRequest, w repository.UserWalletService) error { if err := w.IncreaseBalance(wallet.TypeFreeze, tx.ReferenceOrderID, tx.Amount); err != nil { return err } // 訂單可以做解凍,解凍最大上限金額來自當初凍結金額,所以在每一筆tx可以設定Balance // 後續tx需要依據其他tx做交易時能有所依據 tx.PostTransferBalance = tx.Amount return nil } } //// WithAppendFreeze 追加用戶原凍結餘額 //func (use *WalletUseCase) withAppendFreeze() walletActionOption { // return func(ctx context.Context, tx *usecase.WalletTransferRequest, w repository.UserWalletService) error { // order, err := wallet.GetOrderBalance(ctx, tx.ReferenceOrderIDs) // if err != nil { // return use.translateError(err) // } // // // 以id來做lock更可以確保只lock到該筆,而不會因為index關係lock到多筆導致死鎖 // // 而且先不lock把資料先拉出來判斷餘額是否足夠,在不足夠時可以直接return而不用lock減少開銷 // order, err = wallet.GetOrderBalanceXLock(ctx, order.ID) // if err != nil { // return use.translateError(err) // } // // tx.Crypto = order.Crypto // tx.UID = order.UID // // if err := wallet.AddOrder(order.ID, tx.Amount); err != nil { // return use.translateError(err) // } // // if err := wallet.AddFreeze(tx.BusinessType, tx.Amount); err != nil { // return use.translateError(err) // } // // return nil // } //}