# ارسال پیامک خوش‌آمد هنگام تعریف منشی جدید ## پروژه `clinicpro` (backend — Symfony) ## زمینه هنگام تعریف منشی جدید از طریق `POST /api/v1/secretary`، یک User با نقش `ROLE_SECRETARY` ساخته (یا کاربر موجود بازاستفاده) می‌شود، اما **هیچ پیامکی به منشی ارسال نمی‌شود**. منشی هیچ اطلاعی از شماره‌کاربری، رمز و لینک ورود ندارد. الگوی مشابه در پروژه از قبل وجود دارد: هنگام تأیید پیش‌ثبت‌نام (`PreRegistrationController` با تگ `TAG_PRE_REGISTRATION`) و دعوت پزشک به کلینیک (`ClinicInvitationService::sendSms` با تگ `TAG_CLINIC_INVITATION`). این تسک همان الگو را برای منشی پیاده می‌کند. ## هدف بعد از ساخت موفق منشی جدید، یک پیامک خوش‌آمد به شماره منشی ارسال شود. **متن پیامک باید از سیستم template پیامک بیاید** (تگ اختصاصی + `SmsMessageTemplate::DEFAULTS` به‌عنوان fallback، قابل ویرایش از DB)، نه hard-code داخل کنترلر. ## فایل‌های مرتبط | فایل | نقش | |------|-----| | `src/Secretary/Controller/SecretaryController.php` | endpoint ساخت منشی — محل تزریق ارسال پیامک | | `src/Sms/Entity/SmsLog.php` | ثابت‌های تگ (`TAG_*`) — افزودن تگ جدید | | `src/Sms/Entity/SmsMessageTemplate.php` | آرایه `DEFAULTS` — افزودن متن پیش‌فرض template | | `src/Sms/Service/SmsService.php` | `dispatchAsync(...)` — ارسال async پیامک | | `src/Sms/Service/SmsTextResolver.php` | `resolve(tag, vars)` — resolve متن template و جایگزینی `{key}` | | `src/ClinicInvitation/Service/ClinicInvitationService.php` | **الگوی مرجع** ارسال پیامک با template | | `docs/api/secretary.md` + `docs/api/sms.md` | مستندات — باید به‌روز شوند | ## وضعیت فعلی در `SecretaryController::create()` بعد از ساخت user، فقط ذخیره می‌شود و هیچ پیامکی نمی‌رود: ```php // Assign ROLE_SECRETARY $roles = $secretaryUser->getRoles(); if (!in_array('ROLE_SECRETARY', $roles, true)) { $roles[] = 'ROLE_SECRETARY'; $secretaryUser->setRoles(array_values(array_unique($roles))); } $this->userRepo->save($secretaryUser); // ... check duplicate ... $secretary = new DoctorSecretary($doctor, $secretaryUser, $ownerType, $ownerClinic); // ... $this->secretaryRepo->save($secretary); return $this->success(['data' => $secretary->toArray()], 201); ``` الگوی مرجع ارسال پیامک با template (از `ClinicInvitationService`): ```php $message = $this->smsText->resolve(\App\Sms\Entity\SmsLog::TAG_CLINIC_INVITATION, [ 'clinic' => $clinicName, 'link' => $link, ]); $this->smsService->dispatchAsync($inv->getMobile(), $message, tag: \App\Sms\Entity\SmsLog::TAG_CLINIC_INVITATION); ``` ساختار `DEFAULTS` در `SmsMessageTemplate` (هر تگ: `title`, `body` با placeholderهای `{key}`, `variables[]`): ```php SmsLog::TAG_PRE_REGISTRATION => [ 'title' => 'پیش‌ثبت‌نام', 'body' => 'به کلینیک پرو خوش آمدید! شماره‌کاربری: {username} | رمز عبور: {password} | لینک ورود: {link}', 'variables' => ['username', 'password', 'link'], ], ``` ## وظایف ### ۱. افزودن تگ جدید در `SmsLog` در `src/Sms/Entity/SmsLog.php` یک ثابت تگ جدید اضافه کن (هم‌سبک بقیه تگ‌ها): ```php public const TAG_SECRETARY = 'secretary'; ``` > نکته: investigator گزارش داد تگی به نام `TAG_WELCOME` هم موجود است — قبل از افزودن، فایل را بخوان؛ اگر تگ عمومی خوش‌آمد مناسب بود از همان استفاده کن، در غیر این صورت `TAG_SECRETARY` را اضافه کن. یک تگ اختصاصی «منشی» بهتر است چون متن template مستقل و قابل ویرایش می‌شود. ### ۲. افزودن template پیش‌فرض در `SmsMessageTemplate::DEFAULTS` یک entry جدید برای تگ منشی اضافه کن. متن باید شامل نام کلینیک/دکتر و اطلاعات ورود باشد: ```php SmsLog::TAG_SECRETARY => [ 'title' => 'دعوت به‌عنوان منشی', 'body' => "شما به‌عنوان منشی {owner} در کلینیک‌پرو تعریف شدید.\nشماره‌کاربری: {username}\nلینک ورود: {link}", 'variables' => ['owner', 'username', 'link'], ], ``` - `{owner}` = نام دکتر یا نام کلینیک (بسته به `$ownerType`). - `{username}` = شماره موبایل منشی (`$mobile`). - `{link}` = لینک ورود پنل (از سرویس `$appUrl` مثل بقیه، rtrim + مسیر ورود). - اگر رمز عبور در body لازم شد، فقط زمانی که کاربر **جدید** ساخته شده و `password` در ورودی آمده — رمز خام را نگه‌دار و در vars بگذار (متغیر محلی، نه از دیتابیس؛ hash قابل بازگشت نیست). ### ۳. تزریق سرویس‌های SMS در کنترلر در constructor `SecretaryController` این‌ها را اضافه کن (هم‌سبک `ClinicInvitationService`): ```php private readonly \App\Sms\Service\SmsService $smsService, private readonly \App\Sms\Service\SmsTextResolver $smsText, private readonly string $appUrl, // همان param که ClinicInvitationService می‌گیرد ``` > `$appUrl` را از همان جایی bind کن که `ClinicInvitationService` می‌گیرد — `config/services.yaml` را بررسی کن و همان `$appUrl` را برای `SecretaryController` هم bind کن (یا اگر global bind است، خودکار تزریق می‌شود). ### ۴. ارسال پیامک بعد از ساخت موفق منشی در `create()`، **بعد از** `$this->secretaryRepo->save($secretary);` و **قبل از** `return`، پیامک را resolve و dispatch کن: ```php $ownerName = $ownerClinic !== null ? ($ownerClinic->getName() ?? 'کلینیک') : ($doctor->getUser()->getRealName() ?? 'پزشک'); $link = rtrim($this->appUrl, '/') . '/login'; $message = $this->smsText->resolve(\App\Sms\Entity\SmsLog::TAG_SECRETARY, [ 'owner' => $ownerName, 'username' => $mobile, 'link' => $link, ]); $this->smsService->dispatchAsync($mobile, $message, tag: \App\Sms\Entity\SmsLog::TAG_SECRETARY); ``` ## نکات مهم - **ارسال async است** — `dispatchAsync` فقط در صف Messenger می‌گذارد؛ ارسال واقعی توسط worker (`messenger:consume async`) انجام می‌شود. برای تست local باید worker در حال اجرا باشد. - **متن hard-code نکن** — همیشه از `SmsTextResolver::resolve(tag, vars)` استفاده کن تا ادمین بتواند از DB متن را ویرایش کند. `DEFAULTS` فقط fallback است. - **placeholderها دقیقاً `{key}`** باشند و کلیدهای `variables[]` با کلیدهای آرایه `vars` که به `resolve` می‌دهی یکی باشند. - **duplicate/خطا** — اگر منشی تکراری بود (خط ۱۰۱–۱۰۳) یا هر خطای زودهنگام، `return` قبل از رسیدن به کد ارسال رخ می‌دهد؛ پس پیامک فقط روی مسیر موفق ساخت می‌رود. درست است — همین رفتار مطلوب است. - **کاربر موجود بازاستفاده‌شده**: تصمیم بگیر آیا برای کاربری که از قبل وجود داشت (`findByMobile` غیر null) هم پیامک برود یا فقط برای کاربر تازه‌ساخته. پیشنهاد: چون این یک «دعوت به نقش منشی» است، برای هر دو حالت (چون رابطه منشی جدید ساخته می‌شود) پیامک منطقی است؛ ولی اگر رمز در body هست، رمز را فقط برای کاربر جدید بگذار. - **مستندات (Standing Rule)**: چون `src/Secretary/*` و `src/Sms/*` تغییر می‌کنند، در همین session هم `docs/api/secretary.md` (ذکر ارسال پیامک هنگام create) و هم `docs/api/sms.md` (تگ جدید + template پیش‌فرض) را به‌روز کن. - بعد از تغییر کد: `ddev exec php bin/console cache:clear`. اگر Entity تغییری در schema نداشت (فقط ثابت/آرایه `DEFAULTS`)، migration لازم نیست — ولی اگر template را seed می‌کنی، از همان مسیر seed موجود پروژه استفاده کن.