213 lines
11 KiB
PHP
213 lines
11 KiB
PHP
<?php
|
||
|
||
namespace App\Domain\ReportDuty\Services;
|
||
|
||
use App\Domain\ReportDuty\DTO\PatientSnapshotData;
|
||
use App\Domain\ReportDuty\DTO\SnapshotStatistics;
|
||
use App\Services\DateRange;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Support\LazyCollection;
|
||
|
||
class ReportDutyStatisticsCalculator
|
||
{
|
||
public function calculate(LazyCollection $patients, DateRange $dateRange): SnapshotStatistics
|
||
{
|
||
$stats = new SnapshotStatistics();
|
||
|
||
$periodStart = $dateRange->start();
|
||
$periodEnd = $dateRange->end();
|
||
$periodStartCarbon = Carbon::parse($periodStart);
|
||
$periodEndCarbon = Carbon::parse($periodEnd);
|
||
|
||
$uniqueOperationIds = [];
|
||
|
||
// Используем LazyCollection, чтобы не загружать всё в память
|
||
foreach ($patients as $patient) {
|
||
/** @var PatientSnapshotData $patient */
|
||
|
||
// ===== СТАТУСЫ =====
|
||
// Используем уже вычисленные поля (patientStatus, periodFlags и т.д.)
|
||
// Но для расчёта койко-дней и операций нам нужны миграции и операции внутри периода.
|
||
// Также мы можем переиспользовать логику, которая была в saveReportSnapshot.
|
||
// Но мы перенесём сюда всю обработку, потому что здесь мы вычисляем статистики на основе сырых данных.
|
||
|
||
$hasRecipientInPeriod = false;
|
||
$hasTransferInPeriod = false;
|
||
$hasDischargeInPeriod = false;
|
||
$hasDeathInPeriod = false;
|
||
$hasActiveMigrationInPeriod = false;
|
||
$hasExtractInPeriod = false;
|
||
|
||
// Проверяем extract_date из карты
|
||
if ($patient->extractDate) {
|
||
$extractDate = Carbon::parse($patient->extractDate);
|
||
if ($extractDate >= $periodStartCarbon && $extractDate <= $periodEndCarbon) {
|
||
$hasExtractInPeriod = true;
|
||
}
|
||
}
|
||
|
||
// Идём по миграциям пациента (в массиве migrations)
|
||
$migrations = $patient->migrations; // массив, но можно преобразовать в коллекцию
|
||
$migrationOutDate = null;
|
||
$migrationVisitResultId = null;
|
||
|
||
foreach ($migrations as $migration) {
|
||
$ingoingDate = isset($migration['ingoing_date']) ? Carbon::parse($migration['ingoing_date']) : null;
|
||
$outDate = isset($migration['out_date']) ? Carbon::parse($migration['out_date']) : null;
|
||
$visitResultId = $migration['visit_result_id'] ?? null;
|
||
$statCureResultId = $migration['stat_cure_result_id'] ?? null;
|
||
|
||
// Поступление в периоде
|
||
if ($ingoingDate && $ingoingDate >= $periodStartCarbon && $ingoingDate <= $periodEndCarbon) {
|
||
$hasRecipientInPeriod = true;
|
||
}
|
||
|
||
// Активная миграция на конец периода
|
||
if ($ingoingDate && $ingoingDate <= $periodEndCarbon) {
|
||
if (!$outDate || $outDate > $periodEndCarbon) {
|
||
$hasActiveMigrationInPeriod = true;
|
||
}
|
||
}
|
||
|
||
// Выбытие в периоде (есть out_date в периоде)
|
||
if ($outDate && $outDate >= $periodStartCarbon && $outDate <= $periodEndCarbon) {
|
||
$migrationOutDate = $outDate;
|
||
$migrationVisitResultId = $visitResultId;
|
||
if (in_array($visitResultId, [5, 15])) {
|
||
$hasDeathInPeriod = true;
|
||
} elseif (in_array($visitResultId, [4, 14])) {
|
||
$hasTransferInPeriod = true;
|
||
} else {
|
||
$hasDischargeInPeriod = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Если нет исхода по миграциям, проверяем extract_date и death_date
|
||
if (!$hasDeathInPeriod && !$hasTransferInPeriod && !$hasDischargeInPeriod) {
|
||
if ($hasExtractInPeriod) {
|
||
$visitResultId = $patient->visitResultId;
|
||
$deathDate = $patient->deathDate ? Carbon::parse($patient->deathDate) : null;
|
||
if ($deathDate && $deathDate <= $periodEndCarbon) {
|
||
$hasDeathInPeriod = true;
|
||
} elseif (in_array($visitResultId, [4, 14])) {
|
||
$hasTransferInPeriod = true;
|
||
} else {
|
||
$hasDischargeInPeriod = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Заполнение статистики по статусам
|
||
if ($hasDeathInPeriod) {
|
||
$stats->deceased++;
|
||
$stats->byStatus['deceased'] = ($stats->byStatus['deceased'] ?? 0) + 1;
|
||
} elseif ($hasTransferInPeriod) {
|
||
$stats->transferred++;
|
||
$stats->byStatus['transferred'] = ($stats->byStatus['transferred'] ?? 0) + 1;
|
||
} elseif ($hasDischargeInPeriod) {
|
||
$stats->discharged++;
|
||
$stats->outcome++;
|
||
$stats->byStatus['discharged'] = ($stats->byStatus['discharged'] ?? 0) + 1;
|
||
} elseif ($hasActiveMigrationInPeriod) {
|
||
$stats->inDepartment++;
|
||
$stats->byStatus['in_department'] = ($stats->byStatus['in_department'] ?? 0) + 1;
|
||
}
|
||
|
||
// Поступление (recipient)
|
||
if ($hasRecipientInPeriod) {
|
||
$stats->admitted['today']++;
|
||
$stats->byStatus['recipient'] = ($stats->byStatus['recipient'] ?? 0) + 1;
|
||
if ($patient->urgencyId == 1) {
|
||
$stats->admitted['planned']++;
|
||
$stats->planned++;
|
||
}
|
||
if ($patient->urgencyId == 2) {
|
||
$stats->admitted['urgent']++;
|
||
}
|
||
}
|
||
|
||
// Срочность (из patientUrgency, но можно из urgencyId)
|
||
if ($patient->patientUrgency) {
|
||
$stats->byUrgency[$patient->patientUrgency] = ($stats->byUrgency[$patient->patientUrgency] ?? 0) + 1;
|
||
}
|
||
|
||
// Реанимация
|
||
if ($patient->inReanimation) {
|
||
$stats->inReanimation++;
|
||
}
|
||
|
||
// Поступил сегодня (admittedToday)
|
||
if ($patient->admittedToday) {
|
||
$stats->admittedToday++;
|
||
}
|
||
|
||
// ===== КОЙКО-ДНИ И ОПЕРАЦИИ =====
|
||
// Нужно взять первую миграцию (актуальную) для расчёта койко-дней
|
||
// берём первую миграцию из массива (индекс 0)
|
||
if (!empty($migrations)) {
|
||
$firstMigration = $migrations[0];
|
||
$migrationStart = isset($firstMigration['ingoing_date']) ? Carbon::parse($firstMigration['ingoing_date']) : null;
|
||
$migrationEnd = isset($firstMigration['out_date']) ? Carbon::parse($firstMigration['out_date']) : null;
|
||
|
||
if ($migrationStart) {
|
||
// Проверяем пересечение с отчетным периодом
|
||
$hasIntersection = $migrationStart <= $periodEndCarbon &&
|
||
($migrationEnd === null || $migrationEnd >= $periodStartCarbon);
|
||
if ($hasIntersection) {
|
||
$calcStart = $migrationStart > $periodStartCarbon ? $migrationStart : $periodStartCarbon;
|
||
$calcEnd = $migrationEnd && $migrationEnd < $periodEndCarbon ? $migrationEnd : $periodEndCarbon;
|
||
|
||
$bedDays = $calcStart->diffInDays($calcEnd);
|
||
$stats->totalBedDays += max(0, $bedDays);
|
||
|
||
// Предоперационные дни: операции в периоде
|
||
$opsInPeriod = collect($patient->operations ?? [])
|
||
->filter(function ($op) use ($periodStartCarbon, $periodEndCarbon) {
|
||
$opStart = isset($op['start_date']) ? Carbon::parse($op['start_date']) : null;
|
||
return $opStart && $opStart >= $periodStartCarbon && $opStart < $periodEndCarbon;
|
||
});
|
||
|
||
if ($opsInPeriod->isNotEmpty()) {
|
||
$stats->patientsWithOperations++;
|
||
$firstOpInPeriod = $opsInPeriod->sortBy('start_date')->first();
|
||
if ($firstOpInPeriod && isset($firstOpInPeriod['start_date'])) {
|
||
$opDate = Carbon::parse($firstOpInPeriod['start_date']);
|
||
if ($opDate > $migrationStart) {
|
||
$preOpDays = $migrationStart->copy()->startOfDay()
|
||
->diffInDays($opDate->copy()->startOfDay());
|
||
$stats->totalPreopBedDays += max(0, $preOpDays);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Собираем операции для уникализации
|
||
foreach ($patient->operations ?? [] as $operation) {
|
||
$opStart = isset($operation['start_date']) ? Carbon::parse($operation['start_date']) : null;
|
||
if ($opStart && $opStart >= $periodStartCarbon && $opStart < $periodEndCarbon) {
|
||
$opId = $operation['id'] ?? null;
|
||
if ($opId) {
|
||
$uniqueOperationIds[$opId] = [
|
||
'id' => $opId,
|
||
'urgent_status' => $operation['urgent_status'] ?? null,
|
||
];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Увеличиваем общее количество пациентов (для среднего)
|
||
$stats->totalPatients++;
|
||
}
|
||
|
||
// После цикла заполняем итоговые показатели по операциям
|
||
$stats->totalOperations = count($uniqueOperationIds);
|
||
$stats->plannedOperations = collect($uniqueOperationIds)->where('urgent_status', 6)->count();
|
||
$stats->urgentOperations = collect($uniqueOperationIds)->whereIn('urgent_status', [4,5])->count();
|
||
|
||
return $stats;
|
||
}
|
||
}
|