Files
projet_gestion_commande/backend/gestion/handlers/delivery_admin.go
T

603 lines
18 KiB
Go

// ============================================
// handlers/delivery_admin_handlers.go
// HANDLERS ADMIN POUR LA GESTION DES LIVREURS
// ============================================
package handlers
import (
"fmt"
"gestion/db"
"log"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
// ============================================
// 📊 GET DELIVERY PERSON DETAILS
// ============================================
// GetDeliveryPersonDetails récupère les détails complets d'un livreur
// GET /api/v2/admin/protected/delivery-persons/:username
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
}
log.Printf("👤 [GET_DELIVERY_DETAILS] Récupération détails pour: %s", username)
// ============================================
// ÉTAPE 1: Récupérer les infos de base du livreur
// ============================================
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é",
"details": err.Error(),
})
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
}
// ============================================
// ÉTAPE 2: Récupérer le statut et la position GPS
// ============================================
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,
}
}
// ============================================
// ÉTAPE 3: Récupérer les stats de livraison
// ============================================
queueSize, _ := database.GetDeliverymanQueueSize(username)
currentCommand, _ := database.GetCurrentCommand(username)
// Compter les livraisons
totalDeliveries, _ := database.CountDeliveriesByStatus(username, "")
completedDeliveries, _ := database.CountDeliveriesByStatus(username, "approved")
pendingDeliveries, _ := database.CountDeliveriesByStatus(username, "assigned,en_route,livre")
log.Printf("✅ [GET_DELIVERY_DETAILS] Détails récupérés pour %s", username)
// ============================================
// RÉPONSE
// ============================================
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,
},
})
}
// ============================================
// 🔄 UPDATE DELIVERY PERSON STATUS
// ============================================
// UpdateDeliveryPersonStatusAdmin modifie le statut d'un livreur (Admin)
// PUT /api/v2/admin/protected/delivery-persons/:username/status
func UpdateDeliveryPersonStatusAdmin(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
// ✅ SÉCURITÉ: Admin seulement
userRole := c.GetString("role")
if userRole != "admin" {
log.Printf("❌ [UPDATE_DELIVERY_STATUS] 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 {
Status string `json:"status" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Statut requis",
"details": err.Error(),
})
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)
// ============================================
// Vérifier que le livreur existe
// ============================================
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
}
// ============================================
// Mettre à jour le statut
// ============================================
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",
"details": err.Error(),
})
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,
})
}
// ============================================
// 📊 GET DELIVERY PERSON STATS
// ============================================
// GetDeliveryPersonStats récupère les statistiques d'un livreur
// GET /api/v2/admin/protected/delivery-persons/:username/stats
func GetDeliveryPersonStats(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
// ✅ SÉCURITÉ: Admin seulement
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)
// ============================================
// Vérifier que le livreur existe
// ============================================
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,
},
})
}
// ============================================
// 📜 GET DELIVERY PERSON HISTORY
// ============================================
// GetDeliveryPersonHistory récupère l'historique des livraisons d'un livreur
// GET /api/v2/admin/protected/delivery-persons/:username/history?limit=20&offset=0
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",
"details": err.Error(),
})
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,
})
}
// ============================================
// 📍 UPDATE DELIVERY PERSON LOCATION
// ============================================
// 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",
"details": err.Error(),
})
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",
"details": err.Error(),
})
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",
"details": err.Error(),
})
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 {
// Retirer l'assignation du livreur
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,
})
}