250 lines
11 KiB
PHP
250 lines
11 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\MedicalHistory;
|
||
use App\Models\ReportDuty;
|
||
use App\Models\ReportNurse;
|
||
use App\Models\UnifiedMedicalHistory;
|
||
use App\Models\User;
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
class DutyReportService
|
||
{
|
||
/**
|
||
* Базовый запрос для всех отчётов
|
||
* Фильтрует по отделению и периоду, подгружает связи
|
||
*/
|
||
protected function baseQuery(DateRange $dateRange, int $departmentId): Builder
|
||
{
|
||
return UnifiedMedicalHistory::query()
|
||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||
$q->department($departmentId)->dateRange($dateRange->startSql(), $dateRange->endSql());
|
||
})
|
||
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
|
||
$q->department($departmentId)->dateRange($dateRange->startSql(), $dateRange->endSql());
|
||
}]);
|
||
}
|
||
|
||
/**
|
||
* Сохранить отчет
|
||
*/
|
||
public function saveReport(DateRange $dateRange, ?int $userId = null, ?int $lpuDoctorId = null, ?int $departmentId = null)
|
||
{
|
||
$user = $userId ? User::find($userId) : auth()->user();
|
||
|
||
$lpuDoctorId = $lpuDoctorId ?? $user->rf_lpudoctor_id;
|
||
$departmentId = $departmentId ?? $user->rf_department_id;
|
||
|
||
$data = [
|
||
'report_date' => Carbon::now()->format('Y-m-d'),
|
||
'sent_at' => Carbon::now()->format('Y-m-d H:i:s'),
|
||
'period_type' => 'day',
|
||
'period_start' => $dateRange->startSql(),
|
||
'period_end' => $dateRange->endSql(),
|
||
'status_id' => 2, // опубликован
|
||
'rf_lpudoctor_id' => $lpuDoctorId,
|
||
'rf_department_id' => $departmentId,
|
||
'rf_user_id' => $user->id,
|
||
];
|
||
|
||
$report = ReportDuty::updateOrCreate(
|
||
[
|
||
'report_date' => $data['report_date'], 'period_start' => $data['period_start'],
|
||
'period_end' => $data['period_end']
|
||
],
|
||
$data
|
||
);
|
||
|
||
return $report;
|
||
}
|
||
|
||
/**
|
||
* Сохранить снимок пациентов за период
|
||
*/
|
||
public function saveSnapshot(DateRange $dateRange, ReportNurse $reportNurse, ?int $departmentId = null, ?int $userId = null): array
|
||
{
|
||
$departmentId = $departmentId ?? $reportNurse->department->rf_mis_department_id;
|
||
$userId = $userId ?? $reportNurse->rf_user_id;
|
||
$startYear = Carbon::now()->startOfYear()->format('Y-m-d');
|
||
|
||
$query = MedicalHistory::query()
|
||
// Фильтруем движения по отделению + пересечение дат
|
||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange, $startYear) {
|
||
$q->where('department_id', $departmentId)
|
||
// пребывание пересекается с отчётным периодом
|
||
->where('ingoing_date', '<=', $dateRange->endSql())
|
||
->where('ingoing_date', '>=', $startYear)
|
||
->where(function ($sub) use ($dateRange) {
|
||
$sub->whereNull('out_date')
|
||
->orWhere('out_date', '>=', $dateRange->startSql())
|
||
->where('out_date', '<=', $dateRange->endSql());
|
||
});
|
||
});
|
||
|
||
// Получаем данные (chunk для памяти, если пациентов > 1000)
|
||
$patients = $query->cursor();
|
||
|
||
$savedStats = $this->saveReportSnapshot($reportNurse->id, $patients, $userId);
|
||
|
||
return [
|
||
...$savedStats,
|
||
'report_date' => $dateRange->startSql(),
|
||
'department_id' => $departmentId,
|
||
];
|
||
}
|
||
|
||
public function saveReportSnapshot(int $reportDutyId, iterable $patients, int $userId): array
|
||
{
|
||
if (empty($patients)) {
|
||
return ['saved_patients' => 0, 'saved_migrations' => 0];
|
||
}
|
||
|
||
$patientBatch = [];
|
||
$migrationBatch = [];
|
||
$batchSize = 100;
|
||
|
||
foreach ($patients as $patient) {
|
||
// Подготовка данных пациента
|
||
$patientBatch[] = [
|
||
'report_nurse_id' => $reportDutyId,
|
||
'source_type' => $patient->source_type,
|
||
'original_id' => $patient->original_id,
|
||
'medical_card_number' => $patient->medical_card_number,
|
||
'full_name' => $patient->full_name,
|
||
'birth_date' => $patient->birth_date,
|
||
'recipient_date' => $patient->recipient_date,
|
||
'extract_date' => $patient->extract_date,
|
||
'death_date' => $patient->death_date,
|
||
'male' => $patient->male,
|
||
'urgency_id' => $patient->urgency_id,
|
||
'hospital_result_id' => $patient->hospital_result_id,
|
||
'visit_result_id' => $patient->visit_result_id,
|
||
'comment' => $patient->comment,
|
||
'user_id' => $userId,
|
||
];
|
||
|
||
// Подготовка данных миграции (если есть)
|
||
if (!empty($patient->migrations)) {
|
||
foreach ($patient->migrations as $migration) {
|
||
$migrationBatch[] = [
|
||
// Временный ключ для связи с пациентом (заполним после первого upsert)
|
||
'_temp_key' => [
|
||
'report_duty_id' => $reportDutyId,
|
||
'source_type' => $patient->source_type,
|
||
'original_id' => $patient->original_id,
|
||
],
|
||
'ingoing_date' => $migration->ingoing_date,
|
||
'out_date' => $migration->out_date,
|
||
'diagnosis_id' => $migration->diagnosis_id,
|
||
'diagnosis_code' => $migration->diagnosis_code,
|
||
'diagnosis_name' => $migration->diagnosis_name,
|
||
'interrupted_event_id' => $migration->interrupted_event_id,
|
||
'stationar_branch_id' => $migration->stationar_branch_id,
|
||
'department_id' => $migration->department_id,
|
||
'visit_result_id' => $migration->visit_result_id,
|
||
'stat_cure_result_id' => $migration->stat_cure_result_id,
|
||
'user_id' => $migration->user_id,
|
||
'mis_user_id' => $migration->mis_user_id,
|
||
'comment' => $migration->comment,
|
||
];
|
||
}
|
||
}
|
||
|
||
// Пакетная запись каждые $batchSize записей
|
||
if (count($patientBatch) >= $batchSize) {
|
||
[$savedP, $savedM] = $this->upsertBatches($reportDutyId, $patientBatch, $migrationBatch);
|
||
$patientBatch = [];
|
||
$migrationBatch = [];
|
||
}
|
||
}
|
||
|
||
// Сохраняем остаток
|
||
[$savedP, $savedM] = $this->upsertBatches($reportDutyId, $patientBatch, $migrationBatch);
|
||
|
||
return ['saved_patients' => $savedP, 'saved_migrations' => $savedM];
|
||
}
|
||
|
||
/**
|
||
* Вспомогательный метод: выполняет upsert для пациентов и миграций
|
||
*/
|
||
private function upsertBatches(int $reportDutyId, array $patientBatch, array $migrationBatch): array
|
||
{
|
||
if (empty($patientBatch)) {
|
||
return [0, 0];
|
||
}
|
||
|
||
$savedPatients = 0;
|
||
$savedMigrations = 0;
|
||
|
||
DB::transaction(function () use ($reportDutyId, $patientBatch, $migrationBatch, &$savedPatients, &$savedMigrations) {
|
||
// UPSERT пациентов
|
||
$patientUniqueBy = ['report_duty_id', 'source_type', 'original_id'];
|
||
$patientUpdateColumns = array_diff(array_keys($patientBatch[0]), $patientUniqueBy);
|
||
|
||
DB::table('report_nurse_patients')->upsert(
|
||
$patientBatch,
|
||
$patientUniqueBy,
|
||
$patientUpdateColumns
|
||
);
|
||
$savedPatients = count($patientBatch);
|
||
|
||
// Получаем ID сохранённых пациентов для связи с миграциями
|
||
if (!empty($migrationBatch)) {
|
||
// Извлекаем уникальные ключи для поиска
|
||
$tempKeys = array_map(fn($m) => $m['_temp_key'], $migrationBatch);
|
||
|
||
// Получаем реальные ID из БД
|
||
$patientIds = DB::table('report_duty_patients')
|
||
->whereIn('report_duty_id', [$reportDutyId])
|
||
->get()
|
||
->pluck('id', 'original_id') // key=original_id, value=id
|
||
->toArray();
|
||
|
||
// Формируем финальный массив миграций с реальными medical_history_id
|
||
$finalMigrations = [];
|
||
foreach ($migrationBatch as $m) {
|
||
$tempKey = $m['_temp_key'];
|
||
$originalId = $tempKey['original_id'];
|
||
|
||
if (isset($patientIds[$originalId])) {
|
||
$finalMigrations[] = [
|
||
'medical_history_id' => $patientIds[$originalId], // Реальный ID
|
||
'ingoing_date' => $m['ingoing_date'],
|
||
'out_date' => $m['out_date'],
|
||
'diagnosis_id' => $m['diagnosis_id'],
|
||
'diagnosis_code' => $m['diagnosis_code'],
|
||
'diagnosis_name' => $m['diagnosis_name'],
|
||
'interrupted_event_id' => $m['interrupted_event_id'],
|
||
'stationar_branch_id' => $m['stationar_branch_id'],
|
||
'department_id' => $m['department_id'],
|
||
'visit_result_id' => $m['visit_result_id'],
|
||
'stat_cure_result_id' => $m['stat_cure_result_id'],
|
||
'user_id' => $m['user_id'],
|
||
'mis_user_id' => $m['mis_user_id'],
|
||
'comment' => $m['comment'],
|
||
];
|
||
}
|
||
}
|
||
|
||
if (!empty($finalMigrations)) {
|
||
// UPSERT миграций
|
||
$migrationUniqueBy = ['medical_history_id', 'ingoing_date'];
|
||
$migrationUpdateColumns = array_diff(array_keys($finalMigrations[0]), $migrationUniqueBy);
|
||
|
||
DB::table('report_duty_migration_patients')->upsert(
|
||
$finalMigrations,
|
||
$migrationUniqueBy,
|
||
$migrationUpdateColumns
|
||
);
|
||
$savedMigrations = count($finalMigrations);
|
||
}
|
||
}
|
||
});
|
||
|
||
return [$savedPatients, $savedMigrations];
|
||
}
|
||
}
|