// ============================================ // db/cancel_sanctions_db.go // GESTION DES SANCTIONS ÉVOLUTIVES // ============================================ package db import ( "fmt" "log" ) // GetClientCancellationsCount récupère le nombre d'annulations tardives d'un client func (d *Database) GetClientCancellationsCount(username string) (int, error) { var count int query := `SELECT COALESCE(cancellations_count, 0) FROM clients WHERE username = $1` err := d.QueryRow(query, username).Scan(&count) if err != nil { log.Printf("❌ [GetCancellationsCount] Erreur: %v", err) return 0, fmt.Errorf("erreur récupération compteur: %w", err) } return count, nil } // IncrementClientCancellationsCount incrémente le compteur d'annulations func (d *Database) IncrementClientCancellationsCount(username string) error { query := `UPDATE clients SET cancellations_count = COALESCE(cancellations_count, 0) + 1, updated_at = CURRENT_TIMESTAMP WHERE username = $1` result, err := d.Exec(query, username) if err != nil { log.Printf("❌ [IncrementCancellations] Erreur: %v", err) return fmt.Errorf("erreur incrémentation: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("erreur vérification: %w", err) } if rowsAffected == 0 { return fmt.Errorf("client non trouvé") } log.Printf("✅ [IncrementCancellations] Compteur incrémenté pour %s", username) // Invalider le cache Redis cacheKey := fmt.Sprintf("client:%s", username) Redis.Del(RedisCtx, cacheKey) return nil } // CalculateCancellationPenalty calcule la pénalité selon l'historique // 1ère fois: 20 points // 2ème fois: 50 points // 3ème fois: 100 points // 4ème+ fois: 150 points func (d *Database) CalculateCancellationPenalty(username string) (int, error) { count, err := d.GetClientCancellationsCount(username) if err != nil { return 0, err } var penalty int switch { case count == 0: penalty = 20 // Première annulation tardive case count == 1: penalty = 50 // Deuxième annulation tardive case count == 2: penalty = 100 // Troisième annulation tardive default: penalty = 150 // À partir de la 4ème annulation } log.Printf("💰 [CalculatePenalty] Client %s - Annulations: %d → Pénalité: %d points", username, count, penalty) return penalty, nil } // ApplyCancellationPenalty applique une pénalité, incrémente le compteur et REMET TOUS LES POINTS À ZÉRO // ✅ MODIFIÉ: Récupère les points AVANT de les remettre à zéro pour le log dans le handler func (d *Database) ApplyCancellationPenalty(username string) (int, error) { // ✅ ÉTAPE 0: Récupérer les points AVANT modification (pour le handler) // Note: Le handler récupère aussi les points, mais on garde cette fonction autonome // Calculer la pénalité AVANT d'incrémenter penalty, err := d.CalculateCancellationPenalty(username) if err != nil { return 0, err } log.Printf("⚠️ [ApplyCancellationPenalty] Client %s - Pénalité calculée: %d points", username, penalty) // ✅ ÉTAPE 1: Incrémenter le compteur d'annulations if err := d.IncrementClientCancellationsCount(username); err != nil { return 0, err } // ✅ ÉTAPE 2: Mettre l'amende au montant de la pénalité ET remettre TOUS les points à zéro query := `UPDATE clients SET amende = $1, point = 0, point_zipette = 0, updated_at = CURRENT_TIMESTAMP WHERE username = $2` result, err := d.Exec(query, float64(penalty), username) if err != nil { log.Printf("❌ [ApplyCancellationPenalty] Erreur UPDATE: %v", err) return 0, fmt.Errorf("erreur application pénalité: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { return 0, fmt.Errorf("erreur vérification: %w", err) } if rowsAffected == 0 { return 0, fmt.Errorf("client non trouvé") } log.Printf("✅ [ApplyCancellationPenalty] %d points d'amende appliqués à %s + TOUS points remis à 0 (weed + zipette)", penalty, username) // Invalider le cache Redis cacheKey := fmt.Sprintf("client:%s", username) Redis.Del(RedisCtx, cacheKey) return penalty, nil } // GetClientCancellationHistory récupère l'historique d'annulations d'un client func (d *Database) GetClientCancellationHistory(username string) (map[string]interface{}, error) { count, err := d.GetClientCancellationsCount(username) if err != nil { return nil, err } // Calculer la prochaine pénalité var nextPenalty int switch count { case 0: nextPenalty = 20 case 1: nextPenalty = 50 case 2: nextPenalty = 100 default: nextPenalty = 150 } // Récupérer l'amende actuelle client, err := d.GetClientByUsername(username) var currentAmende float64 if err == nil { currentAmende = client.Amende } return map[string]interface{}{ "cancellations_count": count, "current_amende": currentAmende, "next_penalty": nextPenalty, "penalty_scale": map[string]int{ "1st": 20, "2nd": 50, "3rd": 100, "4th+": 150, }, "warning": "TOUS les points de fidélité (weed/hash ET zipette) seront remis à zéro lors de la prochaine annulation tardive", }, nil }