Перевод на DDD

This commit is contained in:
brusnitsyn
2026-07-31 17:19:03 +09:00
parent 2dd55b9d11
commit 2c6d4a04cc
48 changed files with 1796 additions and 0 deletions

View File

@@ -0,0 +1,212 @@
<?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;
}
}