department; $misDepartmentId = $department->rf_mis_department_id; $branchId = $this->getBranchId($misDepartmentId); // Определяем, используем ли мы снапшоты $useSnapshots = $this->shouldUseSnapshots($user, $dateRange); if ($useSnapshots) { return $this->getStatisticsFromSnapshots($user, $dateRange, $branchId); } return $this->getStatisticsFromReplica($user, $dateRange, $branchId); } /** * Создать или обновить отчет */ public function storeReport(array $data, User $user): Report { DB::beginTransaction(); try { $report = $this->createOrUpdateReport($data, $user); $this->saveMetrics($report, $data['metrics'] ?? []); $this->saveUnwantedEvents($report, $data['unwantedEvents'] ?? []); $this->saveObservationPatients($report, $data['observationPatients'] ?? [], $user->rf_department_id); // Сохраняем снапшоты пациентов $this->snapshotService->createPatientSnapshots($report, $user, $data['dates']); DB::commit(); return $report; } catch (\Exception $e) { DB::rollBack(); throw $e; } } /** * Получить пациентов по статусу */ public function getPatientsByStatus( User $user, string $status, DateRange $dateRange ) { $branchId = $this->getBranchId($user->department->rf_mis_department_id); $useSnapshots = $this->shouldUseSnapshots($user, $dateRange); if ($useSnapshots) { return $this->getPatientsFromSnapshots($user, $status, $dateRange, $branchId); } return $this->getPatientsFromReplica($user, $status, $dateRange, $branchId); } /** * Получить количество пациентов по статусу */ public function getPatientsCountByStatus( User $user, string $status, DateRange $dateRange ): int { $branchId = $this->getBranchId($user->department->rf_mis_department_id); $useSnapshots = $this->shouldUseSnapshots($user, $dateRange); if ($useSnapshots) { return $this->getPatientsCountFromSnapshots($user, $status, $dateRange); } return $this->getPatientsCountFromReplica($user, $status, $dateRange, $branchId); } /** * Получить ID отделения из стационарного отделения */ private function getBranchId(int $misDepartmentId): ?int { return MisStationarBranch::where('rf_DepartmentID', $misDepartmentId) ->value('StationarBranchID'); } /** * Определить, нужно ли использовать снапшоты */ private function shouldUseSnapshots(User $user, DateRange $dateRange): bool { if ($user->isAdmin() || $user->isHeadOfDepartment()) { return true; } // Проверяем, есть ли отчет на сегодня $reportToday = Report::whereDate('sent_at', $dateRange->end()) ->whereDate('created_at', $dateRange->end()) ->first(); return !$dateRange->isEndDateToday() || $reportToday; } /** * Создать или обновить отчет */ private function createOrUpdateReport(array $data, User $user): Report { $reportData = [ 'rf_department_id' => $data['departmentId'], 'rf_user_id' => $user->id, 'rf_lpudoctor_id' => $data['userId'], 'sent_at' => now(), ]; if (isset($data['reportId']) && $data['reportId']) { $report = Report::updateOrCreate( ['report_id' => $data['reportId']], $reportData ); } else { $report = Report::create($reportData); } return $report; } /** * Сохранить метрики отчета */ private function saveMetrics(Report $report, array $metrics): void { foreach ($metrics as $key => $value) { $metrikaId = (int)str_replace('metrika_item_', '', $key); MetrikaResult::updateOrCreate( [ 'rf_report_id' => $report->report_id, 'rf_metrika_item_id' => $metrikaId, ], [ 'value' => $value, ] ); } } /** * Сохранить нежелательные события */ private function saveUnwantedEvents(Report $report, array $unwantedEvents): void { if (empty($unwantedEvents)) { $report->unwantedEvents()->delete(); return; } foreach ($unwantedEvents as $event) { if (isset($event['unwanted_event_id']) && $event['unwanted_event_id']) { UnwantedEvent::updateOrCreate( ['unwanted_event_id' => $event['unwanted_event_id']], [ 'rf_report_id' => $report->report_id, 'comment' => $event['comment'] ?? '', 'title' => $event['title'] ?? '', 'is_visible' => $event['is_visible'] ?? true, ] ); } else { UnwantedEvent::create([ 'rf_report_id' => $report->report_id, 'comment' => $event['comment'] ?? '', 'title' => $event['title'] ?? '', 'is_visible' => $event['is_visible'] ?? true, ]); } } } /** * Сохранить пациентов под наблюдением */ private function saveObservationPatients( Report $report, array $observationPatients, int $departmentId ): void { if (empty($observationPatients)) { ObservationPatient::where('rf_department_id', $departmentId) ->where('rf_report_id', $report->report_id) ->delete(); return; } foreach ($observationPatients as $patient) { ObservationPatient::updateOrCreate( [ 'rf_medicalhistory_id' => $patient['id'], 'rf_department_id' => $departmentId, ], [ 'rf_report_id' => $report->report_id, 'rf_mkab_id' => null, 'comment' => $patient['comment'] ?? null, ] ); } } /** * Получить информацию о текущем отчете */ public function getCurrentReportInfo(User $user, DateRange $dateRange): array { $department = $user->department; $reportToday = Report::whereDate('sent_at', $dateRange->endSql()) ->whereDate('created_at', $dateRange->endSql()) ->first(); $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); $useSnapshots = $isHeadOrAdmin || !$dateRange->isEndDateToday() || $reportToday; // Получаем ID пользователя для заполнения отчета if ($useSnapshots && $isHeadOrAdmin && $reportToday) { $fillableUserId = $reportToday->rf_lpudoctor_id ?? null; } else { $fillableUserId = request()->query('userId', $user->rf_lpudoctor_id); } // Получаем нежелательные события $unwantedEvents = $this->getUnwantedEvents($user, $dateRange); // Определяем активность кнопки отправки $isActiveSendButton = $this->isSendButtonActive($user, $dateRange, $reportToday, $fillableUserId); $message = null; if ($reportToday && $reportToday->rf_lpudoctor_id !== intval($fillableUserId)) { $reportDoctor = $reportToday->lpuDoctor; $message = "Отчет уже создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V"; } // Получаем информацию о враче $lpuDoctor = $this->getDoctorInfo($fillableUserId, $dateRange); // Проверяем, является ли диапазон одним днем // $isRangeOneDay = $this->dateRangeService->isRangeOneDay( // $endDate->copy()->subDay()->format('Y-m-d H:i:s'), // $endDate->format('Y-m-d H:i:s') // ); // Формируем даты для ответа // $date = $isHeadOrAdmin ? [ // $endDate->copy()->subDay()->getTimestampMs(), // $endDate->getTimestampMs() // ] : $endDate->getTimestampMs(); $date = $isHeadOrAdmin ? [ $dateRange->startDate->getTimestampMs(), $dateRange->endDate->getTimestampMs() ] : $dateRange->endDate->getTimestampMs(); return [ 'report_id' => $reportToday?->report_id, 'unwantedEvents' => $unwantedEvents, 'isActiveSendButton' => $isActiveSendButton, 'message' => $message, 'isOneDay' => $dateRange->isOneDay, 'isHeadOrAdmin' => $isHeadOrAdmin, 'dates' => $date, 'userId' => $fillableUserId, 'userName' => $lpuDoctor ? "$lpuDoctor->FAM_V $lpuDoctor->IM_V $lpuDoctor->OT_V" : null ]; } /** * Удалить пациента из наблюдения */ public function removeObservationPatient(int $medicalHistoryId): void { ObservationPatient::where('rf_medicalhistory_id', $medicalHistoryId)->delete(); } /** * Получить статистику из снапшотов */ private function getStatisticsFromSnapshots(User $user, DateRange $dateRange, int $branchId): array { // Получаем отчеты за период $reports = $this->getReportsForDateRange( $user->rf_department_id, $dateRange ); $reportIds = $reports->pluck('report_id')->toArray(); // Получаем статистику из снапшотов $snapshotStats = [ 'plan' => $this->getMetrikaResultCount(4, $reportIds), 'emergency' => $this->getMetrikaResultCount(12, $reportIds), 'outcome' => $this->getMetrikaResultCount(7, $reportIds), 'deceased' => $this->getMetrikaResultCount(9, $reportIds), // 'discharged' => $this->getMetrikaResultCount('discharged', $reportIds), 'transferred' => $this->getMetrikaResultCount(13, $reportIds), 'recipient' => $this->getMetrikaResultCount(3, $reportIds), ]; // Получаем ID поступивших пациентов $recipientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'recipient') ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); // Получаем количество операций из метрик $surgicalCount = [ $this->getMetrikaResultCount(10, $reportIds), // экстренные операции $this->getMetrikaResultCount(11, $reportIds) // плановые операции ]; return [ 'recipientCount' => $snapshotStats['recipient'] ?? 0, 'extractCount' => $snapshotStats['outcome'] ?? 0, 'currentCount' => $this->calculateCurrentPatientsFromSnapshots($reportIds, $branchId), 'deadCount' => $snapshotStats['deceased'] ?? 0, 'surgicalCount' => $surgicalCount, 'recipientIds' => $recipientIds, ]; } /** * Получить статистику из реплики БД */ private function getStatisticsFromReplica(User $user, DateRange $dateRange, int $branchId): array { $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); // Плановые: поступившие сегодня + уже лечащиеся $planCount = $this->patientQueryService->getPatientsCountWithCurrent( 'plan', $isHeadOrAdmin, $branchId, $dateRange ); // Экстренные: поступившие сегодня + уже лечащиеся $emergencyCount = $this->patientQueryService->getPatientsCountWithCurrent( 'emergency', $isHeadOrAdmin, $branchId, $dateRange ); // Все пациенты в отделении: поступившие + лечащиеся $currentCount = $this->patientQueryService->getAllPatientsInDepartment( $isHeadOrAdmin, $branchId, $dateRange, true ); // Поступившие сегодня (только новые поступления) $recipientCount = $this->patientQueryService->getPlanOrEmergencyPatients( null, // все типы $isHeadOrAdmin, $branchId, $dateRange, true, false, false // не включаем уже лечащихся ); // Выбывшие за период $outcomeCount = $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'all' )->count(); // Умершие за период $deadCount = $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'deceased' )->count(); // Операции $surgicalCount = [ $this->patientQueryService->getSurgicalPatients( 'emergency', $branchId, $dateRange, true ), $this->patientQueryService->getSurgicalPatients( 'plan', $branchId, $dateRange, true ) ]; // ID поступивших сегодня (для отметки в таблице) $recipientIds = $this->patientQueryService->getPlanOrEmergencyPatients( null, $isHeadOrAdmin, $branchId, $dateRange, false, true, false // только поступившие сегодня ); return [ 'recipientCount' => $recipientCount, // только поступившие сегодня 'extractCount' => $outcomeCount, 'currentCount' => $currentCount, // все в отделении 'deadCount' => $deadCount, 'surgicalCount' => $surgicalCount, 'recipientIds' => $recipientIds, // ID поступивших сегодня 'planCount' => $planCount, // плановые (поступившие + уже лечащиеся) 'emergencyCount' => $emergencyCount, // экстренные (поступившие + уже лечащиеся) ]; } /** * Получить пациентов из снапшотов */ private function getPatientsFromSnapshots(User $user, string $status, DateRange $dateRange, int $branchId) { $reports = $this->getReportsForDateRange( $user->rf_department_id, $dateRange ); $reportIds = $reports->pluck('report_id')->toArray(); $patientTypeMap = [ 'plan' => 'plan', 'emergency' => 'emergency', 'outcome-discharged' => 'discharged', 'outcome-transferred' => 'transferred', 'outcome-deceased' => 'deceased', 'observation' => 'observation' ]; $patientType = $patientTypeMap[$status] ?? null; if ($patientType === 'observation') { return $this->getObservationPatientsFromSnapshots($user->rf_department_id, $reportIds); } return $this->snapshotService->getPatientsFromSnapshots($patientType, $reportIds, $branchId); } /** * Получить пациентов из реплики БД */ private function getPatientsFromReplica(User $user, string $status, DateRange $dateRange, int $branchId) { $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); // Для плановых и экстренных включаем уже лечащихся $includeCurrent = in_array($status, ['plan', 'emergency']); return match($status) { 'plan', 'emergency' => $this->patientQueryService->getPlanOrEmergencyPatients( $status, $isHeadOrAdmin, $branchId, $dateRange, false, false, $includeCurrent ), 'observation' => $this->patientQueryService->getObservationPatients($user->rf_department_id), 'outcome-discharged' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'discharged' ), 'outcome-transferred' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'transferred' ), 'outcome-deceased' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'deceased' ), 'current' => $this->patientQueryService->getAllPatientsInDepartment( $isHeadOrAdmin, $branchId, $dateRange ), 'recipient' => $this->patientQueryService->getPlanOrEmergencyPatients( null, $isHeadOrAdmin, $branchId, $dateRange, false, false, false // только поступившие ), default => collect() }; } /** * Получить количество пациентов из снапшотов */ private function getPatientsCountFromSnapshots(User $user, string $status, DateRange $dateRange): int { $reports = $this->getReportsForDateRange( $user->rf_department_id, $dateRange ); $reportIds = $reports->pluck('report_id')->toArray(); if ($status === 'outcome') { return MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->whereIn('patient_type', ['discharged', 'transferred', 'deceased']) ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'); } $patientTypeMap = [ 'plan' => 'plan', 'emergency' => 'emergency', 'observation' => 'observation', 'outcome-discharged' => 'discharged', 'outcome-transferred' => 'transferred', 'outcome-deceased' => 'deceased' ]; $patientType = $patientTypeMap[$status] ?? null; if (!$patientType) { return 0; } if ($patientType === 'observation') { return ObservationPatient::whereIn('rf_report_id', $reportIds) ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'); } return MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', $patientType) ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'); } /** * Получить количество пациентов из реплики БД */ private function getPatientsCountFromReplica(User $user, string $status, DateRange $dateRange, int $branchId): int { $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); return match($status) { 'plan', 'emergency' => $this->patientQueryService->getPatientsCountWithCurrent( $status, $isHeadOrAdmin, $branchId, $dateRange, ), 'observation' => ObservationPatient::where('rf_department_id', $user->rf_department_id)->count(), 'outcome' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'all' )->count(), 'outcome-discharged' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'discharged' )->count(), 'outcome-transferred' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'transferred' )->count(), 'outcome-deceased' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, 'deceased' )->count(), default => 0 }; } /** * Получить нежелательные события за дату */ private function getUnwantedEvents(User $user, DateRange $dateRange) { return UnwantedEvent::whereHas('report', function ($query) use ($user, $dateRange) { $query->where('rf_department_id', $user->rf_department_id) ->whereDate('created_at', $dateRange->endSql()); }) ->get() ->map(function ($item) { return [ ...$item->toArray(), 'created_at' => Carbon::parse($item->created_at)->format('Создано d.m.Y в H:i'), ]; }); } /** * Проверить активность кнопки отправки отчета */ private function isSendButtonActive(User $user, DateRange $dateRange, ?Report $reportToday, ?int $fillableUserId): bool { // Для врача: только сегодня и если отчета еще нет if (!$user->isHeadOfDepartment() && !$user->isAdmin()) { return $dateRange->isEndDateToday() && !$reportToday; } // Для заведующего/админа: если есть отчет и он заполнен текущим пользователем if ($reportToday && $reportToday->rf_lpudoctor_id === intval($fillableUserId)) { return true; } return false; } /** * Получить информацию о враче */ private function getDoctorInfo(?int $doctorId, DateRange $dateRange): ?MisLpuDoctor { if (!$doctorId) { return null; } // Если дата не сегодня, не показываем врача if (!$dateRange->isEndDateToday()) { return null; } return MisLpuDoctor::where('LPUDoctorID', $doctorId)->first(); } /** * Получить отчеты за диапазон дат */ private function getReportsForDateRange(int $departmentId, DateRange $dateRange) { if ($dateRange->isOneDay) { return Report::where('rf_department_id', $departmentId) ->whereDate('created_at', $dateRange->endSql()) ->orderBy('created_at', 'ASC') ->get(); } return Report::where('rf_department_id', $departmentId) ->whereBetween('created_at', [$dateRange->startSql(), $dateRange->endSql()]) ->orderBy('created_at', 'ASC') ->get(); } /** * Получить количество из метрик */ private function getMetrikaResultCount(int $metrikaItemId, array $reportIds): int { $count = 0; $reports = Report::whereIn('report_id', $reportIds) ->with('metrikaResults') ->get(); foreach ($reports as $report) { foreach ($report->metrikaResults as $metrikaResult) { if ($metrikaResult->rf_metrika_item_id === $metrikaItemId) { $count += intval($metrikaResult->value) ?? 0; } } } return $count; } /** * Рассчитать текущих пациентов из снапшотов */ private function calculateCurrentPatientsFromSnapshots(array $reportIds, int $branchId): int { // Получаем ID всех пациентов из снапшотов $allPatientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); if (empty($allPatientIds)) { return 0; } // Получаем ID выбывших пациентов $outcomePatientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->whereIn('patient_type', ['discharged', 'transferred', 'deceased']) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); // Текущие = все - выбывшие $currentPatientIds = array_diff($allPatientIds, $outcomePatientIds); return count($currentPatientIds); } /** * Получить пациентов под наблюдением из снапшотов */ private function getObservationPatientsFromSnapshots(int $departmentId, array $reportIds) { $medicalHistoryIds = ObservationPatient::whereIn('rf_report_id', $reportIds) ->where('rf_department_id', $departmentId) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { return collect(); } return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['observationPatient' => function($query) use ($departmentId) { $query->where('rf_department_id', $departmentId) ->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']); }]) ->orderBy('DateRecipient', 'DESC') ->get() ->map(function ($patient) { $patient->comment = $patient->observationPatient ->pluck('comment') ->filter() ->implode('; '); return $patient; }); } }