212 lines
6.5 KiB
PHP
212 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Notificacion;
|
|
use App\Models\PushSubscription;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Minishlink\WebPush\WebPush;
|
|
|
|
use Minishlink\WebPush\Subscription;
|
|
|
|
class NotificacionService
|
|
{
|
|
/**
|
|
* Enviar una notificación in-app a un usuario (jugador o aficionado).
|
|
*/
|
|
public function enviar(
|
|
string $tipo_dest,
|
|
string|int $id_dest,
|
|
string $tipo,
|
|
string $titulo,
|
|
string $mensaje,
|
|
?string $url = null
|
|
): Notificacion {
|
|
$notif = Notificacion::create([
|
|
'tipo_destinatario' => $tipo_dest,
|
|
'id_destinatario' => (string) $id_dest,
|
|
'tipo' => $tipo,
|
|
'titulo' => $titulo,
|
|
'mensaje' => $mensaje,
|
|
'url_accion' => $url,
|
|
'leida' => false,
|
|
'enviada_email' => false,
|
|
'creada_en' => now(),
|
|
]);
|
|
|
|
// Intentar envío Push
|
|
$this->enviarPush([['tipo' => $tipo_dest, 'id' => $id_dest]], $titulo, $mensaje, $url);
|
|
|
|
return $notif;
|
|
}
|
|
|
|
/**
|
|
* Enviar la misma notificación a múltiples destinatarios.
|
|
* $destinatarios = [['tipo' => 'jugador', 'id' => 'XXX'], ...]
|
|
*/
|
|
public function enviarMasivo(array $destinatarios, string $tipo, string $titulo, string $mensaje, ?string $url = null): int
|
|
{
|
|
$rows = [];
|
|
$ahora = now()->toDateTimeString();
|
|
|
|
foreach ($destinatarios as $dest) {
|
|
$rows[] = [
|
|
'tipo_destinatario' => $dest['tipo'],
|
|
'id_destinatario' => (string) $dest['id'],
|
|
'tipo' => $tipo,
|
|
'titulo' => $titulo,
|
|
'mensaje' => $mensaje,
|
|
'url_accion' => $url,
|
|
'leida' => false,
|
|
'enviada_email' => false,
|
|
'creada_en' => $ahora,
|
|
];
|
|
}
|
|
|
|
if (empty($rows)) return 0;
|
|
|
|
// Insert en chunks para no sobrecargar
|
|
foreach (array_chunk($rows, 500) as $chunk) {
|
|
Notificacion::insert($chunk);
|
|
}
|
|
|
|
// Intentar envío Push masivo
|
|
$this->enviarPush($destinatarios, $titulo, $mensaje, $url);
|
|
|
|
return count($rows);
|
|
}
|
|
|
|
/**
|
|
* Lógica centralizada para enviar Web Push Notifications
|
|
*/
|
|
private function enviarPush(array $destinatarios, string $titulo, string $mensaje, ?string $url = null): void
|
|
{
|
|
$vPublic = env('VAPID_PUBLIC_KEY');
|
|
$vPrivate = env('VAPID_PRIVATE_KEY');
|
|
|
|
if (!$vPublic || !$vPrivate) return;
|
|
|
|
// Buscar todas las suscripciones de estos usuarios
|
|
$subsFound = PushSubscription::where(function($query) use ($destinatarios) {
|
|
foreach ($destinatarios as $d) {
|
|
$query->orWhere(function($q) use ($d) {
|
|
$q->where('tipo_usuario', $d['tipo'])
|
|
->where('id_usuario', (string)$d['id']);
|
|
});
|
|
}
|
|
})->get();
|
|
|
|
if ($subsFound->isEmpty()) return;
|
|
|
|
try {
|
|
$auth = [
|
|
'VAPID' => [
|
|
'subject' => env('APP_URL', 'http://localhost'),
|
|
'publicKey' => $vPublic,
|
|
'privateKey' => $vPrivate,
|
|
],
|
|
];
|
|
|
|
$webPush = new WebPush($auth);
|
|
$payload = json_encode([
|
|
'title' => $titulo,
|
|
'body' => $mensaje,
|
|
'url' => $url ?: '/',
|
|
]);
|
|
|
|
foreach ($subsFound as $sub) {
|
|
$webPush->queueNotification(
|
|
Subscription::create([
|
|
'endpoint' => $sub->endpoint,
|
|
'publicKey' => $sub->p256dh,
|
|
'authToken' => $sub->auth,
|
|
]),
|
|
$payload
|
|
);
|
|
}
|
|
|
|
foreach ($webPush->flush() as $report) {
|
|
if (!$report->isSuccess()) {
|
|
// Si falló (ej: suscripción expirada), la borramos para no reintentar
|
|
if ($report->isSubscriptionExpired()) {
|
|
$endpoint = $report->getEndpoint();
|
|
PushSubscription::where('endpoint', $endpoint)->delete();
|
|
}
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::error("Error enviando Web Push: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtener notificaciones no leídas de un usuario.
|
|
*/
|
|
public function obtenerNoLeidas(string $tipo, string|int $id): Collection
|
|
{
|
|
return Notificacion::paraUsuario($tipo, $id)
|
|
->noLeidas()
|
|
->orderByDesc('creada_en')
|
|
->limit(50)
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* Obtener todas las notificaciones de un usuario (paginadas).
|
|
*/
|
|
public function obtenerTodas(string $tipo, string|int $id, int $perPage = 20)
|
|
{
|
|
return Notificacion::paraUsuario($tipo, $id)
|
|
->orderByDesc('creada_en')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Contar notificaciones no leídas.
|
|
*/
|
|
public function contarNoLeidas(string $tipo, string|int $id): int
|
|
{
|
|
return Notificacion::paraUsuario($tipo, $id)->noLeidas()->count();
|
|
}
|
|
|
|
/**
|
|
* Marcar una notificación como leída (verificando pertenencia).
|
|
*/
|
|
public function marcarLeida(int $id_notif, string $tipo, string|int $id_dest): bool
|
|
{
|
|
return (bool) Notificacion::paraUsuario($tipo, $id_dest)
|
|
->where('id', $id_notif)
|
|
->update(['leida' => true]);
|
|
}
|
|
|
|
/**
|
|
* Marcar todas como leídas para un usuario.
|
|
*/
|
|
public function marcarTodasLeidas(string $tipo, string|int $id_dest): int
|
|
{
|
|
return Notificacion::paraUsuario($tipo, $id_dest)
|
|
->noLeidas()
|
|
->update(['leida' => true]);
|
|
}
|
|
|
|
/**
|
|
* Eliminar una notificación (verificando pertenencia).
|
|
*/
|
|
public function eliminar(int $id_notif, string $tipo, string|int $id_dest): bool
|
|
{
|
|
return (bool) Notificacion::paraUsuario($tipo, $id_dest)
|
|
->where('id', $id_notif)
|
|
->delete();
|
|
}
|
|
|
|
/**
|
|
* Eliminar todas las notificaciones de un usuario.
|
|
*/
|
|
public function eliminarTodas(string $tipo, string|int $id_dest): int
|
|
{
|
|
return Notificacion::paraUsuario($tipo, $id_dest)->delete();
|
|
}
|
|
}
|
|
|