Files
onboard/app/Services/PatientService.php
brusnitsyn 741781dcb3 Несколько отделений для врачей
Поправил подсчет операций
Закончил функцию наполнения отчетов
2026-02-06 15:15:03 +09:00

401 lines
14 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\LifeMisMigrationPatient;
use App\Models\MisMedicalHistory;
use App\Models\MisMigrationPatient;
use App\Models\MisSurgicalOperation;
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,
bool $fillableAuto = false
) {
// Получаем поступивших сегодня
$recipientQuery = $this->buildRecipientQuery($type, $isHeadOrAdmin, $branchId, $dateRange, $fillableAuto);
if ($fillableAuto)
$recipientIds = $recipientQuery->distinct()->pluck('rf_MedicalHistoryID')->toArray();
else
$recipientIds = $recipientQuery->pluck('rf_MedicalHistoryID')->toArray();
// Если нужно добавить уже находящихся в отделении
if ($includeCurrent) {
if ($fillableAuto) {
$currentIds = LifeMisMigrationPatient::currentlyInTreatment($branchId, $dateRange)
->distinct()
->pluck('rf_MedicalHistoryID')
->toArray();
} else {
$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 collect();
return collect();
}
// Получаем истории
$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();
}
if ($onlyIds) {
return $query->pluck('MedicalHistoryID');
}
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,
bool $onlyIds = 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);
}
if ($onlyIds) {
return collect($allIds);
}
return MisMedicalHistory::whereIn('MedicalHistoryID', $allIds)
->with(['surgicalOperations' => function ($q) use ($dateRange) {
$q->whereBetween('Date', [$dateRange->startSql(), $dateRange->endSql()]);
}])
->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, bool $onlyIds = false)
{
$query = MisMedicalHistory::whereHas('observationPatient', function ($q) use ($departmentId) {
$q->where('rf_department_id', $departmentId);
});
if (!$onlyIds) {
$query->with(['observationPatient' => function ($query) use ($departmentId) {
$query->where('rf_department_id', $departmentId)
->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']);
}])
->orderBy('DateRecipient', 'DESC');
}
if ($onlyIds) $patients = $query->pluck('MedicalHistoryID');
else $patients = $query->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',
bool $onlyIds = false
) {
$methodMap = [
'discharged' => 'outcomeDischarged',
'transferred' => 'outcomeTransferred',
'without-transferred' => 'outcomeWithoutTransferred',
'deceased' => 'deceasedOutcome',
'all' => 'outcomePatients',
];
$method = $methodMap[$outcomeType] ?? 'outcomePatients';
$medicalHistoryIds = MisMigrationPatient::{$method}($branchId, $dateRange)
->pluck('rf_MedicalHistoryID')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
if ($onlyIds) return collect($medicalHistoryIds);
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 = MisSurgicalOperation::where('rf_StationarBranchID', $branchId)
->whereBetween('Date', [$dateRange->startSql(), $dateRange->endSql()]);
if ($type === 'plan') {
$query->where('rf_TypeSurgOperationInTimeID', 6);
} else {
$query->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,
bool $fillableAuto = false
) {
// Разная логика для заведующего и врача
if ($isHeadOrAdmin) {
// Заведующий: все поступившие за период
if ($fillableAuto) {
$query = LifeMisMigrationPatient::whereInDepartment($branchId)
->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]);
} else {
$query = MisMigrationPatient::whereInDepartment($branchId)
->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]);
}
} else {
// Врач: только поступившие за сутки
if ($fillableAuto) {
$query = LifeMisMigrationPatient::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 . ')'
};
}
}