Root cause of "دکتر دکتر …" (and ellipsis-truncated "…نی") in admin: IRIMC
names already contain the «دکتر» title, while the panel renders «دکتر {name}».
Convention is to store the bare name.
- DoctorImportService: normalize name via PersianText::stripDoctorTitle
(also fixes ي/ی, ك/ک, half-space)
- PersianText::stripDoctorTitle now strips consecutive «دکتر دکتر …» prefixes
- app:doctors:fix-irimc-names: one-off backfill for existing source='irimc'
rows (dry-run supported) — fixed 340 rows
- app:doctors:purge: FK-safe full wipe of doctors + all dependent tables +
orphan surrogate users, for a clean test DB (dry-run default, --force to
apply, prod-guarded)
- tests: PersianTextTest cases for the title stripping; DoctorImportTest
asserts stored name has no «دکتر» prefix
- docs/api/doctor-import.md: name convention + the two new commands
Verified: import "دکتر صفورا حجازی نیا" → stored "صفورا حجازی نیا" → panel
shows single «دکتر صفورا حجازی نیا».
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.9 KiB
رفع نام دوتایی «دکتر» در ایمپورت IRIMC + دستور پاکسازی کامل پزشکان
پروژه
clinicpro (backend + پنل ادمین)
زمینه
پس از ایمپورت پزشکان نظام پزشکی، نام در پنل ادمین اشتباه نمایش داده میشود: بهجای «دکتر صفورا حجازی نیا»، «دکتر دکتر صفورا حجازی نیا» و در کارت grid بهخاطر ellipsis بریده و «دکتر دکتر صفورا حجازی نی» دیده میشود.
ریشه (تأییدشده): نامِ خامِ نظام پزشکی خودش پیشوند «دکتر» دارد (دکتر صفورا حجازی نیا
در doctors.json و در DB، ۲۰ کاراکتر — داده درست ذخیره شده). اما کنوانسیون پنل این است
که نام بدون پیشوند ذخیره شود و خودِ UI «دکتر» را جلو میگذارد:
// assets/admin/pages/DoctorsPage.tsx:326 (جدول) و :412-413 (کارت grid با ellipsis)
<b>دکتر {doc.name}</b>
پس وقتی 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() نام را همانطور که آمده ذخیره میکند:
$name = trim((string) $data['name']);
// ...
$doctor->setName($name); // نام هنوز شامل «دکتر …» است
PersianText::stripDoctorTitle() از قبل هست و دقیقاً همین کار را میکند:
public static function stripDoctorTitle(string $name): string
{
return trim(preg_replace('/^\s*دکتر\s+/u', '', self::normalize($name)) ?? $name);
}
وظایف
۱. حذف پیشوند «دکتر» هنگام ایمپورت
در DoctorImportService::doImport()، نام را قبل از ذخیره normalize کن:
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:
// 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:
$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) و تطبیق نام همچنان درست است — رگرسیون نده. - تست:
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 کند نام ذخیرهشده پیشوند «دکتر» ندارد.