# رفع نام دوتایی «دکتر» در ایمپورت IRIMC + دستور پاک‌سازی کامل پزشکان ## پروژه `clinicpro` (backend + پنل ادمین) ## زمینه پس از ایمپورت پزشکان نظام پزشکی، نام در پنل ادمین اشتباه نمایش داده می‌شود: به‌جای «دکتر صفورا حجازی نیا»، «دکتر دکتر صفورا حجازی نیا» و در کارت grid به‌خاطر ellipsis بریده و «دکتر دکتر صفورا حجازی نی» دیده می‌شود. **ریشه (تأییدشده):** نامِ خامِ نظام پزشکی خودش پیشوند «دکتر» دارد (`دکتر صفورا حجازی نیا` در `doctors.json` و در DB، ۲۰ کاراکتر — داده درست ذخیره شده). اما کنوانسیون پنل این است که نام **بدون** پیشوند ذخیره شود و خودِ UI «دکتر» را جلو می‌گذارد: ```tsx // assets/admin/pages/DoctorsPage.tsx:326 (جدول) و :412-413 (کارت grid با ellipsis) دکتر {doc.name} ``` پس وقتی `doc.name = "دکتر صفورا حجازی نیا"` باشد، خروجی «دکتر دکتر …» می‌شود و در کارت (`whiteSpace:nowrap; overflow:hidden; textOverflow:ellipsis`) طولانی‌تر شده و «…نیا» بریده می‌شود. یک ریشه، هر دو نشانه. علاوه بر این، کاربر می‌خواهد **همهٔ پزشکان و داده‌های وابسته به پزشک** پاک شوند تا یک دیتابیس تمیز برای تست داشته باشیم (این کار با FK حذف مستقیم شکست می‌خورد — قبلاً خطای `FK_4384ADBC87F4FB17` روی `doctor_provinces` دیدیم). ## مشکل / هدف ۱. ایمپورت IRIMC پیشوند «دکتر/دكتر» را از نام حذف کند تا با کنوانسیون پنل یکدست شود. ۲. ۳۴۰ رکورد IRIMC موجود (که با پیشوند ذخیره شده‌اند) اصلاح شوند. ۳. یک دستور کنسول امن برای پاک‌سازی کامل پزشکان + همهٔ داده‌های وابسته (FK-safe). ## فایل‌های مرتبط | فایل | نقش | |------|-----| | `src/Doctor/Service/DoctorImportService.php` | ساخت/به‌روزرسانی پزشک؛ اینجا نام normalize شود | | `src/Shared/Util/PersianText.php` | `stripDoctorTitle()` موجود — «دکتر» ابتدای نام را حذف می‌کند | | `assets/admin/pages/DoctorsPage.tsx` | خط ۳۲۶ و ۴۱۲-۴۱۳ — نمایش `دکتر {doc.name}` (تغییر لازم ندارد، فقط داده اصلاح شود) | | جداول FK به `doctors` (۱۶ عدد) | `weekly_schedules, date_overrides, comments, clinic_doctor_invitations, holidays, doctor_specialties, appointments, doctor_cities, doctor_expertise, rates, doctor_provinces, doctor_insurances, clinic_doctors, doctor_claim_requests, doctor_addresses, doctor_secretaries` | ## وضعیت فعلی `DoctorImportService::doImport()` نام را همان‌طور که آمده ذخیره می‌کند: ```php $name = trim((string) $data['name']); // ... $doctor->setName($name); // نام هنوز شامل «دکتر …» است ``` `PersianText::stripDoctorTitle()` از قبل هست و دقیقاً همین کار را می‌کند: ```php public static function stripDoctorTitle(string $name): string { return trim(preg_replace('/^\s*دکتر\s+/u', '', self::normalize($name)) ?? $name); } ``` ## وظایف ### ۱. حذف پیشوند «دکتر» هنگام ایمپورت در `DoctorImportService::doImport()`، نام را قبل از ذخیره normalize کن: ```php use App\Shared\Util\PersianText; $name = PersianText::stripDoctorTitle((string) ($data['name'] ?? '')); if ($name === '') { /* همان اعتبارسنجی موجود در کنترلر — کنترلر نام خام را چک می‌کند */ } ``` - توجه: کنترلر (`DoctorImportController`) نام خام را برای اعتبارسنجی `!== ''` چک می‌کند؛ strip فقط داخل سرویس برای مقدار ذخیره‌شده انجام شود تا اعتبارسنجی نشکند. - `stripDoctorTitle` علاوه بر حذف پیشوند، `normalize` هم می‌کند (ي→ی، ك→ک، نیم‌فاصله) که برای یکدستی نام مفید است. - **edge:** نام‌هایی که «دکتر» ندارند بدون تغییر می‌مانند؛ نام‌های دو-پیشوندی نادر («دکتر دکتر …») هم چون preg فقط یک بار از ابتدا حذف می‌کند، در صورت وجود باید بررسی شود (regex را در صورت نیاز به `^(?:\s*دکتر\s+)+` تغییر بده تا همهٔ پیشوندهای متوالی برود). ### ۲. اصلاح رکوردهای IRIMC موجود یک دستور یک‌بارمصرف (هم‌سبک `BackfillSurrogateRoleCommand`) به نام `app:doctors:fix-irimc-names`: ```php // SELECT پزشکان source='irimc' که name با 'دکتر ' شروع می‌شود؛ // name = stripDoctorTitle(name)؛ با --dry-run فقط گزارش. ``` - فقط `source='irimc'` را دست بزن (پزشکان manual/seed را تغییر نده). - `--dry-run` داشته باشد؛ در خروجی تعداد اصلاح‌شده را بده. - در همان دستور، اگر `name` کاربرِ جانشین (`realName`) هم پیشوند دارد اختیاری است؛ اولویت با `doctors.name`. ### ۳. دستور پاک‌سازی کامل پزشکان (دیتابیس تمیز تست) دستور `app:doctors:purge` در `src/Doctor/Command/PurgeDoctorsCommand.php`: - **حفاظت:** فقط با `--force` اجرا شود؛ بدون آن فقط تعداد رکوردهای هر جدول را گزارش کند (dry-run پیش‌فرض). چون مخرب است، پیام تأیید واضح بدهد. - ترتیب FK-safe: داخل یک تراکنش، اول جداول فرزند سپس `doctors`، سپس کاربران جانشین. ساده‌ترین و مطمئن‌ترین راه در MariaDB: ```php $conn = $this->em->getConnection(); $conn->executeStatement('SET FOREIGN_KEY_CHECKS=0'); foreach ([ 'doctor_claim_requests','doctor_secretaries','doctor_addresses','doctor_insurances', 'doctor_provinces','doctor_cities','doctor_specialties','doctor_expertise', 'clinic_doctors','clinic_doctor_invitations','weekly_schedules','date_overrides', 'holidays','comments','rates','appointments','doctors', ] as $t) { $n = $conn->executeStatement("DELETE FROM {$t}"); // یا TRUNCATE پس از خالی‌شدن FK $io->text("{$t}: {$n}"); } $conn->executeStatement('SET FOREIGN_KEY_CHECKS=1'); ``` - **کاربران جانشین:** پس از حذف پزشکان، کاربرانِ ایمپورت را هم پاک کن (وگرنه یتیم می‌مانند): `DELETE FROM users WHERE mobile_number LIKE 'imp\\_%' AND status=0`. - **هشدار داده‌های مشترک:** `appointments`, `comments`, `rates` به بیمار/پرداخت هم وصل‌اند؛ چون این دیتابیس فقط برای تستِ ایمپورت است حذف کامل قابل‌قبول است، ولی در دستور صریح هشدار بده که این عمل روی prod اجرا نشود (بررسی `APP_ENV !== 'prod'` یا نیاز به فلگ اضافهٔ `--i-know` برای prod). - بعد از اجرا: `SET FOREIGN_KEY_CHECKS=1` حتی در صورت خطا (finally) اجرا شود. ## نکات مهم - بعد از وظیفهٔ ۱، فقط ایمپورت‌های جدید نام تمیز می‌گیرند؛ وظیفهٔ ۲ برای ۳۴۰ رکورد فعلی لازم است. - تغییری در `DoctorsPage.tsx` لازم نیست — با نام تمیز، `دکتر {doc.name}` درست رندر می‌شود و کارت grid دیگر بریده نمی‌شود. - جریان claim (`DoctorClaimService::verifyIdentity`) از `stripDoctorTitle` روی `doctor.getName()` استفاده می‌کند؛ با نام تمیزِ ذخیره‌شده، این strip بی‌اثر (no-op) و تطبیق نام همچنان درست است — رگرسیون نده. - تست: ```bash ddev exec php bin/console app:doctors:fix-irimc-names --dry-run ddev exec php bin/console app:doctors:purge # dry-run ddev exec php bin/console app:doctors:purge --force # پاک‌سازی # سپس یک ایمپورت تست و بررسی نام در /admin/doctors (باید «دکتر صفورا حجازی نیا» تک‌پیشوند باشد) ``` - بعد از تغییر سرویس/کنترلر ایمپورت، `docs/api/doctor-import.md` را با «نام بدون پیشوند دکتر ذخیره می‌شود» به‌روز کن. - تست integration موجود `DoctorImportTest` را به‌روز کن: assert کند نام ذخیره‌شده پیشوند «دکتر» ندارد.