chore: update

This commit is contained in:
2026-03-11 19:57:34 +01:00
parent f6b3948839
commit 375aa38454
62 changed files with 2714 additions and 1981 deletions
+40 -34
View File
@@ -53,7 +53,6 @@ func validateCommandStatus(status string) error {
"approved": true,
"cancelled": true,
"disabled": true,
"support": true,
}
if !validStatuses[status] {
@@ -423,10 +422,15 @@ func (d *Database) GetCommandCount() (int, error) {
return count, nil
}
func (d *Database) SetCommandReferralUsed(commandID int, amount float64) error {
_, err := d.Exec(`UPDATE commandes SET referral_used = $1 WHERE id = $2`, amount, commandID)
return err
}
func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
// ✅ Déjà sécurisé avec paramètre $1
query := `SELECT id, username, status, adresse, total_prix, livreur_assign, created_at, updated_at,
proposed_address, address_proposal_status
proposed_address, address_proposal_status, referral_used
FROM commandes WHERE id = $1`
var commandID int
@@ -434,7 +438,7 @@ func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
var livreurAssign sql.NullString
var proposedAddress sql.NullString
var addressProposalStatus string
var totalPrix float64
var totalPrix, referralUsed float64
var createdAt, updatedAt time.Time
err := d.QueryRow(query, id).Scan(
@@ -448,6 +452,7 @@ func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
&updatedAt,
&proposedAddress,
&addressProposalStatus,
&referralUsed,
)
if err == sql.ErrNoRows {
@@ -466,6 +471,7 @@ func (d *Database) GetCommandByID(id int) (map[string]interface{}, error) {
"created_at": createdAt,
"updated_at": updatedAt,
"address_proposal_status": addressProposalStatus,
"referral_used": referralUsed,
}
if livreurAssign.Valid {
@@ -595,7 +601,7 @@ func (d *Database) RespondToAddressProposal(commandID int, clientUsername string
// UpdateCommandStatus met à jour le statut d'une commande
func (d *Database) UpdateCommandStatus(commandID int, status string) error {
// ✅ SÉCURITÉ: Validation du statut
validStatuses := []string{"pending", "assigned", "en_route", "arrived", "livre", "approved", "cancelled", "disabled", "support"}
validStatuses := []string{"pending", "assigned", "en_route", "arrived", "livre", "approved", "cancelled", "disabled"}
isValid := false
for _, vs := range validStatuses {
if status == vs {
@@ -706,7 +712,7 @@ func (d *Database) GetCommandsWithFilter(status, username string, excludeApprove
// ✅ Filtrer par status si fourni avec validation
if status != "" {
validStatuses := []string{"pending", "assigned", "en_route", "arrived", "livre", "approved", "cancelled", "disabled", "support"}
validStatuses := []string{"pending", "assigned", "en_route", "arrived", "livre", "approved", "cancelled", "disabled"}
isValid := false
for _, vs := range validStatuses {
if status == vs {
@@ -832,7 +838,7 @@ func (d *Database) ValidateDeliveryAtomic(commandID int, adminUsername string) (
currentStatus, cmdUsername, livreurAssign)
// ✅ ÉTAPE 3: Vérifier que le statut permet la validation
validStatuses := []string{"assigned", "en_route", "pending", "support", "livre"}
validStatuses := []string{"assigned", "en_route", "pending", "livre"}
isValid := false
for _, s := range validStatuses {
if currentStatus == s {
@@ -878,7 +884,7 @@ func (d *Database) ValidateDeliveryAtomic(commandID int, adminUsername string) (
log.Printf("🔍 [ValidateAtomic] Calcul points pour client: %s", cmdUsername)
// Utiliser la version transactionnelle du calcul de points
points, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, cmdUsername)
points, _, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, cmdUsername)
if err != nil {
log.Printf("❌ [ValidateAtomic] Erreur calcul/ajout points: %v", err)
return 0, fmt.Errorf("erreur attribution points: %w", err)
@@ -957,14 +963,14 @@ func (d *Database) ValidateDeliveryAtomic(commandID int, adminUsername string) (
}
// ApproveDeliveryAtomic - Version atomique pour approbation client
func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, error) {
func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, string, error) {
log.Printf("🔒 [ApproveAtomic] START - cmd=%d, client=%s", commandID, username)
// ✅ ÉTAPE 1: Démarrer une transaction
tx, err := d.Begin()
if err != nil {
log.Printf("❌ [ApproveAtomic] Erreur début transaction: %v", err)
return 0, fmt.Errorf("erreur transaction: %w", err)
return 0, "", fmt.Errorf("erreur transaction: %w", err)
}
defer tx.Rollback()
@@ -979,11 +985,11 @@ func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, e
if err == sql.ErrNoRows {
log.Printf("❌ [ApproveAtomic] Commande %d non trouvée", commandID)
return 0, fmt.Errorf("commande non trouvée")
return 0, "", fmt.Errorf("commande non trouvée")
}
if err != nil {
log.Printf("❌ [ApproveAtomic] Erreur SELECT: %v", err)
return 0, fmt.Errorf("erreur lecture commande: %w", err)
return 0, "", fmt.Errorf("erreur lecture commande: %w", err)
}
log.Printf("📋 [ApproveAtomic] Commande trouvée - status=%s, owner=%s", currentStatus, cmdUsername)
@@ -992,13 +998,13 @@ func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, e
if cmdUsername != username {
log.Printf("❌ [ApproveAtomic] Commande n'appartient pas à %s (propriétaire: %s)",
username, cmdUsername)
return 0, fmt.Errorf("cette commande ne vous appartient pas")
return 0, "", fmt.Errorf("cette commande ne vous appartient pas")
}
// ✅ ÉTAPE 4: Vérifier le statut
if currentStatus != "livre" {
log.Printf("❌ [ApproveAtomic] Statut invalide: %s (attendu: livre)", currentStatus)
return 0, fmt.Errorf("commande doit être en statut 'livre' (statut actuel: %s)", currentStatus)
return 0, "", fmt.Errorf("commande doit être en statut 'livre' (statut actuel: %s)", currentStatus)
}
// ✅ ÉTAPE 5: UPDATE avec vérification du statut
@@ -1010,25 +1016,25 @@ func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, e
if err != nil {
log.Printf("❌ [ApproveAtomic] Erreur UPDATE: %v", err)
return 0, fmt.Errorf("erreur mise à jour statut: %w", err)
return 0, "", fmt.Errorf("erreur mise à jour statut: %w", err)
}
rows, _ := result.RowsAffected()
if rows == 0 {
log.Printf("❌ [ApproveAtomic] Commande %d déjà modifiée (race condition évitée)", commandID)
return 0, fmt.Errorf("commande déjà approuvée ou modifiée")
return 0, "", fmt.Errorf("commande déjà approuvée ou modifiée")
}
log.Printf("✅ [ApproveAtomic] Statut mis à jour: livre → approved")
// ✅ ÉTAPE 6: Calculer et ajouter les points
totalPoints, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, username)
totalPoints, pointCategory, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, username)
if err != nil {
log.Printf("❌ [ApproveAtomic] Erreur calcul points: %v", err)
return 0, fmt.Errorf("erreur attribution points: %w", err)
return 0, "", fmt.Errorf("erreur attribution points: %w", err)
}
log.Printf("✅ [ApproveAtomic] %d points attribués à %s", totalPoints, username)
log.Printf("✅ [ApproveAtomic] %d points [%s] attribués à %s", totalPoints, pointCategory, username)
// ✅ ÉTAPE 7: Incrémenter le compteur de commandes
_, err = tx.Exec(`
@@ -1046,7 +1052,7 @@ func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, e
INSERT INTO command_logs (command_id, status, message, author, created_at)
VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)
`, commandID, "approved",
fmt.Sprintf("Livraison confirmée par le client %s - %d points attribués", username, totalPoints),
fmt.Sprintf("Livraison confirmée par le client %s - %d points [%s] attribués", username, totalPoints, pointCategory),
username)
if err != nil {
@@ -1056,11 +1062,11 @@ func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, e
// ✅ ÉTAPE 9: Commit
if err := tx.Commit(); err != nil {
log.Printf("❌ [ApproveAtomic] Erreur COMMIT: %v", err)
return 0, fmt.Errorf("erreur commit transaction: %w", err)
return 0, "", fmt.Errorf("erreur commit transaction: %w", err)
}
log.Printf("🎉 [ApproveAtomic] SUCCÈS - Commande %d approuvée, %d points attribués",
commandID, totalPoints)
log.Printf("🎉 [ApproveAtomic] SUCCÈS - Commande %d approuvée, %d points [%s] attribués",
commandID, totalPoints, pointCategory)
// ✅ ÉTAPE 10: Optimiser queue livreur (async, après commit)
if livreurAssign != "" {
@@ -1087,16 +1093,16 @@ func (d *Database) ApproveDeliveryAtomic(commandID int, username string) (int, e
log.Printf("✅ [ApproveAtomic] Caches invalidés")
}()
return totalPoints, nil
return totalPoints, pointCategory, nil
}
// ApproveDeliveryAtomicByStaff - Confirmation de réception par admin ou cabine à la place du client
func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername string) (int, string, error) {
func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername string) (int, string, string, error) {
log.Printf("🔒 [ApproveAtomicStaff] START - cmd=%d, staff=%s", commandID, staffUsername)
tx, err := d.Begin()
if err != nil {
return 0, "", fmt.Errorf("erreur transaction: %w", err)
return 0, "", "", fmt.Errorf("erreur transaction: %w", err)
}
defer tx.Rollback()
@@ -1109,14 +1115,14 @@ func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername str
`, commandID).Scan(&currentStatus, &clientUsername, &livreurAssign)
if err == sql.ErrNoRows {
return 0, "", fmt.Errorf("commande non trouvée")
return 0, "", "", fmt.Errorf("commande non trouvée")
}
if err != nil {
return 0, "", fmt.Errorf("erreur lecture commande: %w", err)
return 0, "", "", fmt.Errorf("erreur lecture commande: %w", err)
}
if currentStatus != "livre" {
return 0, "", fmt.Errorf("commande doit être en statut 'livre' (statut actuel: %s)", currentStatus)
return 0, "", "", fmt.Errorf("commande doit être en statut 'livre' (statut actuel: %s)", currentStatus)
}
result, err := tx.Exec(`
@@ -1125,17 +1131,17 @@ func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername str
WHERE id = $1 AND status = 'livre'
`, commandID)
if err != nil {
return 0, "", fmt.Errorf("erreur mise à jour statut: %w", err)
return 0, "", "", fmt.Errorf("erreur mise à jour statut: %w", err)
}
rows, _ := result.RowsAffected()
if rows == 0 {
return 0, "", fmt.Errorf("commande déjà approuvée ou modifiée")
return 0, "", "", fmt.Errorf("commande déjà approuvée ou modifiée")
}
totalPoints, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, clientUsername)
totalPoints, pointCategory, err := d.CalculateAndAddPointsForCommandTx(tx, commandID, clientUsername)
if err != nil {
return 0, "", fmt.Errorf("erreur attribution points: %w", err)
return 0, "", "", fmt.Errorf("erreur attribution points: %w", err)
}
_, err = tx.Exec(`
@@ -1158,7 +1164,7 @@ func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername str
}
if err := tx.Commit(); err != nil {
return 0, "", fmt.Errorf("erreur commit: %w", err)
return 0, "", "", fmt.Errorf("erreur commit: %w", err)
}
log.Printf("🎉 [ApproveAtomicStaff] SUCCÈS - cmd=%d approuvée par %s, %d points → client %s",
@@ -1178,5 +1184,5 @@ func (d *Database) ApproveDeliveryAtomicByStaff(commandID int, staffUsername str
Redis.Del(RedisCtx, fmt.Sprintf("client:%s:commands", clientUsername))
}()
return totalPoints, clientUsername, nil
return totalPoints, pointCategory, clientUsername, nil
}