Files
onboard/app/Infrastructure/Reports/Services/ManualPatientManagementService.php

257 lines
9.2 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\Infrastructure\Reports\Services;
use App\Models\Department;
use App\Models\DepartmentPatient;
use App\Models\DepartmentPatientOperation;
use App\Models\MedicalHistorySnapshot;
use App\Models\MisServiceMedical;
use App\Models\Report;
use App\Models\User;
use App\Services\DateRangeService;
use App\Services\UnifiedPatientService;
use Illuminate\Support\Collection;
use InvalidArgumentException;
/**
* Управляет manual/special пациентами отчёта и их операциями.
*
* Сервис держит orchestration вокруг legacy UnifiedPatientService, пока
* patient-write сценарии постепенно выносятся из ReportService.
*/
class ManualPatientManagementService
{
public function __construct(
private readonly DateRangeService $dateRangeService,
private readonly UnifiedPatientService $unifiedPatientService,
private readonly ReportReadContextResolver $contextResolver,
) {}
/**
* @param array<string, mixed> $data
*/
public function createManualPatient(Department $department, User $user, array $data): DepartmentPatient
{
$report = $this->resolveReportForManualPatient($department, $user, $data);
return $this->unifiedPatientService->createManualPatient($department, $user, $data, $report->report_id);
}
/**
* @param array<string, mixed> $data
*/
public function setManualPatientOutcome(User $user, int $departmentPatientId, array $data): DepartmentPatient
{
$patient = DepartmentPatient::query()
->where('department_patient_id', $departmentPatientId)
->firstOrFail();
$updatedPatient = $this->unifiedPatientService->recordManualOutcome($patient, $data);
$this->syncManualPatientSnapshots($updatedPatient, $user, []);
return $updatedPatient;
}
/**
* @param array<string, mixed> $data
*/
public function updateManualPatient(User $user, int $departmentPatientId, array $data): DepartmentPatient
{
$patient = $this->resolveManageableManualPatient($user, $departmentPatientId);
$updatedPatient = $this->unifiedPatientService->updateManualPatient($patient, $data);
$this->syncManualPatientSnapshots($updatedPatient, $user, $data);
return $updatedPatient;
}
public function linkManualPatientToMis(int $departmentPatientId, int $medicalHistoryId): DepartmentPatient
{
$patient = DepartmentPatient::query()
->where('department_patient_id', $departmentPatientId)
->firstOrFail();
return $this->unifiedPatientService->linkManualPatientToMis($patient, $medicalHistoryId);
}
/**
* @return Collection<int, DepartmentPatientOperation>
*/
public function getManualPatientOperations(User $user, int $departmentPatientId): Collection
{
$patient = $this->resolveManageableManualPatient($user, $departmentPatientId);
return $patient->operations()
->with('serviceMedical')
->orderByDesc('started_at')
->get();
}
/**
* @param array<string, mixed> $data
*/
public function createManualPatientOperation(
User $user,
int $departmentPatientId,
array $data
): DepartmentPatientOperation {
$patient = $this->resolveManageableManualPatient($user, $departmentPatientId);
$service = $this->resolveMedicalService((int) $data['service_id']);
return $patient->operations()->create([
'rf_kl_service_medical_id' => $service->ServiceMedicalID,
'service_code' => $service->ServiceMedicalCode,
'service_name' => $service->ServiceMedicalName,
'urgency' => $data['urgency'],
'started_at' => $data['started_at'],
'ended_at' => $data['ended_at'],
'created_by' => $user->id,
])->load('serviceMedical');
}
/**
* @param array<string, mixed> $data
*/
public function updateManualPatientOperation(
User $user,
int $departmentPatientId,
int $operationId,
array $data
): DepartmentPatientOperation {
$patient = $this->resolveManageableManualPatient($user, $departmentPatientId);
$service = $this->resolveMedicalService((int) $data['service_id']);
$operation = $patient->operations()
->where('department_patient_operation_id', $operationId)
->firstOrFail();
$operation->update([
'rf_kl_service_medical_id' => $service->ServiceMedicalID,
'service_code' => $service->ServiceMedicalCode,
'service_name' => $service->ServiceMedicalName,
'urgency' => $data['urgency'],
'started_at' => $data['started_at'],
'ended_at' => $data['ended_at'],
]);
return $operation->fresh()->load('serviceMedical');
}
public function deleteManualPatientOperation(User $user, int $departmentPatientId, int $operationId): void
{
$patient = $this->resolveManageableManualPatient($user, $departmentPatientId);
$patient->operations()
->where('department_patient_operation_id', $operationId)
->firstOrFail()
->delete();
}
private function resolveManageableManualPatient(User $user, int $departmentPatientId): DepartmentPatient
{
$query = DepartmentPatient::query()
->where('department_patient_id', $departmentPatientId)
->whereIn('source_type', ['manual', 'special']);
if (! $user->isAdmin() && ! $user->isHeadOfDepartment()) {
$query->where('rf_department_id', $user->department->department_id);
}
return $query->firstOrFail();
}
/**
* @param array<string, mixed> $data
*/
private function syncManualPatientSnapshots(DepartmentPatient $patient, User $user, array $data): void
{
$reportIds = $patient->rf_report_id
? [$patient->rf_report_id]
: (isset($data['startAt'], $data['endAt']) && $data['startAt'] && $data['endAt']
? $this->contextResolver
->getReportsForDateRange(
$patient->rf_department_id,
$this->dateRangeService->getNormalizedDateRange(
$user,
(string) $data['startAt'],
(string) $data['endAt']
)
)
->pluck('report_id')
->values()
->all()
: []);
if (empty($reportIds)) {
return;
}
MedicalHistorySnapshot::query()
->whereIn('rf_report_id', $reportIds)
->where('rf_department_patient_id', $patient->department_patient_id)
->update([
'patient_kind' => $patient->patient_kind,
'full_name' => $patient->full_name,
'birth_date' => $patient->birth_date,
'diagnosis_code' => $patient->diagnosis_code,
'diagnosis_name' => $patient->diagnosis_name,
'admitted_at' => $patient->admitted_at,
'outcome_type' => $patient->is_current ? null : $patient->outcome_type,
'outcome_at' => $patient->is_current ? null : $patient->outcome_at,
'updated_at' => now(),
]);
}
/**
* @param array<string, mixed> $data
*/
private function resolveReportForManualPatient(Department $department, User $user, array $data): Report
{
$reportId = $data['report_id'] ?? null;
if ($reportId) {
return Report::query()
->where('report_id', $reportId)
->where('rf_department_id', $department->department_id)
->firstOrFail();
}
if (! isset($data['startAt'], $data['endAt']) || ! $data['startAt'] || ! $data['endAt']) {
throw new InvalidArgumentException('Не указан отчет или диапазон для привязки спецконтингента');
}
$dateRange = $this->dateRangeService->getNormalizedDateRange(
$user,
(string) $data['startAt'],
(string) $data['endAt']
);
$existingReport = Report::query()
->where('rf_department_id', $department->department_id)
->exactPeriod($dateRange->startSql(), $dateRange->endSql())
->first();
if ($existingReport) {
return $existingReport;
}
return Report::query()->create([
'rf_department_id' => $department->department_id,
'rf_user_id' => $user->id,
'rf_lpudoctor_id' => $data['user_id'] ?? $user->rf_lpudoctor_id,
'sent_at' => $dateRange->endSql(),
'created_at' => $dateRange->endSql(),
'period_start' => $dateRange->startSql(),
'period_end' => $dateRange->endSql(),
'status' => 'draft',
]);
}
private function resolveMedicalService(int $serviceId): MisServiceMedical
{
return MisServiceMedical::query()
->where('ServiceMedicalID', $serviceId)
->firstOrFail();
}
}