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

125 lines
4.2 KiB
PHP
Raw Permalink 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\MisStationarBranch;
use App\Models\Report;
use App\Models\User;
use App\Services\DateRange;
use Illuminate\Support\Collection;
/**
* Разрешает контекст отчётного периода, необходимый для read-side сервисов.
*
* Класс хранит в одном месте правила поиска отчётов по периоду и решения
* snapshot-vs-replica, чтобы не дублировать их по контроллерам и сервисам.
*/
class ReportReadContextResolver
{
/**
* Определить MIS branch id для отчётного отделения.
*/
public function resolveBranchId(Department $department): ?int
{
return MisStationarBranch::query()
->where('rf_DepartmentID', $department->rf_mis_department_id)
->value('StationarBranchID');
}
/**
* Определить, нужно ли читать submitted-снапшоты вместо live-данных.
*/
public function shouldUseSnapshots(
Department $department,
DateRange $dateRange,
bool $beforeCreate = false
): bool {
if ($beforeCreate) {
return false;
}
$report = $this->getReportForPeriod($department->department_id, $dateRange);
return $report?->status === 'submitted';
}
/**
* Для самых изменчивых статусов врачи должны продолжать видеть live-данные за текущие сутки.
*/
public function shouldUseReplicaForLiveStatus(User $user, string $status, DateRange $dateRange): bool
{
if ($user->isHeadOfDepartment() || $user->isAdmin()) {
return false;
}
return in_array($status, ['plan', 'emergency', 'recipient', 'current', 'reanimation'], true)
&& $dateRange->isOneDay
&& $dateRange->isEndDateToday();
}
/**
* Вернуть submitted-отчёты, относящиеся к выбранному отчётному окну.
*
* @return Collection<int, Report>
*/
public function getReportsForDateRange(int $departmentId, DateRange $dateRange): Collection
{
if ($dateRange->isOneDay) {
return Report::query()
->where('rf_department_id', $departmentId)
->exactPeriod($dateRange->startSql(), $dateRange->endSql())
->onlySubmitted()
->orderBy('period_end', 'DESC')
->get();
}
return Report::query()
->where('rf_department_id', $departmentId)
->withinPeriod($dateRange->startSql(), $dateRange->endSql())
->onlySubmitted()
->orderBy('period_end', 'DESC')
->get();
}
/**
* Recipient-снапшоты читаются из последнего отчёта в выбранном окне.
*
* @param array<int, int> $reportIds
* @return array<int, int>
*/
public function getRecipientReportIds(array $reportIds): array
{
if (empty($reportIds)) {
return [];
}
return [reset($reportIds)];
}
/**
* Найти отчёт, который определяет видимость снапшотов для запрошенного периода.
*/
private function getReportForPeriod(int $departmentId, DateRange $dateRange): ?Report
{
return $this->resolveReportForPeriod($departmentId, $dateRange);
}
/**
* Найти отчёт для точного периода с теми же правилами, что использует legacy read-side.
*/
public function resolveReportForPeriod(int $departmentId, DateRange $dateRange): ?Report
{
$query = Report::query()
->where('rf_department_id', $departmentId)
->exactPeriod($dateRange->startSql(), $dateRange->endSql())
->orderByDesc('report_id');
if ($dateRange->isOneDay) {
return $query->first();
}
return $query->onlySubmitted()->first();
}
}