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

165 lines
6.0 KiB
PHP

<?php
namespace App\Infrastructure\Reports\Services;
use App\Domain\Reports\Models\ReportSnapshot;
use App\Domain\Reports\ValueObjects\MetrikaConfig;
use App\Models\MedicalHistorySnapshot;
use App\Models\MetrikaResult;
use App\Models\Report;
use App\Models\User;
use App\Services\DateRangeService;
use App\Services\SnapshotService;
use DateTimeImmutable;
use Illuminate\Support\Facades\DB;
/**
* Legacy-compatible save orchestration for report persistence.
*
* New architecture uses EloquentReportRepository directly; this service keeps
* the old ReportService public API thin while the cutover is still gradual.
*/
class ReportSaveOrchestrator
{
public function __construct(
private readonly DateRangeService $dateRangeService,
private readonly SnapshotService $snapshotService,
private readonly ReportStorageService $reportStorageService,
private readonly CalculatedMetricsSynchronizer $calculatedMetricsSynchronizer,
private readonly ReportMetricsFinalizer $reportMetricsFinalizer,
private readonly ReportRuntimeService $reportRuntimeService,
) {}
/**
* @param array<string, mixed> $data
*/
public function storeReport(array $data, User $user, bool $fillableAuto = false): Report
{
$this->reportRuntimeService->prepareForHeavySave();
$snapshot = $this->buildSnapshot($data, $user, $fillableAuto);
$report = DB::transaction(function () use ($snapshot, $user, $data, $fillableAuto) {
$report = $this->reportStorageService->createOrUpdateReport($snapshot, $user);
$this->reportStorageService->saveMetrics($report, $snapshot);
$this->reportStorageService->saveUnwantedEvents($report, $snapshot);
$this->reportStorageService->saveObservationPatients($report, $snapshot);
$this->snapshotService->createPatientSnapshots(
$report,
$user,
[
$snapshot->periodStart->getTimestamp(),
$snapshot->periodEnd->getTimestamp(),
],
$fillableAuto
);
$this->syncCalculatedMetrics($report, $user, $data);
return $report;
});
DB::transaction(function () use ($report) {
$this->finalizeStoredReport($report);
$this->saveLethalMetricFromSnapshots($report);
});
$this->reportRuntimeService->clearCacheAfterReportCreation($user, $report);
return $report;
}
public function syncCalculatedMetrics(Report $report, User $user, array $data): void
{
$this->calculatedMetricsSynchronizer->sync($report, $user, $data);
}
public function finalizeStoredReport(Report $report): void
{
$this->reportMetricsFinalizer->finalize($report);
}
public function saveLethalMetricFromSnapshots(Report $report): void
{
$snapshots = MedicalHistorySnapshot::query()
->where('rf_report_id', $report->report_id)
->whereIn('patient_type', ['discharged', 'deceased'])
->with('medicalHistory')
->get();
if ($snapshots->isNotEmpty()) {
return;
}
MetrikaResult::query()->updateOrCreate(
[
'rf_report_id' => $report->report_id,
'rf_metrika_item_id' => MetrikaConfig::AVERAGE_BED_DAYS,
],
['value' => 0]
);
\Log::info("No discharged patients in report {$report->report_id}, saved 0");
}
private function buildSnapshot(array $data, User $user, bool $fillableAuto): ReportSnapshot
{
$dateRange = $this->dateRangeService->getNormalizedDateRange(
$user,
(string) ($data['dates'][0] ?? null),
(string) ($data['dates'][1] ?? null)
);
$rangeEndAt = $dateRange->endSql();
$createdAt = $data['created_at'] ?? $rangeEndAt;
$sentAt = $data['sent_at'] ?? $rangeEndAt;
return new ReportSnapshot(
departmentId: (int) $data['departmentId'],
userId: (int) $data['userId'],
actorUserId: (int) $user->id,
periodStart: new DateTimeImmutable($dateRange->startSql()),
periodEnd: new DateTimeImmutable($dateRange->endSql()),
status: (string) ($data['status'] ?? 'draft'),
autoFill: $fillableAuto,
metrics: MetrikaConfig::normalizeMetrics((array) ($data['metrics'] ?? [])),
observationPatients: $this->normalizeObservationPatients((array) ($data['observationPatients'] ?? [])),
unwantedEvents: $this->normalizeUnwantedEvents((array) ($data['unwantedEvents'] ?? [])),
reportId: isset($data['reportId']) && $data['reportId'] ? (int) $data['reportId'] : null,
createdAt: new DateTimeImmutable((string) $createdAt),
sentAt: new DateTimeImmutable((string) $sentAt),
);
}
/**
* @param array<int, array<string, mixed>> $patients
* @return array<int, array<string, mixed>>
*/
private function normalizeObservationPatients(array $patients): array
{
return array_values(array_map(static function (array $patient): array {
return [
'medical_history_id' => $patient['medical_history_id'] ?? $patient['id'] ?? null,
'department_patient_id' => $patient['department_patient_id'] ?? null,
'comment' => $patient['comment'] ?? null,
];
}, $patients));
}
/**
* @param array<int, array<string, mixed>> $events
* @return array<int, array<string, mixed>>
*/
private function normalizeUnwantedEvents(array $events): array
{
return array_values(array_map(static function (array $event): array {
return [
'unwanted_event_id' => $event['unwanted_event_id'] ?? null,
'title' => $event['title'] ?? '',
'comment' => $event['comment'] ?? '',
'is_visible' => (bool) ($event['is_visible'] ?? true),
];
}, $events));
}
}