feat(doctor): claim captcha+mobile, owner profile delete, admin map zoom fix
- DoctorClaimController: ALTCHA CaptchaGuard on /claim (dev no-op via ALTCHA_ENABLED=false); optional `mobile` field must match the logged-in user's number (422 ERR_CONFLICT_001 on mismatch) - DoctorController::delete: now IS_AUTHENTICATED_FULLY — admin (any) or the owner of a claimed profile (IDOR-guarded); FK appointment guard kept - DoctorDetailPage address map: MapController calls map.invalidateSize() before flyTo (fixes needing to pick a city twice on a freshly-mounted map); geocode retries once (nominatim empty/429 on first hit) - tests: mobile mismatch, owner-delete allowed + others 403, unclaimed not deletable by random user - docs: doctor-claim.md (mobile+captcha), doctor.md (delete permission) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
# رفع زوم نقشه در افزودن آدرس + کپچا و موبایل در claim + حذف پروفایل توسط مالک
|
||||
|
||||
## پروژه
|
||||
|
||||
`clinicpro` (پنل ادمین React + backend)
|
||||
> پرامپت همتا (سایت عمومی): `nobat724_front/.claude/prompt/doctor-map-claim-modal.md` — نقشهٔ صفحه پزشک + مودال claim + دکمهٔ حذف. این پرامپت قرارداد API را که سایت مصرف میکند تغییر میدهد.
|
||||
|
||||
## زمینه
|
||||
|
||||
سه موضوع مرتبط با پروفایل پزشک:
|
||||
1. در فرم افزودن آدرس (پنل ادمین)، با انتخاب شهر نقشه باید روی آن شهر زوم کند؛ ولی بار اول کار نمیکند و کاربر مجبور است شهر را **دو بار** انتخاب کند.
|
||||
2. جریان تصاحب پروفایل (claim) از قبل هست (`DoctorClaimController`) ولی طبق سناریوی جدید باید **کپچای ALTCHA** داشته باشد و **شماره موبایل** بهصراحت در فرم گرفته و تطبیق داده شود.
|
||||
3. پس از claim، **مالک** پروفایل باید بتواند پروفایل خود را حذف کند (الان حذف فقط `ROLE_ADMIN` است).
|
||||
|
||||
## فایلهای مرتبط
|
||||
|
||||
| فایل | نقش |
|
||||
|------|-----|
|
||||
| `assets/admin/pages/DoctorDetailPage.tsx` | فرم آدرس + `MapPicker`/`MapController` (react-leaflet) + geocode شهر |
|
||||
| `src/Doctor/Controller/DoctorClaimController.php` | endpoint `claim` — افزودن کپچا + فیلد mobile |
|
||||
| `src/Doctor/Service/DoctorClaimService.php` | منطق claim |
|
||||
| `src/Shared/Captcha/CaptchaGuard.php` | `assertValid($request)` — الگوی موجود کپچا (در `AuthController`) |
|
||||
| `src/Doctor/Controller/DoctorController.php` | متد `delete` (خط ۳۷۱، الان `#[IsGranted('ROLE_ADMIN')]`) |
|
||||
| `docs/api/doctor.md` + `docs/api/doctor-claim.md` | مستندسازی |
|
||||
|
||||
## وظیفه ۱ — رفع زوم نقشه هنگام انتخاب شهر (نیازِ دوبار انتخاب)
|
||||
|
||||
### وضعیت فعلی
|
||||
|
||||
```tsx
|
||||
// DoctorDetailPage.tsx:468 — recenter فقط با flyTo روی تغییر flyTarget
|
||||
function MapController({ flyTarget }: { flyTarget: [number, number] | null }) {
|
||||
const map = useMap();
|
||||
useEffect(() => {
|
||||
if (flyTarget) map.flyTo(flyTarget, 12, { duration: 1.2 });
|
||||
}, [flyTarget, map]);
|
||||
return null;
|
||||
}
|
||||
|
||||
// :935 — انتخاب شهر → geocode خارجی → setMapFlyTarget
|
||||
onChange={(val, label) => {
|
||||
setValue('city_id', val);
|
||||
if (label) {
|
||||
geocodeCityInIran(label).then(coords => { if (coords) setMapFlyTarget(coords); });
|
||||
}
|
||||
}}
|
||||
```
|
||||
|
||||
### ریشهها
|
||||
|
||||
1. **نقشهٔ تازهمانتشده اندازهاش ۰ است:** وقتی فرم/نقشه تازه باز میشود، Leaflet ابعاد کانتینر را نگرفته و `flyTo` روی نقشهٔ بدوناندازه بیاثر است؛ انتخاب دومِ شهر (که نقشه دیگر layout شده) کار میکند. باید `map.invalidateSize()` قبل از `flyTo` صدا زده شود.
|
||||
2. **geocode خارجی (nominatim) async و rate-limited است:** اولین فراخوان ممکن است خالی/۴۰۳ برگردد (کاربر بلافاصله بعد از باز شدن انتخاب میکند) و `setMapFlyTarget` اجرا نشود.
|
||||
|
||||
### راهحل
|
||||
|
||||
**الف) `invalidateSize` + recenter مقاوم:**
|
||||
|
||||
```tsx
|
||||
function MapController({ flyTarget }: { flyTarget: [number, number] | null }) {
|
||||
const map = useMap();
|
||||
useEffect(() => {
|
||||
map.invalidateSize(); // ابعاد را پس از mount/تغییر layout بهروز کن
|
||||
if (flyTarget) map.flyTo(flyTarget, 12, { duration: 1.2 });
|
||||
}, [flyTarget, map]);
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
اگر نقشه داخل بخشی است که با باز/بسته شدن mount/unmount میشود، یک `invalidateSize` هنگام mount هم لازم است (effect بالا با `map` در deps این را پوشش میدهد).
|
||||
|
||||
**ب) geocode مقاوم — ترجیحاً از مختصات خودِ شهر بهجای سرویس خارجی:**
|
||||
|
||||
- اول بررسی کن آیا آبجکت شهر در `cities` (یا endpoint `/api/v1/cities`) مختصات دارد؛ اگر دارد، مستقیم از همان `flyTarget` را بساز و از nominatim صرفنظر کن (سریع، بدون rate-limit، بدون async ناموفق).
|
||||
- اگر مختصات در دیتا نیست، `geocodeCityInIran` را نگهدار اما با retry ساده (یک تلاش مجدد بعد از ~۱ ثانیه در صورت پاسخ خالی) و بدون بلاککردن UI.
|
||||
|
||||
> **edge:** اگر کاربر پیش از resolve شدن geocode شهر دیگری انتخاب کند، فقط آخرین انتخاب باید اعمال شود (نگهداشتن یک request id/ابطال نتیجهٔ قدیمی).
|
||||
|
||||
## وظیفه ۲ — کپچا و فیلد موبایل در claim
|
||||
|
||||
### وضعیت فعلی
|
||||
|
||||
`DoctorClaimController::claim` پشت `IS_AUTHENTICATED_FULLY` است و کپچا ندارد؛ موبایل را از کاربر لاگینشده میگیرد (`$user->getMobileNumber()`)، فیلد جدا در بدنه ندارد.
|
||||
|
||||
### راهحل
|
||||
|
||||
**الف) کپچای ALTCHA** — الگوی موجود `CaptchaGuard::assertValid($request)` (همان که در `AuthController::sendCode` استفاده میشود):
|
||||
|
||||
```php
|
||||
// ابتدای DoctorClaimController::claim، پیش از rate limiter/منطق
|
||||
$this->captcha->assertValid($request); // تزریق CaptchaGuard در constructor
|
||||
```
|
||||
|
||||
- `assertValid` هنگام `ALTCHA_ENABLED=false` بیاثر است (dev)، و در prod payload کپچا میخواهد؛ خطای آن به `ERR_CAPTCHA_001` (۴۲۲) تبدیل میشود (ExceptionSubscriber).
|
||||
|
||||
**ب) فیلد موبایل صریح** — بدنه فیلد `mobile` بگیرد و با موبایل کاربر لاگینشده تطبیق داده شود (طبق سناریو: «شماره موبایل ثبتشده در حساب کاربری باید به عنوان مالک بررسی شود»):
|
||||
|
||||
```php
|
||||
$mobile = \App\Shared\Util\PersianText::normalize((string) ($data['mobile'] ?? ''));
|
||||
$mobile = preg_replace('/\D/', '', $mobile);
|
||||
if (!preg_match('/^09\d{9}$/', $mobile)) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره موبایل نامعتبر است', 422, 'mobile');
|
||||
}
|
||||
if ($mobile !== $user->getMobileNumber()) {
|
||||
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'شماره موبایل باید با حساب کاربری شما یکی باشد', 422, 'mobile');
|
||||
}
|
||||
```
|
||||
|
||||
- `DoctorClaimService::claim` بدون تغییر میماند (همان موبایل کاربر برای شاهکار استفاده میشود).
|
||||
- سند `docs/api/doctor-claim.md`: افزودن فیلد `mobile` به بدنه + ذکر کپچای ALTCHA و کد `ERR_CAPTCHA_001`.
|
||||
|
||||
## وظیفه ۳ — حذف پروفایل توسط مالک
|
||||
|
||||
### وضعیت فعلی
|
||||
|
||||
```php
|
||||
// DoctorController.php:369
|
||||
#[Route('/api/v1/doctor/{uuid}', methods: ['DELETE'])]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
public function delete(string $uuid): JsonResponse { ... }
|
||||
```
|
||||
|
||||
فقط ادمین حذف میکند؛ مالک پزشک نمیتواند پروفایل خود را حذف کند.
|
||||
|
||||
### راهحل
|
||||
|
||||
مالک (`claimed` و `doctor.getUser()->getId() === user`) هم اجازهٔ حذف بگیرد:
|
||||
|
||||
- `#[IsGranted('ROLE_ADMIN')]` را از متد بردار و به `#[IsGranted('IS_AUTHENTICATED_FULLY')]` تغییر بده؛ داخل متد `#[CurrentUser] User $user` را بگیر و کنترل دسترسی صریح:
|
||||
|
||||
```php
|
||||
public function delete(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$doctor = $this->doctorRepo->findByUuid($uuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
$isAdmin = $user->hasRole('ROLE_ADMIN');
|
||||
$isOwner = $doctor->getOwnerStatus() === 'claimed' && $doctor->getUser()->getId() === $user->getId();
|
||||
if (!$isAdmin && !$isOwner) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'اجازهٔ حذف این پروفایل را ندارید', 403);
|
||||
}
|
||||
// ... گاردِ FK موجود (نوبت ثبتشده → 409) و remove فعلی بدون تغییر ...
|
||||
}
|
||||
```
|
||||
|
||||
- گارد FK موجود (پزشکِ دارای نوبت → ۴۰۹ `ERR_CONFLICT_001`) حفظ شود.
|
||||
- امنیت: IDOR — کاربرِ لاگینشده فقط پروفایلِ **claimed متعلق به خودش** یا (اگر ادمین) هر پروفایلی را حذف کند؛ پروفایلِ `unclaimed` توسط کاربر عادی حذف نشود.
|
||||
|
||||
## نکات مهم
|
||||
|
||||
- بعد از تغییر `delete`/`claim`، `docs/api/doctor.md` و `docs/api/doctor-claim.md` را بهروز کن (قانون پروژه).
|
||||
- نقشهٔ react-leaflet فقط admin frontend است؛ تغییر backend ندارد.
|
||||
- کپچا: مسیر public سایت (`nobat724`) هم باید payload ALTCHA بفرستد (پرامپت همتا)؛ در dev با `ALTCHA_ENABLED=false` بیاثر است.
|
||||
- تست:
|
||||
```bash
|
||||
ddev exec php -l src/Doctor/Controller/DoctorClaimController.php
|
||||
ddev exec php bin/console cache:clear
|
||||
ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head
|
||||
ddev exec yarn dev
|
||||
# claim: با mobile نامطابق → 422؛ delete توسط مالک claimed → 200؛ توسط کاربر دیگر → 403
|
||||
ddev exec php bin/phpunit tests/Doctor
|
||||
```
|
||||
- تستهای موجود `DoctorClaimTest`/`DoctorImportTest` را با فیلد `mobile` و مسیر delete مالک بهروز/تکمیل کن؛ سبز بمانند.
|
||||
@@ -449,15 +449,19 @@ function StarRating({ rate }: { rate: number }) {
|
||||
const IRAN_CENTER: [number, number] = [32.4279, 53.6880];
|
||||
|
||||
async function geocodeCityInIran(cityName: string): Promise<[number, number] | null> {
|
||||
try {
|
||||
const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(cityName + ',ایران')}&format=json&countrycodes=ir&limit=1`;
|
||||
const res = await fetch(url, { headers: { 'Accept-Language': 'fa' } });
|
||||
const data = await res.json();
|
||||
if (data?.[0]) return [parseFloat(data[0].lat), parseFloat(data[0].lon)];
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(cityName + ',ایران')}&format=json&countrycodes=ir&limit=1`;
|
||||
// nominatim گاهی روی اولین فراخوان خالی/۴۲۹ برمیگرداند؛ یک retry تا انتخاب اول هم کار کند.
|
||||
for (let attempt = 0; attempt < 2; attempt++) {
|
||||
try {
|
||||
const res = await fetch(url, { headers: { 'Accept-Language': 'fa' } });
|
||||
const data = await res.json();
|
||||
if (data?.[0]) return [parseFloat(data[0].lat), parseFloat(data[0].lon)];
|
||||
} catch {
|
||||
/* تلاش بعدی */
|
||||
}
|
||||
if (attempt === 0) await new Promise(r => setTimeout(r, 900));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function MapClickHandler({ onPick }: { onPick: (lat: number, lng: number) => void }) {
|
||||
@@ -467,8 +471,14 @@ function MapClickHandler({ onPick }: { onPick: (lat: number, lng: number) => voi
|
||||
|
||||
function MapController({ flyTarget }: { flyTarget: [number, number] | null }) {
|
||||
const map = useMap();
|
||||
// نقشهٔ تازهمانتشده ابعادش را نگرفته؛ flyTo بیاثر میماند تا invalidateSize صدا زده شود.
|
||||
useEffect(() => {
|
||||
if (flyTarget) map.flyTo(flyTarget, 12, { duration: 1.2 });
|
||||
map.invalidateSize();
|
||||
}, [map]);
|
||||
useEffect(() => {
|
||||
if (!flyTarget) return;
|
||||
map.invalidateSize();
|
||||
map.flyTo(flyTarget, 12, { duration: 1.2 });
|
||||
}, [flyTarget, map]);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ unclaimed ──claim/transfer شروع──▶ pending_transfer ──موفق
|
||||
|
||||
**Permission:** `IS_AUTHENTICATED_FULLY` — کاربر با OTP لاگین شده (موبایلش تأییدشده است)
|
||||
**Rate limit:** limiter `doctor_claim` — ۵ تلاش در ساعت بهازای هر (کاربر، پزشک)
|
||||
**Captcha:** ALTCHA — بدنه باید payload کپچا بفرستد (`CaptchaGuard::assertValid`)؛ در dev با `ALTCHA_ENABLED=false` بیاثر است، در prod اجباری. خطا → `ERR_CAPTCHA_001` (۴۲۲).
|
||||
|
||||
### Request
|
||||
```json
|
||||
@@ -48,7 +49,9 @@ unclaimed ──claim/transfer شروع──▶ pending_transfer ──موفق
|
||||
"national_code": "0010007700",
|
||||
"birth_date": "1371/1/1",
|
||||
"first_name": "فرخنده",
|
||||
"last_name": "حسینی"
|
||||
"last_name": "حسینی",
|
||||
"mobile": "09121234567",
|
||||
"altcha": "<payload کپچا>"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,6 +60,8 @@ unclaimed ──claim/transfer شروع──▶ pending_transfer ──موفق
|
||||
| `national_code` | ✅ | ۱۰ رقم (ارقام فارسی پذیرفته و نرمال میشوند) |
|
||||
| `birth_date` | ✅ | شمسی `Y/m/d` |
|
||||
| `first_name` / `last_name` | ✅ | با هویت ثبت احوال و نام پروفایل تطبیق داده میشود (نرمالسازی ي/ی، ك/ک، نیمفاصله — `PersianText`) |
|
||||
| `mobile` | ❌ | اگر داده شود، باید با موبایل حساب کاربری یکی باشد وگرنه `422 ERR_CONFLICT_001`؛ اگر خالی باشد از موبایل کاربر لاگینشده استفاده میشود |
|
||||
| `altcha` | prod | payload کپچای ALTCHA |
|
||||
|
||||
### مراحل سرور (اتمیک/ضد race)
|
||||
|
||||
|
||||
+3
-3
@@ -269,7 +269,7 @@ Updated doctor object (same structure as GET single).
|
||||
|
||||
Delete a doctor profile.
|
||||
|
||||
**Permission:** `ROLE_ADMIN`
|
||||
**Permission:** `IS_AUTHENTICATED_FULLY` — **admin** (any profile) **or the owner** of a `claimed` profile (`doctor.user === current user`). Other users get `403`.
|
||||
|
||||
> **Side effect:** the doctor's insurance configuration (`tenant_insurances`, `entity_insurance_pricing`, and their `tenant_service_coverages`) is purged in the same request — these reference the doctor via a polymorphic `entity_id` with no DB FK, so the cleanup is enforced in the application.
|
||||
|
||||
@@ -292,8 +292,8 @@ Delete a doctor profile.
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_AUTH_006` | 403 | Not admin |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not admin and not the owner of this claimed profile |
|
||||
| `ERR_VALIDATION_002` | 404 | Doctor not found |
|
||||
| `ERR_CONFLICT_001` | 409 | Doctor has existing appointments and cannot be deleted |
|
||||
|
||||
---
|
||||
|
||||
@@ -713,7 +713,6 @@
|
||||
"711": "Community 711",
|
||||
"712": "Community 712",
|
||||
"713": "Community 713",
|
||||
"714": "Community 714",
|
||||
"715": "Community 715",
|
||||
"716": "Community 716",
|
||||
"717": "Community 717",
|
||||
@@ -727,8 +726,6 @@
|
||||
"725": "Community 725",
|
||||
"726": "Community 726",
|
||||
"727": "Community 727",
|
||||
"728": "Community 728",
|
||||
"729": "Community 729",
|
||||
"730": "Community 730",
|
||||
"731": "Community 731",
|
||||
"732": "Community 732",
|
||||
@@ -736,15 +733,11 @@
|
||||
"734": "Community 734",
|
||||
"735": "Community 735",
|
||||
"736": "Community 736",
|
||||
"737": "Community 737",
|
||||
"738": "Community 738",
|
||||
"739": "Community 739",
|
||||
"740": "Community 740",
|
||||
"741": "Community 741",
|
||||
"742": "Community 742",
|
||||
"743": "Community 743",
|
||||
"744": "Community 744",
|
||||
"745": "Community 745",
|
||||
"746": "Community 746",
|
||||
"747": "Community 747",
|
||||
"748": "Community 748",
|
||||
@@ -757,19 +750,15 @@
|
||||
"755": "Community 755",
|
||||
"756": "Community 756",
|
||||
"757": "Community 757",
|
||||
"758": "Community 758",
|
||||
"759": "Community 759",
|
||||
"760": "Community 760",
|
||||
"761": "Community 761",
|
||||
"762": "Community 762",
|
||||
"763": "Community 763",
|
||||
"764": "Community 764",
|
||||
"765": "Community 765",
|
||||
"766": "Community 766",
|
||||
"767": "Community 767",
|
||||
"768": "Community 768",
|
||||
"769": "Community 769",
|
||||
"770": "Community 770",
|
||||
"771": "Community 771",
|
||||
"772": "Community 772"
|
||||
"771": "Community 771"
|
||||
}
|
||||
|
||||
+78
-137
@@ -1,16 +1,16 @@
|
||||
# Graph Report - clinicpro (2026-07-11)
|
||||
|
||||
## Corpus Check
|
||||
- 766 files · ~564,895 words
|
||||
- 767 files · ~566,250 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 9595 nodes · 13312 edges · 773 communities (614 shown, 159 thin omitted)
|
||||
- 9614 nodes · 13338 edges · 762 communities (602 shown, 160 thin omitted)
|
||||
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 294 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `0b49b03a`
|
||||
- Built from commit: `3dcd4f3b`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
@@ -715,7 +715,6 @@
|
||||
- [[_COMMUNITY_Community 711|Community 711]]
|
||||
- [[_COMMUNITY_Community 712|Community 712]]
|
||||
- [[_COMMUNITY_Community 713|Community 713]]
|
||||
- [[_COMMUNITY_Community 714|Community 714]]
|
||||
- [[_COMMUNITY_Community 715|Community 715]]
|
||||
- [[_COMMUNITY_Community 716|Community 716]]
|
||||
- [[_COMMUNITY_Community 717|Community 717]]
|
||||
@@ -729,8 +728,6 @@
|
||||
- [[_COMMUNITY_Community 725|Community 725]]
|
||||
- [[_COMMUNITY_Community 726|Community 726]]
|
||||
- [[_COMMUNITY_Community 727|Community 727]]
|
||||
- [[_COMMUNITY_Community 728|Community 728]]
|
||||
- [[_COMMUNITY_Community 729|Community 729]]
|
||||
- [[_COMMUNITY_Community 730|Community 730]]
|
||||
- [[_COMMUNITY_Community 731|Community 731]]
|
||||
- [[_COMMUNITY_Community 732|Community 732]]
|
||||
@@ -738,15 +735,11 @@
|
||||
- [[_COMMUNITY_Community 734|Community 734]]
|
||||
- [[_COMMUNITY_Community 735|Community 735]]
|
||||
- [[_COMMUNITY_Community 736|Community 736]]
|
||||
- [[_COMMUNITY_Community 737|Community 737]]
|
||||
- [[_COMMUNITY_Community 738|Community 738]]
|
||||
- [[_COMMUNITY_Community 739|Community 739]]
|
||||
- [[_COMMUNITY_Community 740|Community 740]]
|
||||
- [[_COMMUNITY_Community 741|Community 741]]
|
||||
- [[_COMMUNITY_Community 742|Community 742]]
|
||||
- [[_COMMUNITY_Community 743|Community 743]]
|
||||
- [[_COMMUNITY_Community 744|Community 744]]
|
||||
- [[_COMMUNITY_Community 745|Community 745]]
|
||||
- [[_COMMUNITY_Community 746|Community 746]]
|
||||
- [[_COMMUNITY_Community 747|Community 747]]
|
||||
- [[_COMMUNITY_Community 748|Community 748]]
|
||||
@@ -757,21 +750,17 @@
|
||||
- [[_COMMUNITY_Community 753|Community 753]]
|
||||
- [[_COMMUNITY_Community 754|Community 754]]
|
||||
- [[_COMMUNITY_Community 757|Community 757]]
|
||||
- [[_COMMUNITY_Community 758|Community 758]]
|
||||
- [[_COMMUNITY_Community 759|Community 759]]
|
||||
- [[_COMMUNITY_Community 760|Community 760]]
|
||||
- [[_COMMUNITY_Community 761|Community 761]]
|
||||
- [[_COMMUNITY_Community 762|Community 762]]
|
||||
- [[_COMMUNITY_Community 763|Community 763]]
|
||||
- [[_COMMUNITY_Community 764|Community 764]]
|
||||
- [[_COMMUNITY_Community 765|Community 765]]
|
||||
- [[_COMMUNITY_Community 766|Community 766]]
|
||||
- [[_COMMUNITY_Community 767|Community 767]]
|
||||
- [[_COMMUNITY_Community 768|Community 768]]
|
||||
- [[_COMMUNITY_Community 769|Community 769]]
|
||||
- [[_COMMUNITY_Community 770|Community 770]]
|
||||
- [[_COMMUNITY_Community 771|Community 771]]
|
||||
- [[_COMMUNITY_Community 772|Community 772]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `BaseController` - 86 edges
|
||||
@@ -800,7 +789,7 @@
|
||||
## Import Cycles
|
||||
- None detected.
|
||||
|
||||
## Communities (773 total, 159 thin omitted)
|
||||
## Communities (762 total, 160 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.05
|
||||
@@ -827,12 +816,12 @@ Cohesion: 0.07
|
||||
Nodes (3): UserProfile, self, User
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.07
|
||||
Nodes (19): SettlementController, FinancialBreakdown, FinancialBreakdownRepository, SettlementRepository, WalletTransactionRepository, CommissionService, Settlement, JsonResponse (+11 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (15): SettlementController, SettlementRepository, WalletTransactionRepository, CommissionService, Settlement, JsonResponse, Request, User (+7 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.06
|
||||
Nodes (8): DoctorService, Clinic, DoctorServiceRepository, Collection, Doctor, self, User, ManagerRegistry
|
||||
Cohesion: 0.05
|
||||
Nodes (11): DoctorServiceController, DoctorService, Clinic, DoctorServiceRepository, Collection, Doctor, self, User (+3 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.08
|
||||
@@ -867,8 +856,8 @@ Cohesion: 0.25
|
||||
Nodes (7): PaymentController, Appointment, JsonResponse, Payment, Request, Response, User
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.06
|
||||
Nodes (9): PatientSession, SmsWallet, AppLog, AppointmentExpiryService, Appointment, Collection, PatientRecord, self (+1 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (7): PatientSession, SmsWallet, Appointment, Collection, PatientRecord, self, SessionService
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.05
|
||||
@@ -891,8 +880,8 @@ Cohesion: 0.03
|
||||
Nodes (48): formatNumber(), ClinicAddress, ClinicDetailPage(), ClinicDoctorItem, ClinicInvitation, EditForm, editSchema, HUES_LIST (+40 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.11
|
||||
Nodes (14): RatingController, Like, Rate, LikeRepository, RateRepository, JsonResponse, Request, User (+6 more)
|
||||
Cohesion: 0.29
|
||||
Nodes (4): RatingController, JsonResponse, Request, User
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.05
|
||||
@@ -1011,8 +1000,8 @@ Cohesion: 0.07
|
||||
Nodes (26): الزامات UI, باگفیکس صفحه نوبتها, باگ ۱ — کرش تقویم, باگ ۲ — روز هفته در DateNavigator, باگ ۳ — پیام «slot نیست», باگ ۴ — نوبت جدید: نام اجباری + find-or-create patient, باگ ۵ — patient_mobile نشان میدهد موبایل پزشک, باگ ۶ — نوبتهای رزرو شده در نمایش زمانبندی (+18 more)
|
||||
|
||||
### Community 53 - "Community 53"
|
||||
Cohesion: 0.08
|
||||
Nodes (18): AppLogRepository, ClaimItemRepository, DoctorClaimRequestRepository, DoctorInsuranceRepository, PreRegistrationRepository, SessionServiceRepository, SmsSettingsRepository, ServiceEntityRepository (+10 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (15): AppLogRepository, ClaimItemRepository, DoctorClaimRequestRepository, DoctorInsuranceRepository, InvoiceRepository, PreRegistrationRepository, ServiceEntityRepository, ManagerRegistry (+7 more)
|
||||
|
||||
### Community 54 - "Community 54"
|
||||
Cohesion: 0.10
|
||||
@@ -1055,8 +1044,8 @@ Cohesion: 0.05
|
||||
Nodes (50): FreeVisitPrice(), Pricing, cn(), formatDate(), formatDateTime(), formatRial(), iranMobileOptionalSchema, iranMobileSchema (+42 more)
|
||||
|
||||
### Community 64 - "Community 64"
|
||||
Cohesion: 0.29
|
||||
Nodes (4): SmsWalletController, JsonResponse, Request, User
|
||||
Cohesion: 0.19
|
||||
Nodes (7): SmsWalletController, SmsSettingsRepository, SmsSettings, JsonResponse, Request, User, ManagerRegistry
|
||||
|
||||
### Community 65 - "Community 65"
|
||||
Cohesion: 0.08
|
||||
@@ -1127,8 +1116,8 @@ Cohesion: 0.07
|
||||
Nodes (30): devDependencies, @babel/core, @babel/preset-env, @babel/preset-react, @babel/preset-typescript, core-js, @csstools/postcss-oklab-function, @hotwired/stimulus (+22 more)
|
||||
|
||||
### Community 86 - "Community 86"
|
||||
Cohesion: 0.04
|
||||
Nodes (18): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, CaptchaFlowTest, ServiceItemDeleteCleanupTest, ServiceItemStaffOwnershipTest, TenantInsuranceCleanupTest (+10 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (16): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, CaptchaFlowTest, ServiceItemStaffOwnershipTest, EntityManagerInterface, KernelBrowser (+8 more)
|
||||
|
||||
### Community 87 - "Community 87"
|
||||
Cohesion: 0.10
|
||||
@@ -1215,8 +1204,8 @@ Cohesion: 0.33
|
||||
Nodes (6): AppointmentController, Appointment, Doctor, JsonResponse, Request, User
|
||||
|
||||
### Community 108 - "Community 108"
|
||||
Cohesion: 0.13
|
||||
Nodes (10): BaseController, CategoryController, CategoryImportController, SiteContextController, JsonResponse, JsonResponse, Request, JsonResponse (+2 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (13): CaptchaController, BaseController, CategoryController, DoctorImportController, SiteContextController, JsonResponse, JsonResponse, Request (+5 more)
|
||||
|
||||
### Community 109 - "Community 109"
|
||||
Cohesion: 0.29
|
||||
@@ -1315,8 +1304,8 @@ Cohesion: 0.12
|
||||
Nodes (16): Endpoint ها, GET /api/v1/representation/filter/{id}, GET /api/v1/representation/filter/{representationId}, GET /api/v1/representation/my-appointments/{id}, GET /api/v1/representation/{uuid}, GET /api/v1/representation/yearly-income/{id}, GET /api/v1/representation/yearly-income/{representationId}, POST /api/v1/representations/{id}/bank-accounts (+8 more)
|
||||
|
||||
### Community 135 - "Community 135"
|
||||
Cohesion: 0.09
|
||||
Nodes (22): Appointment Settings API, Available Locations, Day Index Convention, DELETE `/api/v1/booking-setting/{uuid}`, Errors, Errors, Errors, Errors (+14 more)
|
||||
Cohesion: 0.12
|
||||
Nodes (16): Day Index Convention, DELETE `/api/v1/booking-setting/{uuid}`, Errors, Errors, Errors, Errors, GET `/api/v1/appointment-settings/weekly-schedule/{uuid}`, PATCH `/api/v1/appointment-settings/weekly-schedule/{uuid}` (+8 more)
|
||||
|
||||
### Community 136 - "Community 136"
|
||||
Cohesion: 0.12
|
||||
@@ -1447,8 +1436,8 @@ Cohesion: 0.10
|
||||
Nodes (20): api.ir (استعلام هویت — Shahkar / IbanMatch), اتصال به دیتابیسهای مستقل (الزامی), اسرار (الزامی — قبل از اولین دیپلوی), امنیت و منابع, بررسی سلامت, دامنهها و CORS, دیپلویهای بعدی, راهنمای دیپلوی ClinicPro (Coolify + Docker Compose) (+12 more)
|
||||
|
||||
### Community 169 - "Community 169"
|
||||
Cohesion: 0.13
|
||||
Nodes (7): EntityInsurancePricing, EntityInsurancePricingRepository, TenantServiceCoverageRepository, TenantInsuranceCleanupService, ManagerRegistry, ManagerRegistry, TenantServiceCoverage
|
||||
Cohesion: 0.15
|
||||
Nodes (5): EntityInsurancePricing, TenantInsuranceCleanupTest, EntityInsurancePricingRepository, TenantInsuranceCleanupService, ManagerRegistry
|
||||
|
||||
### Community 170 - "Community 170"
|
||||
Cohesion: 0.13
|
||||
@@ -1582,9 +1571,13 @@ Nodes (12): Clinic Services API, DELETE /api/v1/service-item/{uuid}, DELETE /api
|
||||
Cohesion: 0.21
|
||||
Nodes (6): AuthenticationException, ExceptionSubscriber, SecurityHeadersSubscriber, EventSubscriberInterface, ExceptionEvent, ResponseEvent
|
||||
|
||||
### Community 205 - "Community 205"
|
||||
Cohesion: 0.21
|
||||
Nodes (4): SeedDemoDataCommand, InputInterface, OutputInterface, SymfonyStyle
|
||||
|
||||
### Community 206 - "Community 206"
|
||||
Cohesion: 0.06
|
||||
Nodes (17): GatewayFactory, MellatGateway, MockGateway, SepGateway, MellatGateway, SepGateway, SoapClient, PaymentGatewayInterface (+9 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (14): MellatGateway, MockGateway, SepGateway, SoapClient, PaymentGatewayInterface, PaymentInitResult, PaymentRefundResult, PaymentVerifyResult (+6 more)
|
||||
|
||||
### Community 207 - "Community 207"
|
||||
Cohesion: 0.15
|
||||
@@ -1835,8 +1828,8 @@ Cohesion: 0.17
|
||||
Nodes (5): Authentication, ClinicPro — API Documentation Index, Error Code Reference, Modules, Standard Response Envelope
|
||||
|
||||
### Community 275 - "Community 275"
|
||||
Cohesion: 0.19
|
||||
Nodes (5): MellatGatewayTest, ErrorCodesTest, KavehNegarProviderTest, TestCase, KavehNegarProvider
|
||||
Cohesion: 0.17
|
||||
Nodes (6): MellatGateway, MellatGatewayTest, ErrorCodesTest, KavehNegarProviderTest, TestCase, KavehNegarProvider
|
||||
|
||||
### Community 276 - "Community 276"
|
||||
Cohesion: 0.20
|
||||
@@ -1947,8 +1940,8 @@ Cohesion: 0.12
|
||||
Nodes (16): آمادهسازی پروژه ClinicPro برای دیپلوی روی Coolify با Docker Compose, زمینه, فایلهای مرتبط, نکات مهم (محدودیتها و edge caseها), هدف, وظایف, ۱. ساخت `Dockerfile` چندمرحلهای, ۱۰. ساخت راهنمای `docs/deploy/coolify.md` (+8 more)
|
||||
|
||||
### Community 304 - "Community 304"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): 30. 🟢 `GET` get 🆕, 33. 🔵 `POST` image_clinic, 36. 🟢 `GET` get my rate, 6. کلینیک (Clinic), هدرهای اضافی, هدرهای اضافی, پاسخها, پاسخها (+1 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (42): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 33. 🔵 `POST` image_clinic, 34. 🔵 `POST` image logo, 36. 🟢 `GET` get my rate, 37. 🔵 `POST` post, 39. 🟡 `PATCH` Comment confirmation, 40. 🔵 `POST` post (+34 more)
|
||||
|
||||
### Community 306 - "Community 306"
|
||||
Cohesion: 0.07
|
||||
@@ -2047,8 +2040,8 @@ Cohesion: 0.22
|
||||
Nodes (8): Query های جدید, بیماران منحصربهفرد در بازه, درآمد بر اساس روز (از patient_sessions), فروش اشتراک بر اساس پنل (admin), نوبتها بر اساس روز (admin chart), نکات مهم, هیچ migration لازم نیست, پایگاه داده — تسک ۱۶: داشبورد هوشمند
|
||||
|
||||
### Community 333 - "Community 333"
|
||||
Cohesion: 0.25
|
||||
Nodes (7): DELETE `/api/v1/clinic-pro/doctor-address/{id}`, Doctor API, Errors, GET `/api/v1/doctors`, Query Parameters, Response `200`, Response `200`
|
||||
Cohesion: 0.05
|
||||
Nodes (39): DELETE `/api/v1/clinic-pro/doctor-address/{id}`, DELETE `/api/v1/doctor/{uuid}`, Doctor API, Errors, Errors, Errors, Errors, Errors (+31 more)
|
||||
|
||||
### Community 334 - "Community 334"
|
||||
Cohesion: 0.25
|
||||
@@ -2294,6 +2287,10 @@ Nodes (6): ClaimAmountBoundsTest, ClaimsListNPlusOneTest, ClaimItem, Claim, Doct
|
||||
Cohesion: 0.22
|
||||
Nodes (7): initiate(), refund(), reverse(), verify(), PaymentInitResult, PaymentRefundResult, PaymentVerifyResult
|
||||
|
||||
### Community 399 - "Community 399"
|
||||
Cohesion: 0.23
|
||||
Nodes (5): AbstractMigration, Schema, Version20260609130407, Schema, Version20260610175105
|
||||
|
||||
### Community 401 - "Community 401"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): `lib/utils.ts`, `SettingsPage.tsx` (ورودیها ریال ذخیره میشوند), زمینه, فایلهای مرتبط, مشکل / هدف, نمونهٔ نمایش (Subscription), نکات مهم, واحد پول = تومان در پنل ادمین (نمایش ÷۱۰ / ورودی ×۱۰) — ذخیره و درگاه ریال میماند (+7 more)
|
||||
@@ -2303,8 +2300,8 @@ Cohesion: 0.11
|
||||
Nodes (17): بازطراحی معماری پرداخت — سرویسمحور، امن، توسعهپذیر (Backend), تست دستی (ddev، در حالت `payment_test_mode=1`), خروجی نهایی (طبق spec — در گزارش اجرا ارائه شود), زمینه, فایلهای مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی (+9 more)
|
||||
|
||||
### Community 407 - "Community 407"
|
||||
Cohesion: 0.26
|
||||
Nodes (7): MessageBusInterface, MockObject, SmsServiceLookupOnlyTest, SmsLogRepository, SmsMessageTemplateRepository, SmsTextResolver, KavehNegarProvider
|
||||
Cohesion: 0.27
|
||||
Nodes (8): MessageBusInterface, MockObject, SmsServiceLookupOnlyTest, SmsLogRepository, SmsMessageTemplateRepository, SmsService, SmsTextResolver, KavehNegarProvider
|
||||
|
||||
### Community 414 - "Community 414"
|
||||
Cohesion: 0.10
|
||||
@@ -2314,9 +2311,9 @@ Nodes (20): edge cases, خلاصهٔ خطاها و اولویت, راهحل,
|
||||
Cohesion: 0.17
|
||||
Nodes (11): دیپلوی ClinicPro روی Coolify (Docker Compose), رفع اشکال, مراحل دیپلوی, معماری دیپلوی, نکات عملیاتی, چند دامنه فرانتاند (مهم), ۱. ساخت Resource در Coolify, ۲. اختصاص دامنه (+3 more)
|
||||
|
||||
### Community 421 - "Community 421"
|
||||
Cohesion: 0.23
|
||||
Nodes (5): AbstractMigration, Schema, Version20260609132009, Schema, Version20260611075829
|
||||
### Community 422 - "Community 422"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): راهحل, راهحل, راهحل, رفع زوم نقشه در افزودن آدرس + کپچا و موبایل در claim + حذف پروفایل توسط مالک, ریشهها, زمینه, فایلهای مرتبط, نکات مهم (+7 more)
|
||||
|
||||
### Community 424 - "Community 424"
|
||||
Cohesion: 0.33
|
||||
@@ -2335,8 +2332,8 @@ Cohesion: 0.35
|
||||
Nodes (5): PatientController, JsonResponse, PatientSession, Request, User
|
||||
|
||||
### Community 435 - "Community 435"
|
||||
Cohesion: 0.16
|
||||
Nodes (10): Command, CancelExpiredAppointmentsCommand, PruneLogsCommand, PurgeDoctorsCommand, InputInterface, OutputInterface, InputInterface, OutputInterface (+2 more)
|
||||
Cohesion: 0.18
|
||||
Nodes (10): Command, CancelExpiredAppointmentsCommand, PruneLogsCommand, SeedSmsMessageTemplatesCommand, InputInterface, OutputInterface, InputInterface, OutputInterface (+2 more)
|
||||
|
||||
### Community 439 - "Community 439"
|
||||
Cohesion: 0.17
|
||||
@@ -2447,8 +2444,8 @@ Cohesion: 0.43
|
||||
Nodes (5): BeforeInstallPromptEvent, usePwaInstall(), PwaInstallBanner(), detectIOS(), PwaLoginCard()
|
||||
|
||||
### Community 483 - "Community 483"
|
||||
Cohesion: 0.43
|
||||
Nodes (3): InvoiceRepository, Invoice, ManagerRegistry
|
||||
Cohesion: 0.33
|
||||
Nodes (5): Like, LikeRepository, Comment, ManagerRegistry, User
|
||||
|
||||
### Community 487 - "Community 487"
|
||||
Cohesion: 0.40
|
||||
@@ -2459,13 +2456,17 @@ Cohesion: 0.40
|
||||
Nodes (5): Admin Endpoints, GET /api/v1/admin/sms/settings/review, GET /api/v1/admin/sms/wallet-report, POST /api/v1/admin/sms/settings/{id}/approve, POST /api/v1/admin/sms/settings/{id}/reject
|
||||
|
||||
### Community 490 - "Community 490"
|
||||
Cohesion: 0.36
|
||||
Nodes (3): DoctorServiceController, JsonResponse, Request
|
||||
Cohesion: 0.38
|
||||
Nodes (5): Rate, RateRepository, Doctor, ManagerRegistry, User
|
||||
|
||||
### Community 491 - "Community 491"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): `Modal.tsx` (بدون Portal), `PersianCalendar.tsx` (buttonها بدون `type`) — نمونهها, باگ ۱ — علت, باگ ۲ — علت, رفع دو باگ Modal و تقویم شمسی در پنل ادمین, زمینه, فایلهای مرتبط, مشکل / هدف (+7 more)
|
||||
|
||||
### Community 492 - "Community 492"
|
||||
Cohesion: 0.18
|
||||
Nodes (4): KavehNegarProvider, SmsService, SendSmsMessage, SmsProviderInterface
|
||||
|
||||
### Community 493 - "Community 493"
|
||||
Cohesion: 0.20
|
||||
Nodes (9): Backend Audit Backlog — ClinicPro, ☐ CRITICAL, ✅ DONE (committed on backend-audit), ☐ EPICS (design debt — cross-repo, defer; do NOT quick-fix), ☐ HIGH, ☐ LOW, ☐ MEDIUM, Progress (this audit session) (+1 more)
|
||||
@@ -2478,10 +2479,6 @@ Nodes (3): InvoiceItemRepository, InvoiceItem, ManagerRegistry
|
||||
Cohesion: 0.12
|
||||
Nodes (15): CORS — `config/packages/nelmio_cors.yaml`, MellatGateway — تشخیص فعالبودن, payment/config — `PaymentController::config()` (خط ۵۴۶), resolveGateway + callback (کد واقعی), رفع باگهای پروداکشن: CORS + payment/config 500 + درگاههای فعال و callback ملت, زمینه, فایلهای مرتبط, فرانت — `SubscriptionPage.tsx` (+7 more)
|
||||
|
||||
### Community 496 - "Community 496"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): Errors, PATCH `/api/v1/clinic-pro/doctor-address/{id}`, Path Parameters, Request Body, Response `200`
|
||||
|
||||
### Community 497 - "Community 497"
|
||||
Cohesion: 0.53
|
||||
Nodes (3): SlotUniquenessTest, Appointment, Doctor
|
||||
@@ -2775,8 +2772,8 @@ Cohesion: 0.50
|
||||
Nodes (3): Entity: Payment, ساختار فایلها, معماری — تسک ۱۵: ماژول پرداخت
|
||||
|
||||
### Community 595 - "Community 595"
|
||||
Cohesion: 0.36
|
||||
Nodes (4): SmsLogRepository, SmsLog, SmsService, ManagerRegistry
|
||||
Cohesion: 0.43
|
||||
Nodes (3): SmsLogRepository, SmsLog, ManagerRegistry
|
||||
|
||||
### Community 596 - "Community 596"
|
||||
Cohesion: 0.29
|
||||
@@ -2814,6 +2811,10 @@ Nodes (5): RepresentationController, JsonResponse, Representation, Request, User
|
||||
Cohesion: 0.67
|
||||
Nodes (3): بکاند, فرانتاند, وضعیت فعلی کد (مهم — قبل از تغییر بخوان)
|
||||
|
||||
### Community 618 - "Community 618"
|
||||
Cohesion: 0.16
|
||||
Nodes (5): ServiceItemDeleteCleanupTest, ServiceCoverageNPlusOneTest, TenantServiceCoverageRepository, ManagerRegistry, TenantServiceCoverage
|
||||
|
||||
### Community 625 - "Community 625"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): 31. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای اضافی, پاسخها
|
||||
@@ -2823,8 +2824,8 @@ Cohesion: 0.23
|
||||
Nodes (7): Money, BillingCalculator, InvoiceService, CoverageRule, ShareBreakdown, Invoice, PatientSession
|
||||
|
||||
### Community 631 - "Community 631"
|
||||
Cohesion: 0.32
|
||||
Nodes (3): SmsService, SendSmsMessage, SmsProviderInterface
|
||||
Cohesion: 0.39
|
||||
Nodes (4): FinancialBreakdown, FinancialBreakdownRepository, ManagerRegistry, Payment
|
||||
|
||||
### Community 632 - "Community 632"
|
||||
Cohesion: 0.40
|
||||
@@ -2919,8 +2920,8 @@ Cohesion: 0.40
|
||||
Nodes (5): addMinutes(), calcSlotCount(), hasOverlap(), parseMinutes(), SessionEditor()
|
||||
|
||||
### Community 677 - "Community 677"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): 37. 🔵 `POST` post, Request Body, مثال Request, هدرهای اضافی, پاسخها
|
||||
Cohesion: 0.29
|
||||
Nodes (6): Appointment Settings API, Available Locations, Errors, `GET /api/v1/appointment-settings/available-locations/{doctorUuid}`, Response `200`, Slot Calculation Logic (Reference)
|
||||
|
||||
### Community 678 - "Community 678"
|
||||
Cohesion: 0.53
|
||||
@@ -2939,25 +2940,21 @@ Cohesion: 0.40
|
||||
Nodes (5): GET `/api/v1/admin/comments`, GET `/api/v1/admin/rates`, Query Parameters, Query Parameters, Rating & Comment Management
|
||||
|
||||
### Community 687 - "Community 687"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): DELETE `/api/v1/doctor/{uuid}`, Errors, Path Parameters, Response `200`
|
||||
Cohesion: 0.38
|
||||
Nodes (3): PurgeDoctorsCommand, InputInterface, OutputInterface
|
||||
|
||||
### Community 689 - "Community 689"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): 41. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 690 - "Community 690"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/api/v1/doctor`, Request Body (`application/json`), Response `201`
|
||||
Cohesion: 0.43
|
||||
Nodes (3): CategoryImportController, JsonResponse, Request
|
||||
|
||||
### Community 692 - "Community 692"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): GET `/api/v1/admin/representations`, Query Parameters, Representation Management, Response `200`
|
||||
|
||||
### Community 693 - "Community 693"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, GET `/api/v1/doctor/{uuid}`, Path Parameters, Response `200`
|
||||
|
||||
### Community 694 - "Community 694"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Error Codes, POST `/api/v1/user/reset-password`, Request Body, Response `200`
|
||||
@@ -2975,8 +2972,8 @@ Cohesion: 0.17
|
||||
Nodes (11): رفع خطاهای ارسال پیامک کاوهنگار در سرور prod (431 + Idle timeout), زمینه, فایلهای مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+3 more)
|
||||
|
||||
### Community 700 - "Community 700"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/file/upload/clinic_pro/doctor/field_image`, Request, Response `200`
|
||||
Cohesion: 0.53
|
||||
Nodes (3): SessionServiceRepository, ManagerRegistry, SessionService
|
||||
|
||||
### Community 701 - "Community 701"
|
||||
Cohesion: 0.17
|
||||
@@ -2986,13 +2983,9 @@ Nodes (11): رفع خطای `Class "SoapClient" not found` در پرداخت م
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/api/v1/user/send-code`, Request Body, Response `200`
|
||||
|
||||
### Community 705 - "Community 705"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/api/v1/clinic-pro/doctor-address`, Request Body, Response `201`
|
||||
|
||||
### Community 706 - "Community 706"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, Path Parameters, POST `/api/v1/clinic-pro/doctor-address/from-clinic/{clinicUuid}`, Response `201`
|
||||
Cohesion: 0.40
|
||||
Nodes (5): Errors, GET `/api/v1/admin/clinic/{uuid}/invitations`, Path Parameters, Query Parameters, Response `200`
|
||||
|
||||
### Community 707 - "Community 707"
|
||||
Cohesion: 0.50
|
||||
@@ -3006,18 +2999,6 @@ Nodes (4): Errors, POST `/api/v1/user/login`, Request Body, Response `200`
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/oauth/token`, Request Body, Response `200`
|
||||
|
||||
### Community 712 - "Community 712"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): GET `/api/v1/clinic-pro/doctor-addresses/{doctorId}`, Path Parameters, Response `200`
|
||||
|
||||
### Community 713 - "Community 713"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): 39. 🟡 `PATCH` Comment confirmation, مثال Request, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 714 - "Community 714"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): 45. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 716 - "Community 716"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, GET `/oauth/userinfo`, Headers, Response `200`
|
||||
@@ -3062,14 +3043,6 @@ Nodes (4): Errors, GET `/api/v1/representation/doctors`, Query Parameters, Respo
|
||||
Cohesion: 0.50
|
||||
Nodes (4): 38. 🟢 `GET` Unapproved comments, هدرهای اضافی, پارامترهای Query, پاسخها
|
||||
|
||||
### Community 728 - "Community 728"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): 40. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 729 - "Community 729"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): 44. 🟢 `GET` list comment, هدرهای اضافی, پارامترهای Query, پاسخها
|
||||
|
||||
### Community 730 - "Community 730"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): 46. 🟡 `PATCH` patch, مثال Request, هدرهای اضافی, پاسخها
|
||||
@@ -3098,10 +3071,6 @@ Nodes (3): POST `/api/v1/admin/insurance/{id}/upload-logo`, Request, Response `2
|
||||
Cohesion: 0.67
|
||||
Nodes (3): GET `/api/v1/representation/{uuid}/dashboard/yearly`, Query Parameters, Response `200`
|
||||
|
||||
### Community 737 - "Community 737"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): 34. 🔵 `POST` image logo, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 738 - "Community 738"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, GET `/api/v1/representation/{uuid}`, Path Parameters, Response `200`
|
||||
@@ -3110,14 +3079,6 @@ Nodes (4): Errors, GET `/api/v1/representation/{uuid}`, Path Parameters, Respons
|
||||
Cohesion: 0.38
|
||||
Nodes (4): DoctorClaimController, JsonResponse, Request, User
|
||||
|
||||
### Community 740 - "Community 740"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): 42. 🔴 `DELETE` delete, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 741 - "Community 741"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): 43. 🟢 `GET` get, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 744 - "Community 744"
|
||||
Cohesion: 0.36
|
||||
Nodes (4): DoctorImportResult, DoctorImportService, Collection, User
|
||||
@@ -3130,10 +3091,6 @@ Nodes (3): BackfillSurrogateRoleCommand, InputInterface, OutputInterface
|
||||
Cohesion: 0.38
|
||||
Nodes (3): CreateAdminCommand, InputInterface, OutputInterface
|
||||
|
||||
### Community 751 - "Community 751"
|
||||
Cohesion: 0.25
|
||||
Nodes (5): HealthController, EntityManagerInterface, InputInterface, OutputInterface, JsonResponse
|
||||
|
||||
### Community 753 - "Community 753"
|
||||
Cohesion: 0.38
|
||||
Nodes (3): FixIrimcNamesCommand, InputInterface, OutputInterface
|
||||
@@ -3142,10 +3099,6 @@ Nodes (3): FixIrimcNamesCommand, InputInterface, OutputInterface
|
||||
Cohesion: 0.38
|
||||
Nodes (3): SystemOwnerCommand, InputInterface, OutputInterface
|
||||
|
||||
### Community 758 - "Community 758"
|
||||
Cohesion: 0.43
|
||||
Nodes (4): DoctorImportController, JsonResponse, Request, User
|
||||
|
||||
### Community 759 - "Community 759"
|
||||
Cohesion: 0.38
|
||||
Nodes (3): RepositoryClassMappingTest, KernelTestCase, DbLoggerTest
|
||||
@@ -3154,10 +3107,6 @@ Nodes (3): RepositoryClassMappingTest, KernelTestCase, DbLoggerTest
|
||||
Cohesion: 0.47
|
||||
Nodes (3): SeedCategoriesCommand, InputInterface, OutputInterface
|
||||
|
||||
### Community 762 - "Community 762"
|
||||
Cohesion: 0.47
|
||||
Nodes (3): SeedSmsMessageTemplatesCommand, InputInterface, OutputInterface
|
||||
|
||||
### Community 763 - "Community 763"
|
||||
Cohesion: 0.33
|
||||
Nodes (6): GET /api/v1/sms/balance — موجودی حساب پیامک, POST /api/v1/sms/queue — افزودن به صف, ۴. سیستم حساب و صف پیامک, ۴.۱ حساب پیامک (SMS Account), ۴.۲ صف پیامک (SMS Queue), ۴.۳ API پیامک
|
||||
@@ -3166,10 +3115,6 @@ Nodes (6): GET /api/v1/sms/balance — موجودی حساب پیامک, POST /a
|
||||
Cohesion: 0.53
|
||||
Nodes (3): PaymentLog, PaymentLogRepository, ManagerRegistry
|
||||
|
||||
### Community 765 - "Community 765"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): Errors, Path Parameters, POST `/api/v1/doctor/invitation/{invUuid}/respond`, Request Body, Response `200`
|
||||
|
||||
### Community 766 - "Community 766"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): ۲.۸ تنظیمات نوبت (Appointment Settings), ۲.۸.۱ برنامه هفتگی (Weekly Schedule), ۲.۸.۲ تعطیلات (Holidays), ۲.۸.۳ لغو تعطیل (Date Override), ۲.۸.۴ الگوریتم محاسبه اسلاتهای خالی
|
||||
@@ -3190,26 +3135,22 @@ Nodes (4): ۲.۱۳ نظرات، لایک و امتیازدهی, ۲.۱۳.۱ نظ
|
||||
Cohesion: 0.67
|
||||
Nodes (3): POST `/api/v1/admin/sms/template/{uuid}/approve`, Request Body (`application/json`), Response `200`
|
||||
|
||||
### Community 772 - "Community 772"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): 29. 🟢 `GET` clinic list 🆕, پارامترهای Query, پاسخها
|
||||
|
||||
## Knowledge Gaps
|
||||
- **4146 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps`, `TenantInsurance`, `CoverageRow` (+4141 more)
|
||||
- **4157 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps`, `TenantInsurance`, `CoverageRow` (+4152 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **159 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
- **160 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `Altcha` connect `Community 367` to `Community 0`, `Community 485`?**
|
||||
_High betweenness centrality (0.065) - this node is a cross-community bridge._
|
||||
- **Why does `BaseController` connect `Community 108` to `Community 6`, `Community 138`, `Community 139`, `Community 14`, `Community 654`, `Community 655`, `Community 15`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 295`, `Community 424`, `Community 300`, `Community 433`, `Community 308`, `Community 58`, `Community 59`, `Community 318`, `Community 64`, `Community 577`, `Community 75`, `Community 77`, `Community 607`, `Community 480`, `Community 739`, `Community 230`, `Community 104`, `Community 745`, `Community 490`, `Community 107`, `Community 109`, `Community 499`, `Community 252`, `Community 758`, `Community 121`, `Community 122`, `Community 380`?**
|
||||
- **Why does `BaseController` connect `Community 108` to `Community 6`, `Community 7`, `Community 138`, `Community 139`, `Community 14`, `Community 654`, `Community 655`, `Community 15`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 295`, `Community 424`, `Community 300`, `Community 433`, `Community 690`, `Community 308`, `Community 58`, `Community 59`, `Community 318`, `Community 64`, `Community 577`, `Community 75`, `Community 77`, `Community 607`, `Community 480`, `Community 739`, `Community 230`, `Community 104`, `Community 107`, `Community 109`, `Community 499`, `Community 252`, `Community 121`, `Community 122`, `Community 380`?**
|
||||
_High betweenness centrality (0.038) - this node is a cross-community bridge._
|
||||
- **Why does `Version20260705070546` connect `Community 646` to `Community 421`?**
|
||||
- **Why does `Version20260705070546` connect `Community 646` to `Community 399`?**
|
||||
_High betweenness centrality (0.024) - this node is a cross-community bridge._
|
||||
- **What connects `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps` to the rest of the system?**
|
||||
_4146 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
_4157 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.05075187969924812 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 1` be split into smaller, more focused modules?**
|
||||
|
||||
graphify-out/cache/ast/v0.8.44/02bf0d9faa621403a1f4e81f2c60384dd1ca334887d5de779e595be0633836ce.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/37e0abb6301621c0888c4a656b37bc60ba1deea4c7f5682270b0a478d51ddd40.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/416749471adfa15ec849cbf143456d33bdefddc9d52c52fa593d99598289da84.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/4b0e0251d1b9331ee58fc182d1a51a2e2297a3f5211886432403a8e8535d3a76.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/4dfa2a1a2a64648f2fdec17dc619423ea3ae2b5eb36fabbca78cf6b02b044002.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/7e99c6ac299dcc704fa82e35341b0d933f80d0290e6851e0c5d7e8c67a44bf4f.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/c18c03b15b41f7a206dc90d63428de7fbcefdb1cab1ef0e6b2202fe4ccda553e.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/c50066c8bd8a380b1536fe3928bd63f7687b5ba6fe066b54b0ea45f4c44a7422.json
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+847
-389
File diff suppressed because it is too large
Load Diff
+21
-16
@@ -230,8 +230,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"assets/admin/pages/DoctorDetailPage.tsx": {
|
||||
"mtime": 1783189217.065477,
|
||||
"ast_hash": "d10e0ec287b6d42011d384e3e8d8583b",
|
||||
"mtime": 1783768892.4257572,
|
||||
"ast_hash": "9e14c9cc70e4707bddaab0a5267e54c0",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"assets/admin/pages/DoctorFormPage.tsx": {
|
||||
@@ -1180,8 +1180,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Doctor/Controller/DoctorController.php": {
|
||||
"mtime": 1783747336.6328127,
|
||||
"ast_hash": "7828b41ebfdf61e887bf38d360c8a8e1",
|
||||
"mtime": 1783769018.613043,
|
||||
"ast_hash": "57c2d4ba148aa5084683dbfd08f8e2b0",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Doctor/Entity/Doctor.php": {
|
||||
@@ -2395,8 +2395,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/api/doctor.md": {
|
||||
"mtime": 1783757329.0750217,
|
||||
"ast_hash": "cb33dfc5636d36fdb6c98402cb789340",
|
||||
"mtime": 1783769157.105152,
|
||||
"ast_hash": "572576e30db5b049cb62ba8d54bdf8a5",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/api/insurance.md": {
|
||||
@@ -3900,8 +3900,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Doctor/Controller/DoctorClaimController.php": {
|
||||
"mtime": 1783757329.0908747,
|
||||
"ast_hash": "ed52d6bb87506e44be9f86adffee5645",
|
||||
"mtime": 1783768981.0086484,
|
||||
"ast_hash": "997576824b9039e977fd58efb417cf70",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Doctor/Controller/DoctorImportController.php": {
|
||||
@@ -3930,8 +3930,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Doctor/Service/DoctorImportService.php": {
|
||||
"mtime": 1783765876.696864,
|
||||
"ast_hash": "56bacf3ad261680191151c347f8cd411",
|
||||
"mtime": 1783766986.8738186,
|
||||
"ast_hash": "a675ebe7e1acac7da14cc8de3364757e",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Util/PersianText.php": {
|
||||
@@ -3940,13 +3940,13 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"tests/Doctor/DoctorClaimTest.php": {
|
||||
"mtime": 1783757329.09223,
|
||||
"ast_hash": "5973aa00e5a19406cd79f93be5455b76",
|
||||
"mtime": 1783769087.937134,
|
||||
"ast_hash": "50ea89c2d246faf98dc843152d51a0c5",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"tests/Doctor/DoctorImportTest.php": {
|
||||
"mtime": 1783765976.698628,
|
||||
"ast_hash": "1d99f84ff5b37dacd93000fa8562f994",
|
||||
"mtime": 1783766996.494613,
|
||||
"ast_hash": "997b8d698c8eb4bbf69652bdcead514d",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"tests/Shared/PersianTextTest.php": {
|
||||
@@ -3960,8 +3960,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/api/doctor-claim.md": {
|
||||
"mtime": 1783757329.090325,
|
||||
"ast_hash": "1cc55e0c447ec1d4176adc0700b8da54",
|
||||
"mtime": 1783769130.2951994,
|
||||
"ast_hash": "2f4e095e4e0cac673062cc48b0577d5d",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/scenarios/climed.md": {
|
||||
@@ -3993,5 +3993,10 @@
|
||||
"mtime": 1783765695.1482882,
|
||||
"ast_hash": "b03ab1640dc46aa51114fbc3eba51870",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
".claude/prompt/doctor-map-claim-captcha-delete.md": {
|
||||
"mtime": 1783768547.5967684,
|
||||
"ast_hash": "d4531629fe5bf2befc994f1a3f2a34ff",
|
||||
"semantic_hash": ""
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ class DoctorClaimController extends BaseController
|
||||
private readonly DoctorClaimService $claimService,
|
||||
private readonly RateLimiterFactory $doctorClaimLimiter,
|
||||
private readonly \App\Doctor\Repository\DoctorClaimRequestRepository $claimRepo,
|
||||
private readonly \App\Shared\Captcha\CaptchaGuard $captcha,
|
||||
) {}
|
||||
|
||||
#[OA\Get(
|
||||
@@ -82,6 +83,9 @@ class DoctorClaimController extends BaseController
|
||||
throw new TooManyRequestsHttpException($limit->getRetryAfter()->getTimestamp() - time());
|
||||
}
|
||||
|
||||
// کپچای ALTCHA (در dev با ALTCHA_ENABLED=false بیاثر) — خطا → ERR_CAPTCHA_001 (422)
|
||||
$this->captcha->assertValid($request);
|
||||
|
||||
$doctor = $this->doctorRepo->findByUuid($uuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
|
||||
@@ -92,7 +96,11 @@ class DoctorClaimController extends BaseController
|
||||
$birthDate = trim(\App\Shared\Util\PersianText::normalize((string) ($data['birth_date'] ?? '')));
|
||||
$firstName = trim((string) ($data['first_name'] ?? ''));
|
||||
$lastName = trim((string) ($data['last_name'] ?? ''));
|
||||
$mobile = preg_replace('/\D/', '', \App\Shared\Util\PersianText::normalize((string) ($data['mobile'] ?? '')));
|
||||
|
||||
if ($mobile !== '' && $mobile !== $user->getMobileNumber()) {
|
||||
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'شماره موبایل باید با حساب کاربری شما یکی باشد', 422, 'mobile');
|
||||
}
|
||||
if (!preg_match('/^\d{10}$/', $nationalCode)) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'کد ملی نامعتبر است', 422, 'national_code');
|
||||
}
|
||||
|
||||
@@ -368,14 +368,20 @@ class DoctorController extends BaseController
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/doctor/{uuid}', methods: ['DELETE'])]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
public function delete(string $uuid): JsonResponse
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function delete(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$doctor = $this->doctorRepo->findByUuid($uuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
// ادمین هر پروفایلی را؛ مالک فقط پروفایلِ claimedِ خودش را حذف میکند (ضد IDOR)
|
||||
$isOwner = $doctor->getOwnerStatus() === 'claimed' && $doctor->getUser()->getId() === $user->getId();
|
||||
if (!$user->hasRole('ROLE_ADMIN') && !$isOwner) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'اجازهٔ حذف این پروفایل را ندارید', 403);
|
||||
}
|
||||
|
||||
if ($this->appointmentRepo->count(['doctor' => $doctor]) > 0) {
|
||||
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این پزشک نوبت ثبتشده دارد و قابل حذف نیست', 409);
|
||||
}
|
||||
|
||||
@@ -169,6 +169,47 @@ class DoctorClaimTest extends ApiTestCase
|
||||
$this->assertSame(422, $this->responseCode());
|
||||
}
|
||||
|
||||
public function testMobileMismatchIsRejected(): void
|
||||
{
|
||||
$uuid = $this->importUnclaimedDoctor();
|
||||
$this->mockApiIr();
|
||||
$claimer = $this->createUser(['ROLE_USER']);
|
||||
|
||||
$body = $this->claimBody();
|
||||
$body['mobile'] = '09990000000'; // متفاوت با موبایل کاربر
|
||||
$this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $body);
|
||||
$this->assertSame(422, $this->responseCode());
|
||||
}
|
||||
|
||||
public function testOwnerCanDeleteOwnProfileButOthersCannot(): void
|
||||
{
|
||||
$uuid = $this->importUnclaimedDoctor();
|
||||
$this->mockApiIr();
|
||||
$claimer = $this->createUser(['ROLE_USER']);
|
||||
$this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $this->claimBody());
|
||||
$this->assertSame(200, $this->responseCode());
|
||||
|
||||
// کاربر دیگر → 403
|
||||
$other = $this->createUser(['ROLE_USER']);
|
||||
$this->authJson('DELETE', "/api/v1/doctor/{$uuid}", $other);
|
||||
$this->assertSame(403, $this->responseCode());
|
||||
|
||||
// مالک → 200
|
||||
$this->authJson('DELETE', "/api/v1/doctor/{$uuid}", $claimer);
|
||||
$this->assertSame(200, $this->responseCode());
|
||||
|
||||
$this->em->clear();
|
||||
$this->assertNull($this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]));
|
||||
}
|
||||
|
||||
public function testUnclaimedProfileNotDeletableByRandomUser(): void
|
||||
{
|
||||
$uuid = $this->importUnclaimedDoctor();
|
||||
$user = $this->createUser(['ROLE_USER']);
|
||||
$this->authJson('DELETE', "/api/v1/doctor/{$uuid}", $user);
|
||||
$this->assertSame(403, $this->responseCode());
|
||||
}
|
||||
|
||||
public function testClaimInfoIsPublic(): void
|
||||
{
|
||||
$uuid = $this->importUnclaimedDoctor();
|
||||
|
||||
Reference in New Issue
Block a user