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

276 lines
8.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================
// handlers/eta_handler_corrected.go
// CORRECTION: ETA visible UNIQUEMENT après en_route
// ============================================
package handlers
import (
"encoding/json"
"fmt"
"gestion/db"
"gestion/services"
"log"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
// ============================================
// GET /api/v1/orders/:id/eta
// ✅ CORRECTION: ETA visible UNIQUEMENT si status >= en_route
// ============================================
func GetOrderETA(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
geoService := c.MustGet("geoService").(*services.GeoService)
// 1️⃣ AUTHENTIFICATION
username, exists := c.Get("username")
if !exists {
log.Printf("❌ [ETA] Non authentifié")
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"error": "Utilisateur non authentifié",
})
return
}
// 2️⃣ RÉCUPÉRER L'ID DE LA COMMANDE
commandID, err := strconv.Atoi(c.Param("id"))
if err != nil {
log.Printf("❌ [ETA] ID invalide: %v", err)
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "ID de commande invalide",
})
return
}
log.Printf("📊 [ETA] START - commandID=%d, username=%s", commandID, username.(string))
// 3️⃣ RÉCUPÉRER LA COMMANDE
command, err := database.GetCommandByID(commandID)
if err != nil {
log.Printf("❌ [ETA] Commande %d non trouvée", commandID)
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"error": "Commande non trouvée",
})
return
}
// 4️⃣ VÉRIFIER LES DROITS D'ACCÈS
cmdUsername, _ := command["username"].(string)
userRole := c.GetString("role")
if userRole != "admin" {
if userRole == "client" && cmdUsername != username.(string) {
log.Printf("❌ [ETA] Accès refusé - commande appartient à %s", cmdUsername)
c.JSON(http.StatusForbidden, gin.H{
"success": false,
"error": "Vous n'avez pas accès à cette commande",
})
return
}
if userRole == "livreur" {
livreurAssign, _ := command["livreur_assign"].(string)
if livreurAssign != username.(string) {
log.Printf("❌ [ETA] Accès refusé - livreur non assigné")
c.JSON(http.StatusForbidden, gin.H{
"success": false,
"error": "Vous n'avez pas accès à cette commande",
})
return
}
}
}
// 5️⃣ VÉRIFIER LE STATUT DE LA COMMANDE
cmdStatus, _ := command["status"].(string)
// ✅ CORRECTION: Vérifier si commande terminée
if cmdStatus == "livre" || cmdStatus == "delivered" || cmdStatus == "approved" {
log.Printf("️ [ETA] Commande déjà %s - pas d'ETA applicable", cmdStatus)
c.JSON(http.StatusOK, gin.H{
"success": true,
"command_id": commandID,
"status": cmdStatus,
"message": "La commande a déjà été livrée",
"eta_available": false,
"estimated_arrival": "Livraison complétée",
})
return
}
// ✅ CORRECTION CRITIQUE: Vérifier si le livreur a démarré
if cmdStatus != "en_route" && cmdStatus != "arrived" {
log.Printf("⏳ [ETA] Commande en statut '%s' - ETA pas encore disponible", cmdStatus)
livreurAssign, _ := command["livreur_assign"].(string)
var livreurInfo string
if livreurAssign != "" {
livreurInfo = fmt.Sprintf("Livreur %s assigné", livreurAssign)
} else {
livreurInfo = "En attente d'assignation"
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"command_id": commandID,
"status": cmdStatus,
"message": "Le livreur n'a pas encore démarré la livraison",
"eta_available": false,
"info": livreurInfo,
})
return
}
// 6️⃣ VÉRIFIER LE CACHE REDIS POUR ETA
etaKey := fmt.Sprintf("command:eta:%d", commandID)
etaData, err := db.Redis.HGetAll(db.RedisCtx, etaKey).Result()
if err == nil && len(etaData) > 0 {
// ETA existe, vérifier s'il est récent
if updatedAtStr, ok := etaData["updated_at"]; ok {
var updatedAt int64
fmt.Sscanf(updatedAtStr, "%d", &updatedAt)
timeSinceUpdate := time.Since(time.Unix(updatedAt, 0))
if timeSinceUpdate < 2*time.Minute {
// Cache valide
var etaMinutes int64
if etaStr, ok := etaData["eta_minutes"]; ok {
fmt.Sscanf(etaStr, "%d", &etaMinutes)
}
var arrivalTime int64
if arrivalStr, ok := etaData["arrival_time"]; ok {
fmt.Sscanf(arrivalStr, "%d", &arrivalTime)
}
log.Printf("✅ [ETA] Cache hit - ETA: %d min", etaMinutes)
c.JSON(http.StatusOK, gin.H{
"success": true,
"command_id": commandID,
"status": cmdStatus,
"eta_minutes": etaMinutes,
"estimated_arrival": time.Unix(arrivalTime, 0).Format("15:04"),
"eta_available": true,
"with_traffic": true,
"livreur_assign": command["livreur_assign"],
"delivery_address": command["adresse"],
})
return
}
}
}
// 7️⃣ Pas de cache valide - Recalculer l'ETA
log.Printf("🔄 [ETA] Cache miss ou expiré - Recalcul de l'ETA...")
// Récupérer coordonnées destination
var destLat, destLon float64
destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
destData, err := db.Redis.Get(db.RedisCtx, destCacheKey).Result()
if err == nil && destData != "" {
var coords struct {
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
}
if err := json.Unmarshal([]byte(destData), &coords); err == nil && coords.Lat != 0 && coords.Lon != 0 {
destLat = coords.Lat
destLon = coords.Lon
log.Printf("📍 Destination depuis cache Redis: (%.6f, %.6f)", destLat, destLon)
}
}
if destLat == 0 || destLon == 0 {
if dLat, okLat := command["dest_latitude"].(float64); okLat && dLat != 0 {
destLat = dLat
}
if dLon, okLon := command["dest_longitude"].(float64); okLon && dLon != 0 {
destLon = dLon
}
}
if destLat == 0 || destLon == 0 {
log.Printf("❌ [ETA] Coordonnées destination manquantes")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Coordonnées de destination manquantes",
})
return
}
// Récupérer position du livreur
livreurAssign, _ := command["livreur_assign"].(string)
if livreurAssign == "" {
log.Printf("⚠️ [ETA] Aucun livreur assigné")
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "Aucun livreur assigné à cette commande",
})
return
}
livreurLocation, err := geoService.GetDeliveryPersonLocation(livreurAssign)
if err != nil {
log.Printf("❌ [ETA] Position livreur introuvable: %s", livreurAssign)
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"error": "Position du livreur non disponible",
})
return
}
toCoords := services.Coordinates{Latitude: destLat, Longitude: destLon}
// Calculer ETA avec TomTom
log.Printf("🛣️ [ETA] Calcul TomTom: (%.6f, %.6f) -> (%.6f, %.6f)",
livreurLocation.Latitude, livreurLocation.Longitude, toCoords.Latitude, toCoords.Longitude)
etaMinutes, distanceKm, err := services.GetETAWithTraffic(*livreurLocation, toCoords)
if err != nil {
log.Printf("⚠️ [ETA] TomTom failed, fallback local: %v", err)
distanceKm = services.CalculateDistance(*livreurLocation, toCoords)
etaMinutes = services.CalculateETA(distanceKm)
}
// Sauvegarder en cache
now := time.Now()
arrivalTime := now.Add(time.Duration(etaMinutes) * time.Minute)
etaCache := map[string]interface{}{
"command_id": commandID,
"eta_minutes": etaMinutes,
"updated_at": now.Unix(),
"arrival_time": arrivalTime.Unix(),
"distance_km": distanceKm,
"with_traffic": err == nil,
}
db.Redis.HSet(db.RedisCtx, etaKey, etaCache)
db.Redis.Expire(db.RedisCtx, etaKey, 4*time.Hour)
log.Printf("✅ [ETA] SUCCESS - ETA: %d min, arrivée: %s", etaMinutes, arrivalTime.Format("15:04"))
c.JSON(http.StatusOK, gin.H{
"success": true,
"command_id": commandID,
"status": cmdStatus,
"eta_minutes": etaMinutes,
"distance_km": fmt.Sprintf("%.2f", distanceKm),
"estimated_arrival": arrivalTime.Format("15:04"),
"eta_available": true,
"with_traffic": err == nil,
"livreur_assign": livreurAssign,
"delivery_address": command["adresse"],
})
}