feat: implement realistic data seeding for doctors, clinics, and secretaries
- Added seed_realistic_data.php to clean existing data and populate the database with realistic entries for doctors, clinics, and secretaries. - Created a structured approach to generate 100 doctors per city with diverse specialties and services. - Implemented database cleanup routines to ensure a fresh start for data seeding. - Enhanced the DoctorSecretaryRepository with improved comments for clarity.
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
# Seed واقعی دیتا — پاکسازی و ایجاد ۱۰۰ دکتر به ازای هر شهر
|
||||
|
||||
## زمینه
|
||||
|
||||
پروژه ClinicPro نیاز به دیتای واقعی و کامل برای تست و نمایش دارد. در حال حاضر دیتا ناقص یا تستنشده است. باید دیتابیس از دکتر، کلینیک، منشی و بیمار پاک شود، سپس دیتای واقعی با پروفایل کامل ایجاد شود.
|
||||
|
||||
## هدف
|
||||
|
||||
۱. پاکسازی کامل: users، doctors، clinics، patient_records، doctor_secretaries
|
||||
۲. ایجاد یک **کلینیک نمونه کامل** با همه فیلدها
|
||||
۳. ایجاد یک **دکتر نمونه کامل** با برنامه کاری و منشی
|
||||
۴. ایجاد **۱۰۰ دکتر به ازای هر شهر** در تخصصهای مختلف
|
||||
|
||||
## ساختار دیتابیس (جداول واقعی)
|
||||
|
||||
```
|
||||
users → id, uuid, mobile_number, password_hash, real_name, roles JSON, status
|
||||
doctors → id, uuid, user_id, name, gender, medical_system_code, mobile_number, activity_time, degree, info, doctor_rate, doctor_rate_percentage, active_doctor_appointment, representation_id
|
||||
clinics → id, uuid, user_id, name, info, address, telephone, is_24_7, working_days, latitude, longitude, city_id, province_id, representation_id, is_active
|
||||
doctor_addresses → id, uuid, doctor_id, clinic_id, city_id, province_id, type('personal'|'clinic'), name, address, telephone
|
||||
weekly_schedules → id, uuid, doctor_id, setting (JSON — آرایه ۷ روز)
|
||||
doctor_specialties → doctor_id, specialty_id
|
||||
doctor_expertise → doctor_id, service_id
|
||||
doctor_insurances → doctor_id, insurance_id
|
||||
doctor_cities → doctor_id, city_id
|
||||
doctor_provinces → doctor_id, province_id
|
||||
clinic_doctors → clinic_id, doctor_id
|
||||
clinic_specialties → clinic_id, specialty_id
|
||||
clinic_services → clinic_id, service_id
|
||||
clinic_insurances → clinic_id, insurance_id
|
||||
doctor_secretaries → id, uuid, doctor_id, secretary_id, active, owner_type, clinic_id
|
||||
patient_records → id, uuid, user_id, entity_type, entity_id, created_by_type, created_by_id
|
||||
```
|
||||
|
||||
## IDهای واقعی lookup tables
|
||||
|
||||
### شهرها (cities.id)
|
||||
```
|
||||
101=تبریز 102=ارومیه 103=اردبیل 104=اصفهان 105=کرج
|
||||
106=ایلام 107=بوشهر 108=تهران 109=شهرکرد 110=بیرجند
|
||||
111=مشهد 112=بجنورد 113=اهواز 114=زنجان 115=سمنان
|
||||
116=زاهدان 117=شیراز 118=قزوین 119=قم 120=سنندج
|
||||
121=کرمان 122=کرمانشاه 123=یاسوج 125=گرگان 126=رشت
|
||||
127=خرمآباد 128=ساری 129=اراک 130=بندرعباس 131=همدان 132=یزد
|
||||
```
|
||||
|
||||
### استانها (provinces.id)
|
||||
```
|
||||
1=آذربایجان شرقی 2=آذربایجان غربی 3=اردبیل 4=اصفهان 5=البرز
|
||||
6=ایلام 7=بوشهر 8=تهران 9=چهارمحال 10=خراسان جنوبی
|
||||
11=خراسان رضوی 12=خراسان شمالی 13=خوزستان 14=زنجان 15=سمنان
|
||||
16=سیستان 17=فارس 18=قزوین 19=قم 20=کردستان
|
||||
21=کرمان 22=کرمانشاه 23=کهگیلویه 24=گلستان 25=گیلان
|
||||
26=لرستان 27=مازندران 28=مرکزی 29=هرمزگان 30=همدان 100=یزد
|
||||
```
|
||||
نگاشت شهر → استان:
|
||||
`101→1, 102→2, 103→3, 104→4, 105→5, 106→6, 107→7, 108→8, 109→9, 110→10, 111→11, 112→12, 113→13, 114→14, 115→15, 116→16, 117→17, 118→18, 119→19, 120→20, 121→21, 122→22, 123→23, 125→24, 126→25, 127→26, 128→27, 129→28, 130→29, 131→30, 132→100`
|
||||
|
||||
### تخصصها (specialties.id) — دستهبندی برای توزیع واقعی
|
||||
```
|
||||
داخلی: 1,2,3,4,5,6,7,8,9,10,11,12
|
||||
جراحی: 20,21,22,23,24,25,26,27,28,29,30
|
||||
اطفال: 40,41,42,43,44,45
|
||||
زنان: 60,61,62,63,64,65
|
||||
روانپزشکی: 70,71,72,73,74
|
||||
پوست: 80,81,82,83,84
|
||||
چشم: 90,91,92,93,94,95,96,97,98
|
||||
ENT: 100,101,102,103,104
|
||||
ارتوپدی: 110,111,112,113,114,115,116
|
||||
اورولوژی: 120,121,122,123,124
|
||||
رادیولوژی: 130,131,132,133,134
|
||||
انکولوژی: 150,151,152
|
||||
طب فیزیکی: 170,171,172,173
|
||||
دندانپزشکی: 180,181,182,183,184,185,186,187,188,189
|
||||
اورژانس/عمومی: 190,200
|
||||
```
|
||||
|
||||
### خدمات پزشکی (doctor_services.id) — نمونههایی برای هر تخصص
|
||||
```
|
||||
قلب: 101,102,103,104,105,110
|
||||
گوارش: 201,202,203,210
|
||||
ریه: 301,302,303,307
|
||||
کلیه: 401,402,403,406
|
||||
غدد: 501,502,503,505,506
|
||||
روماتولوژی: 601,602,603,604
|
||||
خون: 701,702,703,704
|
||||
نورولوژی: 801,802,803,804,805
|
||||
آلرژی: 901,902,903,905
|
||||
عفونی: 1001,1002,1003
|
||||
جراحی عمومی: 2101,2102,2103,2104,2105,2113
|
||||
جراحی مغز: 2201,2202,2203,2207
|
||||
```
|
||||
|
||||
### بیمهها (insurances.id)
|
||||
```
|
||||
1=تأمین اجتماعی 2=خدمات درمانی 3=نیروهای مسلح 4=کمیته امداد
|
||||
5=بهزیستی 6=بیمه روستایی 7=ایران 8=آسیا 9=البرز
|
||||
10=پارسیان 11=سامان 12=میهن 13=دانا 14=رازی 15=کوثر
|
||||
```
|
||||
|
||||
## ساختار weekly_schedules.setting (JSON)
|
||||
|
||||
آرایه ۷ عنصر (شنبه تا جمعه). هر عنصر:
|
||||
```json
|
||||
{
|
||||
"sessions": [
|
||||
{
|
||||
"active": true,
|
||||
"location_id": <doctor_address.id>,
|
||||
"start_time": "09:00",
|
||||
"end_time": "13:00",
|
||||
"duration_per_patient": 20,
|
||||
"has_rest": true,
|
||||
"rest_interval": 60,
|
||||
"time_to_rest": 10,
|
||||
"patient_limit": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
روزهای بدون کار: `{"sessions": []}`
|
||||
|
||||
## وظایف
|
||||
|
||||
### ۱. ایجاد فایل seed_realistic_data.php
|
||||
|
||||
فایل را در ریشه پروژه (`/var/www/html/seed_realistic_data.php`) بساز. این فایل باید:
|
||||
|
||||
#### بخش A — پاکسازی (به ترتیب FK)
|
||||
|
||||
```php
|
||||
// حذف به ترتیب وابستگی FK
|
||||
$conn->executeStatement('SET FOREIGN_KEY_CHECKS=0');
|
||||
$conn->executeStatement('DELETE FROM weekly_schedules WHERE doctor_id IN (SELECT id FROM doctors)');
|
||||
$conn->executeStatement('DELETE FROM doctor_addresses');
|
||||
$conn->executeStatement('DELETE FROM doctor_specialties');
|
||||
$conn->executeStatement('DELETE FROM doctor_expertise');
|
||||
$conn->executeStatement('DELETE FROM doctor_insurances');
|
||||
$conn->executeStatement('DELETE FROM doctor_cities');
|
||||
$conn->executeStatement('DELETE FROM doctor_provinces');
|
||||
$conn->executeStatement('DELETE FROM doctor_secretaries');
|
||||
$conn->executeStatement('DELETE FROM clinic_doctors');
|
||||
$conn->executeStatement('DELETE FROM clinic_specialties');
|
||||
$conn->executeStatement('DELETE FROM clinic_services');
|
||||
$conn->executeStatement('DELETE FROM clinic_insurances');
|
||||
$conn->executeStatement('DELETE FROM patient_records');
|
||||
$conn->executeStatement('DELETE FROM appointments');
|
||||
$conn->executeStatement('DELETE FROM payments');
|
||||
// حذف doctors, clinics, users (به جز ادمینها)
|
||||
$conn->executeStatement("DELETE d FROM doctors d");
|
||||
$conn->executeStatement("DELETE c FROM clinics c");
|
||||
$conn->executeStatement("DELETE u FROM users u WHERE JSON_CONTAINS(u.roles, '\"ROLE_DOCTOR\"') OR JSON_CONTAINS(u.roles, '\"ROLE_CLINIC\"') OR JSON_CONTAINS(u.roles, '\"ROLE_PATIENT\"') OR JSON_CONTAINS(u.roles, '\"ROLE_SECRETARY\"')");
|
||||
$conn->executeStatement('SET FOREIGN_KEY_CHECKS=1');
|
||||
```
|
||||
|
||||
#### بخش B — کلینیک نمونه کامل (تبریز)
|
||||
|
||||
یک کلینیک با همه فیلدها:
|
||||
- نام: کلینیک تخصصی امید تبریز
|
||||
- city_id=101, province_id=1
|
||||
- representation_id=101 (تبریز)
|
||||
- تمام specialties داخلی + قلب
|
||||
- بیمههای 1,2,3,7,8,9
|
||||
- خدمات 201,202,203,101,102,103
|
||||
|
||||
#### بخش C — دکتر نمونه کامل (تبریز)
|
||||
|
||||
دکتر کامل با:
|
||||
- تخصص قلب (id=3) + داخلی (id=2)
|
||||
- بیمههای 1,2,3,7
|
||||
- خدمات 101,102,103,104,110
|
||||
- آدرس مطب در تبریز + آدرس کلینیک امید
|
||||
- برنامه کاری: شنبه تا چهارشنبه ۹-۱۳ و ۱۴-۱۸، پنجشنبه ۹-۱۳، جمعه تعطیل
|
||||
- یک منشی
|
||||
- representation_id=101
|
||||
|
||||
#### بخش D — ۱۰۰ دکتر برای هر شهر (bulk generation)
|
||||
|
||||
برای شهرهای: `[101,102,103,104,105,108,111,113,117,121,122,126,128,131,132]` (۱۵ شهر مهم)
|
||||
|
||||
هر دکتر باید داشته باشد:
|
||||
- user با موبایل منحصربهفرد (فرمت: `09XXXXXXXXX` — از ۰۹۱۰۰۱۰۰۰۰۰ به بالا)
|
||||
- نام واقعی فارسی (از لیست ۱۰۰ نام ایرانی)
|
||||
- پروفایل کامل: gender, degree, medical_system_code, info, rate
|
||||
- تخصص (توزیع واقعی بین تخصصها)
|
||||
- ۲-۳ خدمات متناسب با تخصص
|
||||
- بیمههای 1,2 (پایه) + یک بیمه تکمیلی تصادفی
|
||||
- یک آدرس مطب در شهرش
|
||||
- برنامه کاری (۵ روز کاری)
|
||||
- representation_id برابر با id شهر
|
||||
|
||||
**لیست نامهای واقعی ایرانی (برای استفاده در generator):**
|
||||
```php
|
||||
$firstNames = ['علی', 'محمد', 'حسین', 'رضا', 'امیر', 'مهدی', 'سعید', 'احمد', 'حسن', 'جواد',
|
||||
'فاطمه', 'زهرا', 'مریم', 'سارا', 'نگین', 'الهه', 'نیلوفر', 'شیما', 'لیلا', 'نازنین',
|
||||
'کریم', 'ابراهیم', 'اکبر', 'ناصر', 'پیمان', 'بهروز', 'شاهین', 'کاوه', 'آرش', 'سهراب',
|
||||
'مینا', 'آزاده', 'ندا', 'رویا', 'پریسا', 'گلناز', 'سپیده', 'بهاره', 'شادی', 'فریده'];
|
||||
$lastNames = ['رضایی', 'محمدی', 'حسینی', 'علوی', 'موسوی', 'احمدی', 'کریمی', 'صادقی', 'جعفری',
|
||||
'مرادی', 'نجفی', 'تبریزی', 'اصفهانی', 'مشهدی', 'شیرازی', 'کرمانی', 'رشیدی', 'نظری',
|
||||
'ابراهیمی', 'قاسمی', 'یوسفی', 'منصوری', 'زارعی', 'ملکی', 'بهرامی', 'سلیمانی', 'نوری'];
|
||||
```
|
||||
|
||||
**توزیع واقعی تخصصها در ۱۰۰ دکتر:**
|
||||
```php
|
||||
$specialtyGroups = [
|
||||
['specs' => [200], 'services' => [], 'degree' => 'general', 'count' => 15], // عمومی ۱۵
|
||||
['specs' => [2,3], 'services' => [101,102,103,110], 'degree' => 'specialist', 'count' => 10], // داخلی+قلب ۱۰
|
||||
['specs' => [4], 'services' => [201,202,203,210], 'degree' => 'specialist', 'count' => 6], // گوارش ۶
|
||||
['specs' => [10], 'services' => [801,802,803,805], 'degree' => 'specialist', 'count' => 6], // نورولوژی ۶
|
||||
['specs' => [21], 'services' => [2101,2102,2103,2113], 'degree' => 'specialist', 'count' => 8],// جراحی ۸
|
||||
['specs' => [61], 'services' => [], 'degree' => 'specialist', 'count' => 8], // زنان ۸
|
||||
['specs' => [41], 'services' => [], 'degree' => 'specialist', 'count' => 8], // اطفال ۸
|
||||
['specs' => [111], 'services' => [], 'degree' => 'specialist', 'count' => 7], // ارتوپدی ۷
|
||||
['specs' => [81,82], 'services' => [], 'degree' => 'specialist', 'count' => 6], // پوست ۶
|
||||
['specs' => [91], 'services' => [], 'degree' => 'specialist', 'count' => 6], // چشم ۶
|
||||
['specs' => [101], 'services' => [], 'degree' => 'specialist', 'count' => 5], // ENT ۵
|
||||
['specs' => [71], 'services' => [], 'degree' => 'specialist', 'count' => 5], // روانپزشکی ۵
|
||||
['specs' => [121], 'services' => [], 'degree' => 'specialist', 'count' => 4], // اورولوژی ۴
|
||||
['specs' => [181], 'services' => [], 'degree' => 'dentist', 'count' => 6], // دندانپزشکی ۶
|
||||
];
|
||||
// جمع = 100
|
||||
```
|
||||
|
||||
**فرمت برنامه کاری (۷ روز شنبه تا جمعه):**
|
||||
```php
|
||||
function makeSchedule(int $addressId): string {
|
||||
$days = [];
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
if ($i === 6) { // جمعه تعطیل
|
||||
$days[] = ['sessions' => []];
|
||||
} elseif ($i === 5) { // پنجشنبه نیمهوقت
|
||||
$days[] = ['sessions' => [
|
||||
['active' => true, 'location_id' => $addressId, 'start_time' => '09:00',
|
||||
'end_time' => '13:00', 'duration_per_patient' => 20,
|
||||
'has_rest' => false, 'rest_interval' => 60, 'time_to_rest' => 10, 'patient_limit' => null]
|
||||
]];
|
||||
} else { // شنبه تا چهارشنبه
|
||||
$days[] = ['sessions' => [
|
||||
['active' => true, 'location_id' => $addressId, 'start_time' => '09:00',
|
||||
'end_time' => '13:00', 'duration_per_patient' => 20,
|
||||
'has_rest' => true, 'rest_interval' => 60, 'time_to_rest' => 10, 'patient_limit' => null],
|
||||
['active' => true, 'location_id' => $addressId, 'start_time' => '14:00',
|
||||
'end_time' => '18:00', 'duration_per_patient' => 20,
|
||||
'has_rest' => false, 'rest_interval' => 60, 'time_to_rest' => 10, 'patient_limit' => null],
|
||||
]];
|
||||
}
|
||||
}
|
||||
return json_encode($days, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
```
|
||||
|
||||
## نکات مهم اجرا
|
||||
|
||||
- **نام فایل PHP:** `seed_realistic_data.php` در ریشه پروژه
|
||||
- **password_hash:** از `password_hash('Test@1234', PASSWORD_BCRYPT)` برای همه کاربران
|
||||
- **roles JSON:** `'["ROLE_USER","ROLE_DOCTOR"]'` برای دکتر، `'["ROLE_USER","ROLE_CLINIC"]'` برای کلینیک
|
||||
- **uuid:** از `Symfony\Component\Uid\Uuid::v4()->toRfc4122()` یا `bin2hex(random_bytes(8))` — باید unique باشد
|
||||
- **موبایل منحصربهفرد:** از counter شمارنده استفاده کن تا تکراری نشود
|
||||
- **representation_id:** برابر با city_id شهر مربوطه (چون cities.id = representations.id)
|
||||
- **FK ها:** weekly_schedule بعد از ایجاد doctor_address insert شود (نیاز به address.id دارد)
|
||||
- **transaction:** همه insert ها در یک transaction بزرگ بپیچ تا در صورت خطا rollback شود
|
||||
- **echo پیشرفت:** بعد از هر شهر تعداد دکتر ایجادشده را چاپ کن
|
||||
|
||||
## دستور اجرا
|
||||
|
||||
```bash
|
||||
ddev exec php seed_realistic_data.php
|
||||
```
|
||||
|
||||
## خروجی مورد انتظار
|
||||
|
||||
```
|
||||
✅ پاکسازی کامل شد
|
||||
✅ کلینیک امید تبریز ایجاد شد (id: X)
|
||||
✅ دکتر نمونه تبریز ایجاد شد (id: Y)
|
||||
🏙️ تبریز: 100 دکتر ایجاد شد
|
||||
🏙️ تهران: 100 دکتر ایجاد شد
|
||||
...
|
||||
✅ پایان — مجموع X دکتر در Y شهر ایجاد شد
|
||||
```
|
||||
Reference in New Issue
Block a user