204 lines
5.3 KiB
PHP
204 lines
5.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace MintyPHP\Module\Notifications\Service;
|
||
|
|
|
||
|
|
use MintyPHP\Module\Notifications\Repository\NotificationRepositoryInterface;
|
||
|
|
|
||
|
|
class NotificationService
|
||
|
|
{
|
||
|
|
public function __construct(
|
||
|
|
private readonly NotificationRepositoryInterface $notificationRepository
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return list<array<string, mixed>> */
|
||
|
|
public function listForUser(int $userId, int $limit = 50): array
|
||
|
|
{
|
||
|
|
if ($userId <= 0) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->listByUser($userId, $limit, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function unreadCount(int $userId): int
|
||
|
|
{
|
||
|
|
if ($userId <= 0) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->countUnreadByUser($userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function markAsRead(int $userId, int $notificationId): bool
|
||
|
|
{
|
||
|
|
if ($userId <= 0 || $notificationId <= 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->markRead($notificationId, $userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function markAllAsRead(int $userId): int
|
||
|
|
{
|
||
|
|
if ($userId <= 0) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->markAllReadByUser($userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function dismiss(int $userId, int $notificationId): bool
|
||
|
|
{
|
||
|
|
if ($userId <= 0 || $notificationId <= 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->delete($notificationId, $userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function createForUser(
|
||
|
|
int $recipientUserId,
|
||
|
|
?int $tenantId,
|
||
|
|
string $type,
|
||
|
|
string $title,
|
||
|
|
?string $body,
|
||
|
|
?string $link,
|
||
|
|
array $data
|
||
|
|
): int|false {
|
||
|
|
if ($recipientUserId <= 0 || $type === '' || $title === '') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->create([
|
||
|
|
'recipient_user_id' => $recipientUserId,
|
||
|
|
'tenant_id' => $tenantId,
|
||
|
|
'type' => $type,
|
||
|
|
'title' => $title,
|
||
|
|
'body' => $body,
|
||
|
|
'link' => $link,
|
||
|
|
'data' => $data ?: null,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Fan-out: create notification for all active users in a tenant, optionally excluding actor.
|
||
|
|
*
|
||
|
|
* @return int Number of notifications created
|
||
|
|
*/
|
||
|
|
public function createForTenantUsers(
|
||
|
|
int $tenantId,
|
||
|
|
string $type,
|
||
|
|
string $title,
|
||
|
|
?string $body,
|
||
|
|
?string $link,
|
||
|
|
array $data,
|
||
|
|
?int $excludeUserId
|
||
|
|
): int {
|
||
|
|
if ($tenantId <= 0 || $type === '' || $title === '') {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$userIds = $this->listUserIdsForTenant($tenantId);
|
||
|
|
$created = 0;
|
||
|
|
|
||
|
|
foreach ($userIds as $userId) {
|
||
|
|
if ($excludeUserId !== null && $userId === $excludeUserId) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$result = $this->createForUser($userId, $tenantId, $type, $title, $body, $link, $data);
|
||
|
|
if ($result !== false) {
|
||
|
|
$created++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $created;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Fan-out: create notification only for tenant users whose ID is in $allowedUserIds.
|
||
|
|
*
|
||
|
|
* @param array<int, mixed> $allowedUserIds Flipped array (user ID as key) for fast lookup
|
||
|
|
* @return int Number of notifications created
|
||
|
|
*/
|
||
|
|
public function createForTenantAdminUsers(
|
||
|
|
int $tenantId,
|
||
|
|
string $type,
|
||
|
|
string $title,
|
||
|
|
?string $body,
|
||
|
|
?string $link,
|
||
|
|
array $data,
|
||
|
|
?int $excludeUserId,
|
||
|
|
array $allowedUserIds
|
||
|
|
): int {
|
||
|
|
if ($tenantId <= 0 || $type === '' || $title === '' || $allowedUserIds === []) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$userIds = $this->listUserIdsForTenant($tenantId);
|
||
|
|
$created = 0;
|
||
|
|
|
||
|
|
foreach ($userIds as $userId) {
|
||
|
|
if (!isset($allowedUserIds[$userId])) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if ($excludeUserId !== null && $userId === $excludeUserId) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$result = $this->createForUser($userId, $tenantId, $type, $title, $body, $link, $data);
|
||
|
|
if ($result !== false) {
|
||
|
|
$created++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $created;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function purgeOldRead(int $retentionDays = 90): int
|
||
|
|
{
|
||
|
|
return $this->notificationRepository->purgeReadOlderThanDays($retentionDays);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function deleteAllForUser(int $userId): int
|
||
|
|
{
|
||
|
|
if ($userId <= 0) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notificationRepository->deleteAllByUser($userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Look up active user IDs belonging to a tenant.
|
||
|
|
*
|
||
|
|
* @return list<int>
|
||
|
|
*/
|
||
|
|
private function listUserIdsForTenant(int $tenantId): array
|
||
|
|
{
|
||
|
|
if ($tenantId <= 0) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$rows = \MintyPHP\DB::select(
|
||
|
|
'select ut.user_id from user_tenants ut join users u on u.id = ut.user_id and u.active = 1 where ut.tenant_id = ?',
|
||
|
|
(string) $tenantId
|
||
|
|
);
|
||
|
|
|
||
|
|
if (!is_array($rows)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$ids = [];
|
||
|
|
foreach ($rows as $row) {
|
||
|
|
$data = $row['user_tenants'] ?? $row;
|
||
|
|
if (is_array($data) && isset($data['user_id'])) {
|
||
|
|
$ids[] = (int) $data['user_id'];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return array_values(array_unique($ids));
|
||
|
|
}
|
||
|
|
}
|