Работа над журналом для ст. мед сестер

This commit is contained in:
brusnitsyn
2026-05-04 17:11:16 +09:00
parent f107ebd167
commit 7a58812072
61 changed files with 3532 additions and 1163 deletions

View File

@@ -0,0 +1,256 @@
<?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();
}
}