info('🚀 Начинаем пересчет предоперационного койко-дня...'); $query = Report::query(); // Фильтр по конкретному отчету if ($reportId = $this->option('report')) { $query->where('report_id', $reportId); $this->info("📋 Фильтр: отчет ID {$reportId}"); } // Фильтр по отделению if ($departmentId = $this->option('department')) { $query->where('rf_department_id', $departmentId); $this->info("🏥 Фильтр: отделение ID {$departmentId}"); } // Фильтр по дате if ($from = $this->option('from')) { $query->whereDate('created_at', '>=', $from); $this->info("📅 Фильтр: с {$from}"); } if ($to = $this->option('to')) { $query->whereDate('created_at', '<=', $to); $this->info("📅 Фильтр: по {$to}"); } $force = $this->option('force'); $chunkSize = (int)$this->option('chunk'); $totalReports = $query->count(); if ($totalReports === 0) { $this->warn('❌ Отчеты не найдены'); return 0; } $this->info("📊 Найдено отчетов: {$totalReports}"); if ($totalReports > 1000 && !$this->confirm("⚠️ Обработка {$totalReports} отчетов может занять время. Продолжить?")) { $this->info('❌ Операция отменена'); return 0; } $bar = $this->output->createProgressBar($totalReports); $bar->start(); $updated = 0; $skipped = 0; $errors = 0; $noPatients = 0; $query->orderBy('report_id')->chunk($chunkSize, function ($reports) use ($force, &$updated, &$skipped, &$errors, &$noPatients, $bar) { foreach ($reports as $report) { try { $result = $this->processReport($report, $force); match ($result) { 'updated' => $updated++, 'skipped' => $skipped++, 'no_patients' => $noPatients++, default => null }; } catch (\Exception $e) { $errors++; Log::error("Ошибка обработки отчета {$report->report_id}: " . $e->getMessage()); } $bar->advance(); } }); $bar->finish(); $this->newLine(2); // Итоговая таблица $this->table( ['Статус', 'Количество'], [ ['✅ Обновлено', $updated], ['⏭️ Пропущено (уже есть)', $skipped], ['👤 Нет пациентов', $noPatients], ['❌ Ошибок', $errors], ['📊 Всего', $totalReports], ] ); // Покажем примеры обновленных отчетов if ($updated > 0) { $this->newLine(); $this->info('📋 Примеры обновленных отчетов:'); $samples = Report::whereHas('metrikaResults', function($q) { $q->where('rf_metrika_item_id', 21); }) ->orderBy('report_id', 'desc') ->limit(5) ->get() ->map(function($report) { $metric = $report->metrikaResults ->where('rf_metrika_item_id', 21) ->first(); return [ 'report_id' => $report->report_id, 'department' => $report->rf_department_id, 'date' => $report->created_at->format('Y-m-d'), 'preoperative_days' => $metric ? $metric->value : 'N/A', ]; }); $this->table(['ID отчета', 'Отделение', 'Дата', 'Предоп. дни'], $samples->toArray()); } $this->newLine(); $this->info('✅ Готово!'); return 0; } /** * Обработка одного отчета */ private function processReport(Report $report, bool $force): string { // Проверяем, есть ли уже метрика $existing = MetrikaResult::where('rf_report_id', $report->report_id) ->where('rf_metrika_item_id', 21) ->first(); if ($existing && !$force) { return 'skipped'; } // Получаем пациентов из снапшотов $snapshots = MedicalHistorySnapshot::where('rf_report_id', $report->report_id) ->whereIn('patient_type', ['discharged', 'deceased']) ->pluck('rf_medicalhistory_id'); if ($snapshots->isEmpty()) { // Сохраняем 0 если нет пациентов MetrikaResult::updateOrCreate( [ 'rf_report_id' => $report->report_id, 'rf_metrika_item_id' => 21, ], ['value' => 0] ); return 'no_patients'; } // Получаем первые операции и первые поступления $operations = DB::table('stt_surgicaloperation as so') ->join('stt_migrationpatient as mp', 'so.rf_MedicalHistoryID', '=', 'mp.rf_MedicalHistoryID') ->whereIn('so.rf_MedicalHistoryID', $snapshots) ->whereNotNull('so.Date') ->whereNotNull('mp.DateIngoing') ->select( 'so.rf_MedicalHistoryID', DB::raw('MIN(so."Date") as first_operation'), DB::raw('MIN(mp."DateIngoing") as first_admission') ) ->groupBy('so.rf_MedicalHistoryID') ->get(); if ($operations->isEmpty()) { // Сохраняем 0 если нет операций MetrikaResult::updateOrCreate( [ 'rf_report_id' => $report->report_id, 'rf_metrika_item_id' => 21, ], ['value' => 0] ); return 'no_patients'; } $totalDays = 0; $count = 0; foreach ($operations as $op) { $days = Carbon::parse($op->first_admission) ->diffInDays(Carbon::parse($op->first_operation)); if ($days >= 0) { $totalDays += $days; $count++; } } $avgDays = $count > 0 ? round($totalDays / $count, 1) : 0; // Сохраняем метрику MetrikaResult::updateOrCreate( [ 'rf_report_id' => $report->report_id, 'rf_metrika_item_id' => 21, ], ['value' => $avgDays] ); return 'updated'; } }