101 lines
2.8 KiB
Go
101 lines
2.8 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
)
|
|
|
|
// GetClientReferralBalance retourne le solde parrainage d'un client.
|
|
func (d *Database) GetClientReferralBalance(username string) (float64, error) {
|
|
var balance float64
|
|
err := d.QueryRow(
|
|
`SELECT referral_balance FROM clients WHERE username = $1`,
|
|
username,
|
|
).Scan(&balance)
|
|
if err == sql.ErrNoRows {
|
|
return 0, fmt.Errorf("client non trouvé")
|
|
}
|
|
return balance, err
|
|
}
|
|
|
|
// CreditClientReferral ajoute un montant au solde parrainage d'un client.
|
|
func (d *Database) CreditClientReferral(username string, amount float64) error {
|
|
if amount <= 0 {
|
|
return fmt.Errorf("le montant doit être positif")
|
|
}
|
|
res, err := d.Exec(
|
|
`UPDATE clients SET referral_balance = referral_balance + $1 WHERE username = $2`,
|
|
amount, username,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, _ := res.RowsAffected()
|
|
if n == 0 {
|
|
return fmt.Errorf("client non trouvé")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DebitReferralBalance déduit atomiquement le solde parrainage avant la création de commande.
|
|
// Gère sa propre transaction avec FOR UPDATE pour éviter le double-spend concurrent.
|
|
// Retourne une erreur si le solde est insuffisant.
|
|
func (d *Database) DebitReferralBalance(username string, amount float64) error {
|
|
if amount <= 0 {
|
|
return nil
|
|
}
|
|
tx, err := d.Begin()
|
|
if err != nil {
|
|
return fmt.Errorf("erreur transaction: %w", err)
|
|
}
|
|
var balance float64
|
|
if err := tx.QueryRow(
|
|
`SELECT referral_balance FROM clients WHERE username = $1 FOR UPDATE`,
|
|
username,
|
|
).Scan(&balance); err != nil {
|
|
tx.Rollback()
|
|
return fmt.Errorf("client non trouvé")
|
|
}
|
|
if balance < amount {
|
|
tx.Rollback()
|
|
return fmt.Errorf("solde parrainage insuffisant (disponible: %.2f€)", balance)
|
|
}
|
|
if _, err := tx.Exec(
|
|
`UPDATE clients SET referral_balance = referral_balance - $1 WHERE username = $2`,
|
|
amount, username,
|
|
); err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
// RestoreReferralBalance restaure le solde parrainage si la commande échoue après le débit.
|
|
func (d *Database) RestoreReferralBalance(username string, amount float64) error {
|
|
return d.CreditClientReferral(username, amount)
|
|
}
|
|
|
|
// UseClientReferralBalance déduit un montant du solde parrainage dans une transaction.
|
|
// Retourne une erreur si le solde est insuffisant.
|
|
func (d *Database) UseClientReferralBalance(tx *sql.Tx, username string, amount float64) error {
|
|
if amount <= 0 {
|
|
return nil
|
|
}
|
|
var balance float64
|
|
err := tx.QueryRow(
|
|
`SELECT referral_balance FROM clients WHERE username = $1 FOR UPDATE`,
|
|
username,
|
|
).Scan(&balance)
|
|
if err != nil {
|
|
return fmt.Errorf("client non trouvé")
|
|
}
|
|
if balance < amount {
|
|
return fmt.Errorf("solde parrainage insuffisant (disponible: %.2f€)", balance)
|
|
}
|
|
_, err = tx.Exec(
|
|
`UPDATE clients SET referral_balance = referral_balance - $1 WHERE username = $2`,
|
|
amount, username,
|
|
)
|
|
return err
|
|
}
|