package db import ( "bytes" "database/sql" "encoding/json" "fmt" "log" "net/http" "time" ) func (d *Database) NotifyClient(username string, commandID int, notifType, message string) error { // Sauvegarder la notification dans Redis notifKey := fmt.Sprintf("notifications:%s", username) notification := map[string]interface{}{ "command_id": commandID, "type": notifType, "message": message, "created_at": time.Now().Format(time.RFC3339), "read": false, } notifJSON, _ := json.Marshal(notification) Redis.LPush(RedisCtx, notifKey, notifJSON) Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour) // Envoyer push notification si le client a un token enregistré pushToken, err := d.GetClientPushToken(username) if err == nil && pushToken != "" { go sendExpoPush(pushToken, "Uber Stup", message, commandID, notifType) } log.Printf("📬 Notification envoyée à %s: %s", username, message) return nil } func sendExpoPush(token, title, body string, commandID int, notifType string) { sendExpoPushWithChannel(token, title, body, commandID, notifType, "orders") } func sendExpoPushWithChannel(token, title, body string, commandID int, notifType, channelID string) { payload := map[string]interface{}{ "to": token, "title": title, "body": body, "channelId": channelID, "data": map[string]interface{}{ "command_id": commandID, "type": notifType, }, "sound": "default", } jsonBody, err := json.Marshal(payload) if err != nil { log.Printf("❌ [EXPO_PUSH] Erreur marshal: %v", err) return } req, err := http.NewRequest("POST", "https://exp.host/--/api/v2/push/send", bytes.NewBuffer(jsonBody)) if err != nil { log.Printf("❌ [EXPO_PUSH] Erreur création requête: %v", err) return } req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") req.Header.Set("Accept-Encoding", "gzip, deflate") client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { log.Printf("❌ [EXPO_PUSH] Erreur envoi: %v", err) return } defer resp.Body.Close() log.Printf("✅ [EXPO_PUSH] Push envoyé à %s (channel: %s, status: %d)", token, channelID, resp.StatusCode) } // ============================================ // PUSH TOKEN - LIVREURS (table users) // ============================================ // SaveUserPushToken sauvegarde le token push d'un livreur dans la table users func (d *Database) SaveUserPushToken(username, token string) error { _, err := d.Exec(`UPDATE users SET push_token = $1 WHERE username = $2`, token, username) return err } // GetUserPushToken récupère le token push d'un livreur depuis la table users func (d *Database) GetUserPushToken(username string) (string, error) { var token sql.NullString err := d.QueryRow(`SELECT push_token FROM users WHERE username = $1`, username).Scan(&token) if err != nil || !token.Valid { return "", err } return token.String, nil } // DeleteUserPushToken supprime le token push d'un livreur func (d *Database) DeleteUserPushToken(username string) error { _, err := d.Exec(`UPDATE users SET push_token = NULL WHERE username = $1`, username) return err } // NotifyLivreur envoie une notification in-app (Redis) + push Expo à un livreur func (d *Database) NotifyLivreur(username string, commandID int, notifType, message string) error { notifKey := fmt.Sprintf("notifications:%s", username) notification := map[string]interface{}{ "command_id": commandID, "type": notifType, "message": message, "created_at": time.Now().Format(time.RFC3339), "read": false, } notifJSON, _ := json.Marshal(notification) Redis.LPush(RedisCtx, notifKey, notifJSON) Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour) // Push notification si le livreur a un token enregistré pushToken, err := d.GetUserPushToken(username) if err == nil && pushToken != "" { go sendExpoPushWithChannel(pushToken, "Nouvelle commande assignée", message, commandID, notifType, "deliveries") } log.Printf("📬 [LIVREUR_NOTIF] Notification envoyée à %s: %s", username, message) return nil } // NotifyAllAdminCabine stocke une notification Redis pour tous les admins/cabines // et envoie un push aux ceux qui ont un token. Appelé dès qu'une nouvelle commande est créée. func (d *Database) NotifyAllAdminCabine(commandID int, clientUsername, deliveryAddr string) { rows, err := d.Query( `SELECT username, COALESCE(push_token, '') FROM users WHERE role IN ('admin','cabine')`, ) if err != nil { log.Printf("❌ [ADMIN_NOTIF] Erreur lecture users admin/cabine: %v", err) return } defer rows.Close() msg := fmt.Sprintf("Nouvelle commande #%d de %s — %s", commandID, clientUsername, deliveryAddr) notification := map[string]interface{}{ "command_id": commandID, "type": "new_order", "message": msg, "created_at": time.Now().Format(time.RFC3339), "read": false, } notifJSON, _ := json.Marshal(notification) sent := 0 for rows.Next() { var username, token string if err := rows.Scan(&username, &token); err != nil { continue } notifKey := fmt.Sprintf("notifications:%s", username) Redis.LPush(RedisCtx, notifKey, notifJSON) Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour) if token != "" { go sendExpoPushWithChannel(token, "Nouvelle commande", msg, commandID, "new_order", "orders") sent++ } } log.Printf("📬 [ADMIN_NOTIF] Notif Redis + push (%d tokens) pour commande #%d", sent, commandID) } // NotifyAllAdminCabineAlert envoie une notification Redis + push à tous les admins/cabines // lors du déclenchement d'une alerte par un livreur. func (d *Database) NotifyAllAdminCabineAlert(alertID int, livreurUsername, alertMessage string) { rows, err := d.Query( `SELECT username, COALESCE(push_token, '') FROM users WHERE role IN ('admin','cabine')`, ) if err != nil { log.Printf("❌ [ALERT_NOTIF] Erreur lecture users admin/cabine: %v", err) return } defer rows.Close() title := "🚨 Alerte livreur" body := fmt.Sprintf("%s — livreur : %s", alertMessage, livreurUsername) notification := map[string]interface{}{ "alert_id": alertID, "type": "alert", "message": body, "created_at": time.Now().Format(time.RFC3339), "read": false, } notifJSON, _ := json.Marshal(notification) sent := 0 for rows.Next() { var username, token string if err := rows.Scan(&username, &token); err != nil { continue } notifKey := fmt.Sprintf("notifications:%s", username) Redis.LPush(RedisCtx, notifKey, notifJSON) Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour) if token != "" { go sendExpoPushWithChannel(token, title, body, alertID, "alert", "orders") sent++ } } log.Printf("🚨 [ALERT_NOTIF] Notif Redis + push (%d tokens) pour alerte #%d de %s", sent, alertID, livreurUsername) } // AddDeliveryRating ajoute une note pour un livreur func (d *Database) AddDeliveryRating(livreurUsername string, commandID, rating int, comment string) error { query := ` INSERT INTO delivery_ratings (livreur_username, command_id, rating, comment, created_at) VALUES (?, ?, ?, ?, NOW()) ` _, err := d.Exec(query, livreurUsername, commandID, rating, comment) if err != nil { log.Printf("⚠️ Erreur sauvegarde note livreur: %v", err) return err } log.Printf("⭐ Note %d/5 ajoutée pour livreur %s (commande %d)", rating, livreurUsername, commandID) return nil }