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

328 lines
12 KiB
PHP
Raw 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\Services\DateRange;
use App\Services\OutcomePatientService;
use App\Services\RecipientPatientService;
use App\Services\CurrentPatientService;
use App\Models\MisMedicalHistory;
use App\Models\MisMigrationPatient;
use App\Models\MisReanimation;
use App\Models\MisSurgicalOperation;
use Illuminate\Support\Collection;
/**
* Query-источник для пациентских выборок из МИС и связанных клинических агрегатов.
*
* Этот класс удерживает детали Eloquent/query-builder вне application/domain слоёв
* и централизует read-model логику, специфичную для реплики МИС.
*/
class MisClinicalDataSource
{
public function __construct(
private readonly RecipientPatientService $recipientPatientService,
private readonly CurrentPatientService $currentPatientService,
private readonly OutcomePatientService $outcomePatientService,
) {}
/**
* Загрузить плановых или экстренных пациентов, при необходимости объединив с текущими в отделении.
*/
public function getPlanOrEmergencyPatients(
?string $type,
bool $isHeadOrAdmin,
int $branchId,
DateRange $dateRange,
bool $countOnly = false,
bool $onlyIds = false,
bool $includeCurrent = false,
bool $fillableAuto = false
) {
$medicalHistoryIds = $this->recipientPatientService->resolvePlanOrEmergencyMedicalHistoryIds(
$type,
$isHeadOrAdmin,
$branchId,
$dateRange,
$includeCurrent,
$fillableAuto
);
if (empty($medicalHistoryIds)) {
return $countOnly ? 0 : collect();
}
if ($countOnly) {
return count($medicalHistoryIds);
}
if ($onlyIds) {
return collect($medicalHistoryIds);
}
$recipientIds = $this->recipientPatientService->getRecipientMedicalHistoryIds(
$type,
$isHeadOrAdmin,
$branchId,
$dateRange
);
return $this->buildPatientCardsQuery($medicalHistoryIds, $branchId)
->get()
->map(function ($patient) use ($recipientIds) {
$patient->is_recipient_today = in_array($patient->MedicalHistoryID, $recipientIds, true);
return $patient;
});
}
/**
* Загрузить всех MIS-пациентов, входящих в выборку отчётного отделения.
*/
public function getAllPatientsInDepartment(
bool $isHeadOrAdmin,
int $branchId,
DateRange $dateRange,
bool $countOnly = false,
bool $onlyIds = false,
bool $fillableAuto = false
) {
$recipientIds = $this->recipientPatientService
->buildRecipientQuery(null, $isHeadOrAdmin, $branchId, $dateRange, $fillableAuto)
->pluck('rf_MedicalHistoryID')
->toArray();
$currentIds = $fillableAuto
? $this->currentPatientService->getHistoricalCurrentMedicalHistoryIds(null, $branchId, $dateRange)
: MisMigrationPatient::currentlyInTreatment($branchId)->pluck('rf_MedicalHistoryID')->toArray();
$allIds = array_unique(array_merge($recipientIds, $currentIds));
if (empty($allIds)) {
return $countOnly ? 0 : collect();
}
if ($countOnly) {
return count($allIds);
}
if ($onlyIds) {
return collect($allIds);
}
return $this->buildPatientCardsQuery($allIds, $branchId)
->get()
->map(function ($patient) use ($recipientIds) {
$patient->is_recipient_today = in_array($patient->MedicalHistoryID, $recipientIds, true);
return $patient;
});
}
/**
* Загрузить пациентов с записями о реанимации, входящих в отчётную выборку.
*/
public function getReanimationPatients(
int $branchId,
DateRange $dateRange,
bool $onlyIds = false
) {
$currentIds = $this->getAllPatientsInDepartment(true, $branchId, $dateRange, false, true, false)->all();
$outcomeIds = $this->outcomePatientService->getOutcomePatients($branchId, $dateRange, 'all', true)->all();
$reportCohortIds = array_values(array_unique(array_merge($currentIds, $outcomeIds)));
if (empty($reportCohortIds)) {
return collect();
}
$reanimationByMedicalHistory = MisReanimation::query()
->join('stt_migrationpatient as mp', 'mp.MigrationPatientID', '=', 'stt_reanimation.rf_MigrationPatientID')
->where('mp.rf_StationarBranchID', $branchId)
->where(function ($q) use ($dateRange) {
$q->where('stt_reanimation.DateIn', '<=', $dateRange->endSql())
->where('stt_reanimation.DateOut', '>=', $dateRange->startSql());
})
->where('mp.rf_MedicalHistoryID', '<>', 0)
->whereIn('mp.rf_MedicalHistoryID', $reportCohortIds)
->selectRaw('
mp."rf_MedicalHistoryID" as medical_history_id,
MAX(stt_reanimation."DateIn") as reanimation_date_in,
BOOL_OR(COALESCE(stt_reanimation."isComplete", false)) as reanimation_is_complete
')
->groupBy('mp.rf_MedicalHistoryID')
->get();
$medicalHistoryIds = $reanimationByMedicalHistory
->pluck('medical_history_id')
->map(fn ($id) => (int) $id)
->values()
->all();
if (empty($medicalHistoryIds)) {
return collect();
}
if ($onlyIds) {
return collect($medicalHistoryIds);
}
$reanimationDateByMedicalHistory = $reanimationByMedicalHistory->pluck('reanimation_date_in', 'medical_history_id');
$reanimationCompleteByMedicalHistory = $reanimationByMedicalHistory->pluck('reanimation_is_complete', 'medical_history_id');
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->select($this->patientSelect())
->with($this->patientRelations($branchId))
->orderBy('DateRecipient', 'DESC')
->get()
->map(function ($patient) use ($reanimationDateByMedicalHistory, $reanimationCompleteByMedicalHistory) {
$reanimationDateIn = $reanimationDateByMedicalHistory->get($patient->MedicalHistoryID);
if ($reanimationDateIn) {
$patient->DateRecipient = $reanimationDateIn;
}
$patient->reanimation_is_complete = (bool) $reanimationCompleteByMedicalHistory->get($patient->MedicalHistoryID, false);
return $patient;
});
}
/**
* Загрузить или посчитать хирургические операции в отчётном периоде.
*/
public function getSurgicalPatients(
string $type,
int $branchId,
DateRange $dateRange,
bool $countOnly = false
) {
$query = MisSurgicalOperation::where('rf_StationarBranchID', $branchId)
->completed()
->where('Date', '>=', $dateRange->startSql())
->where('Date', '<=', $dateRange->endSql());
if ($type === 'plan') {
$query->where('rf_TypeSurgOperationInTimeID', 6);
} else {
$query->whereIn('rf_TypeSurgOperationInTimeID', [4, 5]);
}
return $countOnly ? $query->count() : $query->get();
}
/**
* Посчитать плановых или экстренных пациентов, когда нужно включать текущих.
*/
public function getPatientsCountWithCurrent(
?string $type,
bool $isHeadOrAdmin,
int $branchId,
DateRange $dateRange
): int {
return $this->getPlanOrEmergencyPatients(
$type,
$isHeadOrAdmin,
$branchId,
$dateRange,
true,
false,
true
);
}
/**
* Общий MIS-запрос карточек пациентов с операциями и диагнозами.
*/
public function buildPatientCardsQuery(array $medicalHistoryIds, int $branchId)
{
return MisMedicalHistory::query()
->whereIn('MedicalHistoryID', $medicalHistoryIds)
->select($this->patientSelect())
->with($this->patientRelations($branchId))
->orderBy('DateRecipient', 'DESC');
}
/**
* Базовый select, используемый во всех выборках пациентов из МИС.
*/
private function patientSelect(): array
{
return [
'MedicalHistoryID',
'FAMILY',
'Name',
'OT',
'BD',
'DateRecipient',
'DateExtract',
'DateDeath',
'rf_EmerSignID',
'rf_kl_VisitResultID',
];
}
/**
* Общие eager-loaded связи для MIS-карточек пациентов.
*/
private function patientRelations(int $branchId): array
{
return [
'surgicalOperations' => function ($q) {
$q->select([
'SurgicalOperationID',
'rf_MedicalHistoryID',
'rf_kl_ServiceMedicalID',
'Date',
])->with(['serviceMedical' => function ($serviceQuery) {
$serviceQuery->select([
'ServiceMedicalID',
'ServiceMedicalCode',
'ServiceMedicalName',
]);
}]);
},
'outcomeMigration' => function ($q) {
$q->select([
'stt_migrationpatient.MigrationPatientID',
'stt_migrationpatient.rf_MedicalHistoryID',
'stt_migrationpatient.DateOut',
'stt_migrationpatient.rf_DiagnosID',
])->with(['mainDiagnosis' => function ($diagnosisQuery) {
$diagnosisQuery->select([
'DiagnosID',
'rf_MKBID',
])->with(['mkb' => function ($mkbQuery) {
$mkbQuery->select([
'MKBID',
'DS',
'NAME',
]);
}]);
}]);
},
'migrations' => function ($q) use ($branchId) {
$q->where('rf_StationarBranchID', $branchId)
->select([
'MigrationPatientID',
'rf_MedicalHistoryID',
'rf_DiagnosID',
'DateIngoing',
'rf_StationarBranchID',
])
->orderByDesc('DateIngoing')
->with(['mainDiagnosis' => function ($diagnosisQuery) {
$diagnosisQuery->select([
'DiagnosID',
'rf_MKBID',
'rf_MigrationPatientID',
])->with(['mkb' => function ($mkbQuery) {
$mkbQuery->select([
'MKBID',
'DS',
'NAME',
]);
}]);
}]);
},
];
}
}