chore: fix bug
Backend - Build & Lint / build (push) Has been cancelled

This commit is contained in:
Nuxgrid
2026-07-11 11:45:13 +02:00
parent f531ddcda8
commit f674b6ef80
29 changed files with 3621 additions and 313 deletions
+71 -35
View File
@@ -737,52 +737,88 @@ func (d *Database) GetClientPointsAndRewards(username string) (pointsExtra map[s
return pointsExtra, pointsRedeemed, nil
}
// claimPoolRewardTx vérifie l'éligibilité et consomme une récompense pour un
// pool donné, dans la transaction fournie — factorisée pour être appelée
// seule (ClaimPoolReward) ou combinée avec la livraison du produit dans la
// même transaction (ClaimPoolRewardAndAddToBasket), afin qu'une récompense
// ne soit jamais consommée sans que son produit soit effectivement livré.
func claimPoolRewardTx(tx *gorm.DB, username, poolKey string, threshold int) (remainingAvailable int, err error) {
var row struct {
Points int `gorm:"column:pts"`
Redeemed int `gorm:"column:redeemed"`
}
if err := tx.Raw(`
SELECT
COALESCE((points_extra->>?)::int, 0) as pts,
COALESCE((points_redeemed->>?)::int, 0) as redeemed
FROM clients WHERE username = ? FOR UPDATE`,
poolKey, poolKey, username).Scan(&row).Error; err != nil {
return 0, fmt.Errorf("erreur lecture: %w", err)
}
earned := row.Points / threshold
available := earned - row.Redeemed
if available <= 0 {
return 0, fmt.Errorf("pas de récompense disponible pour ce pool")
}
if err := tx.Exec(`
UPDATE clients
SET points_redeemed = jsonb_set(
COALESCE(points_redeemed, '{}'::jsonb),
ARRAY[?],
to_jsonb(COALESCE((points_redeemed->>?)::int, 0) + 1)
), updated_at = CURRENT_TIMESTAMP
WHERE username = ?`,
poolKey, poolKey, username).Error; err != nil {
return 0, err
}
return earned - (row.Redeemed + 1), nil
}
// ClaimPoolReward réclame une récompense pour un pool donné si le client a assez de points.
// Retourne le nombre de récompenses disponibles restantes après la réclamation.
func (d *Database) ClaimPoolReward(username, poolKey string, threshold int) (remainingAvailable int, err error) {
var points, redeemed int
err = d.GDB.Transaction(func(tx *gorm.DB) error {
var row struct {
Points int `gorm:"column:pts"`
Redeemed int `gorm:"column:redeemed"`
}
if err := tx.Raw(`
SELECT
COALESCE((points_extra->>?)::int, 0) as pts,
COALESCE((points_redeemed->>?)::int, 0) as redeemed
FROM clients WHERE username = ? FOR UPDATE`,
poolKey, poolKey, username).Scan(&row).Error; err != nil {
return fmt.Errorf("erreur lecture: %w", err)
}
points = row.Points
redeemed = row.Redeemed
earned := points / threshold
available := earned - redeemed
if available <= 0 {
return fmt.Errorf("pas de récompense disponible pour ce pool")
}
return tx.Exec(`
UPDATE clients
SET points_redeemed = jsonb_set(
COALESCE(points_redeemed, '{}'::jsonb),
ARRAY[?],
to_jsonb(COALESCE((points_redeemed->>?)::int, 0) + 1)
), updated_at = CURRENT_TIMESTAMP
WHERE username = ?`,
poolKey, poolKey, username).Error
var err error
remainingAvailable, err = claimPoolRewardTx(tx, username, poolKey, threshold)
return err
})
if err != nil {
return 0, err
}
earned := points / threshold
remainingAvailable = earned - (redeemed + 1)
return remainingAvailable, nil
}
// ClaimPoolRewardAndAddToBasket réclame une récompense ET ajoute les articles
// récompense au panier dans une seule transaction : si les articles ne
// peuvent pas être ajoutés (produit supprimé/inexistant configuré par
// l'admin), toute l'opération est annulée — la récompense n'est pas
// consommée. Corrige un bug où ClaimPoolReward et AddRewardsToBasket,
// appelés séparément, pouvaient consommer une récompense sans livrer aucun
// produit au client si l'ajout au panier échouait après coup.
func (d *Database) ClaimPoolRewardAndAddToBasket(username, poolKey string, threshold int, items []models.RewardItem) (remainingAvailable int, added []models.Panier, err error) {
err = d.GDB.Transaction(func(tx *gorm.DB) error {
var err error
remainingAvailable, err = claimPoolRewardTx(tx, username, poolKey, threshold)
if err != nil {
return err
}
if len(items) > 0 {
added, err = addRewardsToBasketTx(tx, username, items, poolKey)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return 0, nil, err
}
return remainingAvailable, added, nil
}
// ResetClientRedeemed remet à zéro les récompenses réclamées (admin).
func (d *Database) ResetClientRedeemed(username, poolKey string) error {
if poolKey != "" {