// ============================================ // handlers/delivery_admin_handlers.go // HANDLERS ADMIN POUR LA GESTION DES LIVREURS // ============================================ package handlers import ( "fmt" "gestion/db" "gestion/utils" "log" "net/http" "strconv" "time" "github.com/gin-gonic/gin" ) // GetDeliveryPersonDetails récupère les détails complets d'un livreur func GetDeliveryPersonDetails(c *gin.Context) { database := c.MustGet("database").(*db.Database) // ✅ SÉCURITÉ: Admin seulement userRole := c.GetString("role") if userRole != "admin" && userRole != "cabine" && userRole != "livreur" { log.Printf("❌ [GET_DELIVERY_DETAILS] Accès refusé - role=%s", userRole) c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"}) return } username := c.Param("username") if username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"}) return } livreur, err := database.GetUserByUsername(username) if err != nil { log.Printf("❌ [GET_DELIVERY_DETAILS] Livreur non trouvé: %v", err) c.JSON(http.StatusNotFound, gin.H{ "error": "Livreur non trouvé", }) return } // Vérifier que c'est bien un livreur if livreur.Role != "livreur" { log.Printf("❌ [GET_DELIVERY_DETAILS] Utilisateur n'est pas un livreur: role=%s", livreur.Role) c.JSON(http.StatusBadRequest, gin.H{ "error": "Cet utilisateur n'est pas un livreur", "role": livreur.Role, }) return } status, err := database.GetDeliveryPersonStatus(username) if err != nil { log.Printf("⚠️ [GET_DELIVERY_DETAILS] Impossible de récupérer le statut: %v", err) status = "offline" // Statut par défaut } // Utiliser la fonction GPS existante lat, lon, err := database.GetDeliveryPersonLocation(username) var locationInfo map[string]interface{} if err == nil { locationInfo = map[string]interface{}{ "latitude": lat, "longitude": lon, } } queueSize, _ := database.GetDeliverymanQueueSize(username) currentCommand, _ := database.GetCurrentCommand(username) totalDeliveries, _ := database.CountDeliveriesByStatus(username, "") completedDeliveries, _ := database.CountDeliveriesByStatus(username, "approved") pendingDeliveries, _ := database.CountDeliveriesByStatus(username, "assigned,en_route,livre") c.JSON(http.StatusOK, gin.H{ "success": true, "deliveryman": gin.H{ "id": livreur.ID, "username": livreur.Username, "role": livreur.Role, "status": status, "current_command": currentCommand, "queue_size": queueSize, "total_deliveries": totalDeliveries, "completed_deliveries": completedDeliveries, "pending_deliveries": pendingDeliveries, "location": locationInfo, }, }) } // UpdateDeliveryPersonStatusAdmin modifie le statut d'un livreur (Admin) func UpdateDeliveryPersonStatusAdmin(c *gin.Context) { database := c.MustGet("database").(*db.Database) userRole := c.GetString("role") if !utils.CheckRoleAdmin(c, userRole) { c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"}) return } username := c.Param("username") if username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"}) return } var req struct { Status string `json:"status" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Statut requis", }) return } // Valider le statut validStatuses := []string{"available", "busy", "offline"} isValid := false for _, vs := range validStatuses { if req.Status == vs { isValid = true break } } if !isValid { c.JSON(http.StatusBadRequest, gin.H{ "error": "Statut invalide", "valid_statuses": validStatuses, "received": req.Status, }) return } log.Printf("📝 [UPDATE_DELIVERY_STATUS] Modification: %s → %s", username, req.Status) livreur, err := database.GetUserByUsername(username) if err != nil { log.Printf("❌ [UPDATE_DELIVERY_STATUS] Livreur non trouvé") c.JSON(http.StatusNotFound, gin.H{"error": "Livreur non trouvé"}) return } if livreur.Role != "livreur" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Cet utilisateur n'est pas un livreur", }) return } err = database.UpdateDeliveryPersonStatus(username, req.Status) if err != nil { log.Printf("❌ [UPDATE_DELIVERY_STATUS] Erreur mise à jour: %v", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Erreur mise à jour statut", }) return } adminUsername, _ := c.Get("username") log.Printf("✅ [UPDATE_DELIVERY_STATUS] Statut modifié par admin %s: %s → %s", adminUsername, username, req.Status) c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Statut du livreur mis à jour", "username": username, "new_status": req.Status, "updated_by": adminUsername, }) } // GetDeliveryPersonStats récupère les statistiques d'un livreur func GetDeliveryPersonStats(c *gin.Context) { database := c.MustGet("database").(*db.Database) userRole := c.GetString("role") if userRole != "admin" { log.Printf("❌ [GET_DELIVERY_STATS] Accès refusé - role=%s", userRole) c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"}) return } username := c.Param("username") if username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"}) return } log.Printf("📊 [GET_DELIVERY_STATS] Calcul stats pour: %s", username) livreur, err := database.GetUserByUsername(username) if err != nil { log.Printf("❌ [GET_DELIVERY_STATS] Livreur non trouvé") c.JSON(http.StatusNotFound, gin.H{"error": "Livreur non trouvé"}) return } if livreur.Role != "livreur" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Cet utilisateur n'est pas un livreur", }) return } // ============================================ // Récupérer les statistiques // ============================================ totalDeliveries, _ := database.CountDeliveriesByStatus(username, "") completedDeliveries, _ := database.CountDeliveriesByStatus(username, "approved") cancelledDeliveries, _ := database.CountDeliveriesByStatus(username, "cancelled") pendingDeliveries, _ := database.CountDeliveriesByStatus(username, "pending,assigned") inProgressDeliveries, _ := database.CountDeliveriesByStatus(username, "en_route,livre") // Récupérer statut et queue status, _ := database.GetDeliveryPersonStatus(username) queueSize, _ := database.GetDeliverymanQueueSize(username) // Calculer le taux de succès successRate := 0.0 if totalDeliveries > 0 { successRate = (float64(completedDeliveries) / float64(totalDeliveries)) * 100 } // Récupérer la dernière livraison lastDeliveryDate := "" lastDelivery, err := database.GetLastDeliveryDate(username) if err == nil && lastDelivery != nil { lastDeliveryDate = lastDelivery.Format("2006-01-02 15:04:05") } log.Printf("✅ [GET_DELIVERY_STATS] Stats calculées: total=%d, completed=%d", totalDeliveries, completedDeliveries) // ============================================ // RÉPONSE // ============================================ c.JSON(http.StatusOK, gin.H{ "success": true, "stats": gin.H{ "username": username, "total_deliveries": totalDeliveries, "completed_deliveries": completedDeliveries, "cancelled_deliveries": cancelledDeliveries, "pending_deliveries": pendingDeliveries, "in_progress_deliveries": inProgressDeliveries, "success_rate": successRate, "current_queue_size": queueSize, "last_delivery_date": lastDeliveryDate, "status": status, }, }) } // GetDeliveryPersonHistory récupère l'historique des livraisons d'un livreur func GetDeliveryPersonHistory(c *gin.Context) { database := c.MustGet("database").(*db.Database) // ✅ SÉCURITÉ: Admin seulement userRole := c.GetString("role") if userRole != "admin" { log.Printf("❌ [GET_DELIVERY_HISTORY] Accès refusé - role=%s", userRole) c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"}) return } username := c.Param("username") if username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"}) return } // Paramètres de pagination limit := 20 offset := 0 if limitStr := c.Query("limit"); limitStr != "" { if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 { limit = l } } if offsetStr := c.Query("offset"); offsetStr != "" { if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 { offset = o } } log.Printf("📜 [GET_DELIVERY_HISTORY] Récupération historique: %s (limit=%d, offset=%d)", username, limit, offset) // ============================================ // Vérifier que le livreur existe // ============================================ livreur, err := database.GetUserByUsername(username) if err != nil { log.Printf("❌ [GET_DELIVERY_HISTORY] Livreur non trouvé") c.JSON(http.StatusNotFound, gin.H{"error": "Livreur non trouvé"}) return } if livreur.Role != "livreur" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Cet utilisateur n'est pas un livreur", }) return } // ============================================ // Récupérer l'historique // ============================================ history, err := database.GetDeliveryPersonHistory(username, limit, offset) if err != nil { log.Printf("❌ [GET_DELIVERY_HISTORY] Erreur récupération: %v", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Erreur récupération historique", }) return } // Compter le total total, _ := database.CountDeliveriesByStatus(username, "") log.Printf("✅ [GET_DELIVERY_HISTORY] Historique récupéré: %d livraisons (total=%d)", len(history), total) c.JSON(http.StatusOK, gin.H{ "success": true, "history": history, "count": len(history), "total": total, "limit": limit, "offset": offset, }) } // UpdateDeliveryPersonLocationAdmin modifie la position GPS d'un livreur (Admin) // PUT /api/v2/admin/protected/delivery-persons/:username/location func UpdateDeliveryPersonLocationAdmin(c *gin.Context) { database := c.MustGet("database").(*db.Database) // ✅ SÉCURITÉ: Admin seulement userRole := c.GetString("role") if userRole != "admin" && userRole != "cabine" { log.Printf("❌ [UPDATE_DELIVERY_LOCATION] Accès refusé - role=%s", userRole) c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"}) return } username := c.Param("username") if username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"}) return } var req struct { Latitude float64 `json:"latitude" binding:"required"` Longitude float64 `json:"longitude" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Coordonnées GPS requises", }) return } // Valider les coordonnées if req.Latitude < -90 || req.Latitude > 90 { c.JSON(http.StatusBadRequest, gin.H{ "error": "Latitude invalide (doit être entre -90 et 90)", }) return } if req.Longitude < -180 || req.Longitude > 180 { c.JSON(http.StatusBadRequest, gin.H{ "error": "Longitude invalide (doit être entre -180 et 180)", }) return } log.Printf("📍 [UPDATE_DELIVERY_LOCATION] Modification: %s → (%.6f, %.6f)", username, req.Latitude, req.Longitude) // ============================================ // Vérifier que le livreur existe // ============================================ livreur, err := database.GetUserByUsername(username) if err != nil { log.Printf("❌ [UPDATE_DELIVERY_LOCATION] Livreur non trouvé") c.JSON(http.StatusNotFound, gin.H{"error": "Livreur non trouvé"}) return } if livreur.Role != "livreur" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Cet utilisateur n'est pas un livreur", }) return } // ============================================ // Mettre à jour la position (utilise la fonction existante) // ============================================ err = database.UpdateDeliveryPersonLocation(username, req.Latitude, req.Longitude) if err != nil { log.Printf("❌ [UPDATE_DELIVERY_LOCATION] Erreur mise à jour: %v", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Erreur mise à jour position", }) return } adminUsername, _ := c.Get("username") log.Printf("✅ [UPDATE_DELIVERY_LOCATION] Position modifiée par admin %s: %s → (%.6f, %.6f)", adminUsername, username, req.Latitude, req.Longitude) c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Position GPS mise à jour", "location": gin.H{ "username": username, "latitude": req.Latitude, "longitude": req.Longitude, "updated_at": time.Now().Unix(), }, "updated_by": adminUsername, }) } // ============================================ // 🗑️ REMOVE COMMAND FROM QUEUE // ============================================ // RemoveCommandFromQueue retire une commande de la queue d'un livreur // DELETE /api/v2/admin/protected/delivery-persons/:username/queue/:command_id func RemoveCommandFromQueue(c *gin.Context) { database := c.MustGet("database").(*db.Database) // ✅ SÉCURITÉ: Admin seulement userRole := c.GetString("role") if userRole != "admin" { log.Printf("❌ [REMOVE_FROM_QUEUE] Accès refusé - role=%s", userRole) c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"}) return } username := c.Param("username") commandIDStr := c.Param("command_id") if username == "" || commandIDStr == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Username et command_id requis", }) return } commandID, err := strconv.Atoi(commandIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "ID de commande invalide", }) return } log.Printf("🗑️ [REMOVE_FROM_QUEUE] Suppression: cmd %d de la queue de %s", commandID, username) // ============================================ // Vérifier que le livreur existe // ============================================ livreur, err := database.GetUserByUsername(username) if err != nil { log.Printf("❌ [REMOVE_FROM_QUEUE] Livreur non trouvé") c.JSON(http.StatusNotFound, gin.H{"error": "Livreur non trouvé"}) return } if livreur.Role != "livreur" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Cet utilisateur n'est pas un livreur", }) return } // ============================================ // Vérifier que la commande existe // ============================================ command, err := database.GetCommandByID(commandID) if err != nil { log.Printf("❌ [REMOVE_FROM_QUEUE] Commande non trouvée") c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"}) return } // ============================================ // Retirer de la queue // ============================================ err = database.RemoveCommandFromDeliverymanQueue(username, commandID) if err != nil { log.Printf("❌ [REMOVE_FROM_QUEUE] Erreur suppression: %v", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Erreur suppression de la queue", }) return } // Optionnel: Réassigner la commande en "pending" currentStatus, _ := command["status"].(string) if currentStatus == "assigned" || currentStatus == "en_route" { err = database.UpdateCommandStatus(commandID, "pending") if err != nil { log.Printf("⚠️ [REMOVE_FROM_QUEUE] Impossible de réinitialiser le statut: %v", err) } else { database.UpdateCommandLivreur(commandID, "") log.Printf("✅ [REMOVE_FROM_QUEUE] Commande réinitialisée en 'pending'") } } adminUsername, _ := c.Get("username") database.AddCommandLog(commandID, "queue_removed", fmt.Sprintf("Commande retirée de la queue du livreur %s par admin %s", username, adminUsername), adminUsername.(string)) log.Printf("✅ [REMOVE_FROM_QUEUE] Commande %d retirée de la queue de %s", commandID, username) c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Commande retirée de la queue du livreur", "command_id": commandID, "username": username, "removed_by": adminUsername, }) }