Files
onboard/app/Infrastructure/Reports/Sources/SpecialPatientSource.php

208 lines
8.2 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\Infrastructure\Reports\Sources;
use App\Data\UnifiedPatientData;
use App\Models\Department;
use App\Models\DepartmentPatient;
use App\Models\ObservationPatient;
use App\Models\Report;
use App\Services\DateRange;
use Illuminate\Support\Collection;
class SpecialPatientSource
{
/**
* Загрузить manual/special пациентов и нормализовать их в DTO для отчёта.
*
* @param array<int, string>|null $sourceTypes
*/
public function getDtos(
Department $department,
string $status,
DateRange $dateRange,
?array $sourceTypes = ['manual', 'special'],
bool $forSnapshots = false
): Collection {
return $this->mapManualPatients(
$this->getPatients($department, $status, $dateRange, $sourceTypes, ! $forSnapshots),
$dateRange
);
}
/**
* Загрузить сырые manual/special записи пациентов для запрошенного статуса отчёта.
*
* @param array<int, string>|null $sourceTypes
*/
public function getPatients(
Department $department,
string $status,
DateRange $dateRange,
?array $sourceTypes = ['manual', 'special'],
bool $withOperations = true
): Collection {
$query = $this->buildManualPatientsQuery($department, $dateRange, $sourceTypes, $withOperations);
return match ($status) {
'plan', 'emergency' => $query->current()->where('patient_kind', $status)->get(),
'current' => $query->current()->get(),
'recipient' => $query->whereBetween('admitted_at', [$dateRange->startSql(), $dateRange->endSql()])->get(),
'outcome' => $query
->whereNotNull('outcome_type')
->whereIn('outcome_type', ['discharged', 'deceased'])
->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()])
->get(),
'outcome-discharged', 'outcome-transferred', 'outcome-deceased' => $query
->where('outcome_type', str_replace('outcome-', '', $status))
->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()])
->get(),
'reanimation' => collect(),
default => collect(),
};
}
/**
* Посчитать manual/special пациентов для статуса отчёта.
*
* @param array<int, string>|null $sourceTypes
*/
public function getCount(
Department $department,
string $status,
DateRange $dateRange,
?array $sourceTypes = ['manual', 'special']
): int {
$query = $this->buildManualPatientsQuery($department, $dateRange, $sourceTypes, false);
return match ($status) {
'plan', 'emergency' => $query->current()->where('patient_kind', $status)->count(),
'current' => $query->current()->count(),
'recipient' => $query->whereBetween('admitted_at', [$dateRange->startSql(), $dateRange->endSql()])->count(),
'outcome' => $query
->whereNotNull('outcome_type')
->whereIn('outcome_type', ['discharged', 'deceased'])
->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()])
->count(),
'outcome-discharged', 'outcome-transferred', 'outcome-deceased' => $query
->where('outcome_type', str_replace('outcome-', '', $status))
->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()])
->count(),
default => 0,
};
}
/**
* Загрузить manual-пациентов, уже связанных с MIS-картами в пределах отчётного окна.
*/
public function getLinkedManualPatientsForPeriod(Department $department, DateRange $dateRange): Collection
{
$reportIds = $this->getReportIdsForDepartmentPeriod($department, $dateRange);
return DepartmentPatient::query()
->where(function ($builder) use ($department, $reportIds) {
if (! empty($reportIds)) {
$builder->whereIn('rf_report_id', $reportIds);
}
$builder->orWhere(function ($legacyQuery) use ($department) {
$legacyQuery->whereNull('rf_report_id')
->where('rf_department_id', $department->department_id);
});
})
->whereIn('source_type', ['manual', 'special'])
->whereNotNull('rf_medicalhistory_id')
->get()
->keyBy('rf_medicalhistory_id');
}
/**
* @param array<int, string>|null $sourceTypes
*/
private function buildManualPatientsQuery(
Department $department,
DateRange $dateRange,
?array $sourceTypes,
bool $withOperations
) {
$reportIds = $this->getReportIdsForDepartmentPeriod($department, $dateRange);
$query = DepartmentPatient::query()
->where(function ($builder) use ($department, $reportIds) {
if (! empty($reportIds)) {
$builder->whereIn('rf_report_id', $reportIds);
}
$builder->orWhere(function ($legacyQuery) use ($department) {
$legacyQuery->whereNull('rf_report_id')
->where('rf_department_id', $department->department_id);
});
});
if ($withOperations) {
$query->with(['operations.serviceMedical']);
}
if ($sourceTypes !== null) {
$query->whereIn('source_type', $sourceTypes);
}
return $query;
}
private function getReportIdsForDepartmentPeriod(Department $department, DateRange $dateRange): array
{
return Report::query()
->where('rf_department_id', $department->department_id)
->when(
$dateRange->isOneDay,
fn ($query) => $query->exactPeriod($dateRange->startSql(), $dateRange->endSql()),
fn ($query) => $query->withinPeriod($dateRange->startSql(), $dateRange->endSql()),
)
->pluck('report_id')
->all();
}
private function mapManualPatients(Collection $manualPatients, DateRange $dateRange): Collection
{
return $manualPatients
->map(function (DepartmentPatient $patient) use ($dateRange) {
$operationsRelation = $patient->relationLoaded('operations')
? $patient->operations
: collect();
$operations = $operationsRelation->map(fn ($operation) => [
'id' => $operation->department_patient_operation_id,
'code' => $operation->serviceMedical?->ServiceMedicalCode ?? $operation->service_code,
'name' => $operation->serviceMedical?->ServiceMedicalName ?? $operation->service_name,
'startAt' => $operation->started_at?->toIso8601String(),
'endAt' => $operation->ended_at?->toIso8601String(),
])->filter(fn ($operation) => $operation['code'] || $operation['name'])->values()->all();
return UnifiedPatientData::fromDepartmentPatient(
$patient,
$patient->admitted_at?->betweenIncluded($dateRange->startDate, $dateRange->endDate) ?? false,
$operations,
$this->resolveObservationComment(null, $patient->department_patient_id)
);
})
->sortByDesc(fn (UnifiedPatientData $patient) => $patient->admittedAt ?? '')
->values();
}
private function resolveObservationComment(?int $medicalHistoryId, ?int $departmentPatientId): ?string
{
$query = ObservationPatient::query();
if ($departmentPatientId) {
$query->where('rf_department_patient_id', $departmentPatientId);
} elseif ($medicalHistoryId) {
$query->where('rf_medicalhistory_id', $medicalHistoryId);
} else {
return null;
}
return $query->pluck('comment')->filter()->implode('; ') ?: null;
}
}