1244 lines
43 KiB
PHP
1244 lines
43 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Api;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Resources\Api\DepartmentPatientOperationResource;
|
||
use App\Http\Resources\Mis\FormattedPatientResource;
|
||
use App\Models\Department;
|
||
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\MisMKB;
|
||
use App\Models\MisStationarBranch;
|
||
use App\Models\MisSurgicalOperation;
|
||
use App\Models\ObservationPatient;
|
||
use App\Models\Report;
|
||
use App\Models\UnwantedEvent;
|
||
use App\Services\DateRangeService;
|
||
use App\Services\MisPatientService;
|
||
use App\Services\ReportService;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Facades\Auth;
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\Str;
|
||
|
||
class ReportController extends Controller
|
||
{
|
||
public function __construct(
|
||
protected MisPatientService $misPatientService,
|
||
protected ReportService $reportService,
|
||
protected DateRangeService $dateRangeService) {}
|
||
|
||
public function index(Request $request)
|
||
{
|
||
$user = Auth::user();
|
||
$departmentId = $request->query('departmentId', $user->department->department_id);
|
||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||
$statistics = $this->reportService->getReportStatistics($department, $user, $dateRange);
|
||
$reportInfo = $this->reportService->getCurrentReportInfo($department, $user, $dateRange);
|
||
$metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first();
|
||
$metrikaItems = $metrikaGroup->metrikaItems;
|
||
|
||
return response()->json([
|
||
'department' => [
|
||
'department_id' => $department->department_id,
|
||
'department_name' => $department->name_full,
|
||
'beds' => $department->beds,
|
||
...$statistics,
|
||
],
|
||
'dates' => [
|
||
'startAt' => $dateRange->startTimestamp(),
|
||
'endAt' => $dateRange->endTimestamp(),
|
||
],
|
||
'report' => $reportInfo,
|
||
'metrikaItems' => $metrikaItems,
|
||
'userId' => $reportInfo['userId'],
|
||
'userName' => $reportInfo['userName'],
|
||
]);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* Получить 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;
|
||
}
|
||
|
||
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',
|
||
'startAt' => 'required|integer',
|
||
'endAt' => 'required|integer',
|
||
'userId' => 'required|integer',
|
||
'reportId' => 'nullable',
|
||
]);
|
||
$metrics = $data['metrics'];
|
||
$observationPatients = $data['observationPatients'];
|
||
$unwantedEvents = $data['unwantedEvents'];
|
||
|
||
// Определяем даты в зависимости от роли
|
||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||
|
||
$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;
|
||
}
|
||
|
||
// 1. Плановые
|
||
$planIds = $this->reportService->getPatientsByStatus(
|
||
$user,
|
||
'plan',
|
||
$dateRange,
|
||
true,
|
||
true,
|
||
);
|
||
$planCount = $this->reportService->getPatientsCountByStatus($user, 'plan', $dateRange);
|
||
// 2. Экстренные
|
||
$emergencyIds = $this->reportService->getPatientsByStatus(
|
||
$user,
|
||
'emergency',
|
||
$dateRange,
|
||
true,
|
||
true,
|
||
);
|
||
$emergencyCount = $this->reportService->getPatientsCountByStatus($user, 'emergency', $dateRange);
|
||
// 3. Выписанные
|
||
$dischargedIds = $this->reportService->getPatientsByStatus(
|
||
$user,
|
||
'outcome',
|
||
$dateRange,
|
||
true,
|
||
true
|
||
);
|
||
// 4. Переведенные
|
||
$transferredIds = $this->reportService->getPatientsByStatus(
|
||
$user,
|
||
'outcome-transferred',
|
||
$dateRange,
|
||
true,
|
||
true
|
||
);
|
||
// 5. Умершие
|
||
$deceasedIds = $this->reportService->getPatientsByStatus(
|
||
$user,
|
||
'outcome-deceased',
|
||
$dateRange,
|
||
true,
|
||
true
|
||
);
|
||
// 6. Поступившие
|
||
$recipientIds = $this->reportService->getPatientsByStatus(
|
||
$user,
|
||
'recipient',
|
||
$dateRange,
|
||
true,
|
||
true
|
||
);
|
||
|
||
\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' => $dateRange->endSql(),
|
||
'sent_at' => $dateRange->endSql(),
|
||
'period_start' => $dateRange->startSql(),
|
||
'period_end' => $dateRange->endSql(),
|
||
]
|
||
);
|
||
} else {
|
||
$report = Report::create([
|
||
'rf_department_id' => $data['departmentId'],
|
||
'rf_user_id' => Auth::user()->id,
|
||
'rf_lpudoctor_id' => $data['userId'],
|
||
'created_at' => $dateRange->endSql(),
|
||
'sent_at' => $dateRange->endSql(),
|
||
'period_start' => $dateRange->startSql(),
|
||
'period_end' => $dateRange->endSql(),
|
||
]);
|
||
}
|
||
|
||
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();
|
||
}
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 16,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 16,
|
||
'value' => count($unwantedEvents),
|
||
]
|
||
);
|
||
|
||
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();
|
||
}
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 14,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 14,
|
||
'value' => count($observationPatients),
|
||
]
|
||
);
|
||
|
||
// Сохраняем снимок для каждого типа пациентов
|
||
// Планово
|
||
// $this->getPlanOrEmergencyPatients('plan', false, $branchId, $dateRange->startSql(), $dateRange->endSql(), false, false, true);
|
||
foreach ($planIds as $id) {
|
||
MedicalHistorySnapshot::create([
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_medicalhistory_id' => $id,
|
||
'patient_type' => 'plan',
|
||
]);
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 4,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 4,
|
||
'value' => $planCount,
|
||
]
|
||
);
|
||
|
||
// $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',
|
||
]);
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 12,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 12,
|
||
'value' => $emergencyCount,
|
||
]
|
||
);
|
||
|
||
// $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',
|
||
]);
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 15,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 15,
|
||
'value' => count($dischargedIds),
|
||
]
|
||
);
|
||
|
||
// $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',
|
||
]);
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 13,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 13,
|
||
'value' => count($transferredIds),
|
||
]
|
||
);
|
||
|
||
// $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',
|
||
]);
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 9,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 9,
|
||
'value' => count($deceasedIds),
|
||
]
|
||
);
|
||
|
||
// $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',
|
||
]);
|
||
}
|
||
MetrikaResult::updateOrCreate(
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 3,
|
||
],
|
||
[
|
||
'rf_report_id' => $report->report_id,
|
||
'rf_metrika_item_id' => 3,
|
||
'value' => count($recipientIds),
|
||
]
|
||
);
|
||
|
||
// 7. Находящиеся на лечении
|
||
// $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();
|
||
|
||
$validated = $request->validate([
|
||
'status' => 'required|string',
|
||
'startAt' => 'nullable',
|
||
'endAt' => 'nullable',
|
||
'departmentId' => 'nullable',
|
||
'page' => 'nullable|integer|min:1',
|
||
'perPage' => 'nullable|integer|min:1|max:1000',
|
||
'search' => 'nullable|string|max:255',
|
||
]);
|
||
|
||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||
|
||
$departmentId = $request->get('departmentId', $user->department->department_id);
|
||
$department = Department::where('department_id', $departmentId)->first();
|
||
$page = (int) ($validated['page'] ?? 1);
|
||
$perPage = (int) ($validated['perPage'] ?? 20);
|
||
$search = trim((string) ($validated['search'] ?? ''));
|
||
|
||
$patients = collect($this->reportService->getPatientsByStatus(
|
||
$department,
|
||
Auth::user(),
|
||
$validated['status'],
|
||
$dateRange
|
||
));
|
||
|
||
if ($this->resolveBaseStatus($validated['status']) === 'reanimation') {
|
||
$this->attachReanimationIndicators($patients, (int) $department->department_id);
|
||
}
|
||
|
||
if ($search !== '') {
|
||
$needle = mb_strtolower($search);
|
||
$patients = $patients->filter(function ($patient) use ($needle) {
|
||
$fullName = mb_strtolower(trim((string) ($patient->fullname ?? "{$patient->FAMILY} {$patient->Name} {$patient->OT}")));
|
||
$diagnosisCode = mb_strtolower((string) ($patient->mkb['ds'] ?? ''));
|
||
$diagnosisName = mb_strtolower((string) ($patient->mkb['name'] ?? ''));
|
||
|
||
return str_contains($fullName, $needle)
|
||
|| str_contains($diagnosisCode, $needle)
|
||
|| str_contains($diagnosisName, $needle);
|
||
})->values();
|
||
}
|
||
|
||
$total = $patients->count();
|
||
$items = $patients->forPage($page, $perPage)->values();
|
||
$data = FormattedPatientResource::collection($items)->resolve();
|
||
|
||
return response()->json([
|
||
'data' => $data,
|
||
'meta' => [
|
||
'total' => $total,
|
||
'page' => $page,
|
||
'perPage' => $perPage,
|
||
'lastPage' => max((int) ceil($total / max($perPage, 1)), 1),
|
||
],
|
||
]);
|
||
}
|
||
|
||
public function saveReanimationIndicator(Request $request)
|
||
{
|
||
$user = Auth::user();
|
||
$data = $request->validate([
|
||
'departmentId' => 'nullable|integer',
|
||
'medical_history_id' => 'required|integer',
|
||
'indicator' => 'required|string|max:100',
|
||
'comment' => 'nullable|string|max:1000',
|
||
'startAt' => 'nullable',
|
||
'endAt' => 'nullable',
|
||
]);
|
||
|
||
$departmentId = (int) ($data['departmentId'] ?? $user->department->department_id);
|
||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||
$reportId = $this->getReportsForDateRange(
|
||
$department->department_id,
|
||
$dateRange->startSql(),
|
||
$dateRange->endSql()
|
||
)->pluck('report_id')->last();
|
||
|
||
$indicator = $this->reportService->saveReanimationIndicator(
|
||
$user,
|
||
$department->department_id,
|
||
(int) $data['medical_history_id'],
|
||
trim($data['indicator']),
|
||
isset($data['comment']) ? trim((string) $data['comment']) : null,
|
||
$reportId ? (int) $reportId : null
|
||
);
|
||
|
||
return response()->json($indicator, 201);
|
||
}
|
||
|
||
public function getReanimationIndicatorHistory(Request $request)
|
||
{
|
||
$user = Auth::user();
|
||
$data = $request->validate([
|
||
'departmentId' => 'nullable|integer',
|
||
'medical_history_id' => 'required|integer',
|
||
'limit' => 'nullable|integer|min:1|max:200',
|
||
]);
|
||
|
||
$departmentId = (int) ($data['departmentId'] ?? $user->department->department_id);
|
||
$history = $this->reportService->getReanimationIndicatorsHistory(
|
||
$departmentId,
|
||
(int) $data['medical_history_id'],
|
||
(int) ($data['limit'] ?? 50)
|
||
);
|
||
|
||
return response()->json($history);
|
||
}
|
||
|
||
public function getPatientsCount(Request $request)
|
||
{
|
||
$user = Auth::user();
|
||
|
||
$validated = $request->validate([
|
||
'status' => 'required|string',
|
||
'startAt' => 'nullable',
|
||
'endAt' => 'nullable',
|
||
'departmentId' => 'nullable',
|
||
]);
|
||
|
||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||
|
||
$departmentId = $request->get('departmentId', $user->department->department_id);
|
||
$department = Department::where('department_id', $departmentId)->first();
|
||
|
||
$count = $this->reportService->getPatientsCountByStatus(
|
||
$department,
|
||
$user,
|
||
$validated['status'],
|
||
$dateRange,
|
||
);
|
||
|
||
return response()->json($count);
|
||
}
|
||
|
||
public function getPatientsCounts(Request $request)
|
||
{
|
||
$user = Auth::user();
|
||
|
||
$request->validate([
|
||
'startAt' => 'nullable',
|
||
'endAt' => 'nullable',
|
||
'departmentId' => 'nullable',
|
||
'force' => 'nullable|boolean',
|
||
]);
|
||
|
||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||
$departmentId = $request->get('departmentId', $user->department->department_id);
|
||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||
|
||
$cacheKey = sprintf(
|
||
'report:patients-counts:%d:%d:%s:%s',
|
||
$user->id,
|
||
$department->department_id,
|
||
$dateRange->startSql(),
|
||
$dateRange->endSql()
|
||
);
|
||
|
||
$force = (bool) $request->boolean('force', false);
|
||
|
||
if ($force) {
|
||
$counts = $this->reportService->getPatientsCountsMap($department, $user, $dateRange);
|
||
Cache::put($cacheKey, $counts, now()->addSeconds(30));
|
||
} else {
|
||
$counts = Cache::remember($cacheKey, now()->addSeconds(30), function () use ($department, $user, $dateRange) {
|
||
return $this->reportService->getPatientsCountsMap($department, $user, $dateRange);
|
||
});
|
||
}
|
||
|
||
return response()->json([
|
||
'counts' => $counts,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов (плановых или экстренных)
|
||
*/
|
||
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) {
|
||
$visitResultIds = match ($status) {
|
||
'outcome-transferred' => [4, 14],
|
||
'outcome-discharged' => [1, 11, 2, 12, 7, 18, 48],
|
||
'outcome-deceased' => [5, 6, 15, 16],
|
||
default => [],
|
||
};
|
||
|
||
$historyQuery = $this->buildOutcomeMedicalHistoryQuery(
|
||
$branchId,
|
||
$startDate,
|
||
$endDate,
|
||
$visitResultIds
|
||
);
|
||
|
||
if ($onlyIds) {
|
||
return $historyQuery->pluck('MedicalHistoryID')->values();
|
||
}
|
||
|
||
if ($returnedCount) {
|
||
return $historyQuery->count();
|
||
}
|
||
|
||
return $historyQuery
|
||
->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
|
||
$query->where('Date', '>=', $startDate)
|
||
->where('Date', '<=', $endDate);
|
||
}])
|
||
->orderBy('DateRecipient', 'DESC')
|
||
->get();
|
||
} else {
|
||
// Разная логика для заведующего и врача
|
||
if ($isHeadOrAdmin) {
|
||
// Заведующий: используем whereInDepartment
|
||
$query = MisMigrationPatient::whereInDepartment($branchId)
|
||
// ->whereBetween('DateIngoing', [$startDate, $endDate]);
|
||
->where('DateIngoing', '>=', $startDate)
|
||
->where('DateIngoing', '<=', $endDate);
|
||
} else {
|
||
// Врач: используем currentlyInTreatment + фильтр по дате
|
||
$query = MisMigrationPatient::currentlyInTreatment($branchId)
|
||
->when($today, function ($query) use ($startDate, $endDate) {
|
||
// return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
|
||
return $query->where('DateIngoing', '>=', $startDate)
|
||
->where('DateIngoing', '<=', $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]);
|
||
$query->where('Date', '>=', $startDate)
|
||
->where('Date', '<=', $endDate);
|
||
}])
|
||
->orderBy('DateRecipient', 'DESC');
|
||
|
||
// Выбираем план или экстренность
|
||
if (! $all && ! $isOutcomeStatus) {
|
||
if ($status === 'plan') {
|
||
$query->plan();
|
||
} elseif ($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 getAllOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false)
|
||
{
|
||
$query = $this->buildOutcomeMedicalHistoryQuery($branchId, $startDate, $endDate);
|
||
|
||
if ($returnedCount) {
|
||
return $query->count();
|
||
}
|
||
|
||
return $query
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC')
|
||
->get();
|
||
}
|
||
|
||
/**
|
||
* Получить понятное название типа выбытия
|
||
*/
|
||
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 getDeceasedOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false, bool $onlyIds = false)
|
||
{
|
||
$query = $this->buildOutcomeMedicalHistoryQuery(
|
||
$branchId,
|
||
$startDate,
|
||
$endDate,
|
||
[5, 6, 15, 16]
|
||
);
|
||
|
||
if ($onlyIds) {
|
||
return $query->pluck('MedicalHistoryID')->values();
|
||
}
|
||
|
||
if ($returnedCount) {
|
||
return $query->count();
|
||
} else {
|
||
return $query
|
||
->with(['surgicalOperations'])
|
||
->orderBy('DateRecipient', 'DESC')
|
||
->get();
|
||
}
|
||
}
|
||
|
||
private function buildOutcomeMedicalHistoryQuery(
|
||
int $branchId,
|
||
string $startDate,
|
||
string $endDate,
|
||
?array $visitResultIds = null
|
||
) {
|
||
$startDateOnly = Carbon::parse($startDate)->toDateString();
|
||
$endDateOnly = Carbon::parse($endDate)->toDateString();
|
||
|
||
return MisMedicalHistory::query()
|
||
->where('MedicalHistoryID', '<>', 0)
|
||
->whereDate('DateExtract', '>', $startDateOnly)
|
||
->whereDate('DateExtract', '<=', $endDateOnly)
|
||
->whereHas('migrations', function ($migrationQuery) use ($branchId, $visitResultIds) {
|
||
$migrationQuery->where('rf_StationarBranchID', $branchId);
|
||
|
||
if ($visitResultIds !== null && ! empty($visitResultIds)) {
|
||
$migrationQuery->whereIn('rf_kl_VisitResultID', $visitResultIds);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Получить пациентов с операциями
|
||
*/
|
||
private function getSurgicalPatients(string $status, bool $isHeadOrAdmin, $branchId, $startDate, $endDate, bool $returnedCount = false)
|
||
{
|
||
$query = MisSurgicalOperation::where('rf_StationarBranchID', $branchId)
|
||
->completed()
|
||
// ->whereBetween('Date', [$startDate, $endDate])
|
||
->where('Date', '>=', $startDate)
|
||
->where('Date', '<=', $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|string',
|
||
]);
|
||
|
||
$this->reportService->removeObservationPatient($data['id']);
|
||
|
||
return response()->json()->setStatusCode(200);
|
||
}
|
||
|
||
public function createManualPatient(Request $request)
|
||
{
|
||
$user = Auth::user();
|
||
$data = $request->validate([
|
||
'departmentId' => 'required|integer',
|
||
'report_id' => 'nullable|integer',
|
||
'startAt' => 'required_without:report_id',
|
||
'endAt' => 'required_without:report_id',
|
||
'user_id' => 'nullable|integer',
|
||
'full_name' => 'required|string|max:255',
|
||
'birth_date' => 'required|date',
|
||
'patient_kind' => 'required|in:plan,emergency',
|
||
'diagnosis_code' => 'nullable|string|max:255',
|
||
'diagnosis_name' => 'nullable|string|max:1000',
|
||
'admitted_at' => 'nullable|date',
|
||
]);
|
||
|
||
$department = Department::where('department_id', $data['departmentId'])->firstOrFail();
|
||
$patient = $this->reportService->createManualPatient($department, $user, $data);
|
||
|
||
return response()->json([
|
||
'patient' => $patient,
|
||
'report_id' => $patient->rf_report_id,
|
||
], 201);
|
||
}
|
||
|
||
public function setManualPatientOutcome(Request $request, int $departmentPatientId)
|
||
{
|
||
$user = Auth::user();
|
||
$data = $request->validate([
|
||
'outcome_type' => 'required|in:discharged,transferred,deceased',
|
||
'outcome_at' => 'nullable|date',
|
||
]);
|
||
|
||
return response()->json(
|
||
$this->reportService->setManualPatientOutcome($user, $departmentPatientId, $data)
|
||
);
|
||
}
|
||
|
||
public function updateManualPatient(Request $request, int $departmentPatientId)
|
||
{
|
||
$user = Auth::user();
|
||
|
||
$data = $request->validate([
|
||
'full_name' => 'required|string|max:255',
|
||
'birth_date' => 'required|date',
|
||
'patient_kind' => 'required|in:plan,emergency',
|
||
'manual_status' => 'nullable|in:current,discharged,deceased',
|
||
'outcome_at' => 'nullable|date',
|
||
'diagnosis_code' => 'nullable|string|max:255',
|
||
'diagnosis_name' => 'nullable|string|max:1000',
|
||
'admitted_at' => 'nullable|date',
|
||
'startAt' => 'nullable',
|
||
'endAt' => 'nullable',
|
||
]);
|
||
|
||
return response()->json(
|
||
$this->reportService->updateManualPatient(
|
||
$user,
|
||
$departmentPatientId,
|
||
$data
|
||
)
|
||
);
|
||
}
|
||
|
||
public function linkManualPatient(Request $request, int $departmentPatientId)
|
||
{
|
||
$data = $request->validate([
|
||
'medical_history_id' => 'required|integer',
|
||
]);
|
||
|
||
return response()->json(
|
||
$this->reportService->linkManualPatientToMis($departmentPatientId, $data['medical_history_id'])
|
||
);
|
||
}
|
||
|
||
public function getManualPatientOperations(int $departmentPatientId)
|
||
{
|
||
$user = Auth::user();
|
||
$operations = $this->reportService->getManualPatientOperations($user, $departmentPatientId);
|
||
|
||
return response()->json(
|
||
DepartmentPatientOperationResource::collection($operations)
|
||
);
|
||
}
|
||
|
||
public function createManualPatientOperation(Request $request, int $departmentPatientId)
|
||
{
|
||
$user = Auth::user();
|
||
$data = $request->validate([
|
||
'service_id' => 'required|integer',
|
||
'urgency' => 'required|in:plan,emergency',
|
||
'started_at' => 'required|date',
|
||
'ended_at' => 'required|date|after_or_equal:started_at',
|
||
]);
|
||
|
||
$operation = $this->reportService->createManualPatientOperation($user, $departmentPatientId, $data);
|
||
|
||
return response()->json(new DepartmentPatientOperationResource($operation), 201);
|
||
}
|
||
|
||
public function updateManualPatientOperation(Request $request, int $departmentPatientId, int $operationId)
|
||
{
|
||
$user = Auth::user();
|
||
$data = $request->validate([
|
||
'service_id' => 'required|integer',
|
||
'urgency' => 'required|in:plan,emergency',
|
||
'started_at' => 'required|date',
|
||
'ended_at' => 'required|date|after_or_equal:started_at',
|
||
]);
|
||
|
||
$operation = $this->reportService->updateManualPatientOperation($user, $departmentPatientId, $operationId, $data);
|
||
|
||
return response()->json(new DepartmentPatientOperationResource($operation));
|
||
}
|
||
|
||
public function deleteManualPatientOperation(int $departmentPatientId, int $operationId)
|
||
{
|
||
$user = Auth::user();
|
||
$this->reportService->deleteManualPatientOperation($user, $departmentPatientId, $operationId);
|
||
|
||
return response()->json()->setStatusCode(204);
|
||
}
|
||
|
||
public function searchMisPatients(Request $request)
|
||
{
|
||
$data = $request->validate([
|
||
'departmentId' => 'required|integer',
|
||
'query' => 'required|string|min:2',
|
||
]);
|
||
|
||
$department = Department::where('department_id', $data['departmentId'])->firstOrFail();
|
||
$patients = $this->reportService->searchMisPatientsForDepartment($department, $data['query']);
|
||
|
||
return response()->json(FormattedPatientResource::collection($patients));
|
||
}
|
||
|
||
public function searchMkb(Request $request)
|
||
{
|
||
$data = $request->validate([
|
||
'query' => 'required|string|min:1|max:255',
|
||
]);
|
||
|
||
$query = trim($data['query']);
|
||
$needle = mb_strtolower($query, 'UTF-8');
|
||
$like = "%{$needle}%";
|
||
|
||
$items = MisMKB::query()
|
||
->select(['MKBID', 'DS', 'NAME'])
|
||
->where(function ($builder) use ($like) {
|
||
$builder->whereRaw('LOWER("DS") LIKE ?', [$like])
|
||
->orWhereRaw('LOWER("NAME") LIKE ?', [$like]);
|
||
})
|
||
->orderBy('DS')
|
||
->limit(30)
|
||
->get()
|
||
->map(fn (MisMKB $item) => [
|
||
'id' => $item->MKBID,
|
||
'code' => $item->DS,
|
||
'name' => $item->NAME,
|
||
'label' => trim(($item->DS ? "{$item->DS} " : '').($item->NAME ?? '')),
|
||
])
|
||
->values();
|
||
|
||
return response()->json($items);
|
||
}
|
||
|
||
public function searchMedicalServices(Request $request)
|
||
{
|
||
$data = $request->validate([
|
||
'query' => 'required|string|min:2|max:255',
|
||
]);
|
||
|
||
$query = trim($data['query']);
|
||
|
||
$items = \App\Models\MisServiceMedical::query()
|
||
->select(['ServiceMedicalID', 'ServiceMedicalCode', 'ServiceMedicalName'])
|
||
->where(function ($builder) use ($query) {
|
||
$builder->where('ServiceMedicalCode', 'like', "%{$query}%")
|
||
->orWhere('ServiceMedicalName', 'like', "%{$query}%");
|
||
})
|
||
->orderBy('ServiceMedicalCode')
|
||
->limit(30)
|
||
->get()
|
||
->map(fn (\App\Models\MisServiceMedical $item) => [
|
||
'id' => $item->ServiceMedicalID,
|
||
'code' => $item->ServiceMedicalCode,
|
||
'name' => $item->ServiceMedicalName,
|
||
'label' => trim(($item->ServiceMedicalCode ? "{$item->ServiceMedicalCode} " : '').($item->ServiceMedicalName ?? '')),
|
||
])
|
||
->values();
|
||
|
||
return response()->json($items);
|
||
}
|
||
|
||
// 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'])
|
||
->whereHas('prvds', function ($query) use ($departmentId) {
|
||
$query->where('rf_DepartmentID', $departmentId)
|
||
->whereDate('D_END', '2222-01-01 00:00:00.000000');
|
||
})
|
||
->active()
|
||
->whereNotIn('LPUDoctorID', [0, 1])
|
||
->orderBy('FAM_V')
|
||
->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)
|
||
->withinPeriod($startDate, $endDate)
|
||
->orderBy('period_end', 'ASC')
|
||
->get();
|
||
} else {
|
||
return Report::where('rf_department_id', $departmentId)
|
||
->exactPeriod($startDate, $endDate)
|
||
->orderBy('period_end', 'ASC')
|
||
->get();
|
||
}
|
||
}
|
||
|
||
private function resolveBaseStatus(string $status): string
|
||
{
|
||
if (str_starts_with($status, 'mis-')) {
|
||
return substr($status, 4);
|
||
}
|
||
if (str_starts_with($status, 'special-')) {
|
||
return substr($status, 8);
|
||
}
|
||
|
||
return $status;
|
||
}
|
||
|
||
private function attachReanimationIndicators(Collection $patients, int $departmentId): void
|
||
{
|
||
if ($patients->isEmpty()) {
|
||
return;
|
||
}
|
||
|
||
$medicalHistoryIds = $patients
|
||
->map(function ($patient) {
|
||
return (int) ($patient->medicalHistoryId ?? $patient->MedicalHistoryID ?? 0);
|
||
})
|
||
->filter()
|
||
->unique()
|
||
->values()
|
||
->all();
|
||
|
||
if (empty($medicalHistoryIds)) {
|
||
return;
|
||
}
|
||
|
||
$latestIndicators = $this->reportService->getLatestReanimationIndicators($departmentId, $medicalHistoryIds);
|
||
|
||
$patients->transform(function ($patient) use ($latestIndicators) {
|
||
$medicalHistoryId = (int) ($patient->medicalHistoryId ?? $patient->MedicalHistoryID ?? 0);
|
||
$indicator = $medicalHistoryId ? $latestIndicators->get($medicalHistoryId) : null;
|
||
|
||
$patient->reanimation_indicator = $indicator?->indicator;
|
||
$patient->reanimation_comment = $indicator?->comment;
|
||
|
||
return $patient;
|
||
});
|
||
}
|
||
|
||
public function checkReport(Request $request)
|
||
{
|
||
$request->validate([
|
||
'department_id' => 'required|integer|exists:departments,department_id',
|
||
]);
|
||
|
||
$report = Report::where('rf_department_id', $request->department_id)
|
||
->exactPeriod(now('Asia/Yakutsk')->subDay()->setTime(7, 0)->format('Y-m-d H:i:s'), now('Asia/Yakutsk')->setTime(7, 0)->format('Y-m-d H:i:s'))
|
||
->first();
|
||
|
||
return response()->json([
|
||
'report_id' => $report?->report_id,
|
||
'exists' => (bool) $report,
|
||
]);
|
||
}
|
||
}
|