0 ? $result : false; } public function listByUser(int $userId, int $limit, int $offset): array { if ($userId <= 0) { return []; } $limit = max(1, min($limit, 100)); $offset = max(0, $offset); $rows = DB::select( 'select id, type, title, body, link, is_read, created from user_notifications where recipient_user_id = ? order by created desc limit ? offset ?', (string) $userId, (string) $limit, (string) $offset ); return $this->unwrapList($rows); } public function countUnreadByUser(int $userId): int { if ($userId <= 0) { return 0; } $count = DB::selectValue( 'select count(*) from user_notifications where recipient_user_id = ? and is_read = 0', (string) $userId ); return (int) $count; } public function markRead(int $id, int $userId): bool { if ($id <= 0 || $userId <= 0) { return false; } $affected = DB::update( 'update user_notifications set is_read = 1, read_at = NOW() where id = ? and recipient_user_id = ? and is_read = 0', (string) $id, (string) $userId ); return is_int($affected) && $affected > 0; } public function markAllReadByUser(int $userId): int { if ($userId <= 0) { return 0; } $affected = DB::update( 'update user_notifications set is_read = 1, read_at = NOW() where recipient_user_id = ? and is_read = 0', (string) $userId ); return is_int($affected) ? $affected : 0; } public function delete(int $id, int $userId): bool { if ($id <= 0 || $userId <= 0) { return false; } $affected = DB::delete( 'delete from user_notifications where id = ? and recipient_user_id = ?', (string) $id, (string) $userId ); return is_int($affected) && $affected > 0; } public function deleteAllByUser(int $userId): int { if ($userId <= 0) { return 0; } $affected = DB::delete( 'delete from user_notifications where recipient_user_id = ?', (string) $userId ); return is_int($affected) ? $affected : 0; } public function purgeReadOlderThanDays(int $days): int { if ($days <= 0) { return 0; } $affected = DB::delete( 'delete from user_notifications where is_read = 1 and read_at < DATE_SUB(NOW(), INTERVAL ? DAY)', (string) $days ); return is_int($affected) ? $affected : 0; } }