fix(wallet): enforce consistent withdrawal accounting
This commit is contained in:
@@ -311,14 +311,34 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) {
|
||||
}
|
||||
now := time.Now()
|
||||
if targetStatus == common.StatusRejected {
|
||||
result := tx.Model(&models.WalletBasic{}).
|
||||
Where("id = ? AND withdrawal_balance <= ?", application.WalletBasicID, math.MaxInt64-application.Amount).
|
||||
Update("withdrawal_balance", gorm.Expr("withdrawal_balance + ?", application.Amount))
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
wallet, err := common.LockWalletForUpdate(tx, application.WalletBasicID, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.RowsAffected != 1 {
|
||||
return errors.New("withdrawal balance overflow")
|
||||
released, err := common.ReleaseWithdrawalApplication(&wallet, application)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if released {
|
||||
if err := common.SaveWalletBalances(tx, wallet); err != nil {
|
||||
return err
|
||||
}
|
||||
record := common.NewWalletBalanceRecord(
|
||||
wallet,
|
||||
"withdrawal-reject:"+application.Identity,
|
||||
"income",
|
||||
"withdrawal_release",
|
||||
application.Amount,
|
||||
application.CashNo,
|
||||
"",
|
||||
application.Channel,
|
||||
operatorIdentity,
|
||||
operatorName,
|
||||
request.Reason,
|
||||
)
|
||||
if err := tx.Create(&record).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return tx.Model(&application).Updates(map[string]any{
|
||||
@@ -359,10 +379,43 @@ func CompleteWalletApplyCash(ctx *gin.Context) {
|
||||
if application.ApplyStatus != common.StatusApproved {
|
||||
return errors.New("cash application is not approved")
|
||||
}
|
||||
if !application.BalanceReserved {
|
||||
wallet, err := common.LockWalletForUpdate(tx, application.WalletBasicID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
changed, err := common.SettleLegacyWithdrawal(&wallet, application)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !changed {
|
||||
return errors.New("legacy withdrawal settlement did not change wallet")
|
||||
}
|
||||
if err := common.SaveWalletBalances(tx, wallet); err != nil {
|
||||
return err
|
||||
}
|
||||
operatorIdentity, operatorName := common.PlatformOperator(ctx)
|
||||
record := common.NewWalletBalanceRecord(
|
||||
wallet,
|
||||
"withdrawal-complete:"+application.Identity,
|
||||
"expense",
|
||||
"withdrawal_complete",
|
||||
application.Amount,
|
||||
"",
|
||||
request.TradeNo,
|
||||
application.Channel,
|
||||
operatorIdentity,
|
||||
operatorName,
|
||||
"历史提现申请完成时补记总余额",
|
||||
)
|
||||
if err := tx.Create(&record).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
return tx.Model(&application).Updates(map[string]any{
|
||||
"apply_status": common.StatusCompleted, "trade_no": strings.TrimSpace(request.TradeNo),
|
||||
"callback_msg": request.CallbackMsg, "completed_at": &now,
|
||||
"callback_msg": request.CallbackMsg, "completed_at": &now, "balance_reserved": true,
|
||||
}).Error
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user