125 lines
4.2 KiB
PHP
125 lines
4.2 KiB
PHP
<?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();
|
||
}
|
||
}
|