Профиль хирургии
This commit is contained in:
244
app/Console/Commands/RecalculatePreoperativeMetric.php
Normal file
244
app/Console/Commands/RecalculatePreoperativeMetric.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
// app/Console/Commands/RecalculatePreoperativeMetric.php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Report;
|
||||
use App\Models\MedicalHistorySnapshot;
|
||||
use App\Models\MetrikaResult;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class RecalculatePreoperativeMetric extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'metrics:recalculate-preoperative
|
||||
{--report= : ID конкретного отчета}
|
||||
{--department= : ID отделения}
|
||||
{--from= : Начальная дата (Y-m-d)}
|
||||
{--to= : Конечная дата (Y-m-d)}
|
||||
{--force : Принудительно перезаписать даже если есть значение}
|
||||
{--chunk=100 : Количество отчетов за один раз}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Пересчет предоперационного койко-дня (метрика 25) для отчетов';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user