- 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.
131 lines
6.6 KiB
Markdown
131 lines
6.6 KiB
Markdown
# نکات پیادهسازی — تسک ۱۰
|
||
|
||
## ۱. rollback اجباری، سه لایه
|
||
|
||
```php
|
||
public function simulate(Policy $policy, int $size): SimulationReport
|
||
{
|
||
$this->em->beginTransaction();
|
||
try {
|
||
return $this->runInternal($policy, $size);
|
||
} finally {
|
||
$this->em->rollback(); // ← حتی اگر استثنا پرت شود
|
||
$this->em->clear(); // ← identity map پاک شود
|
||
}
|
||
}
|
||
```
|
||
|
||
`finally` نه `catch`: اگر شبیهسازی استثنا داد، هنوز باید rollback شود.
|
||
`clear()` بدون آن، entity های تغییرکردهٔ درون تراکنش در حافظه میمانند و اولین `flush`
|
||
در ادامهٔ همان request آنها را ثبت میکند — یک باگ که پیدا کردنش روزها میبرد.
|
||
|
||
ثبت `PolicySimulationRun` **بعد** از این بلوک و در تراکنش خودش.
|
||
|
||
## ۲. تست «هیچ چیزی ننوشت» — با شمارش، نه با اعتماد
|
||
|
||
```php
|
||
$before = $this->countRows(['appointments','price_snapshots','resource_occupancy',
|
||
'resource_occupancy_slot','appointment_segments']);
|
||
$this->simulator->simulate($policy, 50);
|
||
$after = $this->countRows([...]);
|
||
self::assertSame($before, $after, 'شبیهسازی نباید هیچ ردیفی بنویسد');
|
||
```
|
||
|
||
این تست ارزشمندترین تست این تسک است. هر بار که کسی `PolicySimulator` را تغییر دهد،
|
||
همین تست جلوی فاجعه را میگیرد.
|
||
|
||
## ۳. `evaluateIsolated` روی همهٔ شش موتور
|
||
|
||
اضافه کردن این متد به شش موتور تسک ۰۹، تغییر اینترفیس است. پس **در تسک ۰۹ اضافه شود**،
|
||
نه اینجا — وگرنه شش کلاس دوباره ویرایش میشوند.
|
||
|
||
اگر تسک ۰۹ تمام شده و این متد نیست، اضافهاش کن ولی بهعنوان یک متد در همان اینترفیس
|
||
موجود، نه یک اینترفیس جدید.
|
||
|
||
## ۴. فرم از schema — بدون استثنا
|
||
|
||
```tsx
|
||
// ❌ اولین وسوسه
|
||
const FIELDS = ['patient.age', 'patient.tags', 'service.category_path'];
|
||
|
||
// ✅
|
||
const { data: schema } = useQuery({ queryKey: ['policy-schema'], staleTime: 300_000 });
|
||
```
|
||
|
||
اگر فیلدها را در فرانت hard-code کنی، هر فیلد جدید در `FieldRegistry` نیاز به تغییر
|
||
فرانت دارد و بعد از دو ماه دو فهرست ناهمگام داریم. `staleTime` بلند اشکالی ندارد —
|
||
schema تقریباً هرگز عوض نمیشود.
|
||
|
||
## ۵. شدت `none` هم هشدار است
|
||
|
||
قانونی که روی هیچ نوبتی اثر نداشت، دو حالت دارد:
|
||
- شرطش هرگز true نمیشود (اشتباه نوشته شده)
|
||
- نمونهٔ ۵۰ نوبتی آن حالت را نداشت (شاید درست است)
|
||
|
||
پیام باید هر دو را بگوید:
|
||
|
||
> «این قانون روی هیچکدام از ۵۰ نوبت نمونه اثر نداشت. یا شرط آن هرگز برقرار نمیشود،
|
||
> یا این حالت در نوبتهای اخیر پیش نیامده. فعالسازی مجاز است.»
|
||
|
||
فعالسازی را نبند — کلینیک جدید هیچ نوبتی ندارد و باید بتواند قانون بسازد.
|
||
|
||
## ۶. الگوها باید واقعاً کار کنند
|
||
|
||
هر الگو در `PolicyTemplateRegistry` باید یک تست داشته باشد که آن را میسازد،
|
||
شبیهسازی میکند و فعال میکند. الگویی که `conditions` نامعتبر تولید کند، بدترین حالت است:
|
||
کاربر فرم آماده را پر میکند و `422` میگیرد.
|
||
|
||
```php
|
||
// tests/Policy/PolicyTemplateTest.php
|
||
/** @dataProvider templates */
|
||
public function testTemplateProducesValidPolicy(string $key): void
|
||
{
|
||
$policy = $this->registry->build($key, $this->sampleInputs($key));
|
||
$this->validator->assertValid($policy); // همان اعتبارسنجی POST /policy
|
||
}
|
||
```
|
||
|
||
## ۷. edge case ها
|
||
|
||
| حالت | رفتار درست |
|
||
|---|---|
|
||
| محیط بدون هیچ نوبت | گزارش خالی، `severity=none`، `activate` مجاز |
|
||
| قانون `deny` که همه را رد میکند | `severity=high`، فعالسازی با تأیید دوباره |
|
||
| `simulate` نسخهٔ ۱، بعد نسخهٔ ۲ ساخته شد | `activate` نسخهٔ ۲ → `422` |
|
||
| `simulate` دو بار پشتسرهم | آخری معیار است؛ قبلی میماند |
|
||
| قانون فعال که دوباره `simulate` میشود | مجاز — کاربر میخواهد اثرش را ببیند |
|
||
| نوبت نمونهای که سرویسش حذف شده | از نمونه حذف شود، در `sample_size` نیاید |
|
||
| قانون `pricing` روی نوبتی بدون snapshot | آن ردیف رد شود با علت `no_snapshot` |
|
||
| `sample_size` بزرگتر از ۵۰ | سقف ۵۰ — درخواست بیشتر `422` |
|
||
|
||
## ۸. تست
|
||
|
||
```
|
||
tests/Policy/PolicySimulatorTest.php ← ⭐
|
||
- هیچ ردیفی نوشته نمیشود (شمارش قبل/بعد)
|
||
- رفتار درست وقتی evaluateIsolated استثنا میدهد (rollback + clear)
|
||
- گزارش فقط نوبتهای تحت تأثیر را دارد
|
||
tests/Policy/SimulationSamplerTest.php
|
||
- فیلتر شعبه و سرویس از شرط قانون استخراج میشود
|
||
- فقط confirmed/completed
|
||
- سقف ۵۰
|
||
tests/Policy/PolicyActivationGuardTest.php ← ⭐
|
||
- activate بدون simulate → 422
|
||
- activate با simulate نسخهٔ قبلی → 422
|
||
- activate با simulate نسخهٔ جاری → 200
|
||
- محیط بدون نوبت: simulate خالی → activate مجاز
|
||
tests/Policy/PolicyTemplateTest.php
|
||
- هر الگو قانون معتبر تولید میکند (dataProvider روی همهٔ الگوها)
|
||
tests/Policy/SeverityTest.php
|
||
- ۰٪ → none · ۱۰٪ → low · ۴۰٪ → medium · ۸۰٪ → high
|
||
assets/admin/pages/PolicyFormPage.test.tsx
|
||
- فیلدها از schema میآیند (mock schema با فیلد ساختگی → در UI ظاهر شود)
|
||
- عملگرهای نامعتبر برای نوع فیلد نمایش داده نمیشوند
|
||
```
|
||
|
||
## ۹. مستندات
|
||
|
||
`docs/api/policy.md` را با `simulate` و `policy-templates` و شرط جدید `activate`
|
||
بهروز کن. در `docs/architecture/policy-engine.md` یک بخش «چرا آزمایش اجباری است»
|
||
اضافه کن با ارجاع به ریسک دوم مستند بند ۱۷.
|