chore: build

This commit is contained in:
2026-06-16 21:17:54 +02:00
parent 440792a066
commit ec7e59550b
11 changed files with 868 additions and 1 deletions
+14
View File
@@ -513,6 +513,20 @@ func (db *Database) createTables() error {
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);`,
// ============================
// TABLE livreur_ratings
// ============================
`CREATE TABLE IF NOT EXISTS livreur_ratings (
id SERIAL PRIMARY KEY,
order_id INTEGER NOT NULL UNIQUE REFERENCES commandes(id) ON DELETE CASCADE,
livreur_username VARCHAR(255) NOT NULL,
client_username VARCHAR(255) NOT NULL,
rating SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
comment TEXT NOT NULL DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`,
`CREATE INDEX IF NOT EXISTS idx_ratings_livreur ON livreur_ratings(livreur_username);`,
}
for _, query := range queries {
+62
View File
@@ -0,0 +1,62 @@
package db
import (
"time"
)
type LivreurRating struct {
ID int `json:"id"`
OrderID int `json:"order_id"`
LivreurUsername string `json:"livreur_username"`
ClientUsername string `json:"client_username"`
Rating int `json:"rating"`
Comment string `json:"comment"`
CreatedAt time.Time `json:"created_at"`
}
func (d *Database) SubmitLivreurRating(orderID int, livreurUsername, clientUsername string, rating int, comment string) error {
return d.GDB.Exec(`
INSERT INTO livreur_ratings (order_id, livreur_username, client_username, rating, comment, created_at)
VALUES (?, ?, ?, ?, ?, NOW())
`, orderID, livreurUsername, clientUsername, rating, comment).Error
}
func (d *Database) GetOrderRating(orderID int) (*LivreurRating, error) {
var r LivreurRating
err := d.GDB.Raw(`SELECT * FROM livreur_ratings WHERE order_id = ? LIMIT 1`, orderID).Scan(&r).Error
if err != nil {
return nil, err
}
if r.ID == 0 {
return nil, nil
}
return &r, nil
}
func (d *Database) GetLivreurRatings(livreurUsername string) ([]LivreurRating, float64, error) {
var ratings []LivreurRating
if err := d.GDB.Raw(`
SELECT * FROM livreur_ratings WHERE livreur_username = ? ORDER BY created_at DESC
`, livreurUsername).Scan(&ratings).Error; err != nil {
return nil, 0, err
}
var avg float64
if len(ratings) > 0 {
d.GDB.Raw(`SELECT COALESCE(AVG(rating), 0) FROM livreur_ratings WHERE livreur_username = ?`, livreurUsername).Scan(&avg)
}
return ratings, avg, nil
}
// GetOrderForRating retourne l'username client et le livreur d'une commande approuvée
func (d *Database) GetOrderForRating(orderID int) (clientUsername, livreurUsername string, err error) {
var row struct {
Username string `gorm:"column:username"`
LivreurAssign string `gorm:"column:livreur_assign"`
}
err = d.GDB.Raw(`
SELECT username, COALESCE(livreur_assign, '') as livreur_assign
FROM commandes WHERE id = ? AND status = 'approved' LIMIT 1
`, orderID).Scan(&row).Error
return row.Username, row.LivreurAssign, err
}
+118
View File
@@ -0,0 +1,118 @@
package handlers
import (
"gestion/db"
"gestion/utils"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func SubmitLivreurRating(c *gin.Context) {
clientUsername := c.GetString("username")
if clientUsername == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
return
}
orderID, err := strconv.Atoi(c.Param("id"))
if err != nil || orderID <= 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID commande invalide"})
return
}
var req struct {
Rating int `json:"rating" binding:"required,min=1,max=5"`
Comment string `json:"comment"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Note invalide (1 à 5 requis)"})
return
}
database := c.MustGet("database").(*db.Database)
ownerUsername, livreurUsername, err := database.GetOrderForRating(orderID)
if err != nil {
utils.ServerErr(c, "Erreur lecture commande", err)
return
}
if ownerUsername == "" {
c.JSON(http.StatusNotFound, gin.H{"error": "Commande introuvable ou non terminée"})
return
}
if ownerUsername != clientUsername {
c.JSON(http.StatusForbidden, gin.H{"error": "Commande non autorisée"})
return
}
if livreurUsername == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun livreur assigné à cette commande"})
return
}
existing, err := database.GetOrderRating(orderID)
if err != nil {
utils.ServerErr(c, "Erreur vérification avis", err)
return
}
if existing != nil {
c.JSON(http.StatusConflict, gin.H{"error": "Vous avez déjà noté ce livreur pour cette commande"})
return
}
if err := database.SubmitLivreurRating(orderID, livreurUsername, clientUsername, req.Rating, req.Comment); err != nil {
utils.ServerErr(c, "Erreur enregistrement avis", err)
return
}
c.JSON(http.StatusOK, gin.H{"success": true})
}
func GetLivreurRatings(c *gin.Context) {
username := c.Param("username")
if username == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"})
return
}
database := c.MustGet("database").(*db.Database)
ratings, avg, err := database.GetLivreurRatings(username)
if err != nil {
utils.ServerErr(c, "Erreur récupération avis", err)
return
}
c.JSON(http.StatusOK, gin.H{
"ratings": ratings,
"average": avg,
"count": len(ratings),
})
}
func GetOrderRatingStatus(c *gin.Context) {
clientUsername := c.GetString("username")
if clientUsername == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
return
}
orderID, err := strconv.Atoi(c.Param("id"))
if err != nil || orderID <= 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"})
return
}
database := c.MustGet("database").(*db.Database)
rating, err := database.GetOrderRating(orderID)
if err != nil {
utils.ServerErr(c, "Erreur", err)
return
}
if rating == nil {
c.JSON(http.StatusOK, gin.H{"rated": false})
return
}
c.JSON(http.StatusOK, gin.H{"rated": true, "rating": rating.Rating, "comment": rating.Comment})
}
+5
View File
@@ -88,6 +88,10 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
// ⭐ NOUVEAU - HISTORIQUE DES COMMANDES TERMINÉES
cartGroupV1.GET("/my-commands/history", handlers.GetClientCommandsHistory)
// NOTATION LIVREUR
cartGroupV1.POST("/orders/:id/rate", handlers.SubmitLivreurRating)
cartGroupV1.GET("/orders/:id/rating", handlers.GetOrderRatingStatus)
// ⭐⭐ PÉNALITÉS CLIENT
cartGroupV1.GET("/penalties", handlers.GetMyPenalties) // Voir mes pénalités
@@ -260,6 +264,7 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
adminGroupV2.PUT("/delivery-persons/update/:username/location", handlers.UpdateDeliveryPersonLocationAdmin)
adminGroupV2.DELETE("/delivery-persons/:username/queue/:command_id", handlers.RemoveCommandFromQueue)
adminGroupV2.GET("/delivery-persons/:username/map-links", handlers.GetDeliveryPersonMapLinks)
adminGroupV2.GET("/delivery-persons/:username/ratings", handlers.GetLivreurRatings)
// Commandes annulées
adminGroupV2.GET("/orders/cancelled", handlers.GetAllCancelledOrders)
// ============================================