* оптимизация доставки контента до клиента * переписал запросы выборок * убрал из подсчета переведенных * добавил сохранение метрикам для вывода в дашборд
365 lines
12 KiB
PHP
365 lines
12 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\MisMedicalHistory;
|
||
use App\Models\MisMigrationPatient;
|
||
use App\Models\ObservationPatient;
|
||
use Carbon\Carbon;
|
||
|
||
class PatientService
|
||
{
|
||
/**
|
||
* Получить плановых или экстренных пациентов
|
||
*/
|
||
public function getPlanOrEmergencyPatients(
|
||
?string $type,
|
||
bool $isHeadOrAdmin,
|
||
int $branchId,
|
||
DateRange $dateRange,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false,
|
||
bool $includeCurrent = false
|
||
) {
|
||
// Получаем поступивших сегодня
|
||
$recipientQuery = $this->buildRecipientQuery($type, $isHeadOrAdmin, $branchId, $dateRange);
|
||
$recipientIds = $recipientQuery->pluck('rf_MedicalHistoryID')->toArray();
|
||
|
||
// Если нужно добавить уже находящихся в отделении
|
||
if ($includeCurrent) {
|
||
$currentIds = MisMigrationPatient::currentlyInTreatment($branchId)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->toArray();
|
||
|
||
$medicalHistoryIds = array_unique(array_merge($recipientIds, $currentIds));
|
||
} else {
|
||
$medicalHistoryIds = $recipientIds;
|
||
}
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
if ($countOnly) return 0;
|
||
if ($onlyIds) return [];
|
||
return collect();
|
||
}
|
||
|
||
if ($onlyIds) {
|
||
return $medicalHistoryIds;
|
||
}
|
||
|
||
// Получаем истории
|
||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->with([
|
||
'surgicalOperations' => function ($q) use ($dateRange) {
|
||
$q->whereBetween('Date', [$dateRange->startSql(), $dateRange->endSql()]);
|
||
},
|
||
'migrations' => function ($q) use ($branchId) {
|
||
$q->where('rf_StationarBranchID', $branchId) // укажите поле сортировки
|
||
->take(1) // берем только одну последнюю
|
||
->with(['diagnosis' => function ($q) {
|
||
$q->where('rf_DiagnosTypeID', 3)
|
||
->take(1)
|
||
->with('mkb');
|
||
}]);
|
||
}
|
||
])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
// Фильтруем по типу (план/экстренные)
|
||
if ($type === 'plan') {
|
||
$query->plan();
|
||
} elseif ($type === 'emergency') {
|
||
$query->emergency();
|
||
}
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get()->map(function ($patient) use ($recipientIds, $branchId) {
|
||
// Добавляем флаг "поступил сегодня"
|
||
$patient->is_recipient_today = in_array($patient->MedicalHistoryID, $recipientIds);
|
||
return $patient;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Получить всех пациентов в отделении (поступившие сегодня + уже лечащиеся)
|
||
*/
|
||
public function getAllPatientsInDepartment(
|
||
bool $isHeadOrAdmin,
|
||
int $branchId,
|
||
DateRange $dateRange,
|
||
bool $countOnly = false
|
||
) {
|
||
// Поступившие сегодня
|
||
$recipientIds = $this->buildRecipientQuery(null, $isHeadOrAdmin, $branchId, $dateRange)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->toArray();
|
||
|
||
// Уже находящиеся на лечении
|
||
$currentIds = MisMigrationPatient::currentlyInTreatment($branchId)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->toArray();
|
||
|
||
// Объединяем и убираем дубли
|
||
$allIds = array_unique(array_merge($recipientIds, $currentIds));
|
||
|
||
if (empty($allIds)) {
|
||
if ($countOnly) return 0;
|
||
return collect();
|
||
}
|
||
|
||
if ($countOnly) {
|
||
return count($allIds);
|
||
}
|
||
|
||
return MisMedicalHistory::whereIn('MedicalHistoryID', $allIds)
|
||
->with(['surgicalOperations' => function ($q) use ($startDate, $endDate) {
|
||
$q->whereBetween('Date', [$startDate, $endDate]);
|
||
}])
|
||
->orderBy('DateRecipient', 'DESC')
|
||
->get()
|
||
->map(function ($patient) use ($recipientIds) {
|
||
$patient->is_recipient_today = in_array($patient->MedicalHistoryID, $recipientIds);
|
||
return $patient;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов под наблюдением
|
||
*/
|
||
public function getObservationPatients(int $departmentId)
|
||
{
|
||
$patients = MisMedicalHistory::whereHas('observationPatient', function ($q) use ($departmentId) {
|
||
$q->where('rf_department_id', $departmentId);
|
||
})
|
||
->with(['observationPatient' => function($query) use ($departmentId) {
|
||
$query->where('rf_department_id', $departmentId)
|
||
->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']);
|
||
}])
|
||
->orderBy('DateRecipient', 'DESC')
|
||
->get();
|
||
|
||
return $patients->map(function ($patient) {
|
||
$patient->comment = $patient->observationPatient
|
||
->pluck('comment')
|
||
->filter()
|
||
->implode('; ');
|
||
return $patient;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Получить выбывших пациентов
|
||
*/
|
||
public function getOutcomePatients(
|
||
int $branchId,
|
||
DateRange $dateRange,
|
||
string $outcomeType = 'all'
|
||
) {
|
||
$methodMap = [
|
||
'discharged' => 'discharged',
|
||
'transferred' => 'transferred',
|
||
'deceased' => 'deceasedOutcome',
|
||
'all' => 'outcomePatients',
|
||
];
|
||
|
||
$method = $methodMap[$outcomeType] ?? 'outcomePatients';
|
||
|
||
$medicalHistoryIds = MisMigrationPatient::{$method}($branchId, $dateRange)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->unique()
|
||
->toArray();
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
return collect();
|
||
}
|
||
|
||
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC')
|
||
->get()
|
||
->map(function ($patient) {
|
||
return $this->addOutcomeInfo($patient);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов с операциями
|
||
*/
|
||
public function getSurgicalPatients(
|
||
string $type,
|
||
int $branchId,
|
||
DateRange $dateRange,
|
||
bool $countOnly = false
|
||
) {
|
||
$query = MisMedicalHistory::whereHas('surgicalOperations', function ($q) use ($type, $branchId, $dateRange) {
|
||
$q->where('rf_StationarBranchID', $branchId)
|
||
->whereBetween('Date', [$dateRange->startSql(), $dateRange->endSql()]);
|
||
|
||
if ($type === 'plan') {
|
||
$q->where('rf_TypeSurgOperationInTimeID', 6);
|
||
} else {
|
||
$q->whereIn('rf_TypeSurgOperationInTimeID', [4, 5]);
|
||
}
|
||
});
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get();
|
||
}
|
||
|
||
/**
|
||
* Получить текущих пациентов
|
||
*/
|
||
public function getCurrentPatients(int $branchId, bool $countOnly = false)
|
||
{
|
||
$medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->unique()
|
||
->toArray();
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
return $countOnly ? 0 : collect();
|
||
}
|
||
|
||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->currentlyHospitalized()
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get();
|
||
}
|
||
|
||
/**
|
||
* Собрать базовый запрос для пациентов
|
||
*/
|
||
private function buildPatientQuery(
|
||
?string $type,
|
||
bool $isHeadOrAdmin,
|
||
int $branchId,
|
||
DateRange $dateRange
|
||
) {
|
||
if ($isHeadOrAdmin) {
|
||
$query = MisMigrationPatient::whereInDepartment($branchId)
|
||
->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]);
|
||
} else {
|
||
$query = MisMigrationPatient::currentlyInTreatment($branchId)
|
||
->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]);
|
||
}
|
||
|
||
$medicalHistoryIds = $query->pluck('rf_MedicalHistoryID')->toArray();
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
return MisMedicalHistory::whereRaw('1 = 0');
|
||
}
|
||
|
||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds);
|
||
|
||
if ($type === 'plan') {
|
||
$query->plan();
|
||
} elseif ($type === 'emergency') {
|
||
$query->emergency();
|
||
}
|
||
|
||
if (!$isHeadOrAdmin && !in_array($type, ['discharged', 'transferred', 'deceased'])) {
|
||
$query->currentlyHospitalized();
|
||
}
|
||
|
||
return $query;
|
||
}
|
||
|
||
/**
|
||
* Построить запрос для поступивших пациентов
|
||
*/
|
||
private function buildRecipientQuery(
|
||
?string $type,
|
||
bool $isHeadOrAdmin,
|
||
int $branchId,
|
||
DateRange $dateRange
|
||
) {
|
||
// Разная логика для заведующего и врача
|
||
if ($isHeadOrAdmin) {
|
||
// Заведующий: все поступившие за период
|
||
$query = MisMigrationPatient::whereInDepartment($branchId)
|
||
->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]);
|
||
} else {
|
||
// Врач: только поступившие за сутки
|
||
$query = MisMigrationPatient::whereInDepartment($branchId)
|
||
->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]);
|
||
}
|
||
|
||
return $query;
|
||
}
|
||
|
||
/**
|
||
* Добавить информацию об исходе пациенту
|
||
*/
|
||
private function addOutcomeInfo(MisMedicalHistory $patient)
|
||
{
|
||
$latestMigration = $patient->migrations
|
||
->whereNotNull('DateOut')
|
||
->where('DateOut', '<>', '1900-01-01')
|
||
->sortByDesc('DateOut')
|
||
->first();
|
||
|
||
if ($latestMigration) {
|
||
$patient->outcome_type = $this->getOutcomeTypeName($latestMigration->rf_kl_VisitResultID);
|
||
$patient->outcome_date = $latestMigration->DateOut;
|
||
$patient->visit_result_id = $latestMigration->rf_kl_VisitResultID;
|
||
}
|
||
|
||
return $patient;
|
||
}
|
||
|
||
/**
|
||
* Получить количество пациентов по типу с учетом уже находящихся в отделении
|
||
*/
|
||
public function getPatientsCountWithCurrent(
|
||
?string $type,
|
||
bool $isHeadOrAdmin,
|
||
int $branchId,
|
||
DateRange $dateRange
|
||
): int {
|
||
// Поступившие сегодня указанного типа
|
||
$recipientCount = $this->buildRecipientQuery($type, $isHeadOrAdmin, $branchId, $dateRange)
|
||
->count();
|
||
|
||
// Если нужны плановые/экстренные среди уже лечащихся
|
||
$currentCount = 0;
|
||
if ($type === 'plan' || $type === 'emergency') {
|
||
$currentIds = MisMigrationPatient::currentlyInTreatment($branchId)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->toArray();
|
||
|
||
if (!empty($currentIds)) {
|
||
$currentCount = MisMedicalHistory::whereIn('MedicalHistoryID', $currentIds)
|
||
->when($type === 'plan', fn($q) => $q->plan())
|
||
->when($type === 'emergency', fn($q) => $q->emergency())
|
||
->count();
|
||
}
|
||
}
|
||
|
||
return $currentCount;
|
||
}
|
||
|
||
/**
|
||
* Получить название типа исхода
|
||
*/
|
||
private function getOutcomeTypeName(int $visitResultId): string
|
||
{
|
||
return match($visitResultId) {
|
||
1, 7, 8, 9, 10, 11, 48, 49, 124 => 'Выписка',
|
||
2, 3, 4, 12, 13, 14 => 'Перевод',
|
||
5, 6, 15, 16 => 'Умер',
|
||
default => 'Другое (' . $visitResultId . ')'
|
||
};
|
||
}
|
||
}
|