whereHas('migrations', function ($q) use ($departmentId, $dateRange) { $q->department($departmentId)->dateRange($dateRange->startSql(), $dateRange->endSql()); }) ->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) { $q->department($departmentId)->dateRange($dateRange->startSql(), $dateRange->endSql()); }]); } /** * Сохранить отчет */ public function saveReport(DateRange $dateRange, ?int $userId = null, ?int $lpuDoctorId = null, ?int $departmentId = null) { $user = $userId ? User::find($userId) : auth()->user(); $lpuDoctorId = $lpuDoctorId ?? $user->rf_lpudoctor_id; $departmentId = $departmentId ?? $user->rf_department_id; $data = [ 'report_date' => Carbon::now()->format('Y-m-d'), 'sent_at' => Carbon::now()->format('Y-m-d H:i:s'), 'period_type' => 'day', 'period_start' => $dateRange->startSql(), 'period_end' => $dateRange->endSql(), 'status_id' => 2, // опубликован 'rf_lpudoctor_id' => $lpuDoctorId, 'rf_department_id' => $departmentId, 'rf_user_id' => $user->id, ]; $report = ReportNurse::updateOrCreate( [ 'report_date' => $data['report_date'], 'period_start' => $data['period_start'], 'period_end' => $data['period_end'] ], $data ); return $report; } /** * Сохранить снимок пациентов за период */ public function saveSnapshot(DateRange $dateRange, ReportNurse $reportNurse, ?int $departmentId = null, ?int $userId = null): array { $departmentId = $departmentId ?? $reportNurse->department->rf_mis_department_id; $userId = $userId ?? $reportNurse->rf_user_id; $startYear = Carbon::now()->startOfYear()->format('Y-m-d'); $query = UnifiedMedicalHistory::query() // Фильтруем движения по отделению + пересечение дат ->whereHas('migrations', function ($q) use ($departmentId, $dateRange, $startYear) { $q->where('department_id', $departmentId) // пребывание пересекается с отчётным периодом ->where('ingoing_date', '<=', $dateRange->endSql()) ->where('ingoing_date', '>=', $startYear) ->where(function ($sub) use ($dateRange) { $sub->whereNull('out_date') ->orWhere('out_date', '>=', $dateRange->startSql()) ->where('out_date', '<=', $dateRange->endSql()); }); }); // Получаем данные (chunk для памяти, если пациентов > 1000) $patients = $query->cursor(); $savedStats = $this->saveReportSnapshot($reportNurse->id, $patients, $userId); return [ ...$savedStats, 'report_date' => $dateRange->startSql(), 'department_id' => $departmentId, ]; } public function saveReportSnapshot(int $reportNurseId, iterable $patients, int $userId): array { if (empty($patients)) { return ['saved_patients' => 0, 'saved_migrations' => 0]; } $patientBatch = []; $migrationBatch = []; $batchSize = 100; foreach ($patients as $patient) { // Подготовка данных пациента $patientBatch[] = [ 'report_nurse_id' => $reportNurseId, 'source_type' => $patient->source_type, 'original_id' => $patient->original_id, 'medical_card_number' => $patient->medical_card_number, 'full_name' => $patient->full_name, 'birth_date' => $patient->birth_date, 'recipient_date' => $patient->recipient_date, 'extract_date' => $patient->extract_date, 'death_date' => $patient->death_date, 'male' => $patient->male, 'urgency_id' => $patient->urgency_id, 'hospital_result_id' => $patient->hospital_result_id, 'visit_result_id' => $patient->visit_result_id, 'comment' => $patient->comment, 'user_id' => $userId, ]; // Подготовка данных миграции (если есть) if (!empty($patient->migrations)) { foreach ($patient->migrations as $migration) { $migrationBatch[] = [ // Временный ключ для связи с пациентом (заполним после первого upsert) '_temp_key' => [ 'report_nurse_id' => $reportNurseId, 'source_type' => $patient->source_type, 'original_id' => $patient->original_id, ], 'ingoing_date' => $migration->ingoing_date, 'out_date' => $migration->out_date, 'diagnosis_id' => $migration->diagnosis_id, 'diagnosis_code' => $migration->diagnosis_code, 'diagnosis_name' => $migration->diagnosis_name, 'interrupted_event_id' => $migration->interrupted_event_id, 'stationar_branch_id' => $migration->stationar_branch_id, 'department_id' => $migration->department_id, 'visit_result_id' => $migration->visit_result_id, 'stat_cure_result_id' => $migration->stat_cure_result_id, 'user_id' => $migration->user_id, 'mis_user_id' => $migration->mis_user_id, 'comment' => $migration->comment, ]; } } // Пакетная запись каждые $batchSize записей if (count($patientBatch) >= $batchSize) { [$savedP, $savedM] = $this->upsertBatches($reportNurseId, $patientBatch, $migrationBatch); $patientBatch = []; $migrationBatch = []; } } // Сохраняем остаток [$savedP, $savedM] = $this->upsertBatches($reportNurseId, $patientBatch, $migrationBatch); return ['saved_patients' => $savedP, 'saved_migrations' => $savedM]; } /** * Вспомогательный метод: выполняет upsert для пациентов и миграций */ private function upsertBatches(int $reportNurseId, array $patientBatch, array $migrationBatch): array { if (empty($patientBatch)) { return [0, 0]; } $savedPatients = 0; $savedMigrations = 0; DB::transaction(function () use ($reportNurseId, $patientBatch, $migrationBatch, &$savedPatients, &$savedMigrations) { // UPSERT пациентов $patientUniqueBy = ['report_nurse_id', 'source_type', 'original_id']; $patientUpdateColumns = array_diff(array_keys($patientBatch[0]), $patientUniqueBy); DB::table('report_nurse_patients')->upsert( $patientBatch, $patientUniqueBy, $patientUpdateColumns ); $savedPatients = count($patientBatch); // Получаем ID сохранённых пациентов для связи с миграциями if (!empty($migrationBatch)) { // Извлекаем уникальные ключи для поиска $tempKeys = array_map(fn($m) => $m['_temp_key'], $migrationBatch); // Получаем реальные ID из БД $patientIds = DB::table('report_nurse_patients') ->whereIn('report_nurse_id', [$reportNurseId]) ->get() ->pluck('id', 'original_id') // key=original_id, value=id ->toArray(); // Формируем финальный массив миграций с реальными medical_history_id $finalMigrations = []; foreach ($migrationBatch as $m) { $tempKey = $m['_temp_key']; $originalId = $tempKey['original_id']; if (isset($patientIds[$originalId])) { $finalMigrations[] = [ 'medical_history_id' => $patientIds[$originalId], // Реальный ID 'ingoing_date' => $m['ingoing_date'], 'out_date' => $m['out_date'], 'diagnosis_id' => $m['diagnosis_id'], 'diagnosis_code' => $m['diagnosis_code'], 'diagnosis_name' => $m['diagnosis_name'], 'interrupted_event_id' => $m['interrupted_event_id'], 'stationar_branch_id' => $m['stationar_branch_id'], 'department_id' => $m['department_id'], 'visit_result_id' => $m['visit_result_id'], 'stat_cure_result_id' => $m['stat_cure_result_id'], 'user_id' => $m['user_id'], 'mis_user_id' => $m['mis_user_id'], 'comment' => $m['comment'], ]; } } if (!empty($finalMigrations)) { // UPSERT миграций $migrationUniqueBy = ['medical_history_id', 'ingoing_date']; $migrationUpdateColumns = array_diff(array_keys($finalMigrations[0]), $migrationUniqueBy); DB::table('report_nurse_migration_patients')->upsert( $finalMigrations, $migrationUniqueBy, $migrationUpdateColumns ); $savedMigrations = count($finalMigrations); } } }); return [$savedPatients, $savedMigrations]; } }