- Ajout système complet de gestion des secteurs avec contours géographiques - Import des contours départementaux depuis GeoJSON - API REST pour la gestion des secteurs (/api/sectors) - Service de géolocalisation pour déterminer les secteurs - Migration base de données avec tables x_departements_contours et sectors_adresses - Interface Flutter pour visualisation et gestion des secteurs - Ajout thème sombre dans l'application - Corrections diverses et optimisations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
264 lines
9.2 KiB
PHP
Executable File
264 lines
9.2 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Script d'envoi de notifications push pour le chat
|
|
*
|
|
* Ce script est appelé par l'API backend pour envoyer des notifications
|
|
* lorsqu'un nouveau message est reçu
|
|
*/
|
|
|
|
require_once 'vendor/autoload.php';
|
|
|
|
use Kreait\Firebase\Factory;
|
|
use Kreait\Firebase\Messaging\CloudMessage;
|
|
use Kreait\Firebase\Messaging\Notification;
|
|
|
|
class ChatNotificationSender {
|
|
private $messaging;
|
|
private $db;
|
|
|
|
public function __construct($firebaseServiceAccount, $dbConnection) {
|
|
$factory = (new Factory)->withServiceAccount($firebaseServiceAccount);
|
|
$this->messaging = $factory->createMessaging();
|
|
$this->db = $dbConnection;
|
|
}
|
|
|
|
/**
|
|
* Envoie une notification à un utilisateur pour un nouveau message
|
|
*/
|
|
public function sendMessageNotification($userId, $messageId, $senderId, $content, $conversationId) {
|
|
try {
|
|
// Récupérer les préférences de notification de l'utilisateur
|
|
$settings = $this->getUserNotificationSettings($userId);
|
|
|
|
if (!$settings['enable_notifications']) {
|
|
return ['status' => 'skipped', 'reason' => 'notifications_disabled'];
|
|
}
|
|
|
|
// Vérifier si la conversation est en silencieux
|
|
if (in_array($conversationId, $settings['muted_conversations'])) {
|
|
return ['status' => 'skipped', 'reason' => 'conversation_muted'];
|
|
}
|
|
|
|
// Vérifier le mode Ne pas déranger
|
|
if ($this->isInDoNotDisturbPeriod($settings)) {
|
|
return ['status' => 'skipped', 'reason' => 'do_not_disturb'];
|
|
}
|
|
|
|
// Obtenir le token du device
|
|
$deviceToken = $this->getUserDeviceToken($userId);
|
|
if (!$deviceToken) {
|
|
return ['status' => 'error', 'reason' => 'no_device_token'];
|
|
}
|
|
|
|
// Obtenir les informations de l'expéditeur
|
|
$sender = $this->getSenderInfo($senderId);
|
|
|
|
// Obtenir le nom de la conversation
|
|
$conversationName = $this->getConversationName($conversationId, $userId);
|
|
|
|
// Préparation du contenu de la notification
|
|
$title = $conversationName ?? $sender['name'];
|
|
$body = $settings['show_preview'] ? $content : 'Nouveau message';
|
|
|
|
// Créer le message Firebase
|
|
$message = CloudMessage::withTarget('token', $deviceToken)
|
|
->withNotification(Notification::create($title, $body))
|
|
->withData([
|
|
'type' => 'chat_message',
|
|
'messageId' => $messageId,
|
|
'conversationId' => $conversationId,
|
|
'senderId' => $senderId,
|
|
'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
|
|
])
|
|
->withAndroidConfig([
|
|
'priority' => 'high',
|
|
'notification' => [
|
|
'sound' => $settings['sound_enabled'] ? 'default' : null,
|
|
'channel_id' => 'chat_messages',
|
|
'icon' => 'ic_launcher',
|
|
],
|
|
])
|
|
->withApnsConfig([
|
|
'payload' => [
|
|
'aps' => [
|
|
'sound' => $settings['sound_enabled'] ? 'default' : null,
|
|
'badge' => 1, // TODO: Calculer le nombre réel de messages non lus
|
|
],
|
|
],
|
|
]);
|
|
|
|
// Envoyer la notification
|
|
$result = $this->messaging->send($message);
|
|
|
|
// Enregistrer la notification dans la base de données
|
|
$this->saveNotificationToDatabase($userId, $messageId, $conversationId, $title, $body);
|
|
|
|
return [
|
|
'status' => 'success',
|
|
'message_id' => $result,
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
return [
|
|
'status' => 'error',
|
|
'reason' => $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Envoie une notification de type broadcast
|
|
*/
|
|
public function sendBroadcastNotification($audienceTargets, $messageId, $content, $conversationId) {
|
|
$results = [];
|
|
|
|
// Résoudre les cibles d'audience
|
|
$userIds = $this->resolveAudienceTargets($audienceTargets);
|
|
|
|
foreach ($userIds as $userId) {
|
|
$result = $this->sendMessageNotification($userId, $messageId, null, $content, $conversationId);
|
|
$results[$userId] = $result;
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Enregistre la notification dans la base de données
|
|
*/
|
|
private function saveNotificationToDatabase($userId, $messageId, $conversationId, $title, $body) {
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO chat_notifications (fk_user, fk_message, fk_room, type, contenu, statut)
|
|
VALUES (?, ?, ?, 'chat_message', ?, 'non_lue')
|
|
");
|
|
|
|
$stmt->execute([$userId, $messageId, $conversationId, json_encode([
|
|
'title' => $title,
|
|
'body' => $body,
|
|
])]);
|
|
}
|
|
|
|
/**
|
|
* Récupère les préférences de notification de l'utilisateur
|
|
*/
|
|
private function getUserNotificationSettings($userId) {
|
|
// Implémenter la logique pour récupérer les paramètres
|
|
return [
|
|
'enable_notifications' => true,
|
|
'sound_enabled' => true,
|
|
'vibration_enabled' => true,
|
|
'muted_conversations' => [],
|
|
'show_preview' => true,
|
|
'do_not_disturb' => false,
|
|
'do_not_disturb_start' => null,
|
|
'do_not_disturb_end' => null,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Vérifie si on est dans la période Ne pas déranger
|
|
*/
|
|
private function isInDoNotDisturbPeriod($settings) {
|
|
if (!$settings['do_not_disturb']) {
|
|
return false;
|
|
}
|
|
|
|
$now = new DateTime();
|
|
$start = new DateTime($settings['do_not_disturb_start']);
|
|
$end = new DateTime($settings['do_not_disturb_end']);
|
|
|
|
if ($start < $end) {
|
|
return $now >= $start && $now <= $end;
|
|
} else {
|
|
// Période qui chevauche minuit
|
|
return $now >= $start || $now <= $end;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupère le token du device de l'utilisateur
|
|
*/
|
|
private function getUserDeviceToken($userId) {
|
|
$stmt = $this->db->prepare("
|
|
SELECT device_token
|
|
FROM notification_settings
|
|
WHERE user_id = ? AND device_token IS NOT NULL
|
|
ORDER BY updated_at DESC LIMIT 1
|
|
");
|
|
|
|
$stmt->execute([$userId]);
|
|
return $stmt->fetchColumn();
|
|
}
|
|
|
|
/**
|
|
* Récupère les informations de l'expéditeur
|
|
*/
|
|
private function getSenderInfo($senderId) {
|
|
$stmt = $this->db->prepare("
|
|
SELECT id, name, username
|
|
FROM users
|
|
WHERE id = ?
|
|
");
|
|
|
|
$stmt->execute([$senderId]);
|
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
/**
|
|
* Récupère le nom de la conversation
|
|
*/
|
|
private function getConversationName($conversationId, $userId) {
|
|
$stmt = $this->db->prepare("
|
|
SELECT title
|
|
FROM chat_rooms
|
|
WHERE id = ?
|
|
");
|
|
|
|
$stmt->execute([$conversationId]);
|
|
return $stmt->fetchColumn();
|
|
}
|
|
|
|
/**
|
|
* Résout les cibles d'audience en une liste d'IDs utilisateur
|
|
*/
|
|
private function resolveAudienceTargets($targets) {
|
|
$userIds = [];
|
|
|
|
foreach ($targets as $target) {
|
|
switch ($target['target_type']) {
|
|
case 'all':
|
|
// Récupérer tous les utilisateurs
|
|
$stmt = $this->db->query("SELECT id FROM users WHERE chk_active = 1");
|
|
$userIds = array_merge($userIds, $stmt->fetchAll(PDO::FETCH_COLUMN));
|
|
break;
|
|
|
|
case 'role':
|
|
// Récupérer les utilisateurs par rôle
|
|
$stmt = $this->db->prepare("SELECT id FROM users WHERE fk_role = ?");
|
|
$stmt->execute([$target['role_filter']]);
|
|
$userIds = array_merge($userIds, $stmt->fetchAll(PDO::FETCH_COLUMN));
|
|
break;
|
|
|
|
case 'entity':
|
|
// Récupérer les utilisateurs par entité
|
|
$stmt = $this->db->prepare("SELECT id FROM users WHERE fk_entite = ?");
|
|
$stmt->execute([$target['entity_filter']]);
|
|
$userIds = array_merge($userIds, $stmt->fetchAll(PDO::FETCH_COLUMN));
|
|
break;
|
|
|
|
case 'combined':
|
|
// Récupérer les utilisateurs par combinaison de rôle et entité
|
|
$stmt = $this->db->prepare("
|
|
SELECT id FROM users
|
|
WHERE fk_role = ? AND fk_entite = ?
|
|
");
|
|
$stmt->execute([$target['role_filter'], $target['entity_filter']]);
|
|
$userIds = array_merge($userIds, $stmt->fetchAll(PDO::FETCH_COLUMN));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return array_unique($userIds);
|
|
}
|
|
}
|