chore: add ansible backend docker frontend-prep

This commit is contained in:
2026-01-21 13:05:13 +01:00
parent 5a280b6b01
commit 943fe4de7d
14930 changed files with 2341433 additions and 0 deletions
@@ -0,0 +1,430 @@
package db
import (
"encoding/json"
"fmt"
"gestion/models"
"log"
"strconv"
"time"
)
// AssignCommandToDeliverymanQueue assigne une commande à la queue d'un livreur
func (d *Database) AssignCommandToDeliverymanQueue(commandID int, deliveryman string, estimatedTravelTime int) error {
command, err := d.GetCommandByID(commandID)
if err != nil {
return fmt.Errorf("commande introuvable: %w", err)
}
activeCount, _ := d.CountActiveDeliverymen()
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
currentQueueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
// Si plusieurs livreurs, appliquer la limite de 10
if activeCount > 1 && currentQueueSize >= MAX_COMMANDS_PER_DELIVERYMAN {
return fmt.Errorf("livreur %s a atteint le maximum de commandes (%d)", deliveryman, MAX_COMMANDS_PER_DELIVERYMAN)
}
// ✅ MODIFIÉ: ETA = temps de trajet direct uniquement
totalETA := estimatedTravelTime
var lat, lng float64
if command["dest_latitude"] != nil {
if latVal, ok := command["dest_latitude"].(float64); ok {
lat = latVal
}
}
if command["dest_longitude"] != nil {
if lngVal, ok := command["dest_longitude"].(float64); ok {
lng = lngVal
}
}
var totalPrice float64
if tp, ok := command["total_prix"].(float64); ok {
totalPrice = tp
}
var address string
if addr, ok := command["delivery_address"].(string); ok {
address = addr
} else if addr, ok := command["adresse"].(string); ok {
address = addr
}
queueItem := models.CommandQueue{
CommandID: commandID,
Username: command["username"].(string),
TotalPrice: totalPrice,
Address: address,
Lat: lat,
Lng: lng,
CreatedAt: time.Now(),
EstimatedETA: totalETA,
}
err = d.AddToDeliverymanQueue(deliveryman, queueItem)
if err != nil {
return fmt.Errorf("erreur ajout à la queue: %w", err)
}
d.UpdateCommandStatus(commandID, "assigned")
d.AssignDeliveryPerson(commandID, deliveryman)
// ✅ MODIFIÉ: Position dans la queue pour info seulement
d.SetCommandETAWithDetails(commandID, totalETA, int(currentQueueSize)+1)
limitInfo := ""
if activeCount > 1 {
limitInfo = fmt.Sprintf("/%d", MAX_COMMANDS_PER_DELIVERYMAN)
} else {
limitInfo = " (sans limite - seul livreur)"
}
d.AddCommandLog(commandID, "queued",
fmt.Sprintf("Ajouté à la queue de %s (position: %d%s, ETA trajet: %d min)",
deliveryman, currentQueueSize+1, limitInfo, totalETA),
"system")
log.Printf("✅ Commande %d -> Queue %s (pos: %d%s, ETA trajet: %d min)",
commandID, deliveryman, currentQueueSize+1, limitInfo, totalETA)
return nil
}
// AssignCommandToDeliverymanQueueUnlimited assigne sans limite (pour un seul livreur)
func (d *Database) AssignCommandToDeliverymanQueueUnlimited(deliveryman string, queueItem models.CommandQueue) error {
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
currentQueueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
// ✅ MODIFIÉ: Calculer le temps de trajet direct depuis la position du livreur
travelTime := d.CalculateETAForDeliveryman(deliveryman, queueItem.Lat, queueItem.Lng)
// ✅ ETA = temps de trajet direct uniquement
queueItem.EstimatedETA = travelTime
err := d.AddToDeliverymanQueue(deliveryman, queueItem)
if err != nil {
return fmt.Errorf("erreur ajout à la queue: %w", err)
}
d.UpdateCommandStatus(queueItem.CommandID, "assigned")
d.AssignDeliveryPerson(queueItem.CommandID, deliveryman)
d.SetCommandETAWithDetails(queueItem.CommandID, travelTime, int(currentQueueSize)+1)
d.AddCommandLog(queueItem.CommandID, "queued",
fmt.Sprintf("Assigné au seul livreur actif %s (position: %d, ETA trajet: %d min)",
deliveryman, currentQueueSize+1, travelTime),
"system")
log.Printf("✅ Commande %d -> Queue %s (SANS LIMITE - pos: %d, ETA trajet: %d min)",
queueItem.CommandID, deliveryman, currentQueueSize+1, travelTime)
return nil
}
// AssignCommandToDeliverymanQueueWithCoords assigne une commande avec les coordonnées GPS
func (d *Database) AssignCommandToDeliverymanQueueWithCoords(commandID int, deliveryman string, estimatedTravelTime int, lat, lng float64, address string) error {
command, err := d.GetCommandByID(commandID)
if err != nil {
return fmt.Errorf("commande introuvable: %w", err)
}
activeCount, _ := d.CountActiveDeliverymen()
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
currentQueueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
if activeCount > 1 && currentQueueSize >= MAX_COMMANDS_PER_DELIVERYMAN {
return fmt.Errorf("livreur %s a atteint le maximum de commandes (%d)", deliveryman, MAX_COMMANDS_PER_DELIVERYMAN)
}
// ✅ ETA = temps de trajet direct uniquement
totalETA := estimatedTravelTime
var totalPrice float64
if tp, ok := command["total_prix"].(float64); ok {
totalPrice = tp
}
var username string
if u, ok := command["username"].(string); ok {
username = u
}
queueItem := models.CommandQueue{
CommandID: commandID,
Username: username,
TotalPrice: totalPrice,
Address: address,
Lat: lat,
Lng: lng,
CreatedAt: time.Now(),
EstimatedETA: totalETA,
}
err = d.AddToDeliverymanQueue(deliveryman, queueItem)
if err != nil {
return fmt.Errorf("erreur ajout à la queue: %w", err)
}
// ============================================
// ✅ CORRECTION: Mettre à jour livreur_assign dans la DB
// ============================================
updateQuery := `UPDATE commandes
SET livreur_assign = $1,
status = 'assigned',
updated_at = CURRENT_TIMESTAMP
WHERE id = $2`
_, err = d.Exec(updateQuery, deliveryman, commandID)
if err != nil {
log.Printf("❌ Erreur mise à jour livreur_assign: %v", err)
return fmt.Errorf("erreur mise à jour DB: %w", err)
}
log.Printf("✅ DB mise à jour: livreur_assign=%s pour commande %d", deliveryman, commandID)
d.SetCommandETAWithDetails(commandID, totalETA, int(currentQueueSize)+1)
// Sauvegarder dans le cache destination
destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
coordsJSON, _ := json.Marshal(map[string]float64{
"lat": lat,
"lon": lng,
})
Redis.Set(RedisCtx, destCacheKey, coordsJSON, 4*time.Hour)
limitInfo := ""
if activeCount > 1 {
limitInfo = fmt.Sprintf("/%d", MAX_COMMANDS_PER_DELIVERYMAN)
} else {
limitInfo = " (sans limite - seul livreur)"
}
d.AddCommandLog(commandID, "assigned",
fmt.Sprintf("Assigné à %s (position: %d%s, ETA trajet: %d min, coords: %.4f,%.4f)",
deliveryman, currentQueueSize+1, limitInfo, totalETA, lat, lng),
"system")
log.Printf("✅ Commande %d -> Livreur %s (pos: %d%s, ETA trajet: %d min, coords: %.4f,%.4f)",
commandID, deliveryman, currentQueueSize+1, limitInfo, totalETA, lat, lng)
return nil
}
// ForceAssignCommandToDeliverymanWithCoords assigne une commande de force avec coordonnées
// ForceAssignCommandToDeliverymanWithCoords assigne une commande de force avec coordonnées
func (d *Database) ForceAssignCommandToDeliverymanWithCoords(commandID int, deliveryman string, estimatedTravelTime int, lat, lng float64, address string) error {
command, err := d.GetCommandByID(commandID)
if err != nil {
return fmt.Errorf("commande introuvable: %w", err)
}
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
currentQueueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
// ✅ MODIFIÉ: ETA = temps de trajet direct uniquement
totalETA := estimatedTravelTime
var totalPrice float64
if tp, ok := command["total_prix"].(float64); ok {
totalPrice = tp
}
var username string
if u, ok := command["username"].(string); ok {
username = u
}
queueItem := models.CommandQueue{
CommandID: commandID,
Username: username,
TotalPrice: totalPrice,
Address: address,
Lat: lat,
Lng: lng,
CreatedAt: time.Now(),
EstimatedETA: totalETA,
}
err = d.AddToDeliverymanQueue(deliveryman, queueItem)
if err != nil {
return fmt.Errorf("erreur ajout à la queue: %w", err)
}
d.UpdateCommandStatus(commandID, "assigned")
d.AssignDeliveryPerson(commandID, deliveryman)
d.SetCommandETAWithDetails(commandID, totalETA, int(currentQueueSize)+1)
// Sauvegarder dans le cache destination
destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
coordsJSON, _ := json.Marshal(map[string]float64{
"lat": lat,
"lon": lng,
})
Redis.Set(RedisCtx, destCacheKey, coordsJSON, 4*time.Hour)
d.AddCommandLog(commandID, "force_queued",
fmt.Sprintf("Assignation forcée à %s (position: %d, ETA trajet: %d min, coords: %.4f,%.4f)",
deliveryman, currentQueueSize+1, totalETA, lat, lng),
"system")
log.Printf("⚠️ FORCE: Commande %d -> Queue %s (pos: %d, ETA trajet: %d min, coords: %.4f,%.4f)",
commandID, deliveryman, currentQueueSize+1, totalETA, lat, lng)
return nil
}
// ForceAssignCommandToDeliveryman assigne une commande à un livreur SANS vérifier la limite de 10
func (d *Database) ForceAssignCommandToDeliveryman(commandID int, deliveryman string, estimatedTravelTime int) error {
command, err := d.GetCommandByID(commandID)
if err != nil {
return fmt.Errorf("commande introuvable: %w", err)
}
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
currentQueueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
// ✅ MODIFIÉ: ETA = temps de trajet direct uniquement
totalETA := estimatedTravelTime
var lat, lng float64
if command["dest_latitude"] != nil {
if latVal, ok := command["dest_latitude"].(float64); ok {
lat = latVal
}
}
if command["dest_longitude"] != nil {
if lngVal, ok := command["dest_longitude"].(float64); ok {
lng = lngVal
}
}
var totalPrice float64
if tp, ok := command["total_prix"].(float64); ok {
totalPrice = tp
}
var address string
if addr, ok := command["delivery_address"].(string); ok {
address = addr
} else if addr, ok := command["adresse"].(string); ok {
address = addr
}
queueItem := models.CommandQueue{
CommandID: commandID,
Username: command["username"].(string),
TotalPrice: totalPrice,
Address: address,
Lat: lat,
Lng: lng,
CreatedAt: time.Now(),
EstimatedETA: totalETA,
}
err = d.AddToDeliverymanQueue(deliveryman, queueItem)
if err != nil {
return fmt.Errorf("erreur ajout à la queue: %w", err)
}
d.UpdateCommandStatus(commandID, "assigned")
d.AssignDeliveryPerson(commandID, deliveryman)
d.SetCommandETAWithDetails(commandID, totalETA, int(currentQueueSize)+1)
d.AddCommandLog(commandID, "force_queued",
fmt.Sprintf("Assignation forcée à %s (position: %d, ETA trajet: %d min)",
deliveryman, currentQueueSize+1, totalETA),
"system")
log.Printf("⚠️ FORCE: Commande %d -> Queue %s (pos: %d, ETA trajet: %d min)",
commandID, deliveryman, currentQueueSize+1, totalETA)
return nil
}
// AutoAssignCommand assigne automatiquement une commande au premier livreur disponible
func (d *Database) AutoAssignCommand(commandID int) error {
// Récupérer les livreurs disponibles
available, err := d.GetAvailableDeliveryPersonsRedis()
if err != nil || len(available) == 0 {
return fmt.Errorf("aucun livreur disponible")
}
// Prendre le premier livreur
livreur := available[0]
// Assigner dans la DB principale
err = d.AssignDeliveryPerson(commandID, livreur.Username)
if err != nil {
return err
}
// Mettre à jour le statut dans Redis
d.SetDeliveryPersonStatus(livreur.Username, "busy", commandID)
// Retirer de la file d'attente
d.RemoveCommandFromQueue(commandID)
// Définir l'ETA initial
d.SetCommandETA(commandID, 30)
log.Printf("✅ Commande %d auto-assignée à %s", commandID, livreur.Username)
return nil
}
func (d *Database) ProcessNextCommandInQueue(deliveryman string) error {
log.Printf("🔄 Traitement de la prochaine commande pour %s", deliveryman)
// Vérifier d'abord la queue spécifique du livreur
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
results, err := Redis.ZRangeWithScores(RedisCtx, queueKey, 0, 0).Result()
if err == nil && len(results) > 0 {
// Une commande est dans sa queue - la traiter
commandID := extractCommandID(results[0].Member)
// Mettre à jour le statut
d.SetDeliveryPersonStatus(deliveryman, "busy", commandID)
log.Printf("✅ Livreur %s traite la commande %d de sa queue", deliveryman, commandID)
return nil
}
// Si aucune commande dans sa queue, chercher dans la queue générale
generalResults, err := Redis.ZRangeWithScores(RedisCtx, "queue:pending:sorted", 0, 0).Result()
if err != nil || len(generalResults) == 0 {
log.Printf("️ Aucune commande en attente pour %s", deliveryman)
return nil
}
commandID := extractCommandID(generalResults[0].Member)
// Assigner la commande
err = d.AssignDeliveryPerson(commandID, deliveryman)
if err != nil {
log.Printf("❌ Erreur assignation commande %d: %v", commandID, err)
return err
}
// Retirer de la queue générale
Redis.ZRem(RedisCtx, "queue:pending:sorted", strconv.Itoa(commandID))
// Mettre à jour le statut
d.SetDeliveryPersonStatus(deliveryman, "busy", commandID)
// Définir un ETA par défaut
d.SetCommandETA(commandID, 30)
// Ajouter log
d.AddCommandLog(commandID, "auto_assigned",
fmt.Sprintf("Assigné automatiquement à %s depuis la queue générale", deliveryman),
"system")
log.Printf("✅ Commande %d assignée automatiquement à %s (queue générale)", commandID, deliveryman)
return nil
}