// ============================================ // db/delivery_management_db.go // FONCTIONS DB POUR LA GESTION COMPLÈTE DES LIVREURS // ============================================ package db import ( "fmt" "log" "strings" "time" ) var allowedStatuses = map[string]bool{ "available": true, "offline": true, "busy": true, } // CountDeliveriesByStatus compte les livraisons d'un livreur par statut func (d *Database) CountDeliveriesByStatus(livreurUsername string, statuses string) (int, error) { if statuses == "" { var result struct { Count int `gorm:"column:count"` } err := d.GDB.Raw(`SELECT COUNT(*) as count FROM commandes WHERE livreur_assign = ?`, livreurUsername).Scan(&result).Error if err != nil { log.Printf("❌ [CountDeliveries] Erreur: %v", err) return 0, fmt.Errorf("erreur comptage livraisons: %w", err) } return result.Count, nil } cleanStatuses, err := ValidateStatuses(statuses) if err != nil { log.Printf("❌ [CountDeliveries] Validation échouée: %v", err) return 0, err } var result struct { Count int `gorm:"column:count"` } err = d.GDB.Raw( `SELECT COUNT(*) as count FROM commandes WHERE livreur_assign = ? AND status IN ?`, livreurUsername, cleanStatuses, ).Scan(&result).Error if err != nil { log.Printf("❌ [CountDeliveries] Erreur: %v", err) return 0, fmt.Errorf("erreur comptage livraisons: %w", err) } log.Printf("✅ [CountDeliveries] %d livraisons pour %s avec statuts %v", result.Count, livreurUsername, cleanStatuses) return result.Count, nil } func ValidateStatuses(statuses string) ([]string, error) { if statuses == "" { return nil, nil } statusList := strings.Split(statuses, ",") validStatusMap := map[string]bool{ "pending": true, "assigned": true, "en_route": true, "arrived": true, "livre": true, "approved": true, "cancelled": true, } var cleanStatuses []string var invalidStatuses []string for _, status := range statusList { status = strings.TrimSpace(status) if status == "" { continue } if validStatusMap[status] { cleanStatuses = append(cleanStatuses, status) } else { invalidStatuses = append(invalidStatuses, status) } } if len(invalidStatuses) > 0 { log.Printf("⚠️ [ValidateStatuses] Statuts invalides ignorés: %v", invalidStatuses) } if len(cleanStatuses) == 0 { return nil, fmt.Errorf("aucun statut valide trouvé dans: %s", statuses) } return cleanStatuses, nil } // GetLastDeliveryDate récupère la date de la dernière livraison d'un livreur func (d *Database) GetLastDeliveryDate(livreurUsername string) (*time.Time, error) { var result struct { LastDate *time.Time `gorm:"column:last_date"` } err := d.GDB.Raw(` SELECT MAX(updated_at) as last_date FROM commandes WHERE livreur_assign = ? AND status = 'approved'`, livreurUsername).Scan(&result).Error if err != nil { return nil, fmt.Errorf("erreur récupération dernière livraison: %w", err) } return result.LastDate, nil } // GetCurrentCommand récupère l'ID de la commande en cours d'un livreur func (d *Database) GetCurrentCommand(livreurUsername string) (int, error) { currentKey := fmt.Sprintf("delivery:current:%s", livreurUsername) currentIDStr, err := Redis.Get(RedisCtx, currentKey).Result() if err != nil { return 0, nil } var currentID int _, err = fmt.Sscanf(currentIDStr, "%d", ¤tID) if err != nil { return 0, nil } return currentID, nil } // GetDeliveryPersonHistory récupère l'historique paginé des livraisons d'un livreur func (d *Database) GetDeliveryPersonHistory(livreurUsername string, limit, offset int) ([]map[string]any, error) { var history []map[string]any err := d.GDB.Raw(` SELECT c.id as command_id, c.username as client, c.status, c.adresse, c.total_prix::float8 as total_prix, c.created_at as assigned_at, c.updated_at as completed_at FROM commandes c WHERE c.livreur_assign = ? ORDER BY c.created_at DESC LIMIT ? OFFSET ?`, livreurUsername, limit, offset).Scan(&history).Error if err != nil { return nil, fmt.Errorf("erreur récupération historique: %w", err) } return history, nil } // GetDeliveryPersonStatus récupère le statut d'un livreur func (d *Database) GetDeliveryPersonStatus(livreurUsername string) (string, error) { statusKey := fmt.Sprintf("delivery:status:%s", livreurUsername) status, err := Redis.Get(RedisCtx, statusKey).Result() if err != nil { return "offline", nil } return status, nil } // UpdateDeliveryPersonStatus met à jour le statut d'un livreur func (d *Database) UpdateDeliveryPersonStatus(livreurUsername string, status string) error { if !allowedStatuses[status] { return fmt.Errorf("statut invalide: %s", status) } log.Printf("🔄 [UpdateStatus] Mise à jour: %s → %s", livreurUsername, status) statusKey := fmt.Sprintf("delivery:status:%s", livreurUsername) if err := Redis.Set(RedisCtx, statusKey, status, 0).Err(); err != nil { log.Printf("❌ [UpdateStatus] Erreur Redis: %v", err) return fmt.Errorf("erreur mise à jour statut: %w", err) } log.Printf("✅ [UpdateStatus] Statut mis à jour pour %s: %s", livreurUsername, status) return nil } // GetDeliverymanQueueSize récupère la taille de la queue d'un livreur func (d *Database) GetDeliverymanQueueSize(livreurUsername string) (int, error) { queueKey := fmt.Sprintf("delivery:queue:%s", livreurUsername) size, err := Redis.LLen(RedisCtx, queueKey).Result() if err != nil { return 0, fmt.Errorf("erreur récupération taille queue: %w", err) } return int(size), nil } // GetDeliverymanQueue récupère la queue complète d'un livreur func (d *Database) GetDeliverymanQueue(livreurUsername string) ([]int, error) { queueKey := fmt.Sprintf("delivery:queue:%s", livreurUsername) commands, err := Redis.LRange(RedisCtx, queueKey, 0, -1).Result() if err != nil { return nil, fmt.Errorf("erreur récupération queue: %w", err) } var queue []int for _, cmdStr := range commands { var cmdID int fmt.Sscanf(cmdStr, "%d", &cmdID) queue = append(queue, cmdID) } return queue, nil } // UpdateCommandLivreur met à jour le livreur assigné à une commande func (d *Database) UpdateCommandLivreur(commandID int, livreurUsername string) error { result := d.GDB.Exec(` UPDATE commandes SET livreur_assign = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, livreurUsername, commandID) if result.Error != nil { return fmt.Errorf("erreur mise à jour livreur: %w", result.Error) } if result.RowsAffected == 0 { return fmt.Errorf("commande non trouvée") } return nil }