Files
onboard/app/Services/SnapshotService.php
2026-03-25 17:37:32 +09:00

281 lines
9.3 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\MedicalHistorySnapshot;
use App\Models\MetrikaResult;
use App\Models\MisMedicalHistory;
use App\Models\MisStationarBranch;
use App\Models\ObservationPatient;
use App\Models\Report;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class SnapshotService
{
public function __construct(
protected PatientService $patientService,
protected DateRangeService $dateRangeService,
) {}
/**
* Создать снапшоты пациентов для отчета
*/
public function createPatientSnapshots(Report $report, User $user, array $dates, $fillableAuto = false): void
{
$branchId = $this->getBranchId($user->department->rf_mis_department_id);
[$startDate, $endDate] = $this->parseDates($dates);
$dateRange = $this->dateRangeService->getNormalizedDateRange($user, $startDate, $endDate);
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
// Массив для хранения подсчитанных метрик
$metrics = [];
// 1. Плановые пациенты
$planIds = $this->patientService->getPlanOrEmergencyPatients(
'plan',
$isHeadOrAdmin,
$branchId,
$dateRange,
false,
true,
true,
$fillableAuto
);
$this->createSnapshotsForType($report, 'plan', $planIds);
$metrics[4] = $this->patientService->getPlanOrEmergencyPatients(
'plan',
$isHeadOrAdmin,
$branchId,
$dateRange,
true,
$fillableAuto
); // metrika_item_3 - плановые
// 2. Экстренные пациенты
$emergencyIds = $this->patientService->getPlanOrEmergencyPatients(
'emergency',
$isHeadOrAdmin,
$branchId,
$dateRange,
false,
true,
true,
$fillableAuto
);
$this->createSnapshotsForType($report, 'emergency', $emergencyIds);
$metrics[12] = $this->patientService->getPlanOrEmergencyPatients(
'emergency',
$isHeadOrAdmin,
$branchId,
$dateRange,
true,
$fillableAuto
);; // metrika_item_12 - экстренные
// 3. Выписанные
$dischargedIds = $this->patientService->getOutcomePatients(
$branchId,
$dateRange,
'discharged',
true
);
$this->createSnapshotsForType($report, 'discharged', $dischargedIds);
$metrics[15] = count($dischargedIds); // metrika_item_15 - выписанные
// 4. Переведенные
$transferredIds = $this->patientService->getOutcomePatients(
$branchId,
$dateRange,
'transferred',
true
);
$this->createSnapshotsForType($report, 'transferred', $transferredIds);
$metrics[13] = count($transferredIds); // metrika_item_13 - переведенные
// 5. Умершие
$deceasedIds = $this->patientService->getOutcomePatients(
$branchId,
$dateRange,
'deceased',
true
);
$this->createSnapshotsForType($report, 'deceased', $deceasedIds);
// $metrics[9] = count($deceasedIds); // metrika_item_9 - умершие
// 6. Поступившие (все новые поступления - плановые + экстренные)
$recipientIds = $this->patientService->getPlanOrEmergencyPatients(
null,
$isHeadOrAdmin,
$branchId,
$dateRange,
false,
true,
false // только поступившие сегодня
);
$this->createSnapshotsForType($report, 'recipient', $recipientIds);
// $metrics[3] = count($recipientIds); // metrika_item_3 - поступившие
// 8. Плановые операции
$planSurgeryCount = $this->patientService->getSurgicalPatients(
'plan',
$branchId,
$dateRange,
true
);
// $metrics[11] = $planSurgeryCount; // metrika_item_11 - плановые операции
// 9. Экстренные операции
$emergencySurgeryCount = $this->patientService->getSurgicalPatients(
'emergency',
$branchId,
$dateRange,
true
);
// $metrics[10] = $emergencySurgeryCount; // metrika_item_10 - экстренные операции
// Сохраняем все метрики
$this->saveMetrics($report, $metrics);
}
/**
* Сохранить метрики в базу
*/
private function saveMetrics(Report $report, array $metrics): void
{
foreach ($metrics as $metrikaItemId => $value) {
MetrikaResult::updateOrCreate(
[
'rf_report_id' => $report->report_id,
'rf_metrika_item_id' => $metrikaItemId,
],
[
'value' => $value,
]
);
}
}
/**
* Получить статистику из снапшотов
*/
public function getStatisticsFromSnapshots(array $reportIds): array
{
return [
'plan' => $this->getCountFromSnapshots('plan', $reportIds),
'emergency' => $this->getCountFromSnapshots('emergency', $reportIds),
'outcome' => $this->getCountFromSnapshots('outcome', $reportIds),
'deceased' => $this->getCountFromSnapshots('deceased', $reportIds),
'discharged' => $this->getCountFromSnapshots('discharged', $reportIds),
'transferred' => $this->getCountFromSnapshots('transferred', $reportIds),
'recipient' => $this->getCountFromSnapshots('recipient', $reportIds),
];
}
/**
* Получить пациентов из снапшотов по типу
*/
public function getPatientsFromSnapshots(
string $type,
array $reportIds,
?int $branchId = null,
bool $onlyIds = false
): Collection {
// Получаем ID историй болезни напрямую через DB::table() — это быстрее
$medicalHistoryIds = DB::table('medical_history_snapshots')
->select('rf_medicalhistory_id')
->whereIn('rf_report_id', $reportIds)
->where('patient_type', $type)
->distinct()
->pluck('rf_medicalhistory_id');
if ($medicalHistoryIds->isEmpty()) {
return collect();
}
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds);
if ($type === 'plan') {
$query->plan();
} elseif ($type === 'emergency') {
$query->emergency();
}
// Загрузка отношений, необходимых для FormattedPatientResource
$query->with([
'outcomeMigration.mainDiagnosis.mkb', // mkb через диагноз
'surgicalOperations.serviceMedical', // операции с услугами
]);
$query->orderBy('DateRecipient', 'DESC');
$results = $query->get();
if ($onlyIds) {
return $results->pluck('MedicalHistoryID');
}
return $results;
}
/**
* Получить количество пациентов из снапшотов
*/
private function getCountFromSnapshots(string $type, array $reportIds): int
{
$query = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds);
if ($type === 'outcome') {
$query->whereIn('patient_type', ['discharged', 'deceased']);
} else {
$query->where('patient_type', $type);
}
return $query->distinct('rf_medicalhistory_id')
->count('rf_medicalhistory_id');
}
/**
* Создать снапшоты для определенного типа пациентов
*/
private function createSnapshotsForType(Report $report, string $type, Collection $medicalHistoryIds): void
{
foreach ($medicalHistoryIds as $id) {
MedicalHistorySnapshot::updateOrCreate(
[
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => $type
],
[
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $id,
'patient_type' => $type,
]
);
}
}
/**
* Получить ID отделения
*/
private function getBranchId(int $misDepartmentId): ?int
{
return MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
->value('StationarBranchID');
}
/**
* Разобрать даты
*/
private function parseDates(array $dates): array
{
return [
Carbon::createFromTimestampMs($dates[0])->setTimezone('Asia/Yakutsk'),
Carbon::createFromTimestampMs($dates[1])->setTimezone('Asia/Yakutsk'),
];
}
}