Agrego archivos iniciales
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user