Restructuration majeure du projet: migration de flutt vers app, ajout de l'API et mise à jour du site web

This commit is contained in:
d6soft
2025-05-16 09:19:03 +02:00
parent b5aafc424b
commit 5c2620de30
391 changed files with 19780 additions and 7233 deletions

View File

@@ -0,0 +1,213 @@
-- Script de création des tables chat pour MariaDB
-- Compatible avec le module chat GEOSECTOR
-- Création des tables pour le système de chat
-- Table des salles de discussion
DROP TABLE IF EXISTS `chat_rooms`;
CREATE TABLE `chat_rooms` (
`id` varchar(50) NOT NULL,
`type` enum('privee', 'groupe', 'liste_diffusion', 'broadcast', 'announcement') NOT NULL,
`title` varchar(100) DEFAULT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`fk_user` int unsigned NOT NULL,
`fk_entite` int unsigned DEFAULT NULL,
`statut` enum('active', 'archive') NOT NULL DEFAULT 'active',
`description` text,
`reply_permission` enum('all', 'admins_only', 'sender_only', 'none') NOT NULL DEFAULT 'all',
`is_pinned` tinyint(1) unsigned NOT NULL DEFAULT 0,
`expiry_date` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user` (`fk_user`),
KEY `idx_entite` (`fk_entite`),
KEY `idx_type` (`type`),
KEY `idx_statut` (`statut`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table des participants aux salles de discussion
DROP TABLE IF EXISTS `chat_participants`;
CREATE TABLE `chat_participants` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`id_room` varchar(50) NOT NULL,
`id_user` int unsigned DEFAULT NULL,
`anonymous_id` varchar(50) DEFAULT NULL,
`role` enum('administrateur', 'participant', 'en_lecture_seule') NOT NULL DEFAULT 'participant',
`date_ajout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`notification_activee` tinyint(1) unsigned NOT NULL DEFAULT 1,
`last_read_message_id` varchar(50) DEFAULT NULL,
`via_target` tinyint(1) unsigned NOT NULL DEFAULT 0,
`can_reply` tinyint(1) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_room` (`id_room`),
KEY `idx_user` (`id_user`),
KEY `idx_anonymous_id` (`anonymous_id`),
CONSTRAINT `fk_chat_participants_room` FOREIGN KEY (`id_room`) REFERENCES `chat_rooms` (`id`) ON DELETE CASCADE,
CONSTRAINT `uc_room_user` UNIQUE (`id_room`, `id_user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table des messages
DROP TABLE IF EXISTS `chat_messages`;
CREATE TABLE `chat_messages` (
`id` varchar(50) NOT NULL,
`fk_room` varchar(50) NOT NULL,
`fk_user` int unsigned DEFAULT NULL,
`sender_type` enum('user', 'anonymous', 'system') NOT NULL DEFAULT 'user',
`content` text,
`content_type` enum('text', 'image', 'file') NOT NULL DEFAULT 'text',
`date_sent` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_delivered` timestamp NULL DEFAULT NULL,
`date_read` timestamp NULL DEFAULT NULL,
`statut` enum('envoye', 'livre', 'lu', 'error') NOT NULL DEFAULT 'envoye',
`is_announcement` tinyint(1) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_room` (`fk_room`),
KEY `idx_user` (`fk_user`),
KEY `idx_date` (`date_sent`),
KEY `idx_status` (`statut`),
CONSTRAINT `fk_chat_messages_room` FOREIGN KEY (`fk_room`) REFERENCES `chat_rooms` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table des cibles d'audience
DROP TABLE IF EXISTS `chat_audience_targets`;
CREATE TABLE `chat_audience_targets` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`fk_room` varchar(50) NOT NULL,
`target_type` enum('role', 'entity', 'all', 'combined') NOT NULL DEFAULT 'all',
`target_id` varchar(50) DEFAULT NULL,
`role_filter` varchar(20) DEFAULT NULL,
`entity_filter` varchar(50) DEFAULT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_room` (`fk_room`),
KEY `idx_type` (`target_type`),
CONSTRAINT `fk_chat_audience_targets_room` FOREIGN KEY (`fk_room`) REFERENCES `chat_rooms` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table des listes de diffusion
DROP TABLE IF EXISTS `chat_broadcast_lists`;
CREATE TABLE `chat_broadcast_lists` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`fk_room` varchar(50) NOT NULL,
`name` varchar(100) NOT NULL,
`description` text,
`fk_user_creator` int unsigned NOT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_room` (`fk_room`),
KEY `idx_user_creator` (`fk_user_creator`),
CONSTRAINT `fk_chat_broadcast_lists_room` FOREIGN KEY (`fk_room`) REFERENCES `chat_rooms` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table pour suivre la lecture des messages
DROP TABLE IF EXISTS `chat_read_messages`;
CREATE TABLE `chat_read_messages` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`fk_message` varchar(50) NOT NULL,
`fk_user` int unsigned NOT NULL,
`date_read` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_message` (`fk_message`),
KEY `idx_user` (`fk_user`),
CONSTRAINT `uc_message_user` UNIQUE (`fk_message`, `fk_user`),
CONSTRAINT `fk_chat_read_messages_message` FOREIGN KEY (`fk_message`) REFERENCES `chat_messages` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table des notifications
DROP TABLE IF EXISTS `chat_notifications`;
CREATE TABLE `chat_notifications` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`fk_user` int unsigned NOT NULL,
`fk_message` varchar(50) DEFAULT NULL,
`fk_room` varchar(50) DEFAULT NULL,
`type` varchar(50) NOT NULL,
`contenu` text,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_lecture` timestamp NULL DEFAULT NULL,
`statut` enum('non_lue', 'lue') NOT NULL DEFAULT 'non_lue',
PRIMARY KEY (`id`),
KEY `idx_user` (`fk_user`),
KEY `idx_message` (`fk_message`),
KEY `idx_room` (`fk_room`),
KEY `idx_statut` (`statut`),
CONSTRAINT `fk_chat_notifications_message` FOREIGN KEY (`fk_message`) REFERENCES `chat_messages` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_chat_notifications_room` FOREIGN KEY (`fk_room`) REFERENCES `chat_rooms` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table des utilisateurs anonymes (pour Resalice)
DROP TABLE IF EXISTS `chat_anonymous_users`;
CREATE TABLE `chat_anonymous_users` (
`id` varchar(50) NOT NULL,
`device_id` varchar(100) NOT NULL,
`name` varchar(100) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`converted_to_user_id` int unsigned DEFAULT NULL,
`metadata` json DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_device_id` (`device_id`),
KEY `idx_converted_user` (`converted_to_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table pour la file d'attente hors ligne
DROP TABLE IF EXISTS `chat_offline_queue`;
CREATE TABLE `chat_offline_queue` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` int unsigned NOT NULL,
`operation_type` varchar(50) NOT NULL,
`operation_data` json NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`processed_at` timestamp NULL DEFAULT NULL,
`status` enum('pending', 'processing', 'completed', 'failed') NOT NULL DEFAULT 'pending',
`error_message` text,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Table pour les pièces jointes
DROP TABLE IF EXISTS `chat_attachments`;
CREATE TABLE `chat_attachments` (
`id` varchar(50) NOT NULL,
`fk_message` varchar(50) NOT NULL,
`file_name` varchar(255) NOT NULL,
`file_path` varchar(500) NOT NULL,
`file_type` varchar(100) NOT NULL,
`file_size` int unsigned NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_message` (`fk_message`),
CONSTRAINT `fk_chat_attachments_message` FOREIGN KEY (`fk_message`) REFERENCES `chat_messages` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Vues utiles
-- Vue des messages avec informations utilisateur
CREATE OR REPLACE VIEW `chat_messages_with_users` AS
SELECT
m.*,
u.name as sender_name,
u.username as sender_username,
u.fk_entite as sender_entity_id
FROM chat_messages m
LEFT JOIN users u ON m.fk_user = u.id;
-- Vue des conversations avec compte de messages non lus
CREATE OR REPLACE VIEW `chat_conversations_unread` AS
SELECT
r.*,
COUNT(DISTINCT m.id) as total_messages,
COUNT(DISTINCT rm.id) as read_messages,
COUNT(DISTINCT m.id) - COUNT(DISTINCT rm.id) as unread_messages,
(SELECT date_sent FROM chat_messages
WHERE fk_room = r.id
ORDER BY date_sent DESC LIMIT 1) as last_message_date
FROM chat_rooms r
LEFT JOIN chat_messages m ON r.id = m.fk_room
LEFT JOIN chat_read_messages rm ON m.id = rm.fk_message
GROUP BY r.id;
-- Index supplémentaires pour les performances
CREATE INDEX idx_messages_unread ON chat_messages(fk_room, statut);
CREATE INDEX idx_participants_active ON chat_participants(id_room, id_user, notification_activee);
CREATE INDEX idx_notifications_unread ON chat_notifications(fk_user, statut);

View File

@@ -0,0 +1,323 @@
<?php
/**
* Service d'envoi de notifications MQTT pour le chat
*
* Ce script gère l'envoi des notifications via MQTT depuis le backend PHP
*/
require_once 'vendor/autoload.php'; // PhpMqtt
use PhpMqtt\Client\MqttClient;
use PhpMqtt\Client\ConnectionSettings;
class MqttNotificationSender {
private $mqtt;
private $db;
private $config;
public function __construct($dbConnection, $mqttConfig) {
$this->db = $dbConnection;
$this->config = $mqttConfig;
// Initialiser le client MQTT
$this->initializeMqttClient();
}
private function initializeMqttClient() {
$this->mqtt = new MqttClient(
$this->config['host'],
$this->config['port'],
'geosector_api_' . uniqid(), // Client ID unique
MqttClient::MQTT_3_1_1
);
$connectionSettings = (new ConnectionSettings)
->setUsername($this->config['username'])
->setPassword($this->config['password'])
->setKeepAliveInterval(60)
->setConnectTimeout(30)
->setUseTls($this->config['use_ssl'] ?? false);
$this->mqtt->connect($connectionSettings, true);
}
/**
* Envoie une notification pour un nouveau message
*/
public function sendMessageNotification($receiverId, $senderId, $messageId, $content, $conversationId) {
try {
// Vérifier les préférences de notification
$settings = $this->getUserNotificationSettings($receiverId);
if (!$this->shouldSendNotification($settings, $conversationId)) {
return ['status' => 'skipped', 'reason' => 'notification_settings'];
}
// Obtenir les informations de l'expéditeur
$sender = $this->getSenderInfo($senderId);
// Obtenir le nom de la conversation
$conversationName = $this->getConversationName($conversationId, $receiverId);
// Préparer le payload de la notification
$payload = [
'type' => 'chat_message',
'messageId' => $messageId,
'conversationId' => $conversationId,
'senderId' => $senderId,
'senderName' => $sender['name'] ?? 'Utilisateur',
'content' => $settings['show_preview'] ? $content : 'Nouveau message',
'conversationName' => $conversationName,
'timestamp' => time(),
];
// Définir le topic MQTT
$topic = sprintf('chat/user/%s/messages', $receiverId);
// Publier le message
$this->mqtt->publish($topic, json_encode($payload), 1);
// Enregistrer la notification dans la base de données
$this->saveNotificationToDatabase($receiverId, $messageId, $conversationId, $payload);
return [
'status' => 'success',
'topic' => $topic
];
} catch (Exception $e) {
return [
'status' => 'error',
'reason' => $e->getMessage()
];
}
}
/**
* Envoie une annonce à plusieurs utilisateurs
*/
public function sendBroadcastAnnouncement($audienceTargets, $messageId, $title, $content, $conversationId) {
$results = [];
$userIds = $this->resolveAudienceTargets($audienceTargets);
foreach ($userIds as $userId) {
// Préparer le payload pour l'annonce
$payload = [
'type' => 'announcement',
'messageId' => $messageId,
'conversationId' => $conversationId,
'title' => $title,
'content' => $content,
'timestamp' => time(),
];
// Envoyer à chaque utilisateur
$topic = sprintf('chat/user/%s/messages', $userId);
try {
$this->mqtt->publish($topic, json_encode($payload), 1);
$results[$userId] = ['status' => 'success'];
// Enregistrer la notification
$this->saveNotificationToDatabase($userId, $messageId, $conversationId, $payload);
} catch (Exception $e) {
$results[$userId] = ['status' => 'error', 'reason' => $e->getMessage()];
}
}
// Publier aussi sur le topic général des annonces
$this->mqtt->publish('chat/announcement', json_encode($payload), 1);
return $results;
}
/**
* Envoie une notification à une conversation spécifique
*/
public function sendConversationNotification($conversationId, $messageId, $senderId, $content) {
$participants = $this->getConversationParticipants($conversationId);
foreach ($participants as $participant) {
if ($participant['id'] !== $senderId) {
$this->sendMessageNotification(
$participant['id'],
$senderId,
$messageId,
$content,
$conversationId
);
}
}
}
/**
* Vérifie si une notification doit être envoyée
*/
private function shouldSendNotification($settings, $conversationId) {
if (!$settings['enable_notifications']) {
return false;
}
if (in_array($conversationId, $settings['muted_conversations'])) {
return false;
}
if ($settings['do_not_disturb'] && $this->isInDoNotDisturbPeriod($settings)) {
return false;
}
return true;
}
/**
* Récupère les paramètres de notification de l'utilisateur
*/
private function getUserNotificationSettings($userId) {
$stmt = $this->db->prepare("
SELECT * FROM notification_settings
WHERE user_id = ?
");
$stmt->execute([$userId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
// Valeurs par défaut si pas de préférences
return $result ?: [
'enable_notifications' => true,
'show_preview' => true,
'muted_conversations' => [],
'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;
}
}
/**
* Enregistre la notification dans la base de données
*/
private function saveNotificationToDatabase($userId, $messageId, $conversationId, $payload) {
$stmt = $this->db->prepare("
INSERT INTO chat_notifications
(fk_user, fk_message, fk_room, type, contenu, statut)
VALUES (?, ?, ?, ?, ?, 'non_lue')
");
$stmt->execute([
$userId,
$messageId,
$conversationId,
$payload['type'],
json_encode($payload)
]);
}
/**
* 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écupère les participants d'une conversation
*/
private function getConversationParticipants($conversationId) {
$stmt = $this->db->prepare("
SELECT id_user as id, role
FROM chat_participants
WHERE id_room = ? AND notification_activee = 1
");
$stmt->execute([$conversationId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 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':
$stmt = $this->db->query("SELECT id FROM users WHERE chk_active = 1");
$userIds = array_merge($userIds, $stmt->fetchAll(PDO::FETCH_COLUMN));
break;
case 'role':
$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':
$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':
$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);
}
/**
* Ferme la connexion MQTT
*/
public function disconnect() {
if ($this->mqtt) {
$this->mqtt->disconnect();
}
}
}

View File

@@ -0,0 +1,263 @@
<?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);
}
}