chore: build
This commit is contained in:
@@ -32,6 +32,7 @@ func (d *Database) CreateClient(client *models.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClientByID récupère un client par son ID
|
||||
func (d *Database) GetClientByID(id int) (*models.Client, error) {
|
||||
var row struct {
|
||||
ID int `gorm:"column:id"`
|
||||
@@ -75,6 +76,7 @@ func (d *Database) GetClientByID(id int) (*models.Client, error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// GetAllClients récupère tous les clients
|
||||
func (d *Database) GetAllClients() ([]*models.Client, error) {
|
||||
var rows []struct {
|
||||
ID int `gorm:"column:id"`
|
||||
@@ -290,6 +292,28 @@ func (d *Database) GetClientByTelephone(telephone string) (*models.Client, error
|
||||
}
|
||||
|
||||
// GetClientByUsername récupère un client par son username
|
||||
// GetClientsByUsernames charge plusieurs clients en une seule requête.
|
||||
// Retourne map[username]*Client ; les usernames sans correspondance sont absents de la map.
|
||||
func (d *Database) GetClientsByUsernames(usernames []string) (map[string]*models.Client, error) {
|
||||
result := make(map[string]*models.Client, len(usernames))
|
||||
if len(usernames) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
var rows []struct {
|
||||
ID int `gorm:"column:id"`
|
||||
Username string `gorm:"column:username"`
|
||||
Nom string `gorm:"column:nom"`
|
||||
Prenom string `gorm:"column:prenom"`
|
||||
}
|
||||
if err := d.GDB.Raw(`SELECT id, username, nom, prenom FROM clients WHERE username IN ?`, usernames).Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rows {
|
||||
result[r.Username] = &models.Client{ID: r.ID, Username: r.Username, Nom: r.Nom, Prenom: r.Prenom}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *Database) GetClientByUsername(username string) (*models.Client, error) {
|
||||
var row struct {
|
||||
ID int `gorm:"column:id"`
|
||||
@@ -713,52 +737,81 @@ 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
|
||||
}
|
||||
|
||||
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 != "" {
|
||||
|
||||
Reference in New Issue
Block a user