180 lines
5.4 KiB
Go
180 lines
5.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"gestion/db"
|
|
"gestion/services"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ============================================
|
|
// 3️⃣ DÉMARRER UNE LIVRAISON (PASSER EN IN_ROUTE)
|
|
// ============================================
|
|
|
|
// StartDelivery permet au livreur de démarrer une livraison (passage en in_transit)
|
|
// POST /api/v1/deliveries/:id/start
|
|
func StartDelivery(c *gin.Context) {
|
|
database := c.MustGet("database").(*db.Database)
|
|
geoService := c.MustGet("geoService").(*services.GeoService)
|
|
|
|
username, exists := c.Get("username")
|
|
if !exists || c.GetString("role") != "livreur" {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"})
|
|
return
|
|
}
|
|
|
|
usernameStr := username.(string)
|
|
commandID, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"})
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Latitude float64 `json:"latitude"`
|
|
Longitude float64 `json:"longitude"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"error": "Coordonnées GPS requises",
|
|
})
|
|
return
|
|
}
|
|
|
|
log.Printf("🚗 [START] %s démarre livraison cmd %d", usernameStr, commandID)
|
|
|
|
command, err := database.GetCommandByID(commandID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
|
|
return
|
|
}
|
|
|
|
// Vérifier propriété
|
|
livreurAssign, _ := command["livreur_assign"].(string)
|
|
if livreurAssign != usernameStr {
|
|
c.JSON(http.StatusForbidden, gin.H{
|
|
"error": "Cette commande ne vous est pas assignée",
|
|
})
|
|
return
|
|
}
|
|
|
|
// Vérifier le statut actuel
|
|
currentStatus, _ := command["status"].(string)
|
|
if currentStatus != "assigned" {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"error": "Impossible de démarrer cette livraison",
|
|
"current_status": currentStatus,
|
|
"message": "La commande doit être en statut 'assigned'",
|
|
})
|
|
return
|
|
}
|
|
|
|
// Mettre à jour le statut en "en_route"
|
|
if err := database.UpdateCommandStatus(commandID, "en_route"); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"error": "Erreur mise à jour statut",
|
|
})
|
|
return
|
|
}
|
|
|
|
// Mettre à jour la position du livreur
|
|
database.UpdateLivreurPosition(usernameStr, req.Latitude, req.Longitude, "busy")
|
|
|
|
// Mettre à jour le statut du livreur
|
|
database.SetDeliveryPersonStatus(usernameStr, "busy", commandID)
|
|
|
|
// Ajouter un log
|
|
database.AddCommandLog(commandID, "en_route",
|
|
fmt.Sprintf("Livraison démarrée par %s", usernameStr),
|
|
usernameStr)
|
|
|
|
// Notifier le client
|
|
if clientUsername, _ := command["username"].(string); clientUsername != "" {
|
|
var msg string
|
|
etaMinutes := 0
|
|
if etaData, err := database.GetCommandETA(commandID); err == nil {
|
|
if v, ok := etaData["total_eta_minutes"]; ok {
|
|
if n, err2 := strconv.Atoi(v); err2 == nil && n > 0 {
|
|
etaMinutes = n
|
|
}
|
|
}
|
|
}
|
|
if etaMinutes == 0 {
|
|
destLat, _ := command["dest_latitude"].(float64)
|
|
destLon, _ := command["dest_longitude"].(float64)
|
|
|
|
// Fallback 1 : cache Redis (géocodage déjà fait à l'assignation
|
|
// mais pas encore persisté en DB — cf. goroutine async dans
|
|
// handlers/commands.go AssignCommandToDeliveryman).
|
|
if destLat == 0 || destLon == 0 {
|
|
destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
|
|
if destData, err := db.Redis.Get(db.RedisCtx, destCacheKey).Result(); 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, destLon = coords.Lat, coords.Lon
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback 2 : géocodage synchrone de l'adresse. Couvre le cas où
|
|
// le livreur démarre la livraison avant que la goroutine async
|
|
// d'assignation ait fini de géocoder (race condition).
|
|
if (destLat == 0 || destLon == 0) && geoService != nil {
|
|
if adresse, _ := command["adresse"].(string); adresse != "" {
|
|
if location, err := geoService.GeocodeAddress(adresse); err == nil && location != nil {
|
|
destLat, destLon = location.Latitude, location.Longitude
|
|
database.GDB.Exec(
|
|
"UPDATE commandes SET dest_latitude = ?, dest_longitude = ? WHERE id = ?",
|
|
destLat, destLon, commandID,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
if destLat != 0 && destLon != 0 {
|
|
etaMinutes = database.CalculateETAForDeliveryman(usernameStr, destLat, destLon)
|
|
} else {
|
|
// Fallback 3 : aucune coordonnée exploitable — ETA par
|
|
// défaut plutôt que pas d'ETA du tout dans le message.
|
|
etaMinutes = 30
|
|
}
|
|
}
|
|
if etaMinutes > 0 {
|
|
var etaStr string
|
|
if etaMinutes >= 60 {
|
|
h := etaMinutes / 60
|
|
m := etaMinutes % 60
|
|
if m > 0 {
|
|
etaStr = fmt.Sprintf("%dh%02d", h, m)
|
|
} else {
|
|
etaStr = fmt.Sprintf("%dh", h)
|
|
}
|
|
} else {
|
|
etaStr = fmt.Sprintf("%d min", etaMinutes)
|
|
}
|
|
msg = fmt.Sprintf("Un livreur vient de prendre en charge ta commande #%d, il est en route (~%s)", database.GetClientOrderID(commandID), etaStr)
|
|
} else {
|
|
msg = fmt.Sprintf("Un livreur vient de prendre en charge ta commande #%d, il est en route.", database.GetClientOrderID(commandID))
|
|
}
|
|
database.NotifyClient(clientUsername, commandID, "en_route", msg)
|
|
}
|
|
|
|
log.Printf("✅ [START] Livraison %d démarrée", commandID)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Livraison démarrée",
|
|
"command_id": commandID,
|
|
"status": "en_route",
|
|
})
|
|
}
|