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

214 lines
8.4 KiB
PHP

<?php
namespace App\Infrastructure\Reports\Services;
use App\Domain\Reports\ValueObjects\MetrikaConfig;
use App\Models\Department;
use App\Models\Report;
use App\Models\User;
use App\Services\DateRange;
use App\Services\PatientService;
use App\Services\SnapshotService;
use App\Services\UnifiedPatientService;
/**
* Сервис чтения сводной статистики отчёта.
*
* Инкапсулирует выбор источника данных: submitted-снапшоты для закрытых
* отчётов или live-реплика для текущей рабочей формы.
*/
class ReportStatisticsReadService
{
public function __construct(
private readonly UnifiedPatientService $unifiedPatientService,
private readonly PatientService $patientService,
private readonly SnapshotService $snapshotService,
private readonly ReportReadContextResolver $contextResolver,
private readonly CalculatedMetricsSynchronizer $calculatedMetricsSynchronizer,
) {}
/**
* Получить статистику для шапки отчёта.
*/
public function getReportStatistics(Department $department, User $user, DateRange $dateRange): array
{
$branchId = $this->contextResolver->resolveBranchId($department);
if (! $branchId) {
return $this->emptyStatistics();
}
if ($this->contextResolver->shouldUseSnapshots($department, $dateRange)) {
return $this->getStatisticsFromSnapshots($department, $dateRange);
}
return $this->getStatisticsFromReplica($department, $user, $dateRange, $branchId);
}
/**
* Получить статистику из сохранённых снапшотов submitted-отчётов.
*/
private function getStatisticsFromSnapshots(Department $department, DateRange $dateRange): array
{
$reports = $this->contextResolver->getReportsForDateRange(
$department->department_id,
$dateRange
);
$reportIds = $reports->pluck('report_id')->all();
$lastReportId = $reportIds[0] ?? null;
$recipientReportIds = $this->contextResolver->getRecipientReportIds($reportIds);
$snapshotStats = [
'plan' => $this->getMetrikaResultCount(MetrikaConfig::PLAN, $reportIds),
'emergency' => $this->getMetrikaResultCount(MetrikaConfig::EMERGENCY, $reportIds),
'outcome' => $this->getMetrikaResultCount(MetrikaConfig::OUTCOME, $reportIds),
'deceased' => $this->getMetrikaResultCount(MetrikaConfig::DECEASED, $reportIds),
'current' => $this->getMetrikaResultCount(MetrikaConfig::CURRENT, $reportIds, false),
'transferred' => $this->getMetrikaResultCount(MetrikaConfig::TRANSFERRED, $reportIds),
'recipient' => $this->getMetrikaResultCount(MetrikaConfig::RECIPIENT, $reportIds),
'beds' => $this->getMetrikaResultCount(MetrikaConfig::BEDS, $reportIds, false),
'countStaff' => $lastReportId
? $this->getMetrikaResultCount(MetrikaConfig::STAFF_COUNT, [$lastReportId], false)
: 0,
];
$recipientIds = $this->snapshotService
->getPatientsFromSnapshots('recipient', $recipientReportIds)
->pluck('id')
->all();
$surgicalCount = [
$this->getMetrikaResultCount(MetrikaConfig::EMERGENCY_SURGERY, $reportIds),
$this->getMetrikaResultCount(MetrikaConfig::PLAN_SURGERY, $reportIds),
];
return [
'recipientCount' => $snapshotStats['recipient'] ?? 0,
'extractCount' => $snapshotStats['outcome'] ?? 0,
'currentCount' => $snapshotStats['current'] ?? 0,
'deadCount' => $snapshotStats['deceased'] ?? 0,
'countStaff' => $snapshotStats['countStaff'] ?? 0,
'surgicalCount' => $surgicalCount,
'recipientIds' => $recipientIds,
'beds' => $snapshotStats['beds'] ?? 0,
'percentDead' => $this->calculatePercentDead($snapshotStats['deceased'], $snapshotStats['outcome']),
];
}
/**
* Получить статистику из live-реплики МИС и manual-источников.
*/
private function getStatisticsFromReplica(
Department $department,
User $user,
DateRange $dateRange,
int $branchId
): array {
$planCount = $this->unifiedPatientService->getLivePatientCountByStatus($department, $user, 'plan', $dateRange, $branchId, true);
$emergencyCount = $this->unifiedPatientService->getLivePatientCountByStatus($department, $user, 'emergency', $dateRange, $branchId, true);
$currentCount = $this->unifiedPatientService->getLivePatientCountByStatus($department, $user, 'current', $dateRange, $branchId);
$recipientCount = $this->unifiedPatientService->getLivePatientCountByStatus($department, $user, 'recipient', $dateRange, $branchId);
$outcomeCount = $this->unifiedPatientService->getLivePatientCountByStatus($department, $user, 'outcome', $dateRange, $branchId);
$deadCount = $this->unifiedPatientService->getLivePatientCountByStatus($department, $user, 'outcome-deceased', $dateRange, $branchId);
$misSurgicalCount = [
$this->patientService->getSurgicalPatients('emergency', $branchId, $dateRange, true),
$this->patientService->getSurgicalPatients('plan', $branchId, $dateRange, true),
];
$manualSurgicalCount = $this->calculatedMetricsSynchronizer->getManualSurgicalCounts($department, $dateRange);
$surgicalCount = [
($misSurgicalCount[0] ?? 0) + ($manualSurgicalCount[0] ?? 0),
($misSurgicalCount[1] ?? 0) + ($manualSurgicalCount[1] ?? 0),
];
$recipientIds = $this->unifiedPatientService
->getRecipientIdsForReport($department, $user, $dateRange, $branchId);
return [
'recipientCount' => $recipientCount,
'extractCount' => $outcomeCount,
'currentCount' => $currentCount,
'deadCount' => $deadCount,
'surgicalCount' => $surgicalCount,
'recipientIds' => $recipientIds,
'planCount' => $planCount,
'emergencyCount' => $emergencyCount,
'percentDead' => $this->calculatePercentDead($deadCount, $outcomeCount),
'beds' => (int) ($department->metrikaDefault
->where('rf_metrika_item_id', MetrikaConfig::BEDS)
->first()
?->value ?? 0),
];
}
/**
* Получить агрегированное значение метрики из набора отчётов.
*
* @param array<int, int> $reportIds
*/
private function getMetrikaResultCount(int $metrikaItemId, array $reportIds, bool $sum = true): int
{
if (empty($reportIds)) {
return 0;
}
$reports = Report::query()
->whereIn('report_id', $reportIds)
->with('metrikaResults')
->orderBy('created_at', 'DESC')
->get();
if (! $sum) {
foreach ($reports as $report) {
$metric = $report->metrikaResults
->firstWhere('rf_metrika_item_id', $metrikaItemId);
if ($metric) {
return (int) $metric->value;
}
}
return 0;
}
$count = 0;
foreach ($reports as $report) {
foreach ($report->metrikaResults as $metrikaResult) {
if ((int) $metrikaResult->rf_metrika_item_id === $metrikaItemId) {
$count += (int) $metrikaResult->value;
}
}
}
return $count;
}
private function calculatePercentDead(int $deadCount, int $outcomeCount): float|int
{
if ($outcomeCount === 0) {
return 0;
}
return round(($deadCount / $outcomeCount) * 100, 2);
}
private function emptyStatistics(): array
{
return [
'recipientCount' => 0,
'extractCount' => 0,
'currentCount' => 0,
'deadCount' => 0,
'surgicalCount' => [0, 0],
'recipientIds' => [],
'planCount' => 0,
'emergencyCount' => 0,
'percentDead' => 0,
'beds' => 0,
'countStaff' => 0,
];
}
}