Files
onboard/app/Domain/ReportDuty/Services/ReportDutyStatisticsCalculator.php
2026-07-31 17:19:03 +09:00

213 lines
11 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}