165 lines
6.0 KiB
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));
|
|
}
|
|
}
|