chore: refacto
This commit is contained in:
@@ -4,11 +4,12 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetAvailableDeliveryPersons récupère tous les livreurs disponibles
|
||||
func (d *Database) GetAvailableDeliveryPersons() ([]map[string]interface{}, error) {
|
||||
func (d *Database) GetAvailableDeliveryPersons() ([]map[string]any, error) {
|
||||
query := `SELECT id, username, total, livraison
|
||||
FROM users
|
||||
WHERE role = 'livreur'
|
||||
@@ -20,7 +21,7 @@ func (d *Database) GetAvailableDeliveryPersons() ([]map[string]interface{}, erro
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var livreurs []map[string]interface{}
|
||||
var livreurs []map[string]any
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var username string
|
||||
@@ -31,7 +32,7 @@ func (d *Database) GetAvailableDeliveryPersons() ([]map[string]interface{}, erro
|
||||
return nil, fmt.Errorf("erreur lors du scan du livreur: %w", err)
|
||||
}
|
||||
|
||||
livreur := map[string]interface{}{
|
||||
livreur := map[string]any{
|
||||
"id": id,
|
||||
"username": username,
|
||||
"total": total,
|
||||
@@ -47,17 +48,13 @@ func (d *Database) GetAvailableDeliveryPersons() ([]map[string]interface{}, erro
|
||||
func (d *Database) AssignDeliveryPerson(commandID int, livreurUsername string) error {
|
||||
log.Printf("📦 [AssignDeliveryPerson] START - commandID=%d, livreur=%s", commandID, livreurUsername)
|
||||
|
||||
// ✅ DÉMARRER UNE TRANSACTION
|
||||
tx, err := d.Begin()
|
||||
if err != nil {
|
||||
log.Printf("❌ Erreur démarrage transaction: %v", err)
|
||||
return fmt.Errorf("erreur démarrage transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback() // Rollback automatique si non commité
|
||||
defer tx.Rollback()
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 1: Vérifier le livreur (DANS la transaction)
|
||||
// ============================================
|
||||
var role string
|
||||
checkQuery := `SELECT role FROM users WHERE username = $1 FOR UPDATE`
|
||||
err = tx.QueryRow(checkQuery, livreurUsername).Scan(&role)
|
||||
@@ -74,18 +71,12 @@ func (d *Database) AssignDeliveryPerson(commandID int, livreurUsername string) e
|
||||
return fmt.Errorf("l'utilisateur n'est pas un livreur")
|
||||
}
|
||||
|
||||
log.Printf(" ✅ Livreur valide: %s", livreurUsername)
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 2: Vérifier et VERROUILLER la commande
|
||||
// ✅ FOR UPDATE empêche les modifications concurrentes
|
||||
// ============================================
|
||||
var currentStatus string
|
||||
var currentLivreur sql.NullString
|
||||
statusQuery := `SELECT status, livreur_assign
|
||||
FROM commandes
|
||||
WHERE id = $1
|
||||
FOR UPDATE` // ⚠️ VERROUILLAGE CRITIQUE
|
||||
FOR UPDATE`
|
||||
|
||||
err = tx.QueryRow(statusQuery, commandID).Scan(¤tStatus, ¤tLivreur)
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -97,33 +88,14 @@ func (d *Database) AssignDeliveryPerson(commandID int, livreurUsername string) e
|
||||
return fmt.Errorf("erreur lors de la vérification de la commande: %w", err)
|
||||
}
|
||||
|
||||
log.Printf(" ✅ Commande trouvée: status=%s, livreur_assign=%s",
|
||||
currentStatus, currentLivreur.String)
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 3: Vérifier que la commande est assignable
|
||||
// ============================================
|
||||
|
||||
// ✅ Vérifier le statut (pending ou assigned pour permettre la réassignation)
|
||||
validStatusesForAssignment := []string{"pending", "assigned"}
|
||||
isValidStatus := false
|
||||
for _, vs := range validStatusesForAssignment {
|
||||
if currentStatus == vs {
|
||||
isValidStatus = true
|
||||
break
|
||||
}
|
||||
}
|
||||
isValidStatus := slices.Contains(validStatusesForAssignment, currentStatus)
|
||||
|
||||
if !isValidStatus {
|
||||
log.Printf("❌ Statut invalide pour assignation: %s", currentStatus)
|
||||
return fmt.Errorf("commande en statut '%s', impossible d'assigner un livreur", currentStatus)
|
||||
}
|
||||
|
||||
log.Printf(" ✅ Statut valide pour assignation: %s", currentStatus)
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 4: Assigner le livreur (ATOMIQUE)
|
||||
// ============================================
|
||||
updateQuery := `UPDATE commandes
|
||||
SET livreur_assign = $1,
|
||||
status = 'assigned',
|
||||
@@ -147,11 +119,6 @@ func (d *Database) AssignDeliveryPerson(commandID int, livreurUsername string) e
|
||||
return fmt.Errorf("impossible d'assigner la commande (déjà assignée ou statut changé)")
|
||||
}
|
||||
|
||||
log.Printf(" ✅ Commande assignée au livreur: %s", livreurUsername)
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 5: Ajouter un log (DANS la transaction)
|
||||
// ============================================
|
||||
logQuery := `INSERT INTO command_logs (command_id, status, message, author, created_at)
|
||||
VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)`
|
||||
|
||||
@@ -163,28 +130,22 @@ func (d *Database) AssignDeliveryPerson(commandID int, livreurUsername string) e
|
||||
// Non bloquant
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✅ COMMIT de la transaction
|
||||
// ============================================
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Printf("❌ Erreur COMMIT: %v", err)
|
||||
return fmt.Errorf("erreur commit transaction: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("🎉 [AssignDeliveryPerson] SUCCÈS - Commande %d assignée à %s", commandID, livreurUsername)
|
||||
log.Printf(" Workflow: pending → ✅ assigned (TRANSACTION COMMITTED)")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDeliveryPersonCommands récupère les commandes assignées à un livreur
|
||||
func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status string) ([]map[string]interface{}, error) {
|
||||
func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status string) ([]map[string]any, error) {
|
||||
query := `SELECT id, username, status, adresse, total_prix, livreur_assign, created_at, updated_at
|
||||
FROM commandes
|
||||
WHERE livreur_assign = $1`
|
||||
|
||||
args := []interface{}{livreurUsername}
|
||||
args := []any{livreurUsername}
|
||||
|
||||
if status != "" {
|
||||
query += " AND status = $2"
|
||||
@@ -199,7 +160,7 @@ func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status stri
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var commands []map[string]interface{}
|
||||
var commands []map[string]any
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var username, status, adresse string
|
||||
@@ -212,7 +173,7 @@ func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status stri
|
||||
return nil, fmt.Errorf("erreur lors du scan: %w", err)
|
||||
}
|
||||
|
||||
command := map[string]interface{}{
|
||||
command := map[string]any{
|
||||
"id": id,
|
||||
"username": username,
|
||||
"status": status,
|
||||
@@ -228,7 +189,6 @@ func (d *Database) GetDeliveryPersonCommands(livreurUsername string, status stri
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
// ✅ NOUVELLE MÉTHODE: IncrementLivreurDeliveryCount incrémente le compteur de livraisons d'un livreur
|
||||
func (d *Database) IncrementLivreurDeliveryCount(livreurUsername string) error {
|
||||
query := `UPDATE users
|
||||
SET livraison = livraison + 1,
|
||||
@@ -249,14 +209,10 @@ func (d *Database) IncrementLivreurDeliveryCount(livreurUsername string) error {
|
||||
return fmt.Errorf("livreur non trouvé")
|
||||
}
|
||||
|
||||
log.Printf("✅ Livraison incrémentée pour le livreur: %s", livreurUsername)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
log.Printf("📝 [ApproveDelivery] START - commandID=%d, client=%s", commandID, clientUsername)
|
||||
|
||||
// ✅ DÉMARRER UNE TRANSACTION
|
||||
tx, err := d.Begin()
|
||||
if err != nil {
|
||||
log.Printf("❌ Erreur démarrage transaction: %v", err)
|
||||
@@ -264,16 +220,13 @@ func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 1: Vérifier et VERROUILLER la commande
|
||||
// ============================================
|
||||
var commandUsername, currentStatus string
|
||||
var livreurAssign sql.NullString
|
||||
|
||||
checkQuery := `SELECT username, status, livreur_assign
|
||||
FROM commandes
|
||||
WHERE id = $1
|
||||
FOR UPDATE` // ⚠️ VERROUILLAGE CRITIQUE
|
||||
FOR UPDATE`
|
||||
|
||||
err = tx.QueryRow(checkQuery, commandID).Scan(&commandUsername, ¤tStatus, &livreurAssign)
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -283,25 +236,14 @@ func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
return fmt.Errorf("erreur lors de la vérification de la commande: %w", err)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 2: Validations métier
|
||||
// ============================================
|
||||
|
||||
// ✅ Vérifier que c'est bien la commande du client
|
||||
if commandUsername != clientUsername {
|
||||
return fmt.Errorf("cette commande ne vous appartient pas")
|
||||
}
|
||||
|
||||
// ✅ Vérifier le statut
|
||||
if currentStatus != "livre" {
|
||||
return fmt.Errorf("cette commande n'est pas encore livrée (statut actuel: %s)", currentStatus)
|
||||
}
|
||||
|
||||
log.Printf(" ✅ Validations OK: client=%s, status=%s", commandUsername, currentStatus)
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 3: Mettre à jour le statut (ATOMIQUE)
|
||||
// ============================================
|
||||
updateQuery := `UPDATE commandes
|
||||
SET status = 'approved',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
@@ -322,11 +264,6 @@ func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
return fmt.Errorf("impossible d'approuver: statut changé ou commande introuvable")
|
||||
}
|
||||
|
||||
log.Printf(" ✅ Statut mis à jour: livre → approved")
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 4: Incrémenter le compteur du livreur (ATOMIQUE)
|
||||
// ============================================
|
||||
if livreurAssign.Valid && livreurAssign.String != "" {
|
||||
incrementQuery := `UPDATE users
|
||||
SET livraison = livraison + 1,
|
||||
@@ -337,7 +274,6 @@ func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
result, err := tx.Exec(incrementQuery, livreurAssign.String)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Erreur incrémentation livreur: %v", err)
|
||||
// Non bloquant mais on continue dans la transaction
|
||||
} else {
|
||||
rows, _ := result.RowsAffected()
|
||||
if rows > 0 {
|
||||
@@ -346,9 +282,6 @@ func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ÉTAPE 5: Ajouter un log (DANS la transaction)
|
||||
// ============================================
|
||||
logQuery := `INSERT INTO command_logs (command_id, status, message, author, created_at)
|
||||
VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)`
|
||||
|
||||
@@ -357,20 +290,13 @@ func (d *Database) ApproveDelivery(commandID int, clientUsername string) error {
|
||||
clientUsername)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Erreur ajout log: %v", err)
|
||||
// Non bloquant
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✅ COMMIT de la transaction
|
||||
// ============================================
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Printf("❌ Erreur COMMIT: %v", err)
|
||||
return fmt.Errorf("erreur commit transaction: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("🎉 [ApproveDelivery] SUCCÈS - Commande %d approuvée par %s", commandID, clientUsername)
|
||||
log.Printf(" Workflow: livre → ✅ approved (TRANSACTION COMMITTED)")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user