Files
clinicpro/docs/api/doctor-import.md
T

7.4 KiB
Raw Blame History

Doctor Import (IRIMC) API

Endpoint: POST /api/v1/admin/doctors/import Permission: ROLE_ADMIN Controller: App\Admin\Controller\AdminApiController::importDoctor

وارد کردن یک پزشک از سازمان نظام پزشکی (membersearch.irimc.org) بدون شماره موبایل. برخلاف POST /api/v1/admin/doctors (که موبایل معتبر ایرانی می‌خواهد)، این اندپوینت برای هر پزشک یک کاربر جانشینِ غیرفعال با شناسهٔ مصنوعی و نقشِ اختصاصی ROLE_UNCLAIMED_DOCTOR می‌سازد و پروفایل را در وضعیت unclaimed ذخیره می‌کند تا بعداً به پزشک واقعی منتقل شود. این نقش، هم کاربر جانشین را قابل‌شناسایی می‌کند و هم مبنای حذفِ امنِ او پس از انتقال است.

مصرف‌کنندهٔ اصلی: خزندهٔ پایتون (clinicpro-crawler/pipeline.py) که با کاربر «مالک سیستمی» (0000000000) لاگین می‌کند و هر ~۶۰ ثانیه یک پزشک را می‌فرستد.


مدل مالکیت (ستون‌های جدید doctors)

ستون مقدار هنگام ایمپورت توضیح
owner_status unclaimed claimed | unclaimed | pending_transfer
source irimc manual (پیش‌فرض رکوردهای قدیمی) | irimc
source_ref profile_url شناسهٔ رکورد مبدأ برای ممیزی
managed_by id کاربر ادمینِ فراخوان کاربری که پروفایل را مدیریت می‌کند
claimed_at null زمان انتقال مالکیت (هنگام claim پر می‌شود)

رکوردهای موجود در migration مقدار manual + claimed می‌گیرند تا رفتارشان تغییر نکند.


idempotency

کلید یکتای منطقی: (source, medical_system_code).

  • اگر پزشکی با همان source+medical_system_code وجود نداشته باشد → ساخته می‌شود (201).
  • اگر وجود داشته باشد و owner_status != claimedبه‌روزرسانی می‌شود (200).
  • اگر وجود داشته باشد و owner_status == claimedرد می‌شود (200, skipped: "claimed") تا دادهٔ مالک واقعی بازنویسی نشود.

Request

{
  "name": "دکتر فرخنده حسینی",
  "medical_system_code": "145657",
  "source": "irimc",
  "source_ref": "https://membersearch.irimc.org/member/profile?id=…",
  "gender": "woman",
  "degree": "general",
  "info": "دکترای حرفه‌ای پزشکی",
  "specialties": [1],
  "states": [23],
  "cities": [123]
}
فیلد الزامی توضیح
name نام کامل پزشک
medical_system_code کد نظام پزشکی (کلید idempotency)
source پیش‌فرض irimc
source_ref profile_url یا شناسهٔ مبدأ
gender man | woman
degree general | expert | specialist | subspecialistplus
info متن تخصص/توضیح
specialties / states / cities آرایهٔ شناسه‌های مرجع (id)

gender/degree باید با ثابت‌های Doctor::GENDERS / Doctor::DEGREES سازگار باشند. specialties/states/cities اگر داده شوند مجموعهٔ فعلی را جایگزین می‌کنند.


Response

201 Created (ساخته شد)

{ "success": true, "data": { "uuid": "…", "created": true } }

200 OK (به‌روزرسانی شد)

{ "success": true, "data": { "uuid": "…", "created": false } }

200 OK (رد به‌دلیل تصاحب‌شده)

{ "success": true, "data": { "uuid": "…", "created": false, "skipped": "claimed" } }

422 (اعتبارسنجی)

{ "success": false, "data": null, "errors": [ { "code": "…", "message": "کد نظام پزشکی الزامی است", "field": "medical_system_code" } ] }

عکس پروفایل

پس از ایمپورت، عکس با دو مرحله اضافه می‌شود (همان مسیر پزشکِ عادی):

  1. POST /file/upload/clinic_pro/doctor/field_image — بدنه: باینری خام؛ هدر Content-Disposition: attachment; filename="145657.jpg". پاسخ: { fid, uuid, url, filename, filemime, filesize }.
  2. PATCH /api/v1/doctor/{uuid} با بدنهٔ { "image_data": <پاسخ مرحلهٔ ۱> } تا به آرایهٔ images پروفایل افزوده شود.

انتقال مالکیت به پزشک واقعی

Endpoint: POST /api/v1/admin/doctors/{uuid}/transfer — permission ROLE_ADMIN

مالکیت یک پروفایلِ unclaimed را به پزشک واقعی منتقل می‌کند.

// Request
{ "mobile": "09120000000" }

مراحل اتمیک:

  1. کاربر واقعی بر پایهٔ mobile پیدا یا ساخته می‌شود (باید موبایل معتبر ایران باشد).
  2. اگر کاربر واقعی از قبل صاحب پزشک دیگری باشد → 409.
  3. user_id پروفایل به کاربر واقعی تغییر می‌کند، ROLE_DOCTOR به او داده می‌شود، owner_status = claimed و claimed_at = now و managed_by = null می‌شود.
  4. کاربر جانشین حذف می‌شود — فقط اگر واقعاً ROLE_UNCLAIMED_DOCTOR داشته باشد و دیگر هیچ پزشکی به او وصل نباشد (حذف امن).
// Response 200
{ "success": true, "data": {
  "uuid": "…", "owner_status": "claimed",
  "transferred_to": "09120000000", "placeholder_deleted": true
} }
کد حالت
404 پزشک یافت نشد
409 قبلاً claimed است، یا کاربر مقصد پزشک دیگری دارد
422 موبایل نامعتبر

کاربر سیستمی و چرخهٔ عمر

# ساخت/فعال‌سازی کاربر مالک سیستمی (کرالر با این لاگین می‌کند)
php bin/console app:system-owner 0000000000 --password=<secret> --activate

# غیرفعال‌سازی پس از پایان کار (کرالر هم با --deactivate-on-finish این را از طریق API انجام می‌دهد)
php bin/console app:system-owner 0000000000 --deactivate

کاربر باید ROLE_ADMIN و status=1 داشته باشد تا لاگینِ رمزی (POST /api/v1/user/login) و فراخوانی این اندپوینت ممکن باشد.

⚠️ captcha: مسیر /api/v1/user/login از CaptchaGuard رد می‌شود و این guard وقتی ALTCHA_ENABLED=true باشد (مقدار فعلی .env) یک payloadِ altcha می‌خواهد. برای اجرای بدونِ‌مرورگرِ کرالر یکی از این‌ها لازم است: ۱) روی همان سرور ALTCHA_ENABLED=false در .env.local (ساده‌ترین برای dev)، یا ۲) افزودن یک استثنا در PasswordAuthenticator که برای کاربر مالک سیستمی captcha را رد کند، یا ۳) لاگین سرویس با یک هدر سرّیِ مورد اعتماد. تا وقتی این حل نشود، لاگین کرالر با ۴۲۲ (ERR_CAPTCHA_001) رد می‌شود.