# نکات پیاده‌سازی — تسک ۱۷: ماژول پیامک ## API Endpoints ### GET /api/v1/sms/balance ```json // Response 200: { "owner_id": 29, "owner_type": "doctor", "balance": 850 } // Error 404: حساب پیامک وجود ندارد // Error 401: احراز هویت لازم است ``` ### POST /api/v1/sms/queue ```json // Request: { "recipient_mobile": "09121234567", "message": "یادآوری: نوبت شما فردا ساعت ۱۰:۰۰ است", "scheduled_at": "2025-06-15T09:00:00+03:30" } // Response 201: { "id": 1, "status": "queued", "scheduled_at": 1749970200, "remaining_balance": 849 } // Error 402: موجودی ناکافی // Error 400: شماره موبایل یا پیام نامعتبر ``` ## منطق کسر موجودی (atomic) ```php // در SmsService.php: $this->entityManager->beginTransaction(); $account = $this->smsAccountRepo->findByOwner($ownerType, $ownerId); if ($account->getBalance() <= 0) { throw new InsufficientBalanceException(); } $account->decrementBalance(); // add to queue $this->entityManager->commit(); ``` ## تشخیص owner از JWT token ```php // کاربر لاگین‌شده → چک کن دکتر است یا کلینیک $user = $this->getUser(); if ($user->hasRole('ROLE_DOCTOR')) { $doctor = $this->doctorRepo->findByUser($user); $ownerType = 'doctor'; $ownerId = $doctor->getId(); } elseif ($user->hasRole('ROLE_CLINIC')) { $clinic = $this->clinicRepo->findByUser($user); $ownerType = 'clinic'; $ownerId = $clinic->getId(); } ``` ## نکته: ارسال واقعی پیامک پیامک‌ها توسط یک Job/Command ارسال می‌شوند (نه در همان request): ``` php bin/console sms:send-queued ``` این command پیامک‌هایی با `status=queued` و `scheduled_at <= now` را ارسال می‌کند.