Refactor SMS campaign handling to dispatch a job for starting campaigns and update notification messages for improved user feedback.

This commit is contained in:
Mekan1206
2026-06-04 19:03:15 +05:00
parent 8e66883587
commit 21feb3baa6
4 changed files with 112 additions and 49 deletions

View File

@@ -2,8 +2,8 @@
namespace App\Filament\Pages;
use App\Jobs\StartSmsCampaignJob;
use App\Models\Coupon;
use App\Models\SmsCampaign;
use App\Services\SmsBroadcastService;
use App\Services\SmsMessageAnalyzer;
use BackedEnum;
@@ -170,53 +170,20 @@ class SendSms extends Page
return;
}
$campaign = $service->startCampaign(
admin: auth()->user(),
StartSmsCampaignJob::dispatch(
adminId: auth()->id(),
message: $data['message'],
sendToAll: $sendToAll,
selectedIds: $selectedIds,
excludedIds: $excludedIds,
);
if ($campaign->isComplete()) {
$this->notifyCampaignFinished($campaign);
} else {
Notification::make()
->title(__('filament.send_sms.notifications.queued'))
->body(__('filament.send_sms.notifications.queued_body', ['count' => $campaign->recipient_count]))
->title(__('filament.send_sms.notifications.submitted'))
->body(__('filament.send_sms.notifications.submitted_body', ['count' => $willReceive]))
->success()
->send();
}
}
protected function notifyCampaignFinished(SmsCampaign $campaign): void
{
$campaign->refresh();
if ($campaign->failed_count > 0 && $campaign->sent_count === 0) {
Notification::make()
->title(__('filament.send_sms.notifications.failed'))
->body(__('filament.send_sms.notifications.failed_body', ['count' => $campaign->failed_count]))
->danger()
->send();
} elseif ($campaign->failed_count > 0) {
Notification::make()
->title(__('filament.send_sms.notifications.finished_with_errors'))
->body(__('filament.send_sms.notifications.finished_with_errors_body', [
'sent' => $campaign->sent_count,
'failed' => $campaign->failed_count,
'skipped' => $campaign->skipped_count,
]))
->warning()
->send();
} else {
Notification::make()
->title(__('filament.send_sms.notifications.completed'))
->body(__('filament.send_sms.notifications.completed_body', ['count' => $campaign->sent_count]))
->success()
->send();
}
}
protected function confirmationDescription(): string
{

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Jobs;
use App\Models\User;
use App\Services\SmsBroadcastService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class StartSmsCampaignJob implements ShouldQueue
{
use Queueable;
/**
* @param array<int, int|string> $selectedIds
* @param array<int, int|string> $excludedIds
*/
public function __construct(
public int $adminId,
public string $message,
public bool $sendToAll,
public array $selectedIds = [],
public array $excludedIds = [],
) {}
public function handle(SmsBroadcastService $service): void
{
$admin = User::query()->findOrFail($this->adminId);
$service->startCampaign(
admin: $admin,
message: $this->message,
sendToAll: $this->sendToAll,
selectedIds: $this->selectedIds,
excludedIds: $this->excludedIds,
);
}
}

View File

@@ -38,14 +38,8 @@ return [
'notifications' => [
'no_valid_recipients' => 'Dogry alyjy ýok',
'no_valid_recipients_body' => 'Saýlawy ýa-da aýyrmalary üýtgediň — dogry telefon belgili kupon eýesi bu habary almaz.',
'queued' => 'SMS kampaniýasy nobata goýuldy',
'queued_body' => 'Arka fonda :count alyja iberilýär.',
'failed' => 'SMS kampaniýasy şowsuz',
'failed_body' => ':count habar iberilmedi.',
'finished_with_errors' => 'SMS kampaniýasy ýalňyşlyklar bilen tamamlandy',
'finished_with_errors_body' => 'Iberildi :sent, şowsuz :failed, geçildi :skipped.',
'completed' => 'SMS kampaniýasy tamamlandy',
'completed_body' => ':count habar üstünlikli iberildi.',
'submitted' => 'SMS kampaniýasy kabul edildi',
'submitted_body' => 'Arka fonda :count alyja iberilýär.',
],
'confirmation' => ':count alyja ibermeli? :summary',
],

View File

@@ -0,0 +1,64 @@
<?php
namespace Tests\Feature;
use App\Jobs\SendSmsToCouponJob;
use App\Jobs\StartSmsCampaignJob;
use App\Models\Coupon;
use App\Models\User;
use App\Services\SmsBroadcastService;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class StartSmsCampaignJobTest extends TestCase
{
use LazilyRefreshDatabase;
public function test_job_is_dispatched_with_expected_payload(): void
{
Queue::fake();
$admin = User::factory()->create();
$coupon = Coupon::factory()->create();
StartSmsCampaignJob::dispatch(
adminId: $admin->id,
message: 'Hello',
sendToAll: false,
selectedIds: [$coupon->id],
excludedIds: [],
);
Queue::assertPushed(StartSmsCampaignJob::class, function (StartSmsCampaignJob $job) use ($admin, $coupon): bool {
return $job->adminId === $admin->id
&& $job->message === 'Hello'
&& $job->sendToAll === false
&& $job->selectedIds === [$coupon->id]
&& $job->excludedIds === [];
});
}
public function test_handle_starts_campaign_and_chains_send_jobs(): void
{
Bus::fake();
$admin = User::factory()->create();
$coupons = Coupon::factory()->count(2)->create();
$job = new StartSmsCampaignJob(
adminId: $admin->id,
message: 'Test broadcast',
sendToAll: false,
selectedIds: $coupons->pluck('id')->all(),
);
$job->handle(app(SmsBroadcastService::class));
Bus::assertChained([
SendSmsToCouponJob::class,
SendSmsToCouponJob::class,
]);
}
}