- Introduced a new document outlining the process for importing a dataset of 160 doctors from the IRIMC system into Clinic Pro. - Defined a new ownership model to handle doctors without a user account, allowing for management by a system owner. - Documented the technical design changes required in the database and API for the import process. - Included detailed steps for the import command, field mappings, and validation rules. - Specified the workflow for claiming profiles by real doctors and the admin approval process.
322 lines
25 KiB
Markdown
322 lines
25 KiB
Markdown
# سناریو: ایمپورت پزشکان سازمان نظام پزشکی و مدیریت مالکیت پروفایل
|
||
|
||
> نسخه: ۱.۰ — تاریخ: ۱۴۰۵/۰۴/۱۹ (۲۰۲۶-۰۷-۱۰)
|
||
> دامنه: `clinicpro` (بکاند + پنل ادمین) · `nobat724_front` (سایت عمومی) · `clinic-pro-tauri` (اپ دسکتاپ)
|
||
> وضعیت: پیشنویس طراحی برای پیادهسازی
|
||
|
||
---
|
||
|
||
## ۱. خلاصه اجرایی
|
||
|
||
یک دیتاست ۱۶۰ نفره از پزشکان از سامانه استعلام اعضای سازمان نظام پزشکی (`membersearch.irimc.org`) استخراج شده است. هدف، وارد کردن این پزشکان به Clinic Pro است تا در سایت عمومی Nobat724 نمایش داده شوند، **پیش از آنکه پزشک واقعی در سیستم ثبتنام کرده باشد**.
|
||
|
||
مشکل محوری: در مدل دادهی فعلی، هر پزشک (`Doctor`) بهصورت اجباری و **یکبهیک و یکتا** به یک کاربر (`User`) متصل است، و هر کاربر نیز الزاماً یک **شماره موبایل یکتا و غیرتهی** دارد. اما رکوردهای سازمان نظام پزشکی فاقد شماره موبایل هستند (`mobileNumber: null`). بنابراین نه میتوان کاربر ساخت (چون موبایل لازم است) و نه میتوان یک پزشک را بدون کاربر ذخیره کرد.
|
||
|
||
این سند یک مدل «مالکیت پروفایل» (Profile Ownership) طراحی میکند که در آن پزشکان ایمپورتشده در حالت **«بدونمالک» (unclaimed)** ذخیره و مدیریت میشوند، و بعداً از طریق یک فرایند احراز هویتشده در Nobat724 به پزشک واقعی **منتقل (claim/transfer)** میشوند.
|
||
|
||
---
|
||
|
||
## ۲. مسئله و محدودیتهای سیستم فعلی
|
||
|
||
پیش از طراحی راهحل، محدودیتهای واقعی کد فعلی مستند میشوند (منبع: `src/Doctor/Entity/Doctor.php`, `src/Auth/Entity/User.php`, `src/Doctor/Controller/DoctorController.php`).
|
||
|
||
| محدودیت | جزئیات کد فعلی | پیامد برای ایمپورت |
|
||
|---|---|---|
|
||
| کاربر برای پزشک اجباری است | `Doctor::$user` → `OneToOne`، `JoinColumn(nullable: false, onDelete: RESTRICT)` | نمیتوان پزشک بدون کاربر ذخیره کرد. |
|
||
| رابطه پزشک↔کاربر یکتاست | `UniqueConstraint idx_doctors_user (user_id)` | **نمیتوان چند پزشک را به یک کاربر مشترک وصل کرد** — ایدهی «همه به یک کاربر سیستمی» با این قید نقض میشود. |
|
||
| موبایل کاربر اجباری و یکتاست | `User::$mobileNumber` → `NOT NULL`، `UniqueConstraint uniq_mobile` | بدون موبایل نمیتوان `User` ساخت؛ دادهی نظام پزشکی موبایل ندارد. |
|
||
| ساخت پزشک به کاربر لاگینشده گره خورده | `DoctorController::create()` از `#[CurrentUser] User $user` استفاده میکند و اگر همان کاربر پزشک داشته باشد خطای ۴۰۹ میدهد | مسیر فعلی ساخت پزشک برای ایمپورت انبوه مناسب نیست. |
|
||
| `medical_system_code` یکتا نیست | `Doctor::$medicalSystemCode` → `nullable`, بدون `unique` | برای جلوگیری از ایمپورت تکراری و برای تطبیق هنگام claim، باید کلید طبیعی یکتا شود. |
|
||
|
||
### نتیجهگیری کلیدی طراحی
|
||
|
||
خواستهی اولیه («همهی پزشکان ایمپورتشده به یک کاربر سیستمی اختصاص یابند») بهدلیل قید یکتای `user_id` روی جدول `doctors` **مستقیماً قابل اجرا نیست**. بنابراین یکی از دو مسیر زیر لازم است، و این سند **گزینه A** را توصیه میکند:
|
||
|
||
- **گزینه A (توصیهشده): جداسازی «مالکیت» از «کاربر».** ستون `Doctor.user` اختیاری (`nullable`) میشود. یک کاربر سیستمی بهنام «مالک سیستمی» (System Owner) صرفاً بهعنوان **مدیرِ منطقیِ** پزشکان بدونمالک عمل میکند (نه از طریق ستون `user_id`، بلکه از طریق فیلد جدید `managed_by`). این کار قید یکتا را نقض نمیکند و مدل تمیزتری میسازد.
|
||
- **گزینه B (جایگزین کمتغییر): کاربر جانشین (Placeholder User) بهازای هر پزشک.** برای هر پزشک یک `User` غیرفعال با شناسهی مصنوعی (مثلاً موبایل رزروشدهی `IRIMC-<code>`) ساخته میشود. اسکیمای `doctors` تقریباً دستنخورده میماند اما جدول `users` با ۱۶۰ کاربر جعلی شلوغ میشود و هنگام claim باید ادغام (merge) انجام شود.
|
||
|
||
مقایسه و تصمیم نهایی در بخش ۱۳ آمده است.
|
||
|
||
---
|
||
|
||
## ۳. مدل مفهومی مالکیت (Ownership Model)
|
||
|
||
هر پروفایل پزشک یکی از این وضعیتهای مالکیت را دارد:
|
||
|
||
- **`unclaimed` (بدونمالک):** ایمپورتشده از نظام پزشکی، هنوز به پزشک واقعی وصل نشده. توسط «مالک سیستمی» مدیریت میشود. در Nobat724 نمایش داده میشود اما قابل ویرایش توسط عموم نیست و نوبتدهی آنلاین آن پیشفرض **غیرفعال** است.
|
||
- **`pending_transfer` (در انتظار انتقال):** پزشک واقعی درخواست تصاحب داده و در حال احراز هویت / انتظار تأیید ادمین است.
|
||
- **`claimed` (تصاحبشده):** مالکیت به پزشک واقعی منتقل شده؛ پروفایل به کاربر واقعی او متصل است و او کنترل کامل دارد.
|
||
|
||
منبع پروفایل نیز ثبت میشود:
|
||
|
||
- **`source`**: `irimc` (نظام پزشکی) یا `manual` (ساخت دستی/ثبتنام عادی — رفتار فعلی).
|
||
- **`source_ref`**: شناسهی یکتای رکورد مبدأ (`profile_url` id یا `medicalSystemCode`) برای idempotency و ممیزی.
|
||
|
||
---
|
||
|
||
## ۴. بخش اول — ایمپورت پزشکان نظام پزشکی
|
||
|
||
### ۴.۱ کاربر «مالک سیستمی»
|
||
|
||
یک کاربر ویژه یکبار ساخته میشود (از طریق دستور کنسول، همسبک `CreateAdminCommand`):
|
||
|
||
- موبایل رزروشده و ثابت، مثلاً `0000000000` (خارج از فضای شمارههای واقعی ایران، ۱۱ رقمی نامعتبر).
|
||
- نقشها: `['ROLE_USER', 'ROLE_ADMIN']` یا نقش اختصاصی `ROLE_SYSTEM_OWNER`.
|
||
- `status = 0` (غیرفعال برای لاگین) تا امکان ورود با آن وجود نداشته باشد.
|
||
- `real_name = 'مالک سیستمی نوبت۷۲۴'`.
|
||
|
||
این کاربر **صاحب `user_id` پزشکان نیست** (چون یکتاست)؛ بلکه شناسهاش در ستون جدید `Doctor.managed_by` قرار میگیرد تا مشخص باشد این پزشکان توسط پلتفرم مدیریت میشوند و بعداً قابل واگذاریاند.
|
||
|
||
### ۴.۲ نگاشت فیلدها از `doctors.json`
|
||
|
||
هر رکورد ورودی به این شکل به موجودیت `Doctor` نگاشت میشود:
|
||
|
||
| فیلد ورودی (JSON) | مقصد در `Doctor` | توضیح |
|
||
|---|---|---|
|
||
| `name` | `name` | مثلاً «دکتر فرخنده حسینی» |
|
||
| `gender` (`woman`/`man`) | `gender` | با `Doctor::GENDERS` سازگار است |
|
||
| `medicalSystemCode` | `medical_system_code` + `source_ref` | کلید طبیعی یکتا برای dedup |
|
||
| `mobileNumber` (`null`) | `mobile_number` = `null` | اجازه دارد null بماند |
|
||
| `degree` (`general`) | `degree` | با `Doctor::DEGREES` سازگار است |
|
||
| `info` | `info` | «دکترای حرفهای پزشکی» |
|
||
| `specialty_id` / `specialty_uuid` | رابطه `specialties` | تطبیق با جدول `specialties` (fallback با uuid) |
|
||
| `state_id` / `state_uuid` | رابطه `provinces` | استان محل فعالیت |
|
||
| `city_id` / `city_uuid` | رابطه `cities` | شهر محل فعالیت |
|
||
| `images` (`[]`) | `images` | خالی → `null` |
|
||
| `socialMedia` | `social_media` | نگاشت به کلیدهای مجاز |
|
||
| `profile_url` / `source_url` | متادیتای ایمپورت | برای ممیزی و لینک بازبینی |
|
||
|
||
فیلدهای ثابت هنگام ایمپورت: `owner_status = 'unclaimed'`، `source = 'irimc'`، `managed_by = <systemOwnerId>`، `active_doctor_appointment = false` (تا وقتی مالک واقعی برنامهی کاری تعریف کند نوبتدهی روشن نشود).
|
||
|
||
### ۴.۳ قواعد Idempotency و اعتبارسنجی
|
||
|
||
- کلید یکتای ایمپورت: `(source = 'irimc', medical_system_code)`. اجرای مجدد ایمپورت رکورد موجود را **بهروزرسانی** میکند نه تکراریسازی.
|
||
- رکوردهای بدون `medicalSystemCode` رد و در گزارش ایمپورت لاگ میشوند.
|
||
- تطبیق تخصص/استان/شهر ابتدا با `*_id` و در صورت نبود، با `*_uuid` انجام میشود؛ عدم تطبیق باعث رد کل رکورد نمیشود بلکه فقط آن رابطه خالی میماند و در گزارش ثبت میشود.
|
||
- خروجی دستور ایمپورت: تعداد ساختهشده / بهروزشده / ردشده + مسیر فایل گزارش.
|
||
|
||
### ۴.۴ روش اجرا
|
||
|
||
دستور کنسول اختصاصی (همسبک `SeedDemoDataCommand` و `SeedCategoriesCommand`):
|
||
|
||
```bash
|
||
ddev exec php bin/console app:doctors:import-irimc var/import/doctors.json --dry-run
|
||
ddev exec php bin/console app:doctors:import-irimc var/import/doctors.json
|
||
```
|
||
|
||
`--dry-run` فقط گزارش میدهد و چیزی ذخیره نمیکند. ایمپورت درون یک تراکنش دیتابیس و بهصورت دستهای (batch/flush هر ۵۰ رکورد) انجام میشود.
|
||
|
||
---
|
||
|
||
## ۵. طراحی فنی دیتابیس
|
||
|
||
تغییرات روی موجودیت `Doctor` (بههمراه یک migration در `migrations/`):
|
||
|
||
```
|
||
doctors:
|
||
user_id INT NULL -- تغییر از NOT NULL به NULL (گزینه A)
|
||
managed_by INT NULL -- FK به users.id؛ کاربر «مالک سیستمی»
|
||
owner_status VARCHAR(20) NOT NULL DEFAULT 'claimed'
|
||
-- unclaimed | pending_transfer | claimed
|
||
source VARCHAR(20) NOT NULL DEFAULT 'manual' -- irimc | manual
|
||
source_ref VARCHAR(100) NULL -- شناسه رکورد مبدأ
|
||
claimed_at INT NULL
|
||
medical_system_code VARCHAR(25) NULL -- (موجود) + ایندکس یکتای جزئی
|
||
```
|
||
|
||
قیود و ایندکسها:
|
||
|
||
- حذف/تعدیل `UniqueConstraint idx_doctors_user`: یکتایی فقط باید برای پزشکانِ **دارای کاربر** اعمال شود. چون MariaDB از partial unique index پشتیبانی مستقیم ندارد، یکتایی `user_id` در سطح اپلیکیشن (هنگام claim) تضمین میشود و ایندکس دیتابیس به `INDEX` ساده تبدیل میشود.
|
||
- ایندکس یکتای طبیعی: `UNIQUE (source, medical_system_code)` برای idempotency ایمپورت.
|
||
- ایندکس `owner_status` برای فیلتر سریع «پزشکان بدونمالک».
|
||
|
||
سازگاری با دادهی موجود: تمام پزشکان فعلی هنگام migration مقدار `owner_status = 'claimed'` و `source = 'manual'` میگیرند تا رفتارشان تغییر نکند.
|
||
|
||
> نکته سازگاری: طبق `CLAUDE.md`، `medical_system_code` تا الان `nullable` و بدون یکتایی بوده؛ پیش از افزودن ایندکس یکتا باید دادهی موجود از نظر تکراری بودن پاکسازی شود.
|
||
|
||
---
|
||
|
||
## ۶. طراحی API (بکاند clinicpro)
|
||
|
||
پاسخها از پوشش `BaseController` پیروی میکنند: `{ success, data }` / `{ success, errors }` / صفحهبندی `{ data, meta }`. مطابق قانون پروژه، هر تغییر کنترلر باید در `docs/api/*` هم مستند شود.
|
||
|
||
### ۶.۱ ایمپورت (داخلی / ادمین)
|
||
|
||
معمولاً از طریق دستور کنسول انجام میشود؛ در صورت نیاز به تریگر از پنل ادمین:
|
||
|
||
```
|
||
POST /api/v1/admin/doctors/import-irimc [ROLE_ADMIN]
|
||
body: { source_url?, dry_run?: bool, records: [...] }
|
||
→ 200 { success, data: { created, updated, skipped, report_url } }
|
||
```
|
||
|
||
### ۶.۲ فهرست پزشکان بدونمالک
|
||
|
||
اندپوینت موجود `GET /api/v1/doctors` با فیلتر جدید `owner_status` توسعه مییابد تا هم برای پنل ادمین و هم برای صفحهی «تصاحب پروفایل» در Nobat724 قابلاستفاده باشد:
|
||
|
||
```
|
||
GET /api/v1/doctors?owner_status=unclaimed&search=&city_id=&specialty_id=
|
||
→ 200 { success, data: [...], meta }
|
||
```
|
||
|
||
### ۶.۳ درخواست تصاحب (Claim) — عمومی و احراز هویتشده
|
||
|
||
```
|
||
POST /api/v1/doctor/{uuid}/claim [IS_AUTHENTICATED_FULLY]
|
||
body: { national_code, medical_system_code, activity_time? }
|
||
قواعد:
|
||
- پروفایل باید owner_status = 'unclaimed' باشد، وگرنه 409.
|
||
- medical_system_code ورودی باید با رکورد پزشک مطابقت کند، وگرنه 422.
|
||
- کاربر لاگینشده (که موبایلش قبلاً با OTP تأیید شده) نباید از قبل پزشکِ دیگری داشته باشد.
|
||
- رکورد به pending_transfer میرود و یک ClaimRequest ثبت میشود.
|
||
→ 202 { success, data: { claim_id, status: 'pending_transfer' } }
|
||
```
|
||
|
||
### ۶.۴ تأیید/رد توسط ادمین و نهاییسازی انتقال
|
||
|
||
```
|
||
GET /api/v1/admin/doctor-claims?status=pending [ROLE_ADMIN]
|
||
POST /api/v1/admin/doctor-claims/{claimId}/approve [ROLE_ADMIN]
|
||
POST /api/v1/admin/doctor-claims/{claimId}/reject [ROLE_ADMIN] { reason }
|
||
```
|
||
|
||
هنگام approve، عملیات انتقال مالکیت (بخش ۷) بهصورت اتمیک اجرا میشود.
|
||
|
||
> امکان «انتقال خودکار» (بدون ادمین) نیز قابل تعریف است: اگر `national_code` کاربر تأییدشده باشد و `medical_system_code` و نام کاملاً منطبق باشند، سیستم میتواند مستقیماً claim را تأیید کند. تصمیم پیشفرض این سند: **تأیید ادمین اجباری برای فاز اول** (بخش ۱۳).
|
||
|
||
---
|
||
|
||
## ۷. بخش دوم — مکانیزم انتقال مالکیت
|
||
|
||
جریان کامل تصاحب پروفایل توسط پزشک واقعی:
|
||
|
||
1. **کشف:** پزشک در Nobat724 نام خود را میبیند (پروفایل `unclaimed`) و روی «این پروفایل من است» کلیک میکند.
|
||
2. **احراز هویت پایه:** اگر لاگین نیست، با موبایل + OTP ثبتنام/ورود میکند. در این مرحله یک `User` واقعی با موبایل واقعی ساخته میشود (مسیر عادی auth موجود).
|
||
3. **تطبیق هویت:** فرم تصاحب، `national_code` و `medical_system_code` را میگیرد و با رکورد پزشک تطبیق میدهد (`POST .../claim`). پروفایل به `pending_transfer` میرود.
|
||
4. **بازبینی:** ادمین در پنل، درخواست را با `profile_url` سازمان نظام پزشکی بازبینی و approve/reject میکند.
|
||
5. **نهاییسازی انتقال (اتمیک):**
|
||
- `Doctor.user` = کاربر واقعی پزشک (پر شدن ستونی که تا الان null بود).
|
||
- `Doctor.managed_by` = `null`؛ `owner_status = 'claimed'`؛ `claimed_at = time()`.
|
||
- افزودن `ROLE_DOCTOR` به کاربر واقعی (همان منطق موجود در `DoctorController::create`).
|
||
- از این پس ویرایش پروفایل توسط خود پزشک از طریق `PATCH /api/v1/doctor/{uuid}` مجاز است (چک مالکیت فعلی `getUser()->getId() === user` اکنون درست کار میکند).
|
||
6. **اطلاعرسانی:** پیامک/نوتیف تأیید به پزشک (همسبک `Sms` موجود).
|
||
|
||
قواعد یکتایی هنگام انتقال: چون یک کاربر واقعی نباید صاحب دو پزشک شود، پیش از اتصال باید بررسی شود که `user_id` مقصد در جدول `doctors` تکراری نشود (تضمین در سطح اپلیکیشن، جایگزین قید یکتای حذفشده).
|
||
|
||
---
|
||
|
||
## ۸. اتصال با Nobat724 (`nobat724_front`)
|
||
|
||
مصرفکنندهی API از طریق `services/response.js` است (که هماکنون `getDoctors`, `postDoctor`, ... را دارد). تغییرات لازم:
|
||
|
||
- **نمایش پزشکان بدونمالک:** فهرست فعلی پزشکان (`api/v1/doctors`) بهطور خودکار پزشکان `unclaimed` را هم شامل میشود؛ در کارت پزشک، بهجای دکمهی «رزرو نوبت»، دکمهی «این پروفایل من است / تکمیل پروفایل» نمایش داده میشود چون `active=false` است.
|
||
- **صفحه/فرم تصاحب:** فراخوانی `POST api/v1/doctor/{uuid}/claim` پس از ورود با OTP. نگهداری `access_token`/`refresh_token` در کوکی (مطابق الگوی فعلی Nobat724).
|
||
- **زبان/تقویم:** تمام رشتههای جدید فارسی و تاریخها جلالی (شمسی) بمانند.
|
||
- رعایت قرارداد: تغییر قالب پاسخ در بکاند باید در `nobat724_front/services/response.js` هم منعکس شود، چون در زمان build خطا نمیدهد.
|
||
|
||
---
|
||
|
||
## ۹. اثر بر `clinic-pro-tauri`
|
||
|
||
اپ دسکتاپ نیز کلاینت همان API است (`src/service/response.js`) و از CASL برای نقشها استفاده میکند (`clinic`, `doctor`, `clinic_doctor`, `secretary`).
|
||
|
||
- اگر لیست پزشکان در اپ نمایش داده میشود، باید فیلد `owner_status` و رفتار `active=false` را مدیریت کند (پزشک بدونمالک قابل رزرو آنلاین نیست).
|
||
- مرز sync آفلاین/آنلاین این اپ هنوز کامل نگاشت نشده؛ پیش از فرض همترازی، رفتار `owner_status` در دیتابیس محلی SQLite باید بررسی شود (طبق هشدار `AGENTS.md`).
|
||
- برای فاز اول، تغییر در Tauri **اختیاری** است؛ فقط در صورتی که این اپ پزشکان `unclaimed` را نشان دهد لازم میشود.
|
||
|
||
---
|
||
|
||
## ۱۰. جریان کاربری (خلاصهی گامبهگام)
|
||
|
||
```
|
||
[نظام پزشکی JSON] → دستور ایمپورت → پزشکِ unclaimed (managed_by = System Owner)
|
||
│
|
||
▼
|
||
نمایش در Nobat724 (active=false، بدون نوبت آنلاین)
|
||
│ پزشک واقعی: «این پروفایل من است»
|
||
▼
|
||
ورود با موبایل + OTP → ساخت User واقعی
|
||
│
|
||
▼
|
||
فرم تصاحب (کد ملی + کد نظام پزشکی) → POST /claim → pending_transfer
|
||
│
|
||
▼
|
||
بازبینی ادمین (approve) → انتقال اتمیک:
|
||
user_id=واقعی، owner_status=claimed، +ROLE_DOCTOR
|
||
│
|
||
▼
|
||
پزشک پروفایل و برنامهی کاری را کامل میکند → active=true → نوبتدهی آنلاین فعال
|
||
```
|
||
|
||
---
|
||
|
||
## ۱۱. حالات مرزی و قواعد کسبوکار
|
||
|
||
- **درخواست تصاحب همزمان دو نفر برای یک پروفایل:** فقط اولین `pending_transfer` پذیرفته میشود؛ بقیه با ۴۰۹ رد میشوند تا تعیین تکلیف قبلی روشن شود.
|
||
- **کاربری که قبلاً پزشک دارد:** نمیتواند پروفایل دوم را تصاحب کند (قید یکتای منطقی `user_id`).
|
||
- **عدم تطابق کد نظام پزشکی:** رد با ۴۲۲ و بدون تغییر وضعیت.
|
||
- **رد توسط ادمین:** پروفایل به `unclaimed` بازمیگردد و برای تصاحب مجدد آزاد میشود.
|
||
- **حذف پزشک بدونمالک:** مجاز برای ادمین (مسیر فعلی `DELETE`); اما پزشکِ `claimed` طبق رفتار فعلی محافظت میشود.
|
||
- **ایمپورت مجدد یک پزشکِ از قبل claimed:** فیلدهای هویتی بهروز نمیشوند (مالک واقعی اولویت دارد)؛ فقط در گزارش «skipped/claimed» ثبت میشود.
|
||
- **نوبتدهی:** تا زمانی که پروفایل `unclaimed` است، `active_doctor_appointment=false` و برنامهی کاری وجود ندارد؛ لذا در `toListArray` مقدار `active=false` میشود و رزرو ممکن نیست.
|
||
|
||
---
|
||
|
||
## ۱۲. مراحل پیادهسازی (بهترتیب و بهتفکیک ریپو)
|
||
|
||
مطابق `CLAUDE.md`: ابتدا بکاند `clinicpro`، سپس مستندسازی API، سپس کلاینتها.
|
||
|
||
**الف) `clinicpro` (بکاند):**
|
||
1. افزودن فیلدهای `managed_by`, `owner_status`, `source`, `source_ref`, `claimed_at` و nullable کردن `user_id` در `Doctor` + migration در `migrations/`.
|
||
2. پاکسازی داده و افزودن ایندکس یکتای `(source, medical_system_code)`.
|
||
3. دستور کنسول `app:doctors:import-irimc` (با `--dry-run`، گزارش، تراکنش).
|
||
4. دستور/سیدر ساخت کاربر «مالک سیستمی».
|
||
5. موجودیت/جدول `DoctorClaim` + اندپوینتهای claim و approve/reject.
|
||
6. توسعهی فیلتر `owner_status` در `GET /api/v1/doctors` و بهروزرسانی چکهای مالکیت.
|
||
7. بهروزرسانی `docs/api/*` (طبق قانون استاندارد پروژه) و افزودن این سند به مستندات.
|
||
|
||
**ب) `nobat724_front`:**
|
||
8. همترازی `services/response.js` با قالبهای جدید.
|
||
9. دکمهی «این پروفایل من است» روی کارت پزشکِ `unclaimed` + صفحهی فرم تصاحب (فارسی، جلالی، RTL).
|
||
|
||
**ج) `clinic-pro-tauri` (در صورت نیاز):**
|
||
10. مدیریت `owner_status`/`active=false` در لیست پزشکان و بررسی مرز sync محلی.
|
||
|
||
**د) بازبینی نهایی:**
|
||
11. تست ایمپورت روی نمونهی ۱۶۰ رکورد، تست جریان claim سرتاسری، و بازسازی کاربران تست (`ddev exec php create_test_users.php`).
|
||
|
||
---
|
||
|
||
## ۱۳. تصمیمات باز و ریسکها
|
||
|
||
- **گزینه A در برابر B:** این سند گزینه A (nullable کردن `user_id` + `managed_by`) را توصیه میکند چون جدول `users` را با کاربران جعلی آلوده نمیکند و مدل مالکیت را صریح میسازد. هزینهاش: از دست رفتن قید یکتای دیتابیسی روی `user_id` و انتقال آن به سطح اپلیکیشن.
|
||
- **تأیید ادمین در برابر انتقال خودکار:** پیشفرض فاز اول تأیید دستی ادمین است (امنتر برای هویت پزشک). خودکارسازی بعداً با اتکا به تأیید کد ملی افزوده میشود.
|
||
- **کیفیت دادهی نظام پزشکی:** برخی رکوردها ممکن است فاقد `specialty_id`/`city_id` معتبر باشند؛ گزارش ایمپورت باید اینها را شفاف کند.
|
||
- **حریم خصوصی:** نمایش عمومی نام و کد نظام پزشکی پیش از رضایت پزشک، ملاحظهی حقوقی دارد و باید با سیاست پلتفرم بررسی شود.
|
||
- **یکتایی موبایل مالک سیستمی:** مقدار رزروشده باید تضمیناً هرگز با موبایل واقعی کاربر تداخل نکند.
|
||
|
||
---
|
||
|
||
## ۱۴. مرجع نمونهی داده
|
||
|
||
نمونهی یک رکورد ورودی از `doctors.json` (۱۶۰ رکورد، همگی `mobileNumber: null`):
|
||
|
||
```json
|
||
{
|
||
"name": "دکتر فرخنده حسینی",
|
||
"gender": "woman",
|
||
"medicalSystemCode": "145657",
|
||
"mobileNumber": null,
|
||
"degree": "general",
|
||
"info": "دکترای حرفهای پزشکی",
|
||
"specialty_id": 1,
|
||
"specialty_name": "پزشک عمومی",
|
||
"state_id": 23,
|
||
"state_name": "کهگیلویه و بویراحمد",
|
||
"city_id": 123,
|
||
"city_name": "یاسوج",
|
||
"profile_url": "https://membersearch.irimc.org/member/profile?id=02058131-...",
|
||
"source_url": "https://membersearch.irimc.org"
|
||
}
|
||
```
|