Files
onboard/app/Services/PatientService.php
brusnitsyn eab78a0291 * переписал функции прототипов в сервисы
* оптимизация доставки контента до клиента
* переписал запросы выборок
* убрал из подсчета переведенных
* добавил сохранение метрикам для вывода в дашборд
2026-02-04 17:05:13 +09:00

365 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\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 . ')'
};
}
}