feat: Implement Fast Request feature with file upload and request management

This commit is contained in:
2026-08-26 17:40:45 +03:30
parent ba734679cf
commit 2d96978c38
33 changed files with 1294 additions and 852 deletions
+103
View File
@@ -0,0 +1,103 @@
<?php
namespace Database\Seeders;
use App\Enums\DocumentModalityEnum;
use App\Enums\DocumentPriorityEnum;
use App\Enums\DocumentStatusEnum;
use App\Enums\UserEnum;
use App\Models\Document;
use App\Models\User;
use Illuminate\Database\Seeder;
class DocumentSeeder extends Seeder
{
protected int $documentsPerOwner = 30;
public function run(): void
{
$doctors = User::where('level', UserEnum::Doctor->value)->get();
$owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get();
if ($doctors->isEmpty() || $owners->isEmpty()) {
$this->call(FastRequestSeeder::class);
$doctors = User::where('level', UserEnum::Doctor->value)->get();
$owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get();
}
$notes = [
'لنگش اندام حرکتی جلو از دو روز پیش، درخواست گرافی مفصل شانه و آرنج.',
'استفراغ مکرر و بی‌اشتهایی، احتمال جسم خارجی در دستگاه گوارش. سونوگرافی شکم انجام شود.',
'سرفه مزمن و تنگی نفس، درخواست گرافی قفسه سینه در دو نما.',
'تشنج ناگهانی بدون سابقه قبلی، بررسی جمجمه از نظر ضایعه فضاگیر.',
'سوفل قلبی درجه سه در سمع، درخواست اکوکاردیوگرافی کامل.',
'ضعف پیش‌رونده اندام حرکتی عقب، بررسی ستون فقرات کمری از نظر دیسک.',
'تروما به دنبال تصادف با خودرو، بررسی لگن و اندام حرکتی از نظر شکستگی.',
'اتساع شکم و درد در لمس، درخواست سونوگرافی اورژانسی جهت رد پیچ‌خوردگی معده.',
'توده قابل لمس در ناحیه گردن، بررسی ابعاد و منشا توده با سونوگرافی.',
'کج شدن سر و نیستاگموس، بررسی گوش میانی و داخلی با سی‌تی‌اسکن.',
'پرنوشی و پرادراری از یک هفته پیش، بررسی کلیه‌ها و مثانه.',
'خونریزی از بینی و عطسه مکرر، بررسی حفره بینی از نظر جسم خارجی.',
'عدم تحمل وزن روی اندام عقب چپ، احتمال پارگی رباط صلیبی. گرافی زانو انجام شود.',
'کاهش وزن مزمن با وجود اشتهای طبیعی، سونوگرافی کامل محوطه بطنی.',
'دیسترس تنفسی حاد، بررسی پرده جنب از نظر تجمع مایع.',
];
$rejectionReasons = [
'کیفیت تصاویر ارسالی برای تفسیر کافی نیست، لطفا مجددا تصویربرداری شود.',
'اطلاعات بالینی ناقص است، شرح حال کامل بیمار ثبت شود.',
'نمای درخواستی با شکایت بالینی همخوانی ندارد.',
];
$statuses = [
DocumentStatusEnum::Registered,
DocumentStatusEnum::InProgress,
DocumentStatusEnum::Registered,
DocumentStatusEnum::InProgress,
DocumentStatusEnum::Registered,
DocumentStatusEnum::Completed,
DocumentStatusEnum::Registered,
DocumentStatusEnum::InProgress,
DocumentStatusEnum::Rejected,
DocumentStatusEnum::Registered,
];
$deadlineMinutes = [-180, 25, 53, 240, 420, null, 700, 1100, 1400, -30, 1900, 2600];
$modalities = DocumentModalityEnum::cases();
foreach ($owners as $owner) {
$pets = $owner->pets;
if ($pets->isEmpty()) {
continue;
}
$missing = $this->documentsPerOwner - Document::where('requester_id', $owner->id)->count();
for ($i = 0; $i < $missing; $i++) {
$status = $statuses[$i % count($statuses)];
$deadline = $deadlineMinutes[$i % count($deadlineMinutes)];
$submittedAt = now()->subDays(rand(0, 20))->subMinutes(rand(0, 1440));
$document = Document::create([
'modality' => $modalities[$i % count($modalities)]->value,
'priority' => ($i % 4 === 0 ? DocumentPriorityEnum::High : DocumentPriorityEnum::Medium)->value,
'status' => $status->value,
'note' => $notes[$i % count($notes)],
'submitted_at' => $submittedAt,
'scan_date' => $status === DocumentStatusEnum::Registered ? null : $submittedAt->copy()->addHours(rand(1, 12)),
'processing_deadline' => $deadline !== null ? now()->addMinutes($deadline) : null,
'finalized_at' => $status === DocumentStatusEnum::Completed ? $submittedAt->copy()->addDays(rand(1, 3)) : null,
'finalized_by' => $status === DocumentStatusEnum::Completed ? $doctors->random()->id : null,
'rejection_reason' => $status === DocumentStatusEnum::Rejected ? $rejectionReasons[$i % count($rejectionReasons)] : null,
'pet_id' => $pets->random()->id,
'requester_id' => $owner->id,
]);
$document->assignments()->create([
'user_id' => $doctors->random()->id,
'role' => 'رادیولوژیست',
]);
}
}
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
namespace Database\Seeders;
use App\Enums\DocumentModalityEnum;
use App\Enums\DocumentPriorityEnum;
use App\Enums\DocumentStatusEnum;
use App\Enums\PetGenderEnum;
use App\Enums\PetTypeEnum;
use App\Enums\UserEnum;
use App\Models\Document;
use App\Models\Pet;
use App\Models\User;
use Illuminate\Database\Seeder;
class FastRequestSeeder extends Seeder
{
public function run(): void
{
$doctors = $this->seedDoctors();
$this->seedPets();
$this->seedDocuments($doctors);
}
protected function seedDoctors()
{
$doctors = [
['email' => 'dr.hosseini@radical.dev', 'fullname' => 'دکتر حسین حسینی', 'specialty' => 'متخصص جراحی پوست و مو'],
['email' => 'dr.amin@radical.dev', 'fullname' => 'دکتر امین حسینی', 'specialty' => 'متخصص و رادیولوژیست'],
['email' => 'dr.mahdipour@radical.dev', 'fullname' => 'دکتر مریم مهدی‌پور', 'specialty' => 'متخصص تصویربرداری'],
['email' => 'dr.golian@radical.dev', 'fullname' => 'دکتر رضا گلیان', 'specialty' => 'متخصص داخلی دام کوچک'],
['email' => 'dr.karimi@radical.dev', 'fullname' => 'دکتر سارا کریمی', 'specialty' => 'متخصص سونوگرافی و اکو'],
['email' => 'dr.nazari@radical.dev', 'fullname' => 'دکتر بهرام نظری', 'specialty' => 'متخصص ارتوپدی دام کوچک'],
];
return collect($doctors)->map(function ($doctor) {
return User::firstOrCreate(
['username' => explode('@', $doctor['email'])[0]],
User::factory()->raw([
'email' => $doctor['email'],
'level' => UserEnum::Doctor->value,
'fullname' => $doctor['fullname'],
'specialty' => $doctor['specialty'],
])
);
});
}
protected function seedPets(): void
{
$pets = [
['name' => 'مایلو', 'type' => PetTypeEnum::Dog, 'gender' => PetGenderEnum::Male, 'breed' => 'ژرمن شپرد', 'weight' => 28, 'sterilized' => true],
['name' => 'تدی', 'type' => PetTypeEnum::Cat, 'gender' => PetGenderEnum::Male, 'breed' => 'پرشین', 'weight' => 4, 'sterilized' => false],
['name' => 'پونی', 'type' => PetTypeEnum::Rabbit, 'gender' => PetGenderEnum::Female, 'breed' => 'لوپ هلندی', 'weight' => 2, 'sterilized' => false],
['name' => 'روبیک', 'type' => PetTypeEnum::Hamster, 'gender' => PetGenderEnum::Male, 'breed' => 'سوری', 'weight' => 1, 'sterilized' => false],
['name' => 'کولا', 'type' => PetTypeEnum::GuineaPig, 'gender' => PetGenderEnum::Female, 'breed' => 'آمریکایی', 'weight' => 1, 'sterilized' => true],
['name' => 'هاپو', 'type' => PetTypeEnum::Dog, 'gender' => PetGenderEnum::Male, 'breed' => 'هاسکی سیبری', 'weight' => 24, 'sterilized' => false],
['name' => 'نازی', 'type' => PetTypeEnum::Cat, 'gender' => PetGenderEnum::Female, 'breed' => 'اسکاتیش فولد', 'weight' => 3, 'sterilized' => true],
['name' => 'قناری', 'type' => PetTypeEnum::Bird, 'gender' => PetGenderEnum::Unknown, 'breed' => 'قناری رسمی', 'weight' => 1, 'sterilized' => false],
['name' => 'لاکی', 'type' => PetTypeEnum::Turtle, 'gender' => PetGenderEnum::Female, 'breed' => 'برکه‌ای', 'weight' => 1, 'sterilized' => false],
['name' => 'اسنوپی', 'type' => PetTypeEnum::Dog, 'gender' => PetGenderEnum::Female, 'breed' => 'شیتزو', 'weight' => 6, 'sterilized' => true],
];
$owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get();
foreach ($owners as $owner) {
foreach ($pets as $pet) {
Pet::firstOrCreate(
['name' => $pet['name'], 'owner_id' => $owner->id],
[
'type' => $pet['type']->value,
'gender' => $pet['gender']->value,
'breed' => $pet['breed'],
'weight' => $pet['weight'],
'sterilized' => $pet['sterilized'],
'birthday' => now()->subYears(rand(1, 8))->startOfDay(),
]
);
}
}
}
protected function seedDocuments($doctors): void
{
$requests = [
['pet' => 'مایلو', 'modality' => DocumentModalityEnum::Radiology, 'priority' => DocumentPriorityEnum::High, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => -120, 'note' => 'لنگش اندام حرکتی عقب پس از پرش، احتمال آسیب مفصل ران. لطفا گرافی لگن بررسی شود.'],
['pet' => 'تدی', 'modality' => DocumentModalityEnum::Sonography, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => 53, 'note' => 'بی‌اشتهایی و استفراغ متناوب از سه روز پیش، درخواست سونوگرافی شکمی.'],
['pet' => 'هاپو', 'modality' => DocumentModalityEnum::CtScan, 'priority' => DocumentPriorityEnum::High, 'status' => DocumentStatusEnum::InProgress, 'deadline_minutes' => 290, 'note' => 'تشنج ناگهانی، درخواست سی‌تی‌اسکن جمجمه جهت رد ضایعه فضاگیر.'],
['pet' => 'نازی', 'modality' => DocumentModalityEnum::Echo, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => 1400, 'note' => 'سوفل قلبی در معاینه شنیده شد، درخواست اکوکاردیوگرافی.'],
['pet' => 'اسنوپی', 'modality' => DocumentModalityEnum::Mri, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::InProgress, 'deadline_minutes' => 1900, 'note' => 'ضعف پیش‌رونده اندام حرکتی، بررسی ستون فقرات از نظر دیسک بین‌مهره‌ای.'],
['pet' => 'پونی', 'modality' => DocumentModalityEnum::Radiology, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => null, 'note' => 'کاهش وزن و دندان قروچه، درخواست گرافی دندان و فک.'],
['pet' => 'روبیک', 'modality' => DocumentModalityEnum::Sonography, 'priority' => DocumentPriorityEnum::High, 'status' => DocumentStatusEnum::InProgress, 'deadline_minutes' => 45, 'note' => 'تورم ناحیه شکم و بی‌حالی شدید، درخواست سونوگرافی اورژانسی.'],
['pet' => 'کولا', 'modality' => DocumentModalityEnum::CtScan, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => 600, 'note' => 'کج شدن سر و عدم تعادل، بررسی گوش میانی و داخلی.'],
];
$owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get();
foreach ($owners as $owner) {
if (Document::where('requester_id', $owner->id)->exists()) {
continue;
}
foreach ($requests as $index => $request) {
$pet = Pet::where('name', $request['pet'])->where('owner_id', $owner->id)->first();
if (!$pet) {
continue;
}
$document = Document::create([
'modality' => $request['modality']->value,
'priority' => $request['priority']->value,
'status' => $request['status']->value,
'note' => $request['note'],
'submitted_at' => now()->subDays(count($requests) - $index),
'processing_deadline' => $request['deadline_minutes'] !== null ? now()->addMinutes($request['deadline_minutes']) : null,
'pet_id' => $pet->id,
'requester_id' => $owner->id,
]);
$document->assignments()->create([
'user_id' => $doctors[$index % $doctors->count()]->id,
'role' => 'رادیولوژیست',
]);
}
}
}
}