- Add implementation notes for cancellation and waitlist features. - Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting. - Establish architecture for domain events and outbox pattern to ensure reliable event publishing. - Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports. - Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
82 lines
4.6 KiB
Markdown
82 lines
4.6 KiB
Markdown
# تسک ۰۷ — رزرو موقت و ثبت نهایی چندمنبعی
|
||
|
||
**فاز:** ۱ (هسته) · **وابستگی:** ۰۶ · **زمان:** ۱۶-۲۰ ساعت
|
||
|
||
---
|
||
|
||
## هدف
|
||
|
||
مستند بند ۱۱ و قانون سوم جمعبندی: «جلوگیری از رزرو تکراری کار دیتابیس است، نه کار کد».
|
||
سه مرحلهٔ `جستجو → رزرو موقت → ثبت نهایی` روی **چند منبع** پیاده شود، با تضمین یکتایی
|
||
در سطح دیتابیس.
|
||
|
||
## وضعیت فعلی — نقطهٔ قوت پروژه
|
||
|
||
```php
|
||
// src/Appointment/Entity/Appointment.php
|
||
public const PAYMENT_TTL = 900;
|
||
#[ORM\Column(name: 'active_slot_key', length: 64, nullable: true, unique: true)]
|
||
private ?string $activeSlotKey = null; // "{doctorId}:{slotStart}" یا NULL
|
||
|
||
private function refreshActiveSlotKey(): void {
|
||
$this->activeSlotKey = !$this->isReserve && in_array($this->status, self::SLOT_OCCUPYING_STATUSES, true)
|
||
? sprintf('%d:%d', $this->doctor->getId(), $this->slotStart)
|
||
: null;
|
||
}
|
||
```
|
||
|
||
سه مرحله و تضمین دیتابیسی **از قبل درست پیاده شدهاند**. محدودیت: کلید فقط
|
||
`doctor + slot_start` است و هیچ منبع دیگری را نمیپوشاند، و مدل «یک ردیف = یک بازهٔ پیوسته»
|
||
است.
|
||
|
||
## دامنه
|
||
|
||
**هست:**
|
||
- `resource_occupancy` — تنها مرجع حقیقت اشغال منابع
|
||
- `appointment_segments` — بخشهای نوبت ثبتشده
|
||
- `HoldService` — رزرو موقت چندمنبعی با TTL
|
||
- `BookingService` — ثبت نهایی اتمی
|
||
- تضمین یکتایی در MariaDB (بدون `EXCLUDE` — راهحل «سطل زمانی»)
|
||
- انقضای خودکار hold ها (توسعهٔ `ExpireAppointmentsHandler` موجود)
|
||
- وضعیت `rescheduled` و رویداد جابهجایی
|
||
|
||
**نیست:** قیمتگذاری تفکیکشده (تسک ۰۸)، سیاست لغو و جریمه (تسک ۱۳).
|
||
|
||
## Endpoint ها
|
||
|
||
| متد | مسیر | توضیح |
|
||
|---|---|---|
|
||
| POST | `/api/v1/appointment-hold` | رزرو موقت یک زمان + منابعش |
|
||
| DELETE | `/api/v1/appointment-hold/{uuid}` | آزادسازی زودهنگام |
|
||
| POST | `/api/v1/appointment-confirm` | ثبت نهایی از یک hold معتبر |
|
||
| POST | `/api/v1/appointment/{uuid}/reschedule` | جابهجایی (hold جدید + آزادسازی قدیم، اتمی) |
|
||
|
||
## معیار پذیرش
|
||
|
||
- ✅ موفق: `POST /appointment-hold` با زمان و `assignment` معتبر → `201` با
|
||
`hold_uuid` و `expires_at`؛ ردیفهای `resource_occupancy` با `status='hold'` برای
|
||
**هر بخش × هر منبع** ثبت میشوند.
|
||
- ✅ موفق: بلافاصله بعد از hold، `POST /appointment-availability` همان زمان را **برنمیگرداند**.
|
||
- ✅ موفق: `POST /appointment-confirm` → `200`، وضعیت `confirmed`، همهٔ ردیفهای اشغال
|
||
`status='booked'` و `expires_at = NULL`.
|
||
- ✅ موفق (**تست همزمانی — اصلیترین**): دو درخواست hold همزمان روی همان منبع و بازه →
|
||
دقیقاً **یکی** `201` و دیگری `409` با `ERR_SLOT_TAKEN`. تست باید با تراکنش واقعی
|
||
موازی اجرا شود، نه mock.
|
||
- ✅ موفق: hold منقضیشده → `POST /appointment-confirm` با `409 ERR_HOLD_EXPIRED` و آن
|
||
زمان دوباره در جستجو ظاهر میشود.
|
||
- ✅ موفق: آزادسازی ظرفیت حفظ میشود — اپراتور در بخش انتظار ردیف اشغال **ندارد**.
|
||
- ❌ خطا: `confirm` با hold متعلق به کاربر دیگر → `404`.
|
||
- ❌ خطا: hold با منبعی که در `assignment` نیست ولی نیازمندی دارد → `422`.
|
||
- ⚠️ مرزی: منبع با `capacity=3` → سه hold همزمان موفق، چهارمی `409`.
|
||
- ⚠️ مرزی: لغو نوبت → همهٔ ردیفهای اشغالش آزاد (`status='released'`)، نه حذف فیزیکی.
|
||
- ⚠️ مرزی: `reschedule` که hold جدیدش شکست بخورد → نوبت قدیمی **دستنخورده** بماند.
|
||
- ⚠️ مرزی: نوبتهای حالت `slot`/`service` → `active_slot_key` قدیمی همچنان کار میکند و
|
||
ردیف اشغال هم برایشان ساخته میشود (تور ایمنی دوگانه).
|
||
|
||
## خروجی
|
||
|
||
- `src/Appointment/Booking/`
|
||
- توسعهٔ `Appointment` entity با `segments` و رابطهٔ اشغال
|
||
- `docs/api/appointment-booking.md` + بهروزرسانی `docs/api/appointment.md`
|
||
- تست همزمانی واقعی
|