chore: refacto
This commit is contained in:
@@ -1,8 +1,3 @@
|
||||
// ============================================
|
||||
// handlers/redis_handlers.go - VERSION FINALE
|
||||
// UTILISE UNIQUEMENT LES MÉTHODES DB PostgreSQL
|
||||
// ============================================
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
@@ -13,6 +8,7 @@ import (
|
||||
"gestion/utils"
|
||||
"log"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -20,10 +16,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ============================================
|
||||
// GESTION DE LA FILE DE COMMANDES
|
||||
// ============================================
|
||||
|
||||
func validatePenaltyPoints(points int) error {
|
||||
if points <= 0 {
|
||||
return fmt.Errorf("points invalides: %d (doit être > 0)", points)
|
||||
@@ -47,72 +39,6 @@ func sanitizeReason(reason string) string {
|
||||
return strings.TrimSpace(reason)
|
||||
}
|
||||
|
||||
// GetCommandQueue récupère toutes les commandes en attente dans la file Redis
|
||||
// GET /api/v2/admin/protected/queue/pending
|
||||
func GetCommandQueue(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
nextCommand, err := database.GetNextCommandInQueue()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Aucune commande en attente",
|
||||
"queue": []interface{}{},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"next_command": nextCommand,
|
||||
})
|
||||
}
|
||||
|
||||
// AutoAssignNextCommand assigne automatiquement la prochaine commande en file
|
||||
// POST /api/v2/admin/protected/queue/auto-assign
|
||||
func AutoAssignNextCommand(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
nextCommand, err := database.GetNextCommandInQueue()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "Aucune commande en attente",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = database.AutoAssignCommand(nextCommand.CommandID)
|
||||
if err != nil {
|
||||
utils.ServerErr(c, "Erreur lors de l'assignation automatique", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Commande assignée automatiquement",
|
||||
"command_id": nextCommand.CommandID,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// GESTION DES LIVREURS - LOCALISATION
|
||||
// ============================================
|
||||
|
||||
// UpdateLivreurLocation met à jour la position GPS du livreur
|
||||
// POST /api/v1/livreur/location/update
|
||||
// Body: {"latitude": 48.8566, "longitude": 2.3522}
|
||||
func UpdateLivreurLocation(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
@@ -171,7 +97,7 @@ func UpdateLivreurLocation(c *gin.Context) {
|
||||
usernameStr, req.Latitude, req.Longitude)
|
||||
|
||||
// ✅ Recalculer l'ETA en temps réel si livreur en_route
|
||||
go refreshETAForActivDelivery(database, usernameStr, req.Latitude, req.Longitude)
|
||||
go refreshETAForActivDelivery(usernameStr, req.Latitude, req.Longitude)
|
||||
|
||||
// ✅ 2. Vérifier/Initialiser le statut du livreur
|
||||
statusKey := fmt.Sprintf("delivery:status:%s", usernameStr)
|
||||
@@ -290,14 +216,6 @@ func GetDeliveryPersonLocation(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// LOCALISATION DU LIVREUR POUR UNE COMMANDE
|
||||
// ============================================
|
||||
|
||||
// GetDeliverymanLocationForCommand récupère la position GPS du livreur assigné à une commande
|
||||
// GET /api/v2/admin/protected/commands/:id/deliveryman/location (ADMIN)
|
||||
// GET /api/v1/cabine/commands/:id/deliveryman/location (CABINE)
|
||||
// Accessible uniquement par les admins et la cabine
|
||||
func GetDeliverymanLocationForCommand(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
@@ -460,13 +378,6 @@ func GetDeliverymanLocationForCommand(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// GESTION DES LIVREURS - STATUT
|
||||
// ============================================
|
||||
|
||||
// UpdateDeliveryPersonStatus met à jour le statut de disponibilité du livreur
|
||||
// POST /api/v1/livreur/status
|
||||
// Body: {"status": "available" | "busy" | "offline"}
|
||||
func UpdateDeliveryPersonStatus(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
@@ -490,18 +401,9 @@ func UpdateDeliveryPersonStatus(c *gin.Context) {
|
||||
utils.BindErr(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validation du statut
|
||||
validStatuses := []string{"available", "busy", "offline"}
|
||||
isValid := false
|
||||
for _, s := range validStatuses {
|
||||
if req.Status == s {
|
||||
isValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
if !slices.Contains(validStatuses, req.Status) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Statut invalide",
|
||||
"valid_statuses": validStatuses,
|
||||
@@ -509,7 +411,6 @@ func UpdateDeliveryPersonStatus(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
usernameStr := username.(string)
|
||||
|
||||
err := database.SetDeliveryPersonStatus(usernameStr, req.Status, 0)
|
||||
@@ -604,118 +505,6 @@ func GetMyQueue(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetAvailableDeliveryPersonsRealtime récupère les livreurs disponibles depuis Redis
|
||||
// GET /api/v2/admin/protected/delivery/available-realtime
|
||||
func GetAvailableDeliveryPersonsRealtime(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
livreurs, err := database.GetAvailableDeliveryPersonsRedis()
|
||||
if err != nil {
|
||||
utils.ServerErr(c, "Erreur lors de la récupération des livreurs", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"livreurs": livreurs,
|
||||
"count": len(livreurs),
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// GESTION ETA (Estimated Time of Arrival)
|
||||
// ============================================
|
||||
|
||||
// SetCommandETAHandler permet au livreur de définir l'ETA d'une livraison
|
||||
// POST /api/v1/livreur/deliveries/:id/set-eta
|
||||
// Body: {"eta_minutes": 25}
|
||||
func SetCommandETAHandler(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
username, exists := c.Get("username")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
||||
return
|
||||
}
|
||||
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "livreur" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"})
|
||||
return
|
||||
}
|
||||
|
||||
commandID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ETAMinutes int `json:"eta_minutes" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.BindErr(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validation de l'ETA
|
||||
if req.ETAMinutes < 1 || req.ETAMinutes > 120 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "L'ETA doit être entre 1 et 120 minutes",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
usernameStr := username.(string)
|
||||
|
||||
// Vérifier que la commande existe et est assignée au livreur
|
||||
command, err := database.GetCommandByID(commandID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "Commande non trouvée",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
livreurAssign, ok := command["livreur_assign"].(string)
|
||||
if !ok || livreurAssign != usernameStr {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Cette commande ne vous est pas assignée",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Mettre à jour l'ETA dans Redis
|
||||
err = database.SetCommandETA(commandID, req.ETAMinutes)
|
||||
if err != nil {
|
||||
utils.ServerErr(c, "Erreur lors de la mise à jour de l'ETA", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("⏱️ ETA défini pour commande %d par %s: %d minutes", commandID, usernameStr, req.ETAMinutes)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "ETA mis à jour avec succès",
|
||||
"command_id": commandID,
|
||||
"eta_minutes": req.ETAMinutes,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PÉNALITÉS - UTILISE PostgreSQL
|
||||
// ============================================
|
||||
|
||||
// ApplyClientPenalty applique une pénalité à un client (Admin seulement)
|
||||
// POST /api/v2/admin/protected/penalty
|
||||
// Body: {"username": "john", "points": 50, "reason": "Retard paiement"}
|
||||
func ApplyClientPenalty(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
@@ -993,9 +782,6 @@ func ResetClientPenaltiesAdmin(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// AddClientPointsAdmin ajoute des points à un client dans un pool donné (Admin/Cabine)
|
||||
// POST /api/v2/admin/protected/client/:username/points/add
|
||||
// Body: {"pool_key": "pool_0", "points": 10}
|
||||
func AddClientPointsAdmin(c *gin.Context) {
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" && userRole != "cabine" {
|
||||
@@ -1040,7 +826,7 @@ func AddClientPointsAdmin(c *gin.Context) {
|
||||
}
|
||||
if !poolExists {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Pool de points invalide",
|
||||
"error": "Pool de points invalide",
|
||||
"pools_valides": func() []string {
|
||||
keys := make([]string, 0, len(settings.PointsPools))
|
||||
for _, p := range settings.PointsPools {
|
||||
@@ -1073,9 +859,6 @@ func AddClientPointsAdmin(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// SubtractClientPointsAdmin retire des points à un client (plancher à 0)
|
||||
// POST /api/v2/admin/protected/client/:username/points/subtract
|
||||
// Body: {"pool_key": "pool_0", "points": 10}
|
||||
func SubtractClientPointsAdmin(c *gin.Context) {
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" && userRole != "cabine" {
|
||||
@@ -1164,12 +947,6 @@ func SubtractClientPointsAdmin(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// STATISTIQUES TEMPS RÉEL
|
||||
// ============================================
|
||||
|
||||
// GetRealtimeStats récupère les statistiques en temps réel
|
||||
// GET /api/v2/admin/protected/stats/realtime
|
||||
func GetRealtimeStats(c *gin.Context) {
|
||||
userRole := c.GetString("role")
|
||||
if userRole != "admin" {
|
||||
@@ -1200,13 +977,7 @@ func GetRealtimeStats(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RECALCUL ETA EN TEMPS RÉEL (appelé à chaque update GPS)
|
||||
// ============================================
|
||||
|
||||
// refreshETAForActivDelivery recalcule l'ETA depuis la position actuelle du livreur.
|
||||
// Appelé en goroutine à chaque mise à jour GPS (toutes les ~15s).
|
||||
func refreshETAForActivDelivery(database *db.Database, username string, lat, lon float64) {
|
||||
func refreshETAForActivDelivery(username string, lat, lon float64) {
|
||||
// 1. Récupérer le statut actuel du livreur
|
||||
statusKey := fmt.Sprintf("delivery:status:%s", username)
|
||||
statusData, err := db.Redis.Get(db.RedisCtx, statusKey).Result()
|
||||
|
||||
Reference in New Issue
Block a user