chore: add ansible backend docker frontend-prep
This commit is contained in:
@@ -0,0 +1,747 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gestion/models"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// ============================================
|
||||
// 🆕 GESTION AUTOMATIQUE DU STATUT BUSY
|
||||
// ============================================
|
||||
|
||||
// UpdateDeliverymanStatusBasedOnQueue met à jour automatiquement le statut
|
||||
// ✅ Status = "busy" si queue >= 10
|
||||
// ✅ Status = "available" si queue < 10
|
||||
func (d *Database) UpdateDeliverymanStatusBasedOnQueue(deliveryman string) error {
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
|
||||
queueSize, err := Redis.ZCard(RedisCtx, queueKey).Result()
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur récupération taille queue: %w", err)
|
||||
}
|
||||
|
||||
// Récupérer le statut actuel
|
||||
statusKey := fmt.Sprintf("delivery:status:%s", deliveryman)
|
||||
data, err := Redis.Get(RedisCtx, statusKey).Result()
|
||||
if err != nil {
|
||||
log.Printf("⚠️ [STATUS] Livreur %s n'a pas de statut Redis", deliveryman)
|
||||
return nil
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
if err := json.Unmarshal([]byte(data), &status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Déterminer le nouveau statut
|
||||
var newStatus string
|
||||
var currentCommand int
|
||||
|
||||
if queueSize >= MAX_COMMANDS_PER_DELIVERYMAN {
|
||||
// 🔴 BUSY car queue pleine (10 commandes ou plus)
|
||||
newStatus = "busy"
|
||||
currentCommand = 0
|
||||
log.Printf("🔴 [STATUS] %s -> BUSY (queue pleine: %d/10)", deliveryman, queueSize)
|
||||
} else {
|
||||
// 🟢 AVAILABLE tant que queue < 10
|
||||
// Exception: si le livreur est en train de livrer (delivering), on garde ce statut
|
||||
if status.Status == "delivering" && status.CurrentCommand > 0 {
|
||||
newStatus = "delivering"
|
||||
currentCommand = status.CurrentCommand
|
||||
log.Printf("🟡 [STATUS] %s -> DELIVERING (queue: %d/10, livraison en cours: cmd %d)",
|
||||
deliveryman, queueSize, currentCommand)
|
||||
} else {
|
||||
newStatus = "available"
|
||||
currentCommand = 0
|
||||
log.Printf("🟢 [STATUS] %s -> AVAILABLE (queue: %d/10)", deliveryman, queueSize)
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour le statut
|
||||
return d.SetDeliveryPersonStatus(deliveryman, newStatus, currentCommand)
|
||||
}
|
||||
|
||||
// CanDeliverymanAcceptCommands vérifie si un livreur peut accepter de nouvelles commandes
|
||||
// ✅ Retourne false si: status=busy ET queue>=10, ou status=offline
|
||||
func (d *Database) CanDeliverymanAcceptCommands(deliveryman string) bool {
|
||||
// 1. Vérifier le statut Redis
|
||||
statusKey := fmt.Sprintf("delivery:status:%s", deliveryman)
|
||||
data, err := Redis.Get(RedisCtx, statusKey).Result()
|
||||
if err != nil {
|
||||
log.Printf("⚠️ [CHECK] Livreur %s sans statut Redis", deliveryman)
|
||||
return true // Fallback: autoriser si pas de statut
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
if err := json.Unmarshal([]byte(data), &status); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. Si offline, refuser
|
||||
if status.Status == "offline" {
|
||||
log.Printf("⚫ [CHECK] %s REFUSÉ: offline", deliveryman)
|
||||
return false
|
||||
}
|
||||
|
||||
// 3. Vérifier la taille de la queue
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
|
||||
queueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
|
||||
|
||||
// 4. Si queue >= 10, refuser
|
||||
if queueSize >= MAX_COMMANDS_PER_DELIVERYMAN {
|
||||
log.Printf("🔴 [CHECK] %s REFUSÉ: queue pleine (%d/10)", deliveryman, queueSize)
|
||||
return false
|
||||
}
|
||||
|
||||
log.Printf("🟢 [CHECK] %s AUTORISÉ (%d/10)", deliveryman, queueSize)
|
||||
return true
|
||||
}
|
||||
|
||||
// GetAvailableDeliveryPersonsForAssignment récupère UNIQUEMENT les livreurs pouvant accepter
|
||||
func (d *Database) GetAvailableDeliveryPersonsForAssignment() ([]models.DeliveryPersonStatus, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var available []models.DeliveryPersonStatus
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
if err := json.Unmarshal([]byte(data), &status); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// ✅ Vérifier si le livreur peut accepter des commandes
|
||||
if d.CanDeliverymanAcceptCommands(status.Username) {
|
||||
available = append(available, status)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("📊 [AVAILABLE] %d livreur(s) disponible(s) pour assignation", len(available))
|
||||
|
||||
return available, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 FONCTIONS MODIFIÉES AVEC AUTO-STATUS
|
||||
// ============================================
|
||||
|
||||
// AddToDeliverymanQueue - VERSION MISE À JOUR avec auto-update du statut
|
||||
func (d *Database) AddToDeliverymanQueue(deliveryman string, queueItem models.CommandQueue) error {
|
||||
data, err := json.Marshal(queueItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur serialisation: %w", err)
|
||||
}
|
||||
|
||||
// Sauvegarder les données de la commande
|
||||
commandKey := fmt.Sprintf("queue:pending:%d", queueItem.CommandID)
|
||||
Redis.Set(RedisCtx, commandKey, data, 24*time.Hour)
|
||||
|
||||
// Ajouter à la queue sorted set du livreur (score = timestamp)
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
|
||||
score := float64(time.Now().Unix())
|
||||
|
||||
err = Redis.ZAdd(RedisCtx, queueKey, redis.Z{
|
||||
Score: score,
|
||||
Member: queueItem.CommandID,
|
||||
}).Err()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur ajout queue Redis: %w", err)
|
||||
}
|
||||
|
||||
// Incrémenter le compteur de commandes en attente pour ce livreur
|
||||
counterKey := fmt.Sprintf("queue:deliveryman:%s:count", deliveryman)
|
||||
Redis.Incr(RedisCtx, counterKey)
|
||||
|
||||
log.Printf("✅ Commande %d ajoutée à la queue de %s", queueItem.CommandID, deliveryman)
|
||||
|
||||
// ✅ NOUVEAU: Mettre à jour automatiquement le statut
|
||||
go d.UpdateDeliverymanStatusBasedOnQueue(deliveryman)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCommandToQueue ajoute une commande à la file d'attente Redis (version simple)
|
||||
func (d *Database) AddCommandToQueue(commandID int) error {
|
||||
if err := d.ValidateCommandBeforeQueue(commandID); err != nil {
|
||||
log.Printf("❌ [QUEUE] Commande %d REFUSÉE: %v", commandID, err)
|
||||
return fmt.Errorf("validation échouée: %w", err)
|
||||
}
|
||||
|
||||
command, err := d.GetCommandByID(commandID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("commande introuvable: %w", err)
|
||||
}
|
||||
|
||||
// Gestion des coordonnées (NULL safe)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer le total_prix avec gestion de type
|
||||
var totalPrice float64
|
||||
if tp, ok := command["total_prix"].(float64); ok {
|
||||
totalPrice = tp
|
||||
}
|
||||
|
||||
// Récupérer l'adresse
|
||||
var address string
|
||||
if addr, ok := command["delivery_address"].(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: 0,
|
||||
}
|
||||
|
||||
// Ajouter à la queue générale
|
||||
return d.AddToGeneralQueue(queueItem)
|
||||
}
|
||||
|
||||
// AddCommandToSmartQueue - Ajoute une commande avec attribution au livreur le moins chargé
|
||||
func (d *Database) AddCommandToSmartQueue(commandID int, address string) error {
|
||||
if err := d.ValidateCommandBeforeQueue(commandID); err != nil {
|
||||
log.Printf("❌ [QUEUE] Commande %d REFUSÉE: %v", commandID, err)
|
||||
return fmt.Errorf("validation échouée: %w", err)
|
||||
}
|
||||
command, err := d.GetCommandByID(commandID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("commande introuvable: %w", err)
|
||||
}
|
||||
|
||||
// Gestion des coordonnées (NULL safe)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer le total_prix avec gestion de type
|
||||
var totalPrice float64
|
||||
if tp, ok := command["total_prix"].(float64); ok {
|
||||
totalPrice = tp
|
||||
}
|
||||
|
||||
queueItem := models.CommandQueue{
|
||||
CommandID: commandID,
|
||||
Username: command["username"].(string),
|
||||
TotalPrice: totalPrice,
|
||||
Address: address,
|
||||
Lat: lat,
|
||||
Lng: lng,
|
||||
CreatedAt: time.Now(),
|
||||
EstimatedETA: 0,
|
||||
}
|
||||
|
||||
// ✅ MODIFIÉ: Utiliser FindLeastLoadedDeliveryman qui respecte maintenant le statut
|
||||
assignedDeliveryman, err := d.FindLeastLoadedDeliveryman()
|
||||
if err != nil {
|
||||
// Aucun livreur trouvé, ajouter à la queue générale
|
||||
log.Printf("⚠️ Aucun livreur trouvé, ajout à la queue générale")
|
||||
return d.AddToGeneralQueue(queueItem)
|
||||
}
|
||||
|
||||
// Ajouter la commande à la queue spécifique du livreur (auto-update du statut)
|
||||
err = d.AddToDeliverymanQueue(assignedDeliveryman, queueItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur ajout à la queue du livreur: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("📋 Commande %d assignée à la queue de %s", commandID, assignedDeliveryman)
|
||||
|
||||
// Publier l'événement
|
||||
d.PublishCommandEvent(commandID, "queued",
|
||||
fmt.Sprintf("En attente dans la queue de %s", assignedDeliveryman))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddToGeneralQueue ajoute une commande à la queue générale (fallback)
|
||||
func (d *Database) AddToGeneralQueue(queueItem models.CommandQueue) error {
|
||||
data, err := json.Marshal(queueItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur serialisation: %w", err)
|
||||
}
|
||||
|
||||
score := float64(time.Now().Unix())
|
||||
key := fmt.Sprintf("queue:pending:%d", queueItem.CommandID)
|
||||
|
||||
pipe := Redis.Pipeline()
|
||||
pipe.Set(RedisCtx, key, data, 24*time.Hour)
|
||||
pipe.ZAdd(RedisCtx, "queue:pending:sorted", redis.Z{
|
||||
Score: score,
|
||||
Member: queueItem.CommandID,
|
||||
})
|
||||
|
||||
_, err = pipe.Exec(RedisCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur ajout queue générale: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("📥 Commande %d ajoutée à la queue générale", queueItem.CommandID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCommandFromQueue - VERSION AMÉLIORÉE avec auto-update du statut
|
||||
func (d *Database) RemoveCommandFromQueue(commandID int) error {
|
||||
key := fmt.Sprintf("queue:pending:%d", commandID)
|
||||
commandIDStr := strconv.Itoa(commandID)
|
||||
|
||||
pipe := Redis.Pipeline()
|
||||
pipe.Del(RedisCtx, key)
|
||||
pipe.ZRem(RedisCtx, "queue:pending:sorted", commandIDStr)
|
||||
pipe.ZRem(RedisCtx, "queue:priority:sorted", commandIDStr)
|
||||
|
||||
// Trouver et retirer de la queue du livreur
|
||||
keys, _ := Redis.Keys(RedisCtx, "queue:deliveryman:*").Result()
|
||||
var affectedDeliveryman string
|
||||
|
||||
for _, queueKey := range keys {
|
||||
if len(queueKey) > 6 && queueKey[len(queueKey)-6:] == ":count" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Vérifier si la commande est dans cette queue
|
||||
_, err := Redis.ZRank(RedisCtx, queueKey, commandIDStr).Result()
|
||||
if err == nil {
|
||||
// Commande trouvée dans cette queue
|
||||
affectedDeliveryman = queueKey[len("queue:deliveryman:"):]
|
||||
pipe.ZRem(RedisCtx, queueKey, commandIDStr)
|
||||
|
||||
// Décrémenter le compteur
|
||||
counterKey := fmt.Sprintf("queue:deliveryman:%s:count", affectedDeliveryman)
|
||||
pipe.Decr(RedisCtx, counterKey)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, err := pipe.Exec(RedisCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur suppression: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("✅ Commande %d retirée de la file", commandID)
|
||||
|
||||
// ✅ NOUVEAU: Mettre à jour le statut si un livreur était affecté
|
||||
if affectedDeliveryman != "" {
|
||||
go d.UpdateDeliverymanStatusBasedOnQueue(affectedDeliveryman)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Database) GetNextCommandInQueue() (*models.CommandQueue, error) {
|
||||
|
||||
normalResults, err := Redis.ZRangeWithScores(RedisCtx, "queue:pending:sorted", 0, 0).Result()
|
||||
|
||||
if err != nil || len(normalResults) == 0 {
|
||||
return nil, fmt.Errorf("aucune commande en attente")
|
||||
}
|
||||
|
||||
commandID := extractCommandID(normalResults[0].Member)
|
||||
if commandID <= 0 {
|
||||
return nil, fmt.Errorf("ID commande invalide")
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("queue:pending:%d", commandID)
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("commande introuvable: %w", err)
|
||||
}
|
||||
|
||||
var queue models.CommandQueue
|
||||
if err := json.Unmarshal([]byte(data), &queue); err != nil {
|
||||
return nil, fmt.Errorf("erreur désérialisation: %w", err)
|
||||
}
|
||||
return &queue, nil
|
||||
}
|
||||
|
||||
// GetLastCommandInQueue récupère la dernière commande dans la queue d'un livreur
|
||||
func (d *Database) GetLastCommandInQueue(deliveryman string) (*models.CommandQueue, error) {
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
|
||||
|
||||
// Récupérer la dernière commande (index -1)
|
||||
commandIDs, err := Redis.ZRange(RedisCtx, queueKey, -1, -1).Result()
|
||||
if err != nil || len(commandIDs) == 0 {
|
||||
return nil, fmt.Errorf("queue vide")
|
||||
}
|
||||
|
||||
commandID := extractCommandID(commandIDs[0])
|
||||
if commandID <= 0 {
|
||||
return nil, fmt.Errorf("ID invalide")
|
||||
}
|
||||
|
||||
commandKey := fmt.Sprintf("queue:pending:%d", commandID)
|
||||
data, err := Redis.Get(RedisCtx, commandKey).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var queueItem models.CommandQueue
|
||||
if err := json.Unmarshal([]byte(data), &queueItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &queueItem, nil
|
||||
}
|
||||
|
||||
// GetCommandQueuePosition récupère la position d'une commande dans la queue
|
||||
func (d *Database) GetCommandQueuePosition(commandID int) (int, error) {
|
||||
commandIDStr := strconv.Itoa(commandID)
|
||||
|
||||
// Chercher d'abord dans les queues des livreurs
|
||||
keys, _ := Redis.Keys(RedisCtx, "queue:deliveryman:*").Result()
|
||||
|
||||
for _, queueKey := range keys {
|
||||
// Éviter les clés de compteur
|
||||
if len(queueKey) > 6 && queueKey[len(queueKey)-6:] == ":count" {
|
||||
continue
|
||||
}
|
||||
|
||||
rank, err := Redis.ZRank(RedisCtx, queueKey, commandIDStr).Result()
|
||||
if err == nil {
|
||||
return int(rank) + 1, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Chercher dans la queue générale
|
||||
rank, err := Redis.ZRank(RedisCtx, "queue:pending:sorted", commandIDStr).Result()
|
||||
if err == nil {
|
||||
return int(rank) + 1, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("commande non trouvée dans les queues")
|
||||
}
|
||||
|
||||
func (d *Database) ClearDeliverymanQueue(deliveryman string) error {
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", deliveryman)
|
||||
|
||||
// Récupérer toutes les commandes
|
||||
commandIDs, _ := Redis.ZRange(RedisCtx, queueKey, 0, -1).Result()
|
||||
|
||||
// Redistribuer chaque commande
|
||||
for _, cmdIDStr := range commandIDs {
|
||||
commandID := extractCommandID(cmdIDStr)
|
||||
if commandID <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Récupérer les données de la commande
|
||||
commandKey := fmt.Sprintf("queue:pending:%d", commandID)
|
||||
data, err := Redis.Get(RedisCtx, commandKey).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var queueItem models.CommandQueue
|
||||
if err := json.Unmarshal([]byte(data), &queueItem); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Trouver un nouveau livreur
|
||||
newDeliveryman, err := d.FindLeastLoadedDeliveryman()
|
||||
if err != nil {
|
||||
// Fallback: queue générale
|
||||
d.AddToGeneralQueue(queueItem)
|
||||
continue
|
||||
}
|
||||
|
||||
// Réassigner à un autre livreur
|
||||
if newDeliveryman != deliveryman {
|
||||
d.AddToDeliverymanQueue(newDeliveryman, queueItem)
|
||||
log.Printf("🔄 Commande %d réassignée de %s à %s",
|
||||
commandID, deliveryman, newDeliveryman)
|
||||
}
|
||||
}
|
||||
|
||||
// Vider la queue
|
||||
Redis.Del(RedisCtx, queueKey)
|
||||
Redis.Del(RedisCtx, fmt.Sprintf("queue:deliveryman:%s:count", deliveryman))
|
||||
|
||||
log.Printf("🗑️ Queue de %s vidée et redistribuée", deliveryman)
|
||||
|
||||
// ✅ NOUVEAU: Mettre à jour le statut
|
||||
go d.UpdateDeliverymanStatusBasedOnQueue(deliveryman)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Database) SetDeliveryPersonStatus(username, status string, commandID int) error {
|
||||
key := fmt.Sprintf("delivery:status:%s", username)
|
||||
|
||||
statusData := models.DeliveryPersonStatus{
|
||||
Username: username,
|
||||
Status: status,
|
||||
CurrentCommand: commandID,
|
||||
LastUpdate: time.Now(),
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(statusData)
|
||||
err := Redis.Set(RedisCtx, key, data, 24*time.Hour).Err()
|
||||
|
||||
if err == nil {
|
||||
log.Printf("✅ Statut livreur %s: %s", username, status)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAvailableDeliveryPersonsRedis récupère les livreurs disponibles (LEGACY)
|
||||
func (d *Database) GetAvailableDeliveryPersonsRedis() ([]models.DeliveryPersonStatus, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var available []models.DeliveryPersonStatus
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status == "available" {
|
||||
available = append(available, status)
|
||||
}
|
||||
}
|
||||
|
||||
return available, nil
|
||||
}
|
||||
|
||||
// GetAllActiveDeliveryPersons - VERSION MISE À JOUR avec vérification capacité
|
||||
func (d *Database) GetAllActiveDeliveryPersons() ([]models.DeliveryPersonStatus, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var active []models.DeliveryPersonStatus
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status != "offline" {
|
||||
// ✅ Vérifier si le livreur peut accepter des commandes
|
||||
if d.CanDeliverymanAcceptCommands(status.Username) {
|
||||
active = append(active, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return active, nil
|
||||
}
|
||||
|
||||
// CountActiveDeliverymen compte le nombre de livreurs actifs (non offline)
|
||||
func (d *Database) CountActiveDeliverymen() (int, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status != "offline" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (d *Database) GetSingleActiveDeliveryman() (string, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status != "offline" {
|
||||
return status.Username, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("aucun livreur actif trouvé")
|
||||
}
|
||||
|
||||
// GetAllActiveDeliverymenUsernames retourne les usernames de tous les livreurs actifs
|
||||
func (d *Database) GetAllActiveDeliverymenUsernames() ([]string, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var activeUsernames []string
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status != "offline" {
|
||||
activeUsernames = append(activeUsernames, status.Username)
|
||||
}
|
||||
}
|
||||
|
||||
return activeUsernames, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 SYNCHRONISATION DES STATUTS
|
||||
// ============================================
|
||||
|
||||
// SyncAllDeliverymanStatuses synchronise tous les statuts (à appeler au démarrage)
|
||||
func (d *Database) SyncAllDeliverymanStatuses() error {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("🔄 [SYNC] Synchronisation des statuts livreurs...")
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
if err := json.Unmarshal([]byte(data), &status); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Mettre à jour le statut basé sur la queue
|
||||
d.UpdateDeliverymanStatusBasedOnQueue(status.Username)
|
||||
}
|
||||
|
||||
log.Println("✅ [SYNC] Synchronisation terminée")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 RAPPORT DE CAPACITÉ
|
||||
// ============================================
|
||||
|
||||
// GetDeliverymanCapacityReport génère un rapport détaillé
|
||||
func (d *Database) GetDeliverymanCapacityReport() (map[string]interface{}, error) {
|
||||
keys, err := Redis.Keys(RedisCtx, "delivery:status:*").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
report := map[string]interface{}{
|
||||
"total_deliverymen": 0,
|
||||
"available": 0,
|
||||
"busy_full": 0, // BUSY car queue pleine
|
||||
"busy_delivering": 0, // BUSY car en livraison
|
||||
"offline": 0,
|
||||
"details": []map[string]interface{}{},
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
data, err := Redis.Get(RedisCtx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
if err := json.Unmarshal([]byte(data), &status); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", status.Username)
|
||||
queueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
|
||||
|
||||
canAccept := d.CanDeliverymanAcceptCommands(status.Username)
|
||||
|
||||
detail := map[string]interface{}{
|
||||
"username": status.Username,
|
||||
"status": status.Status,
|
||||
"queue_size": queueSize,
|
||||
"capacity": fmt.Sprintf("%d/10", queueSize),
|
||||
"can_accept": canAccept,
|
||||
"current_order": status.CurrentCommand,
|
||||
}
|
||||
|
||||
report["total_deliverymen"] = report["total_deliverymen"].(int) + 1
|
||||
|
||||
if status.Status == "offline" {
|
||||
report["offline"] = report["offline"].(int) + 1
|
||||
} else if status.Status == "busy" {
|
||||
if queueSize >= MAX_COMMANDS_PER_DELIVERYMAN {
|
||||
report["busy_full"] = report["busy_full"].(int) + 1
|
||||
} else {
|
||||
report["busy_delivering"] = report["busy_delivering"].(int) + 1
|
||||
}
|
||||
} else if canAccept {
|
||||
report["available"] = report["available"].(int) + 1
|
||||
}
|
||||
|
||||
report["details"] = append(report["details"].([]map[string]interface{}), detail)
|
||||
}
|
||||
|
||||
return report, nil
|
||||
}
|
||||
Reference in New Issue
Block a user