isDraft()) { return 0; } $total = 0; $this->recipients->query($campaign->filters ?? []) ->select(['id', 'email']) ->chunkById(500, function ($users) use ($campaign, &$total): void { $rows = $users->map(fn (User $user): array => [ 'newsletter_campaign_id' => $campaign->id, 'user_id' => $user->id, 'email' => $user->email, 'status' => NewsletterRecipientStatus::Pending->value, 'created_at' => now(), 'updated_at' => now(), ])->all(); $campaign->recipients()->insert($rows); $total += count($rows); }); if ($total === 0) { return 0; } $campaign->update([ 'status' => NewsletterCampaignStatus::Sending->value, 'recipients_total' => $total, ]); $batchSize = max(1, (int) config('newsletter.campaign.batch_size')); $intervalSeconds = max(1, (int) config('newsletter.campaign.batch_interval_minutes')) * 60; $position = 0; $campaign->recipients() ->toBase() ->select('id') ->chunkById(500, function ($rows) use ($batchSize, $intervalSeconds, &$position): void { foreach ($rows as $row) { SendNewsletterCampaignMail::dispatch((int) $row->id) ->delay(intdiv($position, $batchSize) * $intervalSeconds); $position++; } }); return $total; } /** * Drossel-Hinweis für die Admin-UI, z. B. „max. 50 Mails alle 15 Minuten — * Gesamtdauer ca. 3,5 Stunden“. Null, wenn alles in die erste Welle passt. */ public static function throttleSummary(int $total): ?string { $batchSize = max(1, (int) config('newsletter.campaign.batch_size')); $intervalMinutes = max(1, (int) config('newsletter.campaign.batch_interval_minutes')); if ($total <= $batchSize) { return null; } $durationMinutes = ((int) ceil($total / $batchSize) - 1) * $intervalMinutes; $duration = $durationMinutes < 60 ? sprintf('%d Minuten', $durationMinutes) : sprintf('%s Stunden', number_format($durationMinutes / 60, 1, ',', '.')); return sprintf('max. %d Mails alle %d Minuten — Gesamtdauer ca. %s', $batchSize, $intervalMinutes, $duration); } }