* добавил получение выбывших * фильтрация выбывших по результатам лечения * добавил подсказку при наведении на операции * добавил вывод причины наблюдения * добавил вкладки для выбывших * изменил связь и сохранение пациентов на контроле * добавил возможность редактирования причины контроля * полное изменение окна с нежелательными событиями * исправил просмотр причины контроля * работа над окном редактирования причины контроля в таблице * визуальное выделение умерших и проведенных операций * добавил выбор даты для роли врач * центрирование блоков статистики * разделение выполненных операций на срочность * поправил метод определения текущего дня для роли врач * функция блокировки при выборе другой даты для роли врач
450 lines
16 KiB
PHP
450 lines
16 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\MisMigrationPatient;
|
||
use App\Models\MisMedicalHistory;
|
||
use App\Models\MisSurgicalOperation;
|
||
use App\Models\ObservationPatient;
|
||
use Illuminate\Support\Collection;
|
||
|
||
class PatientService
|
||
{
|
||
public function getPatientsByType(
|
||
string $type,
|
||
?bool $isHeadOrAdmin,
|
||
?int $branchId,
|
||
?string $startDate = null,
|
||
?string $endDate = null,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false
|
||
): Collection|int|array {
|
||
// Обрабатываем outcome- типы
|
||
if (str_starts_with($type, 'outcome-')) {
|
||
$outcomeType = str_replace('outcome-', '', $type);
|
||
return $this->getOutcomePatients($branchId, $startDate, $endDate, $outcomeType, $countOnly, $onlyIds);
|
||
}
|
||
|
||
// Обрабатываем обычные типы
|
||
$method = 'get' . ucfirst($type) . 'Patients';
|
||
|
||
if (method_exists($this, $method)) {
|
||
return $this->$method($isHeadOrAdmin, $branchId, $startDate, $endDate, $countOnly, $onlyIds);
|
||
}
|
||
|
||
throw new \InvalidArgumentException("Unknown patient type: {$type}");
|
||
}
|
||
|
||
public function getPlanPatients(
|
||
?bool $isHeadOrAdmin,
|
||
?int $branchId,
|
||
?string $startDate,
|
||
?string $endDate,
|
||
bool $countOnly,
|
||
bool $onlyIds
|
||
): Collection|int|array {
|
||
return $this->getAdmissionPatients('plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, $countOnly, $onlyIds);
|
||
}
|
||
|
||
public function getEmergencyPatients(
|
||
?bool $isHeadOrAdmin,
|
||
?int $branchId,
|
||
?string $startDate,
|
||
?string $endDate,
|
||
bool $countOnly,
|
||
bool $onlyIds
|
||
): Collection|int|array {
|
||
return $this->getAdmissionPatients('emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, $countOnly, $onlyIds);
|
||
}
|
||
|
||
private function getAdmissionPatients(
|
||
string $admissionType,
|
||
?bool $isHeadOrAdmin,
|
||
?int $branchId,
|
||
?string $startDate,
|
||
?string $endDate,
|
||
bool $countOnly,
|
||
bool $onlyIds
|
||
): Collection|int|array {
|
||
$query = $this->getBasePatientsQuery($isHeadOrAdmin, $branchId, $startDate, $endDate);
|
||
|
||
if ($admissionType === 'plan') {
|
||
$query->plan();
|
||
} else {
|
||
$query->emergency();
|
||
}
|
||
|
||
return $this->executeQuery($query, $countOnly, $onlyIds);
|
||
}
|
||
|
||
private function getBasePatientsQuery(
|
||
?bool $isHeadOrAdmin,
|
||
?int $branchId,
|
||
?string $startDate,
|
||
?string $endDate
|
||
) {
|
||
$migrationPatient = new MisMigrationPatient();
|
||
|
||
if ($isHeadOrAdmin) {
|
||
$medicalHistoryIds = $migrationPatient->newQuery()
|
||
->whereInDepartment($branchId)
|
||
->whereBetween('DateIngoing', [$startDate, $endDate])
|
||
->pluck('rf_MedicalHistoryID')
|
||
->toArray();
|
||
} else {
|
||
$medicalHistoryIds = $migrationPatient->newQuery()
|
||
->currentlyInTreatment($branchId)
|
||
->whereBetween('DateIngoing', [$startDate, $endDate])
|
||
->pluck('rf_MedicalHistoryID')
|
||
->toArray();
|
||
}
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
return MisMedicalHistory::whereRaw('1=0'); // Пустой запрос
|
||
}
|
||
|
||
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
|
||
$query->whereBetween('Date', [$startDate, $endDate]);
|
||
}])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
}
|
||
|
||
private function executeQuery($query, bool $countOnly, bool $onlyIds): Collection|int|array
|
||
{
|
||
if ($onlyIds) {
|
||
return $query->pluck('MedicalHistoryID')->toArray();
|
||
}
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get();
|
||
}
|
||
|
||
public function getObservationPatients(int $departmentId): Collection
|
||
{
|
||
return ObservationPatient::where('rf_department_id', $departmentId)
|
||
->with(['medicalHistory'])
|
||
->get()
|
||
->map(function ($observation) {
|
||
$patient = $observation->medicalHistory;
|
||
$patient->observation_comment = $observation->comment;
|
||
return $patient;
|
||
});
|
||
}
|
||
|
||
public function getOutcomePatients(int $branchId, string $startDate, string $endDate, string $outcomeType, $countOnly = false, $onlyIds = false): Collection|int
|
||
{
|
||
return match($outcomeType) {
|
||
'discharged' => $this->getDischargedOutcomePatients($branchId, $startDate, $endDate, $countOnly, $onlyIds),
|
||
'transferred' => $this->getTransferredOutcomePatients($branchId, $startDate, $endDate, $countOnly, $onlyIds),
|
||
'deceased' => $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, $countOnly, $onlyIds),
|
||
default => throw new \InvalidArgumentException("Неизвестный тип исхода: {$outcomeType}")
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Получить всех выбывших пациентов
|
||
*/
|
||
public function getAllOutcomePatients(
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
bool $countOnly = false
|
||
): Collection|int {
|
||
$migrationPatient = new MisMigrationPatient();
|
||
|
||
// Получаем миграции выбывших пациентов за период
|
||
$migrations = $migrationPatient->newQuery()
|
||
->outcomePatients($branchId, $startDate, $endDate)
|
||
->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut')
|
||
->get()
|
||
->groupBy('rf_MedicalHistoryID');
|
||
|
||
if ($migrations->isEmpty()) {
|
||
return $countOnly ? 0 : collect();
|
||
}
|
||
|
||
$medicalHistoryIds = $migrations->keys()->toArray();
|
||
|
||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
$patients = $query->get();
|
||
|
||
return $this->addOutcomeInfoToPatients($patients, $migrations);
|
||
}
|
||
|
||
/**
|
||
* Получить миграции выбывших пациентов
|
||
*/
|
||
private function getOutcomeMigrations(int $branchId, string $startDate, string $endDate): Collection
|
||
{
|
||
$migrationPatient = new MisMigrationPatient();
|
||
|
||
return $migrationPatient->newQuery()
|
||
->outcomePatients($branchId, $startDate, $endDate)
|
||
->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut')
|
||
->get()
|
||
->groupBy('rf_MedicalHistoryID');
|
||
}
|
||
|
||
/**
|
||
* Добавить информацию о выбытии к пациентам
|
||
*/
|
||
private function addOutcomeInfoToPatients(Collection $patients, Collection $migrations): Collection
|
||
{
|
||
return $patients->map(function ($patient) use ($migrations) {
|
||
$patientMigrations = $migrations->get($patient->MedicalHistoryID, collect());
|
||
|
||
if ($patientMigrations->isNotEmpty()) {
|
||
$latestMigration = $patientMigrations->sortByDesc('DateOut')->first();
|
||
$patient->outcome_type = $this->getOutcomeTypeName($latestMigration->rf_kl_VisitResultID);
|
||
$patient->outcome_date = $latestMigration->DateOut;
|
||
$patient->visit_result_id = $latestMigration->rf_kl_VisitResultID;
|
||
}
|
||
|
||
return $patient;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Получить понятное название типа выбытия
|
||
*/
|
||
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 . ')'
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Получить выписанных пациентов
|
||
*/
|
||
public function getDischargedOutcomePatients(
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false
|
||
): Collection|array|int {
|
||
return $this->getSpecificOutcomePatients('discharged', $branchId, $startDate, $endDate, $onlyIds, $countOnly);
|
||
}
|
||
|
||
/**
|
||
* Получить переведенных пациентов
|
||
*/
|
||
public function getTransferredOutcomePatients(
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false
|
||
): Collection|array|int {
|
||
return $this->getSpecificOutcomePatients('transferred', $branchId, $startDate, $endDate, $onlyIds, $countOnly);
|
||
}
|
||
|
||
/**
|
||
* Получить умерших пациентов
|
||
*/
|
||
public function getDeceasedOutcomePatients(
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false
|
||
): Collection|int|array {
|
||
return $this->getSpecificOutcomePatients('deceased', $branchId, $startDate, $endDate, $onlyIds, $countOnly);
|
||
}
|
||
|
||
/**
|
||
* Общий метод для получения пациентов с определенным типом выбытия
|
||
*/
|
||
private function getSpecificOutcomePatients(
|
||
string $type,
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
bool $onlyIds = false,
|
||
bool $countOnly = false
|
||
): Collection|int|array {
|
||
$migrationPatient = new MisMigrationPatient();
|
||
|
||
switch ($type) {
|
||
case 'discharged':
|
||
$query = $migrationPatient->newQuery()->discharged($branchId, $startDate, $endDate);
|
||
break;
|
||
case 'transferred':
|
||
$query = $migrationPatient->newQuery()->transferred($branchId, $startDate, $endDate);
|
||
break;
|
||
case 'deceased':
|
||
$query = $migrationPatient->newQuery()->deceasedOutcome($branchId, $startDate, $endDate);
|
||
break;
|
||
default:
|
||
throw new \InvalidArgumentException("Неизвестный тип выбытия: {$type}");
|
||
}
|
||
|
||
$medicalHistoryIds = $query->pluck('rf_MedicalHistoryID')->unique()->toArray();
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
if ($countOnly) return 0;
|
||
if ($onlyIds) return [];
|
||
return collect();
|
||
}
|
||
|
||
if ($onlyIds) {
|
||
return $medicalHistoryIds;
|
||
}
|
||
|
||
$patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
if ($countOnly) {
|
||
return $patients->count();
|
||
}
|
||
|
||
return $patients->get();
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов, находящихся на лечении
|
||
*/
|
||
public function getCurrentPatients(
|
||
int $branchId,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false
|
||
): Collection|int|array {
|
||
$migrationPatient = new MisMigrationPatient();
|
||
|
||
$medicalHistoryIds = $migrationPatient->newQuery()
|
||
->currentlyInTreatment($branchId)
|
||
->pluck('rf_MedicalHistoryID')
|
||
->unique()
|
||
->toArray();
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
if ($countOnly) return 0;
|
||
if ($onlyIds) return [];
|
||
return collect();
|
||
}
|
||
|
||
if ($onlyIds) {
|
||
return $medicalHistoryIds;
|
||
}
|
||
|
||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||
->currentlyHospitalized()
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get();
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов с операциями
|
||
*/
|
||
public function getSurgicalPatients(
|
||
string $status,
|
||
bool $isHeadOrAdmin,
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
bool $countOnly = false
|
||
): Collection|int {
|
||
$query = MisSurgicalOperation::where('rf_StationarBranchID', $branchId)
|
||
->whereBetween('Date', [$startDate, $endDate])
|
||
->orderBy('Date', 'DESC');
|
||
|
||
if ($status === 'plan') {
|
||
$query->where('rf_TypeSurgOperationInTimeID', 6);
|
||
} else {
|
||
$query->whereIn('rf_TypeSurgOperationInTimeID', [4, 5]);
|
||
}
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get();
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов (плановых или экстренных), которые были в отделении в течение периода
|
||
*/
|
||
public function getPatientsInDepartmentDuringPeriod(
|
||
?string $patientType, // 'plan', 'emergency', null (все)
|
||
?bool $isHeadOrAdmin,
|
||
?int $branchId,
|
||
?string $startDate,
|
||
?string $endDate,
|
||
bool $countOnly = false,
|
||
bool $onlyIds = false,
|
||
bool $today = false
|
||
): Collection|int|array {
|
||
// Используем скоуп inDepartment из модели MisMedicalHistory
|
||
$query = MisMedicalHistory::query()
|
||
->whereHas('migrations', function ($q) use ($branchId, $startDate, $endDate) {
|
||
$q->where('rf_StationarBranchID', $branchId)
|
||
->where('rf_MedicalHistoryID', '<>', 0)
|
||
->where(function ($subQ) use ($startDate, $endDate) {
|
||
// Пациент находился в отделении в течение периода
|
||
// 1. Поступил в течение периода
|
||
$subQ->whereBetween('DateIngoing', [$startDate, $endDate])
|
||
// 2. Или выбыл в течение периода
|
||
->orWhereBetween('DateOut', [$startDate, $endDate])
|
||
// 3. Или находился в отделении в течение всего периода
|
||
->orWhere(function ($innerQ) use ($startDate, $endDate) {
|
||
$innerQ->where('DateIngoing', '<=', $startDate)
|
||
->where(function ($deepQ) use ($endDate) {
|
||
$deepQ->where('DateOut', '>=', $endDate)
|
||
->orWhereNull('DateOut')
|
||
->orWhere('DateOut', '1900-01-01');
|
||
});
|
||
});
|
||
});
|
||
})
|
||
->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
|
||
$query->whereBetween('Date', [$startDate, $endDate]);
|
||
}])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
// Фильтруем по типу (план/экстренность)
|
||
if ($patientType === 'plan') {
|
||
$query->plan();
|
||
} elseif ($patientType === 'emergency') {
|
||
$query->emergency();
|
||
}
|
||
|
||
// Для врача добавляем условие "все еще в отделении"
|
||
if (!$isHeadOrAdmin && !$today) {
|
||
$query->currentlyHospitalized();
|
||
}
|
||
|
||
if ($onlyIds) {
|
||
return $query->select('MedicalHistoryID')
|
||
->pluck('MedicalHistoryID')->values();
|
||
}
|
||
|
||
if ($countOnly) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query->get();
|
||
}
|
||
}
|