Files
onboard/app/Http/Controllers/Api/ReportController.php
brusnitsyn 9ee33bc517 * блокировка изменения отчета для врача
* вывод данных из отчетов для ролей адм и зав
* поправил ширину стобцов ввода
* добавил календарь на страницу статистики
* переделал календарь у заведующего на странице отчета
* добавил и привязал метрики в статистику
2026-02-03 17:03:37 +09:00

1380 lines
55 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\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\Mis\FormattedPatientResource;
use App\Models\MedicalHistorySnapshot;
use App\Models\MetrikaGroup;
use App\Models\MetrikaResult;
use App\Models\MisLpuDoctor;
use App\Models\MisMedicalHistory;
use App\Models\MisMigrationPatient;
use App\Models\MisStationarBranch;
use App\Models\MisSurgicalOperation;
use App\Models\ObservationPatient;
use App\Models\Report;
use App\Models\UnwantedEvent;
use App\Models\User;
use App\Services\DateRangeService;
use App\Services\MisPatientService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Inertia\Inertia;
class ReportController extends Controller
{
protected MisPatientService $misPatientService;
protected DateRangeService $dateService;
public function __construct(MisPatientService $misPatientService, DateRangeService $dateRangeService)
{
$this->misPatientService = $misPatientService;
$this->dateService = $dateRangeService;
}
public function index(Request $request)
{
$user = Auth::user();
$department = $user->department;
$startDateCarbon = Carbon::now()->firstOfMonth();
$endDateCarbon = Carbon::now();
// Определяем даты в зависимости от роли
[$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $request->query('startAt'), $request->query('endAt'));
if (Carbon::parse($startDate)->isValid()) {
$startDateCarbon = Carbon::parse($startDate)->setTimeZone('Asia/Yakutsk');
}
if (Carbon::parse($endDate)->isValid()) {
$endDateCarbon = Carbon::parse($endDate)->setTimeZone('Asia/Yakutsk');
}
$reportIds = [];
$reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate);
$reportIds = $reports->pluck('report_id')->toArray();
// Определяем, используем ли мы снапшоты
$reportToday = Report::whereDate('sent_at', $endDate)
->whereDate('created_at', $endDate)
->first();
$useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || (Carbon::parse($endDate)->isToday() === false || $reportToday);
if ($useSnapshots && ($user->isHeadOfDepartment() || $user->isAdmin())) {
$report = Report::whereDate('sent_at', $endDate)
->where('rf_department_id', $department->department_id)
->first();
$fillableUserId = $report->rf_lpudoctor_id ?? null;
} else {
$fillableUserId = $request->query('userId', $user->rf_lpudoctor_id);
}
$beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
$occupiedBeds = optional(Report::where('rf_department_id', $user->rf_department_id)
->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id')
->where('metrika_results.rf_metrika_item_id', 8)
->orderBy('sent_at', 'desc')->first())->value ?? 0;
$percentLoadedBeds = round(intval($occupiedBeds) * 100 / $beds); //intval($occupiedBeds) * 100 / $beds;
$metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first();
$metrikaItems = $metrikaGroup->metrikaItems;
$misDepartmentId = $request->user()->department->rf_mis_department_id;
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
->value('StationarBranchID');
$unwantedEvents = UnwantedEvent::whereHas('report', function ($query) use ($user, $startDate, $endDate) {
$query->where('rf_department_id', $user->rf_department_id)
->whereDate('created_at', $endDate);
})
->get()->map(function ($item) {
return [
...$item->toArray(),
'created_at' => Carbon::parse($item->created_at)->format('Создано d.m.Y в H:i'),
];
});
// Определяем, является ли пользователь заведующим/администратором
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
// Получаем статистику в зависимости от источника данных
if ($useSnapshots || $reportToday) {
// Используем снапшоты для статистики
// $plan = $this->getCountFromSnapshots('plan', $reportIds);
// $emergency = $this->getCountFromSnapshots('emergency', $reportIds);
$recipientCount = $this->getPatientsCountFromSnapshot('recipient', $reportIds);
$outcomeCount = $this->getPatientsCountFromSnapshot('outcome', $reportIds);
$currentCount = $this->getPatientsCountFromSnapshot('current', $reportIds);
$deadCount = $this->getPatientsCountFromSnapshot('deceased', $reportIds);
// Для операций все равно используем реплику с фильтрацией по датам
$surgicalCount = [
$this->getSurgicalPatientsFromSnapshot('plan', $reportIds),
$this->getSurgicalPatientsFromSnapshot('emergency', $reportIds)
];
$recipientIds = $this->getRecipientIdsFromSnapshots($reportIds);
} else {
// Используем реплику для статистики
$plan = $this->getPlanOrEmergencyPatients(
'plan',
$isHeadOrAdmin,
$branchId,
$startDate,
$endDate,
true,
today: true
);
$emergency = $this->getPlanOrEmergencyPatients(
'emergency',
$isHeadOrAdmin,
$branchId,
$startDate,
$endDate,
true,
today: true
);
$outcomeCount = $this->getAllOutcomePatients(
$branchId,
$startDate,
$endDate,
true
);
$currentCount = $this->getCurrentPatients($branchId, true);
$deadCount = $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, true);
$surgicalCount = [
$this->getSurgicalPatients('plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, true),
$this->getSurgicalPatients('emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, true)
];
$recipientIds = $this->getPlanOrEmergencyPatients(
null,
$isHeadOrAdmin,
$branchId,
$startDate,
$endDate,
false,
true,
true,
today: true
);
}
$isActiveSendButton = Carbon::createFromFormat('Y-m-d H:i:s', $endDate)->isToday() &&
(!$user->isHeadOfDepartment() && !$user->isAdmin()) && $reportToday == null;
$reportDoctor = $reportToday?->lpuDoctor;
$message = null;
if ($reportToday) {
if ($reportDoctor && $reportDoctor->LPUDoctorID === intval($fillableUserId)) {
$isActiveSendButton = true;
} else {
$message = "Отчет уже создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V";
}
$lpuDoctor = $reportDoctor;
} else {
if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0) {
$lpuDoctor = null;
} else {
$lpuDoctor = MisLpuDoctor::where('LPUDoctorID', $fillableUserId)->first();
}
}
$isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
$date = $isHeadOrAdmin ? [
$this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(),
$this->dateService->parseDate($endDate)->getTimestampMs()
] : $this->dateService->parseDate($endDate)->getTimestampMs();
return response()->json([
'department' => [
'beds' => $beds,
'percentLoadedBeds' => $percentLoadedBeds,
'recipientCount' => $useSnapshots ? $recipientCount : $plan + $emergency, //$recipientCount,
'extractCount' => $outcomeCount, //$extractedCount,
'currentCount' => $currentCount,
'deadCount' => $deadCount,
'surgicalCount' => $surgicalCount,
'recipientIds' => $recipientIds,
],
'dates' => [
'startAt' => $startDateCarbon->getTimestampMs(),
'endAt' => $endDateCarbon->getTimestampMs()
],
'report' => [
'report_id' => $reportToday?->report_id,
'unwantedEvents' => $unwantedEvents,
'isActiveSendButton' => $isActiveSendButton,
'message' => $message,
'isOneDay' => $isRangeOneDay,
'isHeadOrAdmin' => $isHeadOrAdmin,
'dates' => $date
],
'metrikaItems' => $metrikaItems,
'userId' => $fillableUserId,
'userName' => $lpuDoctor ? "$lpuDoctor->FAM_V $lpuDoctor->IM_V $lpuDoctor->OT_V" : null
]);
}
private function getSurgicalPatientsFromSnapshot(string $type, array $reportIds)
{
$count = 0;
switch ($type) {
case 'emergency':
$count = $this->getMetrikaResult(10, $reportIds);
break;
case 'plan':
$count = $this->getMetrikaResult(11, $reportIds);
break;
case 'recipient':
$count = $this->getMetrikaResult(3, $reportIds);
break;
}
return $count;
}
private function getPatientsCountFromSnapshot(string $type, array $reportIds)
{
$count = 0;
switch ($type) {
case 'emergency':
$count = $this->getMetrikaResult(10, $reportIds);
break;
case 'plan':
$count = $this->getMetrikaResult(11, $reportIds);
break;
case 'recipient':
$count = $this->getMetrikaResult(3, $reportIds);
break;
case 'outcome':
$count = $this->getMetrikaResult(7, $reportIds);
break;
case 'deceased':
$count = $this->getMetrikaResult(9, $reportIds);
break;
case 'current':
$count = $this->getMetrikaResult(8, $reportIds);
break;
}
return $count;
}
private function getMetrikaResult(int $metrikaItemId, array $reportIds)
{
$reports = Report::whereIn('report_id', $reportIds)
->with('metrikaResults')
->get();
$count = 0;
foreach ($reports as $report) {
foreach ($report->metrikaResults as $metrikaResult) {
if ($metrikaResult->rf_metrika_item_id === $metrikaItemId)
$count += intval($metrikaResult->value) ?? 0;
}
}
return $count;
}
/**
* Получить количество пациентов из снапшотов
*/
private function getCountFromSnapshots(string $type, array $reportIds)
{
return match($type) {
'plan' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'plan')
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
'emergency' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'emergency')
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
'outcome' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->whereIn('patient_type', ['discharged', 'transferred', 'deceased'])
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
'deceased' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'deceased')
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
'discharged' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'discharged')
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
'transferred' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'transferred')
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
'recipient' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'recipient')
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id'),
default => 0
};
}
/**
* Получить ID пациентов на лечении из снапшотов
*/
private function getCurrentPatientsFromSnapshots(array $reportIds, $branchId)
{
// Получаем ID всех пациентов из снапшотов за период
$allSnapshotPatientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
if (empty($allSnapshotPatientIds)) {
return 0;
}
// Получаем ID выбывших пациентов
$outcomePatientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->whereIn('patient_type', ['discharged', 'transferred', 'deceased'])
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
// Текущие пациенты = все пациенты - выбывшие
$currentPatientIds = array_diff($allSnapshotPatientIds, $outcomePatientIds);
return count($currentPatientIds);
}
/**
* Получить ID поступивших пациентов из снапшотов
*/
private function getRecipientIdsFromSnapshots(array $reportIds)
{
$recipientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', 'recipient')
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
return $recipientIds;
}
// /**
// * Получить отчеты по промежутку дат (оптимизированная версия)
// */
// private function getReportsForDateRange($departmentId, $startDate, $endDate)
// {
// $start = Carbon::parse($startDate);
// $end = Carbon::parse($endDate);
//
// // Если промежуток большой, ограничиваем количество отчетов
// $daysDiff = $start->diffInDays($end);
//
// if ($daysDiff > 30) {
// // Для больших промежутков берем только последние отчеты
// return Report::where('rf_department_id', $departmentId)
// ->whereBetween('created_at', [$start, $end])
// ->orderBy('created_at', 'DESC')
// ->take(30) // Ограничиваем количеством
// ->get()
// ->reverse(); // Возвращаем в правильном порядке
// }
//
// return Report::where('rf_department_id', $departmentId)
// ->whereBetween('created_at', [$start, $end])
// ->orderBy('created_at', 'ASC')
// ->get();
// }
public function store(Request $request)
{
$user = Auth::user();
$misDepartmentId = $user->department->rf_mis_department_id;
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
->value('StationarBranchID');
// Определяем, является ли пользователь заведующим/администратором
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
$data = $request->validate([
'metrics' => 'required',
'observationPatients' => 'nullable',
'departmentId' => 'required|integer',
'unwantedEvents' => 'nullable|array',
'dates' => 'required|array',
'userId' => 'required|integer',
'reportId' => 'nullable'
]);
$metrics = $data['metrics'];
$observationPatients = $data['observationPatients'];
$unwantedEvents = $data['unwantedEvents'];
// Определяем даты в зависимости от роли
[$startDate, $endDate] = $this->getDateRangeForRole($user, $data['dates'][0], $data['dates'][1]);
$metriks = [];
foreach ($metrics as $key => $value) {
$metrika = new MetrikaResult;
$metrikaId = (int)Str::replace('metrika_item_', '', $key);
$metrika->rf_metrika_item_id = $metrikaId;
$metrika->value = $value;
$metriks[] = $metrika;
}
\DB::beginTransaction();
if (isset($data['reportId']) && $data['reportId']) {
$report = Report::updateOrCreate(
[
'report_id' => $data['reportId']
],
[
'rf_department_id' => $data['departmentId'],
'rf_user_id' => Auth::user()->id,
'rf_lpudoctor_id' => $data['userId'],
'created_at' => now(),
'sent_at' => now()
]
);
} else {
$report = Report::create([
'rf_department_id' => $data['departmentId'],
'rf_user_id' => Auth::user()->id,
'rf_lpudoctor_id' => $data['userId'],
'created_at' => now(),
'sent_at' => now()
]);
}
if (count($unwantedEvents)) {
foreach ($unwantedEvents as $unwantedEvent) {
// Если есть ID - ищем по нему
if (isset($unwantedEvent['unwanted_event_id']) && $unwantedEvent['unwanted_event_id']) {
UnwantedEvent::updateOrCreate(
['unwanted_event_id' => $unwantedEvent['unwanted_event_id']],
[
'rf_report_id' => $report->report_id,
'comment' => $unwantedEvent['comment'] ?? '',
'title' => $unwantedEvent['title'] ?? '',
'is_visible' => $unwantedEvent['is_visible'] ?? true,
]
);
} else {
// Если нет ID - создаем новую запись
UnwantedEvent::create([
'rf_report_id' => $report->report_id,
'comment' => $unwantedEvent['comment'] ?? '',
'title' => $unwantedEvent['title'] ?? '',
'is_visible' => $unwantedEvent['is_visible'] ?? true,
]);
}
}
} else {
$unwantedEvents = $report->unwantedEvents;
foreach ($unwantedEvents as $unwantedEvent) {
$unwantedEvent->delete();
}
}
foreach ($metriks as $metrika) {
MetrikaResult::updateOrCreate(
[
'rf_report_id' => $report->report_id,
'rf_metrika_item_id' => $metrika->rf_metrika_item_id
],
[
'rf_report_id' => $report->report_id,
'rf_metrika_item_id' => $metrika->rf_metrika_item_id,
'value' => $metrika->value,
]
);
}
if (count($observationPatients)) {
foreach ($observationPatients as $observationPatient) {
ObservationPatient::updateOrCreate(
[
'rf_medicalhistory_id' => $observationPatient['id'],
'rf_department_id' => $data['departmentId']
],
[
'rf_department_id' => $data['departmentId'],
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $observationPatient['id'],
'rf_mkab_id' => null,
'comment' => $observationPatient['comment'] ?? null
]
);
}
} else {
foreach ($report->observationPatients as $observationPatient) {
$observationPatient->delete();
}
}
// Сохраняем снимок для каждого типа пациентов
// 1. Плановые
$planIds = $this->getPlanOrEmergencyPatients('plan', false, $branchId, $startDate, $endDate, false, false, true);
foreach ($planIds as $id) {
MedicalHistorySnapshot::create([
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => 'plan'
]);
}
// 2. Экстренные
$emergencyIds = $this->getPlanOrEmergencyPatients('emergency', false, $branchId, $startDate, $endDate, false, false, true);
foreach ($emergencyIds as $id) {
MedicalHistorySnapshot::create([
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => 'emergency'
]);
}
// 3. Выписанные
$dischargedIds = $this->getDischargedPatients($branchId, $startDate, $endDate, true);
foreach ($dischargedIds as $id) {
MedicalHistorySnapshot::create([
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => 'discharged'
]);
}
// 4. Переведенные
$transferredIds = $this->getTransferredPatients($branchId, $startDate, $endDate, true);
foreach ($transferredIds as $id) {
MedicalHistorySnapshot::create([
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => 'transferred'
]);
}
// 5. Умершие
$deceasedIds = $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, false, true);
foreach ($deceasedIds as $id) {
MedicalHistorySnapshot::create([
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => 'deceased'
]);
}
// 5. Поступившие
$recipientIds = $this->getPlanOrEmergencyPatients(
null,
$isHeadOrAdmin,
$branchId,
$startDate,
$endDate,
false,
true,
true,
today: true
);
foreach ($recipientIds as $id) {
MedicalHistorySnapshot::create([
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => 'recipient'
]);
}
// 6. Находящиеся на лечении
// $currentIds = $this->getCurrentPatients($branchId, false, true);
// foreach ($currentIds as $id) {
// MedicalHistorySnapshot::create([
// 'rf_report_id' => $report->report_id,
// 'rf_medicalhistory_id' => $id,
// 'patient_type' => 'current'
// ]);
// }
\DB::commit();
return response()->json([
'message' => 'success'
]);
}
public function getPatients(Request $request)
{
$user = Auth::user();
$data = $request->validate([
'status' => 'required|string', // plan emergency observation deceased
'startAt' => 'nullable',
'endAt' => 'nullable',
]);
// Получаем базовые данные
$status = $data['status'];
$model = new MisMedicalHistory();
$misDepartmentId = $request->user()->department->rf_mis_department_id;
$userDepartmentId = $request->user()->department->department_id;
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID');
if (!$branchId) {
return response()->json([]);
}
// Определяем даты в зависимости от роли
[$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null);
// Для заведующего/админа ищем отчет по endDate (дате просмотра)
$reportIds = [];
$reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate);
$reportIds = $reports->pluck('report_id')->toArray();
// Определяем, используем ли мы снапшоты
$useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false;
// Обработка каждого статуса
if ($useSnapshots) {
// Используем снапшоты: получаем ID пациентов, затем данные из реплики
$patients = match($status) {
'plan', 'emergency' => $this->getPatientsFromSnapshotsUsingReplica($status, $reportIds, $branchId, $startDate, $endDate),
'observation' => $this->getObservationPatientsFromSnapshotsUsingReplica($userDepartmentId, $reportIds),
'outcome-discharged' => $this->getOutcomePatientsFromSnapshotsUsingReplica('discharged', $reportIds, $branchId, $startDate, $endDate),
'outcome-transferred' => $this->getOutcomePatientsFromSnapshotsUsingReplica('transferred', $reportIds, $branchId, $startDate, $endDate),
'outcome-deceased' => $this->getOutcomePatientsFromSnapshotsUsingReplica('deceased', $reportIds, $branchId, $startDate, $endDate),
default => collect()
};
} else {
// // Используем реплику для врачей или когда нет отчетов
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
$patients = match($status) {
'plan', 'emergency' => $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate),
'observation' => $this->getObservationPatients($userDepartmentId),
'outcome-discharged' => $this->getDischargedPatients($branchId, $startDate, $endDate),
'outcome-transferred' => $this->getTransferredPatients($branchId, $startDate, $endDate),
'outcome-deceased' => $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate),
default => 0
};
}
// Если есть пациенты, добавляем дополнительные данные
if ($patients->isNotEmpty()) {
$patients = $patients->map(function ($item, $index) use ($branchId, $startDate, $endDate) {
$item->num = $index + 1;
$item->misStationarBranchId = $branchId;
$item->startDate = $startDate;
$item->endDate = $endDate;
return $item;
});
// Загружаем связи
$patients->load(['migrations' => function ($query) use ($startDate, $endDate, $branchId) {
$query->whereHas('diagnosis', function ($q) {
$q->where('rf_DiagnosTypeID', 3);
})
->with('diagnosis.mkb')
->where('rf_StationarBranchID', $branchId);
}]);
}
return response()->json(FormattedPatientResource::collection($patients));
}
public function getPatientsCount(Request $request)
{
$user = Auth::user();
$data = $request->validate([
'status' => 'required|string', // plan emergency observation deceased
'startAt' => 'nullable',
'endAt' => 'nullable',
]);
// Получаем базовые данные
$status = $data['status'];
$model = new MisMedicalHistory();
$misDepartmentId = $request->user()->department->rf_mis_department_id;
$userDepartmentId = $request->user()->department->department_id;
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID');
if (!$branchId) {
return response()->json([]);
}
// Определяем даты в зависимости от роли
[$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null);
// Для заведующего/админа ищем отчеты по промежутку дат
$reportIds = [];
$reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate);
$reportIds = $reports->pluck('report_id')->toArray();
// Определяем, используем ли мы снапшоты
$useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false;
if ($useSnapshots) {
// Считаем из снапшотов
$patientTypeMap = [
'plan' => 'plan',
'emergency' => 'emergency',
'observation' => 'observation',
'outcome' => null,
'outcome-discharged' => 'discharged',
'outcome-transferred' => 'transferred',
'outcome-deceased' => 'deceased'
];
$patientType = $patientTypeMap[$status] ?? null;
if ($status === 'outcome') {
// Считаем уникальных пациентов по всем типам исходов
$count = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->whereIn('patient_type', ['discharged', 'transferred', 'deceased'])
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id');
} elseif ($patientType) {
$count = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', $patientType)
->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id');
} else {
$count = 0;
}
} else {
// Определяем, является ли пользователь заведующим/администратором
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
$isOutcomeOrObservation = in_array($status, ['outcome', 'observation']);
if ($isOutcomeOrObservation)
{
switch ($status) {
case 'observation':
$count = ObservationPatient::where('rf_department_id', $userDepartmentId)->count();
break;
case 'outcome':
$count = $this->getAllOutcomePatients($branchId, $startDate, $endDate, true);
break;
}
} else {
$count = $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate, true);
}
}
return response()->json($count);
}
/**
* Определить диапазон дат в зависимости от роли
*/
private function getDateRangeForRole($user, $startAt = null, $endAt = null): array
{
// Функция для парсинга даты
$parseDate = function($dateInput) {
if (is_numeric($dateInput)) {
return Carbon::createFromTimestampMs($dateInput)
->setTimezone('Asia/Yakutsk');
}
return Carbon::parse($dateInput, 'Asia/Yakutsk');
};
// Если переданы обе даты (и заведующий, и врач могут выбрать даты)
if ($startAt && $endAt) {
$startDate = $parseDate($startAt);
$endDate = $parseDate($endAt);
if ($startDate->isSameDay($endDate)) {
// Сдвигаем начало на день назад для всех ролей при выборе одного дня
// И всегда для врача
$startDate = $startDate->copy()->addDays(-1)->setTime(6, 0)->format('Y-m-d H:i:s');
$endDate = $endDate->setTime(6, 0)->format('Y-m-d H:i:s');
} else {
// Для диапазона оставляем как есть (только для заведующих)
$startDate = $startDate->setTime(6, 0)->format('Y-m-d H:i:s');
$endDate = $endDate->setTime(6, 0)->format('Y-m-d H:i:s');
}
}
// Если даты не переданы - логика по умолчанию в зависимости от роли
else {
// Для заведующего или администратора - сутки
if ($user->isHeadOfDepartment() || $user->isAdmin()) {
$startDate = Carbon::now('Asia/Yakutsk')
->subDay()
->setTime(6, 0)
->format('Y-m-d H:i:s');
$endDate = Carbon::now('Asia/Yakutsk')
->setTime(6, 0)
->format('Y-m-d H:i:s');
}
// Для врача - только сутки (вчера 06:00 - сегодня 06:00)
else {
$startDate = Carbon::now('Asia/Yakutsk')->subDay()->setTime(6, 0)->format('Y-m-d H:i:s');
$endDate = Carbon::now('Asia/Yakutsk')->setTime(6, 0)->format('Y-m-d H:i:s');
}
}
return [$startDate, $endDate];
}
/**
* Получить пациентов (плановых или экстренных)
*/
private function getPlanOrEmergencyPatients(
?string $status,
bool $isHeadOrAdmin,
$branchId,
$startDate, $endDate,
bool $returnedCount = false,
bool $all = false,
bool $onlyIds = false,
bool $today = false
) {
// Определяем, является ли статус outcome
$isOutcomeStatus = in_array($status, ['outcome-transferred', 'outcome-discharged', 'outcome-deceased']);
if ($isOutcomeStatus) {
switch ($status) {
case 'outcome-transferred':
$query = MisMigrationPatient::transferred($branchId, $startDate, $endDate);
break;
case 'outcome-discharged':
$query = MisMigrationPatient::discharged($branchId, $startDate, $endDate);
break;
case 'outcome-deceased':
$query = MisMigrationPatient::deceasedOutcome($branchId, $startDate, $endDate);
break;
}
} else {
// Разная логика для заведующего и врача
if ($isHeadOrAdmin) {
// Заведующий: используем whereInDepartment
$query = MisMigrationPatient::whereInDepartment($branchId)
->whereBetween('DateIngoing', [$startDate, $endDate]);
} else {
// Врач: используем currentlyInTreatment + фильтр по дате
$query = MisMigrationPatient::currentlyInTreatment($branchId)
->when($today, function ($query) use ($startDate, $endDate) {
return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
});
}
}
$medicalHistoryIds = $query->pluck('rf_MedicalHistoryID')->toArray();
if (empty($medicalHistoryIds)) {
if ($returnedCount) return 0;
return collect();
}
// Получаем истории
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
$query->whereBetween('Date', [$startDate, $endDate]);
}])
->orderBy('DateRecipient', 'DESC');
// Выбираем план или экстренность
if (!$all && !$isOutcomeStatus) {
if ($status === 'plan') {
$query->plan();
} else if ($status === 'emergency') {
$query->emergency();
}
}
// Для врача добавляем условие "в отделении"
if (!$isHeadOrAdmin && !$isOutcomeStatus) {
$query->currentlyHospitalized();
}
if ($onlyIds) {
return $query->select('MedicalHistoryID')
->pluck('MedicalHistoryID')->values();
} else {
if ($returnedCount) return $query->count();
else return $query->get();
}
}
/**
* Получить пациентов под наблюдением
*/
private function getObservationPatients(
$userDepartmentId
) {
$observationPatients = ObservationPatient::where('rf_department_id', $userDepartmentId)
->select(['rf_medicalhistory_id', 'observation_patient_id'])
->get()
->groupBy('rf_medicalhistory_id');
$medicalHistoryIds = $observationPatients->keys()->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
$patients = MisMedicalHistory::whereHas('observationPatient', function ($q) use ($userDepartmentId) {
$q->where('rf_department_id', $userDepartmentId);
})
->with(['observationPatient' => function($query) use ($userDepartmentId) {
$query->where('rf_department_id', $userDepartmentId)
->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']);
}])
->orderBy('DateRecipient', 'DESC')
->get();
// Добавляем комментарии
$patients = $patients->map(function ($patient) {
// Объединяем все комментарии
$patient->comment = $patient->observationPatient
->pluck('comment')
->filter()
->implode('; ');
return $patient;
});
return $patients;
}
/**
* Получить выбывших пациентов
*/
private function getOutcomePatients(
$misDepartmentId,
$startDate,
$endDate
) {
// Здесь оставляем оригинальный запрос, т.к. он специфичен для умерших
return MisMedicalHistory::deceased()
->inDepartment($misDepartmentId, $startDate, $endDate)
->get();
}
/**
* Получить всех выбывших пациентов
*/
private function getAllOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false)
{
// Сначала получаем миграции с типами выбытия
$migrations = MisMigrationPatient::outcomePatients($branchId, $startDate, $endDate)
->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut')
->get()
->groupBy('rf_MedicalHistoryID');
if ($migrations->isEmpty()) {
if ($returnedCount) return 0;
return collect();
}
$medicalHistoryIds = $migrations->keys()->toArray();
// Получаем истории
$patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC');
if ($returnedCount) return $patients->count();
else $patients = $patients->get();
// Добавляем информацию о типе выбытия
return $patients->map(function ($patient) use ($migrations) {
$patientMigrations = $migrations->get($patient->MedicalHistoryID, collect());
// Определяем основной тип выбытия (берем последнюю миграцию)
$latestMigration = $patientMigrations->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;
});
}
/**
* Получить понятное название типа выбытия
*/
private function getOutcomeTypeName($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 . ')'
};
}
/**
* Получить выписанных пациентов
*/
private function getDischargedPatients($branchId, $startDate, $endDate, bool $onlyIds = false)
{
$medicalHistoryIds = MisMigrationPatient::discharged($branchId, $startDate, $endDate)
->pluck('rf_MedicalHistoryID')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
if ($onlyIds) {
return $medicalHistoryIds;
}
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC')
->get();
}
/**
* Получить переведенных пациентов
*/
private function getTransferredPatients($branchId, $startDate, $endDate, bool $onlyIds = false)
{
$medicalHistoryIds = MisMigrationPatient::transferred($branchId, $startDate, $endDate)
->pluck('rf_MedicalHistoryID')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
if ($onlyIds) {
return $medicalHistoryIds;
}
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC')
->get();
}
/**
* Получить умерших пациентов (исход)
*/
private function getDeceasedOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false, bool $onlyIds = false)
{
$medicalHistoryIds = MisMigrationPatient::deceasedOutcome($branchId, $startDate, $endDate)
->pluck('rf_MedicalHistoryID')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
if ($returnedCount) return 0;
return collect();
}
if ($onlyIds) {
return $medicalHistoryIds;
}
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC');
if ($returnedCount) return $query->count();
else return $query->get();
}
/**
* Получить пациентов с операциями
*/
private function getSurgicalPatients(string $status, bool $isHeadOrAdmin, $branchId, $startDate, $endDate, bool $returnedCount = false)
{
$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 ($returnedCount) return $query->count();
else return $query->get();
}
/**
* Находятся на лечении
*/
private function getCurrentPatients($branchId, bool $returnedCount = false, bool $onlyIds = false)
{
// $currentCount = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
// ->currentlyHospitalized()
// ->orderBy('DateRecipient', 'DESC')
// ->count();
$medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId)
->pluck('rf_MedicalHistoryID')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
if ($returnedCount) return 0;
return collect();
}
if ($onlyIds) return $medicalHistoryIds;
$patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->currentlyHospitalized()
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC');
if ($returnedCount) return $patients->count();
else return $patients->get();
}
public function removeObservation(
Request $request,
) {
$data = $request->validate([
'id' => 'required'
]);
ObservationPatient::where('rf_medicalhistory_id', $data['id'])->delete();
return response()->json()->setStatusCode(200);
}
// api/report/unwanted-event
public function removeUnwantedEvent(UnwantedEvent $unwantedEvent, Request $request)
{
$unwantedEvent->delete();
return response()->json()->setStatusCode(200);
}
public function getDepartmentUsers(Request $request)
{
$user = Auth::user();
$departmentId = $user->department->rf_mis_department_id;
$users = MisLpuDoctor::select(['LPUDoctorID', 'FAM_V', 'IM_V', 'OT_V'])
->active()
->inMyDepartment()
->get();
return response()->json([
...$users
])->setStatusCode(200);
}
/**
* Получить все отчеты в промежутке дат (для агрегации данных)
*/
private function getReportsForDateRange($departmentId, $startDate, $endDate)
{
if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0)
return Report::where('rf_department_id', $departmentId)
->whereBetween('created_at', [$startDate, $endDate])
->orderBy('created_at', 'ASC')
->get();
else
return Report::where('rf_department_id', $departmentId)
->whereDate('created_at', $endDate)
->orderBy('created_at', 'ASC')
->get();
}
/**
* Получить плановых/экстренных пациентов из снапшотов через реплику
*/
private function getPatientsFromSnapshotsUsingReplica(string $status, array $reportIds, $branchId, $startDate, $endDate)
{
// Получаем ID пациентов из снапшотов
$patientType = $status === 'plan' ? 'plan' : 'emergency';
$medicalHistoryIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', $patientType)
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
// Используем существующие скоупы для получения данных из реплики
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
$query->whereBetween('Date', [$startDate, $endDate]);
}])
->orderBy('DateRecipient', 'DESC');
// Применяем те же фильтры
if ($status === 'plan') {
$query->plan();
} else {
$query->emergency();
}
return $query->get();
}
/**
* Получить пациентов под наблюдением из снапшотов через реплику
*/
private function getObservationPatientsFromSnapshotsUsingReplica($userDepartmentId, array $reportIds)
{
// Получаем ID пациентов из снапшотов наблюдения
$medicalHistoryIds = ObservationPatient::whereIn('rf_report_id', $reportIds)
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
// Используем существующий метод с фильтрацией по ID из снапшотов
$patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->whereHas('observationPatient', function ($q) use ($userDepartmentId) {
$q->where('rf_department_id', $userDepartmentId);
})
->with(['observationPatient' => function($query) use ($userDepartmentId) {
$query->where('rf_department_id', $userDepartmentId)
->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']);
}])
->orderBy('DateRecipient', 'DESC')
->get();
// Добавляем комментарии
$patients = $patients->map(function ($patient) {
$patient->comment = $patient->observationPatient
->pluck('comment')
->filter()
->implode('; ');
return $patient;
});
return $patients;
}
/**
* Получить пациентов с исходом из снапшотов через реплику
*/
private function getOutcomePatientsFromSnapshotsUsingReplica(string $outcomeType, array $reportIds, $branchId, $startDate, $endDate)
{
// Получаем ID пациентов из снапшотов исходов
$medicalHistoryIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
->where('patient_type', $outcomeType)
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
if (empty($medicalHistoryIds)) {
return collect();
}
// Используем соответствующий метод для получения данных из реплики
return match($outcomeType) {
'discharged' => $this->getDischargedPatientsByIds($medicalHistoryIds, $branchId),
'transferred' => $this->getTransferredPatientsByIds($medicalHistoryIds, $branchId),
'deceased' => $this->getDeceasedPatientsByIds($medicalHistoryIds, $branchId),
default => collect()
};
}
/**
* Получить выписанных пациентов по ID из снапшотов
*/
private function getDischargedPatientsByIds(array $medicalHistoryIds, $branchId)
{
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC')
->get()
->map(function ($patient) use ($branchId) {
// Получаем информацию о выписке
$migration = $patient->migrations
->where('rf_kl_VisitResultID', 'in', [1, 7, 8, 9, 10, 11, 48, 49, 124])
->whereNotNull('DateOut')
->sortByDesc('DateOut')
->first();
if ($migration) {
$patient->outcome_type = $this->getOutcomeTypeName($migration->rf_kl_VisitResultID);
$patient->outcome_date = $migration->DateOut;
$patient->visit_result_id = $migration->rf_kl_VisitResultID;
}
return $patient;
});
}
/**
* Получить переведенных пациентов по ID из снапшотов
*/
private function getTransferredPatientsByIds(array $medicalHistoryIds, $branchId)
{
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC')
->get()
->map(function ($patient) use ($branchId) {
// Получаем информацию о переводе
$migration = $patient->migrations
->where('rf_kl_VisitResultID', 'in', [2, 3, 4, 12, 13, 14])
->whereNotNull('DateOut')
->sortByDesc('DateOut')
->first();
if ($migration) {
$patient->outcome_type = $this->getOutcomeTypeName($migration->rf_kl_VisitResultID);
$patient->outcome_date = $migration->DateOut;
$patient->visit_result_id = $migration->rf_kl_VisitResultID;
}
return $patient;
});
}
/**
* Получить умерших пациентов по ID из снапшотов
*/
private function getDeceasedPatientsByIds(array $medicalHistoryIds, $branchId)
{
return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
->with(['surgicalOperations'])
->orderBy('DateRecipient', 'DESC')
->get()
->map(function ($patient) use ($branchId) {
// Получаем информацию о смерти
$migration = $patient->migrations
->where('rf_kl_VisitResultID', 'in', [5, 6, 15, 16])
->whereNotNull('DateOut')
->sortByDesc('DateOut')
->first();
if ($migration) {
$patient->outcome_type = $this->getOutcomeTypeName($migration->rf_kl_VisitResultID);
$patient->outcome_date = $migration->DateOut;
$patient->visit_result_id = $migration->rf_kl_VisitResultID;
}
return $patient;
});
}
}