117 lines
3.9 KiB
PHP
117 lines
3.9 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
|
|
{
|
|
$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();
|
|
}
|
|
}
|