modified: .gitignore
This commit is contained in:
@@ -2,20 +2,22 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Data\UnifiedPatientData;
|
||||
use App\Models\Department;
|
||||
use App\Models\DepartmentPatientOperation;
|
||||
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 UnifiedPatientService $unifiedPatientService,
|
||||
protected PatientService $patientService,
|
||||
protected DateRangeService $dateRangeService,
|
||||
) {}
|
||||
@@ -25,118 +27,124 @@ class SnapshotService
|
||||
*/
|
||||
public function createPatientSnapshots(Report $report, User $user, array $dates, $fillableAuto = false): void
|
||||
{
|
||||
$branchId = $this->getBranchId($user->department->rf_mis_department_id);
|
||||
$department = Department::query()->where('department_id', $report->rf_department_id)->first() ?? $user->department;
|
||||
$branchId = $department
|
||||
? $this->getBranchId($department->rf_mis_department_id)
|
||||
: null;
|
||||
|
||||
if (!$department || !$branchId) {
|
||||
return;
|
||||
}
|
||||
|
||||
MedicalHistorySnapshot::query()
|
||||
->where('rf_report_id', $report->report_id)
|
||||
->delete();
|
||||
|
||||
[$startDate, $endDate] = $this->parseDates($dates);
|
||||
$dateRange = $this->dateRangeService->getNormalizedDateRange($user, $startDate, $endDate);
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
|
||||
// Массив для хранения подсчитанных метрик
|
||||
$metrics = [];
|
||||
|
||||
// 1. Плановые пациенты
|
||||
$planIds = $this->patientService->getPlanOrEmergencyPatients(
|
||||
$planPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'plan',
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
$branchId,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
!$fillableAuto,
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'plan', $planIds);
|
||||
$metrics[4] = $this->patientService->getPlanOrEmergencyPatients(
|
||||
'plan',
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
true,
|
||||
$fillableAuto
|
||||
); // metrika_item_3 - плановые
|
||||
$this->createSnapshotsForType($report, 'plan', $planPatients);
|
||||
$metrics[4] = $planPatients->count();
|
||||
|
||||
// 2. Экстренные пациенты
|
||||
$emergencyIds = $this->patientService->getPlanOrEmergencyPatients(
|
||||
$emergencyPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'emergency',
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
$branchId,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
!$fillableAuto,
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'emergency', $emergencyIds);
|
||||
$metrics[12] = $this->patientService->getPlanOrEmergencyPatients(
|
||||
'emergency',
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
true,
|
||||
$fillableAuto
|
||||
);; // metrika_item_12 - экстренные
|
||||
$this->createSnapshotsForType($report, 'emergency', $emergencyPatients);
|
||||
$metrics[12] = $emergencyPatients->count();
|
||||
|
||||
// 3. Выписанные
|
||||
$dischargedIds = $this->patientService->getOutcomePatients(
|
||||
$branchId,
|
||||
$dischargedPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'outcome-discharged',
|
||||
$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(
|
||||
false,
|
||||
null,
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
false,
|
||||
true,
|
||||
false // только поступившие сегодня
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'recipient', $recipientIds);
|
||||
// $metrics[3] = count($recipientIds); // metrika_item_3 - поступившие
|
||||
$this->createSnapshotsForType($report, 'discharged', $dischargedPatients);
|
||||
$metrics[15] = $dischargedPatients->count();
|
||||
|
||||
$transferredPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'outcome-transferred',
|
||||
$dateRange,
|
||||
$branchId,
|
||||
false,
|
||||
null,
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'transferred', $transferredPatients);
|
||||
$metrics[13] = $transferredPatients->count();
|
||||
|
||||
$deceasedPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'outcome-deceased',
|
||||
$dateRange,
|
||||
$branchId,
|
||||
false,
|
||||
null,
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'deceased', $deceasedPatients);
|
||||
|
||||
$recipientPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'recipient',
|
||||
$dateRange,
|
||||
$branchId,
|
||||
false,
|
||||
null,
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'recipient', $recipientPatients);
|
||||
|
||||
$currentPatients = $this->unifiedPatientService->getLivePatientsByStatus(
|
||||
$department,
|
||||
$user,
|
||||
'current',
|
||||
$dateRange,
|
||||
$branchId,
|
||||
false,
|
||||
null,
|
||||
$fillableAuto
|
||||
);
|
||||
$this->createSnapshotsForType($report, 'current', $currentPatients);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -181,43 +189,136 @@ class SnapshotService
|
||||
string $type,
|
||||
array $reportIds,
|
||||
?int $branchId = null,
|
||||
bool $onlyIds = false
|
||||
bool $onlyIds = false,
|
||||
bool $markRecipients = false,
|
||||
?array $recipientReportIds = null
|
||||
): Collection {
|
||||
// Получаем ID историй болезни напрямую через DB::table() — это быстрее
|
||||
$medicalHistoryIds = DB::table('medical_history_snapshots')
|
||||
->select('rf_medicalhistory_id')
|
||||
$snapshots = MedicalHistorySnapshot::query()
|
||||
->whereIn('rf_report_id', $reportIds)
|
||||
->where('patient_type', $type)
|
||||
->distinct()
|
||||
->pluck('rf_medicalhistory_id');
|
||||
->get()
|
||||
->unique(fn (MedicalHistorySnapshot $snapshot) => $snapshot->patient_uid ?: ($snapshot->rf_medicalhistory_id ? "mis:{$snapshot->rf_medicalhistory_id}" : "snapshot:{$snapshot->medical_history_snapshot_id}"))
|
||||
->values();
|
||||
|
||||
if ($medicalHistoryIds->isEmpty()) {
|
||||
if ($snapshots->isEmpty()) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds);
|
||||
|
||||
if ($type === 'plan') {
|
||||
$query->plan();
|
||||
} elseif ($type === 'emergency') {
|
||||
$query->emergency();
|
||||
$recipientIds = [];
|
||||
if ($markRecipients) {
|
||||
$recipientReportIds ??= $reportIds;
|
||||
$recipientIds = MedicalHistorySnapshot::query()
|
||||
->whereIn('rf_report_id', $recipientReportIds)
|
||||
->where('patient_type', 'recipient')
|
||||
->get()
|
||||
->map(fn (MedicalHistorySnapshot $snapshot) => UnifiedPatientData::fromSnapshot($snapshot)->id)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
// Загрузка отношений, необходимых для FormattedPatientResource
|
||||
$query->with([
|
||||
'outcomeMigration.mainDiagnosis.mkb', // mkb через диагноз
|
||||
'surgicalOperations.serviceMedical', // операции с услугами
|
||||
]);
|
||||
$operationsByHistoryId = $this->getOperationsByMedicalHistoryId($snapshots);
|
||||
$operationsByDepartmentPatientId = $this->getOperationsByDepartmentPatientId($snapshots);
|
||||
|
||||
$query->orderBy('DateRecipient', 'DESC');
|
||||
$patients = $snapshots->map(function (MedicalHistorySnapshot $snapshot) use ($recipientIds, $operationsByHistoryId, $operationsByDepartmentPatientId) {
|
||||
$patientId = $snapshot->rf_department_patient_id
|
||||
? "manual:{$snapshot->rf_department_patient_id}"
|
||||
: ($snapshot->patient_uid ?: "mis:{$snapshot->rf_medicalhistory_id}");
|
||||
|
||||
$results = $query->get();
|
||||
$misOperations = $snapshot->rf_medicalhistory_id
|
||||
? ($operationsByHistoryId[$snapshot->rf_medicalhistory_id] ?? [])
|
||||
: [];
|
||||
$manualOperations = $snapshot->rf_department_patient_id
|
||||
? ($operationsByDepartmentPatientId[$snapshot->rf_department_patient_id] ?? [])
|
||||
: [];
|
||||
$operations = collect($misOperations)
|
||||
->merge($manualOperations)
|
||||
->unique(fn (array $operation) => ($operation['code'] ?? '') . '|' . ($operation['name'] ?? ''))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
return UnifiedPatientData::fromSnapshot(
|
||||
$snapshot,
|
||||
in_array($patientId, $recipientIds, true),
|
||||
$operations
|
||||
);
|
||||
})->sortByDesc(fn (UnifiedPatientData $patient) => $patient->admittedAt ?? '')->values();
|
||||
|
||||
if ($onlyIds) {
|
||||
return $results->pluck('MedicalHistoryID');
|
||||
return $patients->pluck('id');
|
||||
}
|
||||
|
||||
return $results;
|
||||
return $patients;
|
||||
}
|
||||
|
||||
public function getPatientsFromOneDayCurrentSnapshots(
|
||||
string $type,
|
||||
array $reportIds,
|
||||
bool $onlyIds = false,
|
||||
?array $recipientReportIds = null
|
||||
): Collection {
|
||||
$snapshots = MedicalHistorySnapshot::query()
|
||||
->whereIn('rf_report_id', $reportIds)
|
||||
->where('patient_type', 'current')
|
||||
->get();
|
||||
|
||||
if ($snapshots->isEmpty()) {
|
||||
return $this->getPatientsFromSnapshots(
|
||||
$type,
|
||||
$reportIds,
|
||||
null,
|
||||
$onlyIds,
|
||||
true,
|
||||
$recipientReportIds
|
||||
);
|
||||
}
|
||||
|
||||
$recipientReportIds ??= $reportIds;
|
||||
$recipientIds = MedicalHistorySnapshot::query()
|
||||
->whereIn('rf_report_id', $recipientReportIds)
|
||||
->where('patient_type', 'recipient')
|
||||
->get()
|
||||
->map(fn (MedicalHistorySnapshot $snapshot) => UnifiedPatientData::fromSnapshot($snapshot)->id)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$operationsByHistoryId = $this->getOperationsByMedicalHistoryId($snapshots);
|
||||
$operationsByDepartmentPatientId = $this->getOperationsByDepartmentPatientId($snapshots);
|
||||
|
||||
$patients = $snapshots
|
||||
->filter(fn (MedicalHistorySnapshot $snapshot) => $snapshot->patient_kind === $type)
|
||||
->unique(fn (MedicalHistorySnapshot $snapshot) => $snapshot->patient_uid ?: ($snapshot->rf_medicalhistory_id ? "mis:{$snapshot->rf_medicalhistory_id}" : "snapshot:{$snapshot->medical_history_snapshot_id}"))
|
||||
->map(function (MedicalHistorySnapshot $snapshot) use ($recipientIds, $operationsByHistoryId, $operationsByDepartmentPatientId) {
|
||||
$misOperations = $snapshot->rf_medicalhistory_id
|
||||
? ($operationsByHistoryId[$snapshot->rf_medicalhistory_id] ?? [])
|
||||
: [];
|
||||
$manualOperations = $snapshot->rf_department_patient_id
|
||||
? ($operationsByDepartmentPatientId[$snapshot->rf_department_patient_id] ?? [])
|
||||
: [];
|
||||
$operations = collect($misOperations)
|
||||
->merge($manualOperations)
|
||||
->unique(fn (array $operation) => ($operation['code'] ?? '') . '|' . ($operation['name'] ?? ''))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$patient = UnifiedPatientData::fromSnapshot(
|
||||
$snapshot,
|
||||
false,
|
||||
$operations
|
||||
);
|
||||
$patient->isRecipientToday = in_array($patient->id, $recipientIds, true);
|
||||
|
||||
return $patient;
|
||||
})
|
||||
->sortByDesc(fn (UnifiedPatientData $patient) => $patient->admittedAt ?? '')
|
||||
->values();
|
||||
|
||||
if ($onlyIds) {
|
||||
return $patients->pluck('id');
|
||||
}
|
||||
|
||||
return $patients;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,26 +334,32 @@ class SnapshotService
|
||||
$query->where('patient_type', $type);
|
||||
}
|
||||
|
||||
return $query->distinct('rf_medicalhistory_id')
|
||||
->count('rf_medicalhistory_id');
|
||||
return $query->get()
|
||||
->map(fn (MedicalHistorySnapshot $snapshot) => $snapshot->patient_uid ?: ($snapshot->rf_medicalhistory_id ? "mis:{$snapshot->rf_medicalhistory_id}" : "snapshot:{$snapshot->medical_history_snapshot_id}"))
|
||||
->unique()
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать снапшоты для определенного типа пациентов
|
||||
*/
|
||||
private function createSnapshotsForType(Report $report, string $type, Collection $medicalHistoryIds): void
|
||||
private function createSnapshotsForType(Report $report, string $type, Collection $patients): void
|
||||
{
|
||||
foreach ($medicalHistoryIds as $id) {
|
||||
foreach ($patients as $patient) {
|
||||
if (!$patient instanceof UnifiedPatientData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MedicalHistorySnapshot::updateOrCreate(
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_medicalhistory_id' => $id,
|
||||
'patient_type' => $type
|
||||
'patient_uid' => $patient->patientUid,
|
||||
'patient_type' => $type,
|
||||
],
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_medicalhistory_id' => $id,
|
||||
'patient_type' => $type,
|
||||
...$patient->toSnapshotPayload($type),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -277,4 +384,47 @@ class SnapshotService
|
||||
Carbon::createFromTimestampMs($dates[1])->setTimezone('Asia/Yakutsk'),
|
||||
];
|
||||
}
|
||||
|
||||
private function getOperationsByMedicalHistoryId(Collection $snapshots): array
|
||||
{
|
||||
$historyIds = $snapshots->pluck('rf_medicalhistory_id')->filter()->unique()->values();
|
||||
|
||||
if ($historyIds->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return MisMedicalHistory::query()
|
||||
->whereIn('MedicalHistoryID', $historyIds)
|
||||
->with(['surgicalOperations.serviceMedical'])
|
||||
->get()
|
||||
->mapWithKeys(function (MisMedicalHistory $history) {
|
||||
return [
|
||||
$history->MedicalHistoryID => $history->surgicalOperations->map(fn ($operation) => [
|
||||
'code' => $operation->serviceMedical?->ServiceMedicalCode,
|
||||
'name' => $operation->serviceMedical?->ServiceMedicalName,
|
||||
])->values()->all()
|
||||
];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
private function getOperationsByDepartmentPatientId(Collection $snapshots): array
|
||||
{
|
||||
$departmentPatientIds = $snapshots->pluck('rf_department_patient_id')->filter()->unique()->values();
|
||||
|
||||
if ($departmentPatientIds->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return DepartmentPatientOperation::query()
|
||||
->whereIn('rf_department_patient_id', $departmentPatientIds)
|
||||
->with('serviceMedical')
|
||||
->get()
|
||||
->groupBy('rf_department_patient_id')
|
||||
->map(fn (Collection $operations) => $operations->map(fn (DepartmentPatientOperation $operation) => [
|
||||
'code' => $operation->serviceMedical?->ServiceMedicalCode ?? $operation->service_code,
|
||||
'name' => $operation->serviceMedical?->ServiceMedicalName ?? $operation->service_name,
|
||||
])->values()->all())
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user