chore: refacto
This commit is contained in:
@@ -16,8 +16,6 @@ import (
|
||||
// ============================================
|
||||
|
||||
// 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()
|
||||
@@ -25,7 +23,6 @@ func (d *Database) UpdateDeliverymanStatusBasedOnQueue(deliveryman string) error
|
||||
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 {
|
||||
@@ -38,18 +35,14 @@ func (d *Database) UpdateDeliverymanStatusBasedOnQueue(deliveryman string) error
|
||||
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
|
||||
@@ -62,19 +55,16 @@ func (d *Database) UpdateDeliverymanStatusBasedOnQueue(deliveryman string) error
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return true
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
@@ -82,9 +72,7 @@ func (d *Database) CanDeliverymanAcceptCommands(deliveryman string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. Si offline, refuser
|
||||
if status.Status == "offline" {
|
||||
log.Printf("⚫ [CHECK] %s REFUSÉ: offline", deliveryman)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -122,7 +110,6 @@ func (d *Database) GetAvailableDeliveryPersonsForAssignment() ([]models.Delivery
|
||||
continue
|
||||
}
|
||||
|
||||
// ✅ Vérifier si le livreur peut accepter des commandes
|
||||
if d.CanDeliverymanAcceptCommands(status.Username) {
|
||||
available = append(available, status)
|
||||
}
|
||||
@@ -144,11 +131,9 @@ func (d *Database) AddToDeliverymanQueue(deliveryman string, queueItem models.Co
|
||||
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())
|
||||
|
||||
@@ -161,13 +146,9 @@ func (d *Database) AddToDeliverymanQueue(deliveryman string, queueItem models.Co
|
||||
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
|
||||
@@ -184,8 +165,6 @@ func (d *Database) AddCommandToQueue(commandID int) error {
|
||||
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 {
|
||||
@@ -198,13 +177,11 @@ func (d *Database) AddCommandToQueue(commandID int) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -221,7 +198,6 @@ func (d *Database) AddCommandToQueue(commandID int) error {
|
||||
EstimatedETA: 0,
|
||||
}
|
||||
|
||||
// Ajouter à la queue générale
|
||||
return d.AddToGeneralQueue(queueItem)
|
||||
}
|
||||
|
||||
@@ -235,8 +211,6 @@ func (d *Database) AddCommandToSmartQueue(commandID int, address string) error {
|
||||
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 {
|
||||
@@ -249,7 +223,6 @@ func (d *Database) AddCommandToSmartQueue(commandID int, address string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer le total_prix avec gestion de type
|
||||
var totalPrice float64
|
||||
if tp, ok := command["total_prix"].(float64); ok {
|
||||
totalPrice = tp
|
||||
@@ -269,12 +242,10 @@ func (d *Database) AddCommandToSmartQueue(commandID int, address string) error {
|
||||
// ✅ 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)
|
||||
@@ -282,7 +253,6 @@ func (d *Database) AddCommandToSmartQueue(commandID int, address string) error {
|
||||
|
||||
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))
|
||||
|
||||
@@ -459,7 +429,6 @@ func (d *Database) ClearDeliverymanQueue(deliveryman string) error {
|
||||
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 {
|
||||
@@ -471,15 +440,12 @@ func (d *Database) ClearDeliverymanQueue(deliveryman string) error {
|
||||
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",
|
||||
@@ -491,9 +457,6 @@ func (d *Database) ClearDeliverymanQueue(deliveryman string) error {
|
||||
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
|
||||
@@ -521,227 +484,125 @@ func (d *Database) SetDeliveryPersonStatus(username, status string, commandID in
|
||||
|
||||
// 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
|
||||
err := d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) {
|
||||
if s.Status == "available" {
|
||||
available = append(available, s)
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status == "available" {
|
||||
available = append(available, status)
|
||||
}
|
||||
}
|
||||
|
||||
return available, nil
|
||||
})
|
||||
return available, err
|
||||
}
|
||||
|
||||
// GetAllActiveDeliveryPersons - VERSION MISE À JOUR avec vérification capacité
|
||||
// GetAllActiveDeliveryPersons retourne les livreurs actifs pouvant accepter des commandes
|
||||
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
|
||||
err := d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) {
|
||||
if s.Status != "offline" && d.CanDeliverymanAcceptCommands(s.Username) {
|
||||
active = append(active, s)
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
return active, err
|
||||
}
|
||||
|
||||
// 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" {
|
||||
err := d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) {
|
||||
if s.Status != "offline" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
})
|
||||
return count, err
|
||||
}
|
||||
|
||||
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
|
||||
found := ""
|
||||
d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) { //nolint
|
||||
if found == "" && s.Status != "offline" {
|
||||
found = s.Username
|
||||
}
|
||||
})
|
||||
if found == "" {
|
||||
return "", fmt.Errorf("aucun livreur actif trouvé")
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("aucun livreur actif trouvé")
|
||||
return found, nil
|
||||
}
|
||||
|
||||
// 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 usernames []string
|
||||
err := d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) {
|
||||
if s.Status != "offline" {
|
||||
usernames = append(usernames, s.Username)
|
||||
}
|
||||
|
||||
var status models.DeliveryPersonStatus
|
||||
json.Unmarshal([]byte(data), &status)
|
||||
|
||||
if status.Status != "offline" {
|
||||
activeUsernames = append(activeUsernames, status.Username)
|
||||
}
|
||||
}
|
||||
|
||||
return activeUsernames, nil
|
||||
})
|
||||
return usernames, err
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 SYNCHRONISATION DES STATUTS
|
||||
// ============================================
|
||||
|
||||
// SyncAllDeliverymanStatuses synchronise tous les statuts (à appeler au démarrage)
|
||||
func (d *Database) SyncAllDeliverymanStatuses() error {
|
||||
log.Println("🔄 [SYNC] Synchronisation des statuts livreurs...")
|
||||
return d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) {
|
||||
d.UpdateDeliverymanStatusBasedOnQueue(s.Username)
|
||||
})
|
||||
}
|
||||
|
||||
// GetDeliverymanCapacityReport génère un rapport détaillé
|
||||
func (d *Database) GetDeliverymanCapacityReport() (map[string]any, error) {
|
||||
report := map[string]any{
|
||||
"total_deliverymen": 0,
|
||||
"available": 0,
|
||||
"busy_full": 0,
|
||||
"busy_delivering": 0,
|
||||
"offline": 0,
|
||||
"details": []map[string]any{},
|
||||
}
|
||||
|
||||
err := d.iterDeliveryStatuses(func(s models.DeliveryPersonStatus) {
|
||||
queueKey := fmt.Sprintf("queue:deliveryman:%s", s.Username)
|
||||
queueSize, _ := Redis.ZCard(RedisCtx, queueKey).Result()
|
||||
canAccept := d.CanDeliverymanAcceptCommands(s.Username)
|
||||
|
||||
report["total_deliverymen"] = report["total_deliverymen"].(int) + 1
|
||||
switch {
|
||||
case s.Status == "offline":
|
||||
report["offline"] = report["offline"].(int) + 1
|
||||
case s.Status == "busy" && queueSize >= MAX_COMMANDS_PER_DELIVERYMAN:
|
||||
report["busy_full"] = report["busy_full"].(int) + 1
|
||||
case s.Status == "busy":
|
||||
report["busy_delivering"] = report["busy_delivering"].(int) + 1
|
||||
case canAccept:
|
||||
report["available"] = report["available"].(int) + 1
|
||||
}
|
||||
|
||||
report["details"] = append(report["details"].([]map[string]any), map[string]any{
|
||||
"username": s.Username,
|
||||
"status": s.Status,
|
||||
"queue_size": queueSize,
|
||||
"capacity": fmt.Sprintf("%d/10", queueSize),
|
||||
"can_accept": canAccept,
|
||||
"current_order": s.CurrentCommand,
|
||||
})
|
||||
})
|
||||
return report, err
|
||||
}
|
||||
|
||||
// iterDeliveryStatuses itère sur tous les statuts Redis des livreurs et appelle fn pour chacun.
|
||||
func (d *Database) iterDeliveryStatuses(fn func(models.DeliveryPersonStatus)) 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)
|
||||
fn(status)
|
||||
}
|
||||
|
||||
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