Files
clinicpro/.claude/prompt/doctor-appointment-notification-sms.md
T

142 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# پیامک اعلان نوبت جدید به «شمارهٔ اعلان» دکتر (فقط نوبت‌های پرداخت‌شدهٔ سایت)
## پروژه
`clinicpro` (backend / Payment + Sms)
## زمینه
هر دکتر در `/admin/profile` یک «شمارهٔ اعلان نوبت» (`Doctor::notificationMobile`) ست می‌کند («پیامک نوبت جدید به این شماره ارسال می‌شود»). این فیلد و تگ `SmsLog::TAG_NOTIFICATION_MOBILE` وجود دارند، ولی تگ فعلی فقط برای **تأیید ثبت شماره** (کد تأیید در `NotificationMobileController`) استفاده می‌شود — به رویداد «نوبت جدید» وصل نیست.
الان هنگام موفقیت پرداخت نوبت، فقط به **بیمار** پیامک می‌رود (`PaymentManager::handleAppointmentConfirmation` با تگ `TAG_PAYMENT`). هدف: علاوه بر آن، به **شمارهٔ اعلانِ دکتر** هم یک پیامک با قالب مشخص (شامل نام بیمار و ساعت نوبت) ارسال شود.
**فقط نوبت‌های پرداخت‌شده از سایت** باید این پیامک را بدهند — نه نوبت‌هایی که منشی ثبت می‌کند. چون `handleAppointmentConfirmation` **فقط** در مسیر موفقیت پرداخت (`PaymentManager::processCallback``runPostAction`) اجرا می‌شود و نوبت‌های ثبت‌شده توسط منشی از این مسیر عبور نمی‌کنند، افزودن کد در همین‌جا به‌طور طبیعی شرط «فقط سایت/پرداخت» را رعایت می‌کند.
## مشکل / هدف
هنگام تأیید پرداختِ یک نوبت، اگر دکترِ آن نوبت `notificationMobile` ست کرده باشد، یک پیامک با تگ/قالب اختصاصی (`TAG_DOCTOR_APPOINTMENT`) شامل `{patient}` و `{date}` و `{time}` به آن شماره ارسال شود.
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `clinicpro/src/Sms/Entity/SmsLog.php` | افزودن تگ `TAG_DOCTOR_APPOINTMENT` + به آرایهٔ `TAGS` |
| `clinicpro/src/Sms/Entity/SmsMessageTemplate.php` | افزودن قالب پیش‌فرض این تگ به `DEFAULTS` |
| `clinicpro/src/Payment/Service/PaymentManager.php` | ارسال پیامک به `notificationMobile` دکتر در `handleAppointmentConfirmation` |
| `clinicpro/docs/api/sms.md` | مستندسازی تگ/قالب جدید |
## وضعیت فعلی
### `PaymentManager::handleAppointmentConfirmation` (فقط پیامک بیمار)
```php
private function handleAppointmentConfirmation(Payment $payment): void
{
$appointment = $payment->getAppointment();
if ($appointment === null || !$appointment->canTransitionTo(Appointment::STATUS_CONFIRMED)) {
return;
}
$appointment->transitionTo(Appointment::STATUS_CONFIRMED);
$this->em->persist($appointment);
$doctor = $appointment->getDoctor();
$this->commissionService->processAppointment(
$payment,
$doctor->getRepresentationId(),
$appointment->getBookingRepresentationId(),
$doctor->getId(),
);
$mobile = $appointment->getPatientMobile();
if ($mobile) {
$when = $this->jalali->formatDateTime($appointment->getSlotStart());
$message = $this->smsText->resolve(SmsLog::TAG_PAYMENT, [
'doctor' => $doctor->getName(),
'date' => $when,
]);
$this->smsService->dispatchAsync($mobile, $message, tag: SmsLog::TAG_PAYMENT);
}
}
```
### مقادیر در دسترس
- `Doctor::getNotificationMobile(): ?string` (فیلد `notification_mobile`, nullable)
- `Doctor::getName()`
- `Appointment::getPatientName(): ?string`، `getPatientMobile()`، `getSlotStart(): int` (unix)
- `JalaliDateService::formatDateTime(int $ts, bool $withTime = true): string``false` = فقط تاریخ شمسی. متد جدا برای «ساعت» ندارد؛ ساعت را با `date('H:i', $slotStart)` بگیر.
### الگوی تگ‌ها (SmsLog)
```php
public const TAG_NOTIFICATION_MOBILE = 'notification_mobile';
public const TAGS = [ self::TAG_GLOBAL, self::TAG_OTP, self::TAG_PAYMENT, /* ... */ self::TAG_SECRETARY ];
```
### الگوی قالب‌ها (SmsMessageTemplate::DEFAULTS)
```php
SmsLog::TAG_PAYMENT => [
'title' => 'تأیید پرداخت و نوبت',
'body' => 'نوبت شما با {doctor} در تاریخ {date} ثبت و تأیید شد.',
'variables' => ['doctor', 'date'],
],
```
## وظایف
### ۱. تگ جدید در `SmsLog`
```php
public const TAG_DOCTOR_APPOINTMENT = 'doctor_appointment';
```
و آن را به آرایهٔ `TAGS` اضافه کن (تا در پنل مدیریت قالب‌ها و seed شناخته شود).
### ۲. قالب پیش‌فرض در `SmsMessageTemplate::DEFAULTS`
```php
SmsLog::TAG_DOCTOR_APPOINTMENT => [
'title' => 'نوبت جدید (اعلان به پزشک)',
'body' => "نوبت جدید ثبت شد.\nبیمار: {patient}\nتاریخ: {date} ساعت {time}",
'variables' => ['patient', 'date', 'time'],
],
```
### ۳. ارسال پیامک به شمارهٔ اعلان دکتر در `handleAppointmentConfirmation`
بعد از بلوک پیامک بیمار (داخل همان متد)، اضافه کن:
```php
$notify = $doctor->getNotificationMobile();
if ($notify) {
$docMessage = $this->smsText->resolve(SmsLog::TAG_DOCTOR_APPOINTMENT, [
'patient' => $appointment->getPatientName() ?? '—',
'date' => $this->jalali->formatDateTime($appointment->getSlotStart(), false),
'time' => date('H:i', $appointment->getSlotStart()),
]);
$this->smsService->dispatchAsync($notify, $docMessage, tag: SmsLog::TAG_DOCTOR_APPOINTMENT);
}
```
### ۴. seed قالب جدید
بعد از افزودن به `DEFAULTS`، دستور موجود را اجرا کن تا رکورد قالب برای تگ جدید ساخته شود (فقط تگ‌های بدون رکورد را می‌سازد):
```bash
ddev exec php bin/console app:seed-sms-message-templates
```
### ۵. مستندسازی
`docs/api/sms.md`: تگ `doctor_appointment`، قالب پیش‌فرض، placeholderها (`patient`/`date`/`time`)، و اینکه فقط برای نوبت‌های **پرداخت‌شدهٔ سایت** (در مسیر verify پرداخت) ارسال می‌شود و به `Doctor.notificationMobile` می‌رود.
## نکات مهم
- **فقط مسیر پرداخت:** کد داخل `handleAppointmentConfirmation` است که تنها از `runPostAction` بعد از verify موفق صدا زده می‌شود؛ نوبت‌های منشی (بدون پرداخت) این‌جا نمی‌آیند — پس شرط «نه نوبت منشی» خودکار برقرار است. کد را جای دیگری (مثل ساخت نوبت) نگذار.
- اگر `notificationMobile` خالی بود، هیچ پیامکی نرود (شرط `if ($notify)`).
- `dispatchAsync` مثل پیامک بیمار استفاده شود (صف async، شکست پیامک نباید جریان پرداخت را بشکند — الگوی موجود).
- ارسال پیامک دکتر **مستقل از** پیامک بیمار است (حتی اگر `patientMobile` خالی باشد، پیامک دکتر باید برود).
- `title`/`body` قالب فارسی و قابل ویرایش از پنل قالب‌هاست؛ فقط مقدار پیش‌فرض را در `DEFAULTS` بگذار.
- `Doctor` تغییر Entity ندارد (فیلد موجود است) → migration لازم نیست.
- هزینهٔ پیامک: این پیامک هم مثل بقیه از کیف‌پول/سهمیهٔ پیامک همان entity کسر می‌شود (اگر `SmsService`/wallet چنین منطقی دارد، خودکار اعمال می‌شود؛ چیزی اضافه نکن مگر لازم شود).