- 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.
5.1 KiB
معماری — تسک ۰۱
ساختار فایل
src/Branch/
├── Controller/
│ ├── BranchController.php # CRUD شعبه + ساعت کاری
│ └── RoomController.php # CRUD اتاق
├── Entity/
│ ├── Branch.php
│ ├── BranchWorkingHours.php
│ └── Room.php
├── Repository/
│ ├── BranchRepository.php
│ ├── BranchWorkingHoursRepository.php
│ └── RoomRepository.php
├── Service/
│ ├── BranchService.php # ساخت/ویرایش/حذف + قواعد حذف
│ └── WorkingHoursService.php # اعتبارسنجی و ذخیرهٔ هفت روز
└── Command/
└── BackfillBranchCommand.php # app:branch:backfill
assets/admin/pages/
├── BranchesPage.tsx
├── BranchFormPage.tsx # شامل تب ساعت کاری
└── RoomsPage.tsx
لایهبندی
BranchController نازک است: اعتبارسنجی ورودی + EntityContextResolver + صدا زدن سرویس.
همهٔ قواعد (حذف امن، یکتایی نام در محیط، نرمالسازی ساعت) در BranchService و
WorkingHoursService.
final class BranchService
{
public function __construct(
private readonly BranchRepository $branches,
private readonly RoomRepository $rooms,
private readonly EntityManagerInterface $em,
) {}
public function create(EntityContext $ctx, BranchInput $input): Branch
{
$branch = new Branch($input->name);
$branch->assignTenant($ctx); // ← اجباری، وگرنه flush میشکند
// ...
}
/** حذف فقط وقتی هیچ اتاق یا منبعِ فعالی به شعبه وصل نیست. */
public function delete(Branch $branch): void
{
if ($this->rooms->countActiveByBranch($branch) > 0) {
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'شعبه دارای اتاق فعال است', 422);
}
// ...
}
}
رابطهٔ Branch با DoctorAddress
DoctorAddress حذف نمیشود. یک ستون branch_id تهیپذیر میگیرد:
DoctorAddress.branch_id ──▶ branches.id (nullable, ON DELETE SET NULL)
دلیل: location_id در JSON برنامهٔ هفتگی به doctor_addresses.id اشاره دارد و در
SlotCalculatorService و AppointmentController::bookingLocations() و سایت عمومی مصرف میشود.
تغییر آن قرارداد یعنی شکستن سه کلاینت. پس شعبه یک لایهٔ بالاتر مینشیند و آدرس به آن
لینک میشود، نه برعکس.
BackfillBranchCommand برای هر محیطی که آدرس دارد یک شعبه با نام آدرس میسازد و
branch_id را پر میکند. dry-run پیشفرض، --force برای اجرا.
ساعت کاری شعبه
مثل WeeklySchedule یک JSON نیست — جدول جداست، چون تسک ۰۳ باید بتواند
WHERE branch_id = ? AND day = ? بزند بدون خواندن و decode کردن JSON برای هر روز از ۹۰ روز.
#[ORM\Entity]
#[ORM\Table(name: 'branch_working_hours')]
#[ORM\UniqueConstraint(name: 'uniq_branch_day_seq', columns: ['branch_id', 'day_of_week', 'sequence'])]
class BranchWorkingHours
{
private int $dayOfWeek; // 0=شنبه … 6=جمعه — همان قرارداد SlotCalculatorService
private int $startMinute; // دقیقه از نیمهشب، 0..1440
private int $endMinute;
private int $sequence; // چند بازه در روز (صبح/عصر)
}
startMinute/endMinute بهجای رشتهٔ "08:30" ذخیره میشوند تا مقایسه و تقاطع در تسک ۰۶
حسابی باشد نه رشتهای. تبدیل به H:i فقط در toArray().
اتاق
class Room
{
use TenantOwnedTrait;
private Branch $branch;
private string $name;
private ?string $roomType = null; // متن آزاد — نوعِ اتاق را کلینیک تعریف میکند
private int $capacity = 1; // چند بیمار همزمان (اتاق تزریق سهتخته = 3)
private bool $active = true;
}
capacity از همینجا شروع میشود چون مستند بند ۶ صریح میگوید سه تخت = یک منبع با
ظرفیت سه، نه سه منبع. تسک ۰۲ همین معنا را روی Resource تکرار میکند و اتاق را
بهعنوان یک Resource با resource_type=room منعکس میکند.
پنل ادمین
BranchesPage.tsx—DataTable+PageHeaderباbackTo، وضعیت لیست در URL باuseUrlStateBranchFormPage.tsx— دو تب: مشخصات / ساعت کاری.SearchableSelectبرای شهر (هرگز<select>بومی)RoomsPage.tsx— زیرصفحهٔ شعبه،<BackButton fallback="/admin/branches" />- مسیرها در
App.tsx:/admin/branches,/admin/branches/new,/admin/branches/:uuid,/admin/branches/:uuid/rooms