fix(doctor): hide deactivated doctors from public site

The public list GET /api/v1/doctors only excluded inactive doctors
when an explicit `active` filter was passed; with no param it returned
everyone (deactivated doctors just ranked lower). Deactivated doctors
(admin toggled active_doctor_appointment off) leaked onto nobat724.

- DoctorRepository::findWithFilters: default (no `active` param) now
  filters activeDoctorAppointment = true. The active=1 (bookable) and
  active=0 (admin, inactive-only) escape hatches are unchanged.
- Doctor::toDetailArray: expose raw `is_active` (= activeDoctorAppointment,
  independent of schedule) so public clients can 404 a deactivated
  doctor's profile page; distinct from `active` (flag && has_schedule).
- Tests + docs/api/doctor.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-23 19:39:16 +03:30
co-authored by Claude Opus 4.8
parent 46be6a4575
commit d0fbe204a1
5 changed files with 48 additions and 8 deletions
+13 -5
View File
@@ -197,10 +197,18 @@ Get doctor detail for clinic owner — only doctors who are members of the authe
> برمی‌گردد؛ `hours_of_work` در صورت وجود برنامه حفظ می‌شود. تا وقتی حتی یک برنامه روشن و
> دارای روز فعال باشد، همان مبنا قرار می‌گیرد.
>
> چنین پزشکی (همه خاموش) در لیست عمومی `GET /api/v1/doctors` **نمایش داده می‌شود** — این
> اندپوینت فیلتر `active` پیش‌فرض ندارد — ولی با `bookableRank` پایین‌تر از پزشکان دارای
> نوبت مرتب می‌شود و تنها با `active=1` از نتایج حذف می‌گردد. صفحهٔ تکی
> `GET /api/v1/doctor/{slug}` همیشه قابل دسترسی است.
> چنین پزشکی (همه برنامه‌ها خاموش، ولی فلگ `active_doctor_appointment` **روشن**) در لیست
> عمومی `GET /api/v1/doctors` **نمایش داده می‌شود** — چون هنوز فعال است — ولی با
> `bookableRank` پایین‌تر از پزشکان دارای نوبت مرتب می‌شود و تنها با `active=1` از نتایج حذف
> می‌گردد.
>
> **پزشک غیرفعال (`active_doctor_appointment` خاموش):** با غیرفعال‌کردن پزشک از پنل ادمین،
> او دیگر در لیست عمومی `GET /api/v1/doctors` ظاهر نمی‌شود (پیش‌فرض این اندپوینت پزشکان
> غیرفعال را کنار می‌گذارد). صفحهٔ تکی `GET /api/v1/doctor/{slug}` همچنان پاسخ می‌دهد
> (ادمین/کلینیک از همین اندپوینت برای مشاهده/ویرایش استفاده می‌کنند) و در بدنهٔ پاسخ فیلد
> خام `is_active` (`= active_doctor_appointment`، مستقل از داشتن برنامه) را برمی‌گرداند؛
> کلاینت عمومی مثل nobat724 با `is_active === false` صفحهٔ پزشک را `404` می‌کند. این با
> فیلد `active` (که `active_doctor_appointment && has_schedule` است) فرق دارد.
---
@@ -224,7 +232,7 @@ List doctors with pagination and filters.
| `degree` | string | ❌ | `expert`, `general`, `specialist`, `subspecialistplus` |
| `name` | string | ❌ | جستجوی `LIKE` روی نام پزشک |
| `sort` | string | ❌ | `ASC` یا `DESC` (پیش‌فرض `DESC`) — مرتب‌سازی ثانویه بر اساس `doctorRate` |
| `active` | `0`/`1` | ❌ | `1` → فقط پزشکان **دارای نوبت** (تعریف پایین). `0` → فقط پزشکانی که فلگ `active_doctor_appointment` آن‌ها خاموش است (کاربرد ادمین). بدون این پارامتر → **همه** پزشکان برمی‌گردند |
| `active` | `0`/`1` | ❌ | `1` → فقط پزشکان **دارای نوبت** (تعریف پایین). `0` → فقط پزشکانی که فلگ `active_doctor_appointment` آن‌ها خاموش است (کاربرد ادمین). بدون این پارامتر → فقط پزشکان **فعال** (`active_doctor_appointment` روشن)؛ پزشکان غیرفعال هرگز در لیست عمومی نمی‌آیند |
### مرتب‌سازی و تعریف «دارای نوبت»