Files
onboard/app/Services/PatientService.php
brusnitsyn 87e21f0e08 * восстановление окна наблюдения
* добавил получение выбывших
* фильтрация выбывших по результатам лечения
* добавил подсказку при наведении на операции
* добавил вывод причины наблюдения
* добавил вкладки для выбывших
* изменил связь и сохранение пациентов на контроле
* добавил возможность редактирования причины контроля
* полное изменение окна с нежелательными событиями
* исправил просмотр причины контроля
* работа над окном редактирования причины контроля в таблице
* визуальное выделение умерших и проведенных операций
* добавил выбор даты для роли врач
* центрирование блоков статистики
* разделение выполненных операций на срочность
* поправил метод определения текущего дня для роли врач
* функция блокировки при выборе другой даты для роли врач
2026-01-29 16:42:42 +09:00

450 lines
16 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\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();
}
}