where('banned', false)->whereNotNull('plan_id') ->where('expired_at', '>', $state['since'])->where('expired_at', '<=', now()->timestamp) ->chunkById(500, function ($users) use (&$queued) { foreach ($users as $user) { if (!filter_var($user->email, FILTER_VALIDATE_EMAIL)) { continue; } $key = 'subscription-expired:' . $user->id . ':' . $user->expired_at; $params = ['message_key' => $key, 'email' => $user->email, 'user_id' => $user->id, 'purpose' => 'service_expired', 'subscription_expired_at' => (int) $user->expired_at, 'template_name' => 'serviceExpired', 'subject' => admin_setting('app_name', 'XBoard') . ' - 套餐已到期', 'template_value' => ['name' => admin_setting('app_name', 'XBoard'), 'url' => admin_setting('app_url')]]; if (AutomaticMailPolicy::decision('service_expired', $params)) { continue; } // Persist before dispatch. The same user can renew and receive a later distinct expiry event. $message = MailMessage::firstOrCreate(['message_key' => $key], [ 'purpose' => 'service_expired', 'user_id' => $user->id, 'email' => $user->email, 'subject' => $params['subject'], 'template_name' => 'serviceExpired', 'expires_at' => now()->addDay(), 'payload' => ['email_params' => $params], ]); if (!$message->wasRecentlyCreated && ($message->status !== 'pending' || !$message->payload || $message->updated_at->gt(now()->subMinutes(2)))) { continue; } if ($message->attempts >= 6) { $message->update(['status' => 'failed', 'error_code' => 'mail_retry_exhausted', 'payload' => null]); continue; } $message->touch(); SendMailMessageJob::dispatch($message->id); $queued++; } }); return $queued; } }