nothing
This commit is contained in:
@@ -37,8 +37,9 @@ class AutoReportService
|
||||
$period = CarbonPeriod::create($startDate, $endDate);
|
||||
|
||||
foreach ($period as $date) {
|
||||
$dateRange = $this->dateRangeService->getNormalizedDateRange($user, $date, $date);
|
||||
try {
|
||||
$reportCreated = $this->createReportForDate($user, $date, $departmentId, $force);
|
||||
$reportCreated = $this->createReportForDate($user, $dateRange, $departmentId, $force);
|
||||
|
||||
if ($reportCreated) {
|
||||
$createdCount++;
|
||||
@@ -55,13 +56,13 @@ class AutoReportService
|
||||
/**
|
||||
* Создать отчет для конкретной даты
|
||||
*/
|
||||
public function createReportForDate(User $user, Carbon $date, $departmentId, bool $force = false): bool
|
||||
public function createReportForDate(User $user, DateRange $dateRange, $departmentId, bool $force = false): bool
|
||||
{
|
||||
$user->rf_department_id = $departmentId;
|
||||
// Проверяем, существует ли уже отчет на эту дату
|
||||
$existingReport = Report::where('rf_department_id', $departmentId)
|
||||
->whereDate('created_at', $date)
|
||||
->whereDate('sent_at', $date)
|
||||
->whereDate('created_at', $dateRange->endSql())
|
||||
->whereDate('sent_at', $dateRange->endSql())
|
||||
->first();
|
||||
|
||||
if ($existingReport && !$force) {
|
||||
@@ -75,18 +76,11 @@ class AutoReportService
|
||||
$existingReport->delete();
|
||||
}
|
||||
|
||||
// Создаем DateRange для этой даты
|
||||
// Приводим к Illuminate\Carbon если нужно
|
||||
if (!$date instanceof \Illuminate\Support\Carbon) {
|
||||
$date = \Illuminate\Support\Carbon::instance($date);
|
||||
}
|
||||
$dateRange = $this->dateRangeService->createDateRangeForDate($date, $user);
|
||||
|
||||
// Получаем данные для отчета
|
||||
$reportData = $this->prepareReportData($user, $dateRange, $date, $departmentId);
|
||||
$reportData = $this->prepareReportData($user, $dateRange, $departmentId);
|
||||
|
||||
// Создаем отчет
|
||||
DB::transaction(function () use ($user, $reportData, $date) {
|
||||
DB::transaction(function () use ($user, $reportData) {
|
||||
$this->reportService->storeReport($reportData, $user);
|
||||
});
|
||||
|
||||
@@ -96,7 +90,7 @@ class AutoReportService
|
||||
/**
|
||||
* Подготовить данные для отчета
|
||||
*/
|
||||
private function prepareReportData(User $user, DateRange $dateRange, Carbon $date, $departmentId): array
|
||||
private function prepareReportData(User $user, DateRange $dateRange, $departmentId): array
|
||||
{
|
||||
$department = Department::where('department_id', $departmentId)->first();
|
||||
$branchId = $this->getBranchId($department->rf_mis_department_id);
|
||||
@@ -121,10 +115,10 @@ class AutoReportService
|
||||
$dateRange->startTimestamp(),
|
||||
$dateRange->endTimestamp()
|
||||
],
|
||||
'sent_at' => $this->dateRangeService->toSqlFormat($date),
|
||||
'created_at' => $this->dateRangeService->toSqlFormat($date),
|
||||
'sent_at' => $dateRange->endSql(),
|
||||
'created_at' => $dateRange->endSql(),
|
||||
'metrics' => $this->formatMetrics($metrics),
|
||||
'observationPatients' => $this->getObservationPatients($departmentId, $date),
|
||||
'observationPatients' => $this->getObservationPatients($departmentId, $dateRange),
|
||||
'unwantedEvents' => [],
|
||||
];
|
||||
}
|
||||
@@ -247,7 +241,7 @@ class AutoReportService
|
||||
/**
|
||||
* Получить пациентов под наблюдением на дату
|
||||
*/
|
||||
private function getObservationPatients(int $departmentId, Carbon $date): array
|
||||
private function getObservationPatients(int $departmentId, DateRange $dateRange): array
|
||||
{
|
||||
// Здесь нужно реализовать логику получения пациентов под наблюдением
|
||||
// на конкретную дату. Возможно, из снапшотов или истории.
|
||||
|
||||
326
app/Services/BedDayService.php
Normal file
326
app/Services/BedDayService.php
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
// app/Services/BedDayService.php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\MetrikaResult;
|
||||
use App\Models\Report;
|
||||
use App\Models\MedicalHistorySnapshot;
|
||||
use App\Models\MisMedicalHistory;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class BedDayService
|
||||
{
|
||||
/**
|
||||
* ID метрики для среднего койко-дня
|
||||
*/
|
||||
const METRIC_BED_DAYS_ID = 18;
|
||||
|
||||
/**
|
||||
* Кэш для хранения результатов запросов
|
||||
*/
|
||||
protected array $cache = [];
|
||||
|
||||
/**
|
||||
* Получить средний койко-день для отделения из снапшотов за период
|
||||
*/
|
||||
public function getAverageBedDaysFromSnapshots(int $departmentId, string $startDate, string $endDate, bool $isRangeOneDay): float
|
||||
{
|
||||
$cacheKey = "snapshots_{$departmentId}_{$startDate}_{$endDate}_" . ($isRangeOneDay ? '1day' : 'range');
|
||||
|
||||
if (isset($this->cache[$cacheKey])) {
|
||||
return $this->cache[$cacheKey];
|
||||
}
|
||||
|
||||
// Для одного дня берем последние 30 дней для статистической значимости
|
||||
$actualStartDate = $isRangeOneDay
|
||||
? Carbon::parse($endDate)->subDays(30)->format('Y-m-d')
|
||||
: $startDate;
|
||||
|
||||
// Находим отчеты за период
|
||||
$reports = Report::where('rf_department_id', $departmentId)
|
||||
->whereBetween('created_at', [$actualStartDate, $endDate])
|
||||
->pluck('report_id');
|
||||
|
||||
if ($reports->isEmpty()) {
|
||||
$this->cache[$cacheKey] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Получаем все снапшоты выписанных пациентов из этих отчетов
|
||||
$snapshots = MedicalHistorySnapshot::whereIn('rf_report_id', $reports)
|
||||
->where('patient_type', MedicalHistorySnapshot::PATIENT_TYPE_DISCHARGED)
|
||||
->distinct()
|
||||
->with('medicalHistory')
|
||||
->get();
|
||||
|
||||
if ($snapshots->isEmpty()) {
|
||||
$this->cache[$cacheKey] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Рассчитываем средний койко-день
|
||||
$totalDays = 0;
|
||||
$validCount = 0;
|
||||
|
||||
foreach ($snapshots as $snapshot) {
|
||||
$history = $snapshot->medicalHistory;
|
||||
if ($history && $history->DateRecipient && $history->DateExtract) {
|
||||
$start = Carbon::parse($history->DateRecipient);
|
||||
$end = Carbon::parse($history->DateExtract);
|
||||
|
||||
// Проверяем, что дата выписки входит в период отчета
|
||||
if ($end->between($actualStartDate, $endDate)) {
|
||||
$days = $start->diffInDays($end);
|
||||
$totalDays += $days;
|
||||
$validCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$avgDays = $validCount > 0 ? round($totalDays / $validCount, 1) : 0;
|
||||
$this->cache[$cacheKey] = $avgDays;
|
||||
|
||||
return $avgDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить средние койко-дни для всех отделений из снапшотов
|
||||
*/
|
||||
public function getAverageBedDaysByDepartmentsFromSnapshots(array $departmentIds, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||
{
|
||||
if (empty($departmentIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheKey = 'all_snapshots_' . md5(implode(',', $departmentIds) . $startDate . $endDate . ($isRangeOneDay ? '1day' : 'range'));
|
||||
|
||||
if (isset($this->cache[$cacheKey])) {
|
||||
return $this->cache[$cacheKey];
|
||||
}
|
||||
|
||||
$actualStartDate = $isRangeOneDay
|
||||
? Carbon::parse($endDate)->subDays(30)->format('Y-m-d')
|
||||
: $startDate;
|
||||
|
||||
// Находим все отчеты за период по отделениям
|
||||
$reportsByDepartment = Report::whereIn('rf_department_id', $departmentIds)
|
||||
->whereBetween('created_at', [$actualStartDate, $endDate])
|
||||
->select('report_id', 'rf_department_id')
|
||||
->get()
|
||||
->groupBy('rf_department_id');
|
||||
|
||||
$averages = [];
|
||||
|
||||
foreach ($departmentIds as $departmentId) {
|
||||
$departmentReports = $reportsByDepartment->get($departmentId, collect());
|
||||
|
||||
if ($departmentReports->isEmpty()) {
|
||||
$averages[$departmentId] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
$reportIds = $departmentReports->pluck('report_id')->toArray();
|
||||
|
||||
// Получаем снапшоты для отчетов отделения
|
||||
$snapshots = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
|
||||
->where('patient_type', MedicalHistorySnapshot::PATIENT_TYPE_DISCHARGED)
|
||||
->distinct()
|
||||
->with('medicalHistory')
|
||||
->get();
|
||||
|
||||
if ($snapshots->isEmpty()) {
|
||||
$averages[$departmentId] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
$totalDays = 0;
|
||||
$validCount = 0;
|
||||
|
||||
foreach ($snapshots as $snapshot) {
|
||||
$history = $snapshot->medicalHistory;
|
||||
if ($history && $history->DateRecipient && $history->DateExtract) {
|
||||
$end = Carbon::parse($history->DateExtract);
|
||||
if ($end->between($actualStartDate, $endDate)) {
|
||||
$start = Carbon::parse($history->DateRecipient);
|
||||
$days = $start->diffInDays($end);
|
||||
$totalDays += $days;
|
||||
$validCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$averages[$departmentId] = $validCount > 0 ? round($totalDays / $validCount, 1) : 0;
|
||||
}
|
||||
|
||||
$this->cache[$cacheKey] = $averages;
|
||||
|
||||
return $averages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить общий средний койко-день из всех снапшотов
|
||||
*/
|
||||
public function getOverallAverageBedDaysFromSnapshots(array $departmentIds, string $startDate, string $endDate, bool $isRangeOneDay): float
|
||||
{
|
||||
$averages = $this->getAverageBedDaysByDepartmentsFromSnapshots($departmentIds, $startDate, $endDate, $isRangeOneDay);
|
||||
|
||||
$total = 0;
|
||||
$count = 0;
|
||||
|
||||
foreach ($averages as $avg) {
|
||||
if ($avg > 0) {
|
||||
$total += $avg;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count > 0 ? round($total / $count, 1) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить детальную статистику по койко-дням из снапшотов
|
||||
*/
|
||||
public function getDetailedStatsFromSnapshots(int $departmentId, string $startDate, string $endDate): array
|
||||
{
|
||||
$reports = Report::where('rf_department_id', $departmentId)
|
||||
->whereBetween('created_at', [$startDate, $endDate])
|
||||
->pluck('report_id');
|
||||
|
||||
if ($reports->isEmpty()) {
|
||||
return [
|
||||
'department_id' => $departmentId,
|
||||
'period' => ['start' => $startDate, 'end' => $endDate],
|
||||
'total_patients' => 0,
|
||||
'average_bed_days' => 0,
|
||||
'distribution' => [],
|
||||
'by_month' => []
|
||||
];
|
||||
}
|
||||
|
||||
$snapshots = MedicalHistorySnapshot::whereIn('rf_report_id', $reports)
|
||||
->where('patient_type', MedicalHistorySnapshot::PATIENT_TYPE_DISCHARGED)
|
||||
->distinct()
|
||||
->with('medicalHistory')
|
||||
->get();
|
||||
|
||||
if ($snapshots->isEmpty()) {
|
||||
return [
|
||||
'department_id' => $departmentId,
|
||||
'period' => ['start' => $startDate, 'end' => $endDate],
|
||||
'total_patients' => 0,
|
||||
'average_bed_days' => 0,
|
||||
'distribution' => [],
|
||||
'by_month' => []
|
||||
];
|
||||
}
|
||||
|
||||
$distribution = [
|
||||
'1-3' => 0,
|
||||
'4-7' => 0,
|
||||
'8-14' => 0,
|
||||
'15-21' => 0,
|
||||
'22-30' => 0,
|
||||
'30+' => 0
|
||||
];
|
||||
|
||||
$byMonth = [];
|
||||
$totalDays = 0;
|
||||
$validCount = 0;
|
||||
|
||||
foreach ($snapshots as $snapshot) {
|
||||
$history = $snapshot->medicalHistory;
|
||||
if ($history && $history->DateRecipient && $history->DateExtract) {
|
||||
$end = Carbon::parse($history->DateExtract);
|
||||
if ($end->between($startDate, $endDate)) {
|
||||
$start = Carbon::parse($history->DateRecipient);
|
||||
$days = $start->diffInDays($end);
|
||||
|
||||
$totalDays += $days;
|
||||
$validCount++;
|
||||
|
||||
// Распределение
|
||||
if ($days <= 3) $distribution['1-3']++;
|
||||
elseif ($days <= 7) $distribution['4-7']++;
|
||||
elseif ($days <= 14) $distribution['8-14']++;
|
||||
elseif ($days <= 21) $distribution['15-21']++;
|
||||
elseif ($days <= 30) $distribution['22-30']++;
|
||||
else $distribution['30+']++;
|
||||
|
||||
// По месяцам
|
||||
$month = $end->format('Y-m');
|
||||
if (!isset($byMonth[$month])) {
|
||||
$byMonth[$month] = ['total' => 0, 'count' => 0];
|
||||
}
|
||||
$byMonth[$month]['total'] += $days;
|
||||
$byMonth[$month]['count']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассчитываем средние по месяцам
|
||||
$monthlyStats = [];
|
||||
foreach ($byMonth as $month => $data) {
|
||||
$monthlyStats[] = [
|
||||
'month' => $month,
|
||||
'avg_days' => round($data['total'] / $data['count'], 1),
|
||||
'count' => $data['count']
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'department_id' => $departmentId,
|
||||
'period' => ['start' => $startDate, 'end' => $endDate],
|
||||
'total_patients' => $validCount,
|
||||
'average_bed_days' => $validCount > 0 ? round($totalDays / $validCount, 1) : 0,
|
||||
'distribution' => $distribution,
|
||||
'by_month' => $monthlyStats
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить метрики для всех отчетов на основе снапшотов
|
||||
*/
|
||||
public function updateAllMetricsFromSnapshots(): array
|
||||
{
|
||||
$reports = Report::all();
|
||||
$results = [];
|
||||
|
||||
foreach ($reports as $report) {
|
||||
// Для каждого отчета считаем средний койко-день за последние 30 дней до даты отчета
|
||||
$endDate = $report->created_at;
|
||||
$startDate = Carbon::startOfYear();
|
||||
dd($startDate);
|
||||
|
||||
$avg = $this->getAverageBedDaysFromSnapshots(
|
||||
$report->rf_department_id,
|
||||
$startDate,
|
||||
$endDate,
|
||||
false
|
||||
);
|
||||
|
||||
// Сохраняем в метрики
|
||||
MetrikaResult::updateOrCreate(
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_metrika_item_id' => self::METRIC_BED_DAYS_ID,
|
||||
],
|
||||
['value' => $avg]
|
||||
);
|
||||
|
||||
$results[$report->report_id] = $avg;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистить кэш памяти
|
||||
*/
|
||||
public function clearMemoryCache(): void
|
||||
{
|
||||
$this->cache = [];
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,10 @@ class DateRangeService
|
||||
return $this->getDateRangeForUser($user, $startAt, $endAt);
|
||||
}
|
||||
|
||||
if ($user->isDoctor()) {
|
||||
return $this->getDateRangeForUser($user, $startAt, $endAt);
|
||||
}
|
||||
|
||||
// По умолчанию: с начала года до сегодня
|
||||
$startDate = Carbon::now('Asia/Yakutsk')
|
||||
->startOfYear() // 1 января текущего года
|
||||
|
||||
@@ -30,17 +30,16 @@ class ReportService
|
||||
/**
|
||||
* Получить статистику для отчета
|
||||
*/
|
||||
public function getReportStatistics(User $user, DateRange $dateRange): array
|
||||
public function getReportStatistics(Department $department, User $user, DateRange $dateRange): array
|
||||
{
|
||||
$department = $user->department;
|
||||
$misDepartmentId = $department->rf_mis_department_id;
|
||||
$branchId = $this->getBranchId($misDepartmentId);
|
||||
|
||||
// Определяем, используем ли мы снапшоты
|
||||
$useSnapshots = $this->shouldUseSnapshots($user, $dateRange);
|
||||
$useSnapshots = $this->shouldUseSnapshots($department, $user, $dateRange);
|
||||
|
||||
if ($useSnapshots) {
|
||||
return $this->getStatisticsFromSnapshots($user, $dateRange, $branchId);
|
||||
return $this->getStatisticsFromSnapshots($department, $dateRange, $branchId);
|
||||
}
|
||||
|
||||
return $this->getStatisticsFromReplica($user, $dateRange, $branchId);
|
||||
@@ -63,6 +62,9 @@ class ReportService
|
||||
// Сохраняем снапшоты пациентов
|
||||
$this->snapshotService->createPatientSnapshots($report, $user, $data['dates'], $fillableAuto);
|
||||
|
||||
// Сохраняем метрику среднего койко-дня из снапшотов
|
||||
$this->saveAverageBedDaysMetricFromSnapshots($report);
|
||||
|
||||
DB::commit();
|
||||
|
||||
// ОЧИСТКА КЭША ПОСЛЕ УСПЕШНОГО СОЗДАНИЯ ОТЧЕТА
|
||||
@@ -75,6 +77,76 @@ class ReportService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить метрику среднего койко-дня из снапшотов отчета
|
||||
*/
|
||||
protected function saveAverageBedDaysMetricFromSnapshots(Report $report): void
|
||||
{
|
||||
try {
|
||||
// Получаем все снапшоты выписанных пациентов из этого отчета
|
||||
$snapshots = MedicalHistorySnapshot::where('rf_report_id', $report->report_id)
|
||||
->whereIn('patient_type', ['discharged', 'deceased']) // выписанные и умершие
|
||||
->with('medicalHistory')
|
||||
->get();
|
||||
|
||||
if ($snapshots->isEmpty()) {
|
||||
// Если нет выписанных, сохраняем 0
|
||||
MetrikaResult::updateOrCreate(
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_metrika_item_id' => 18,
|
||||
],
|
||||
['value' => 0]
|
||||
);
|
||||
|
||||
\Log::info("No discharged patients in report {$report->report_id}, saved 0");
|
||||
return;
|
||||
}
|
||||
|
||||
// Рассчитываем средний койко-день по снапшотам
|
||||
$totalDays = 0;
|
||||
$validCount = 0;
|
||||
|
||||
foreach ($snapshots as $snapshot) {
|
||||
$history = $snapshot->medicalHistory;
|
||||
|
||||
if ($history && $history->DateRecipient && $history->DateExtract) {
|
||||
// Проверяем что дата выписки не специальная
|
||||
if ($history->DateExtract->format('Y-m-d') === '2222-01-01') {
|
||||
continue; // пропускаем текущих пациентов
|
||||
}
|
||||
|
||||
$start = Carbon::parse($history->DateRecipient);
|
||||
$end = Carbon::parse($history->DateExtract);
|
||||
|
||||
// Проверяем что дата выписки позже даты поступления
|
||||
if ($end->gt($start)) {
|
||||
$days = $start->diffInDays($end);
|
||||
$totalDays += $days;
|
||||
$validCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$avgBedDays = $validCount > 0 ? round($totalDays / $validCount, 1) : 0;
|
||||
|
||||
// Сохраняем метрику
|
||||
MetrikaResult::updateOrCreate(
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_metrika_item_id' => 18,
|
||||
],
|
||||
['value' => $avgBedDays]
|
||||
);
|
||||
|
||||
\Log::info("Saved average bed days metric for report {$report->report_id}: {$avgBedDays} (from {$validCount} patients)");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error("Failed to save average bed days metric: " . $e->getMessage());
|
||||
// Не прерываем выполнение, если метрика не сохранилась
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистить кэш после создания отчета
|
||||
*/
|
||||
@@ -115,6 +187,7 @@ class ReportService
|
||||
* Получить пациентов по статусу
|
||||
*/
|
||||
public function getPatientsByStatus(
|
||||
Department $department,
|
||||
User $user,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
@@ -122,34 +195,35 @@ class ReportService
|
||||
bool $beforeCreate = false,
|
||||
?bool $includeCurrentPatients = null
|
||||
) {
|
||||
$branchId = $this->getBranchId($user->department->rf_mis_department_id);
|
||||
$branchId = $this->getBranchId($department->rf_mis_department_id);
|
||||
|
||||
$useSnapshots = $this->shouldUseSnapshots($user, $dateRange, $beforeCreate);
|
||||
$useSnapshots = $this->shouldUseSnapshots($department, $user, $dateRange, $beforeCreate);
|
||||
|
||||
if ($useSnapshots) {
|
||||
return $this->getPatientsFromSnapshots($user, $status, $dateRange, $branchId);
|
||||
return $this->getPatientsFromSnapshots($department, $status, $dateRange, $branchId);
|
||||
}
|
||||
|
||||
return $this->getPatientsFromReplica($user, $status, $dateRange, $branchId, $onlyIds, $includeCurrentPatients);
|
||||
return $this->getPatientsFromReplica($department, $user, $status, $dateRange, $branchId, $onlyIds, $includeCurrentPatients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить количество пациентов по статусу
|
||||
*/
|
||||
public function getPatientsCountByStatus(
|
||||
Department $department,
|
||||
User $user,
|
||||
string $status,
|
||||
DateRange $dateRange
|
||||
): int {
|
||||
$branchId = $this->getBranchId($user->department->rf_mis_department_id);
|
||||
$branchId = $this->getBranchId($department->rf_mis_department_id);
|
||||
|
||||
$useSnapshots = $this->shouldUseSnapshots($user, $dateRange);
|
||||
$useSnapshots = $this->shouldUseSnapshots($department, $user, $dateRange);
|
||||
|
||||
if ($useSnapshots) {
|
||||
return $this->getPatientsCountFromSnapshots($user, $status, $dateRange);
|
||||
return $this->getPatientsCountFromSnapshots($department, $status, $dateRange);
|
||||
}
|
||||
|
||||
return $this->getPatientsCountFromReplica($user, $status, $dateRange, $branchId);
|
||||
return $this->getPatientsCountFromReplica($department, $user, $status, $dateRange, $branchId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +238,7 @@ class ReportService
|
||||
/**
|
||||
* Определить, нужно ли использовать снапшоты
|
||||
*/
|
||||
private function shouldUseSnapshots(User $user, DateRange $dateRange, bool $beforeCreate = false): bool
|
||||
private function shouldUseSnapshots(Department $department, User $user, DateRange $dateRange, bool $beforeCreate = false): bool
|
||||
{
|
||||
if (($user->isAdmin() || $user->isHeadOfDepartment()) && !$beforeCreate) {
|
||||
return true;
|
||||
@@ -173,6 +247,7 @@ class ReportService
|
||||
// Проверяем, есть ли отчет на сегодня
|
||||
$reportToday = Report::whereDate('sent_at', $dateRange->end())
|
||||
->whereDate('created_at', $dateRange->end())
|
||||
->where('rf_department_id', $department->department_id)
|
||||
->first();
|
||||
|
||||
return !$dateRange->isEndDateToday() || $reportToday;
|
||||
@@ -237,6 +312,7 @@ class ReportService
|
||||
{
|
||||
if (empty($unwantedEvents)) {
|
||||
$report->unwantedEvents()->delete();
|
||||
$this->saveMetrics($report, [16 => 0]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,6 +336,9 @@ class ReportService
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Обновить метрику
|
||||
$this->saveMetrics($report, [16 => count($unwantedEvents)]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,6 +353,8 @@ class ReportService
|
||||
ObservationPatient::where('rf_department_id', $departmentId)
|
||||
->where('rf_report_id', $report->report_id)
|
||||
->delete();
|
||||
// Обновить метрику
|
||||
$this->saveMetrics($report, [14 => 0]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -290,16 +371,19 @@ class ReportService
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Обновить метрику
|
||||
$this->saveMetrics($report, [14 => count($observationPatients)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить информацию о текущем отчете
|
||||
*/
|
||||
public function getCurrentReportInfo(User $user, DateRange $dateRange): array
|
||||
public function getCurrentReportInfo(Department $department, User $user, DateRange $dateRange): array
|
||||
{
|
||||
$department = $user->department;
|
||||
$reportToday = Report::whereDate('sent_at', $dateRange->endSql())
|
||||
->whereDate('created_at', $dateRange->endSql())
|
||||
->where('rf_department_id', $department->department_id)
|
||||
->first();
|
||||
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
@@ -313,7 +397,7 @@ class ReportService
|
||||
}
|
||||
|
||||
// Получаем нежелательные события
|
||||
$unwantedEvents = $this->getUnwantedEvents($user, $dateRange);
|
||||
$unwantedEvents = $this->getUnwantedEvents($department, $dateRange);
|
||||
|
||||
// Определяем активность кнопки отправки
|
||||
$isActiveSendButton = $this->isSendButtonActive($user, $dateRange, $reportToday, $fillableUserId);
|
||||
@@ -367,11 +451,11 @@ class ReportService
|
||||
/**
|
||||
* Получить статистику из снапшотов
|
||||
*/
|
||||
private function getStatisticsFromSnapshots(User $user, DateRange $dateRange, int $branchId): array
|
||||
private function getStatisticsFromSnapshots(Department $department, DateRange $dateRange, int $branchId): array
|
||||
{
|
||||
// Получаем отчеты за период
|
||||
$reports = $this->getReportsForDateRange(
|
||||
$user->rf_department_id,
|
||||
$department->department_id,
|
||||
$dateRange
|
||||
);
|
||||
|
||||
@@ -403,6 +487,13 @@ class ReportService
|
||||
$this->getMetrikaResultCount(11, $reportIds) // плановые операции
|
||||
];
|
||||
|
||||
if ($snapshotStats['outcome'] == 0) {
|
||||
$percentDead = 0;
|
||||
} else {
|
||||
$percentDead = ($snapshotStats['deceased'] / $snapshotStats['outcome']) * 100;
|
||||
$percentDead = round($percentDead, 2);
|
||||
}
|
||||
|
||||
return [
|
||||
'recipientCount' => $snapshotStats['recipient'] ?? 0,
|
||||
'extractCount' => $snapshotStats['outcome'] ?? 0,
|
||||
@@ -410,7 +501,8 @@ class ReportService
|
||||
'deadCount' => $snapshotStats['deceased'] ?? 0,
|
||||
'surgicalCount' => $surgicalCount,
|
||||
'recipientIds' => $recipientIds,
|
||||
'beds' => $snapshotStats['beds'] ?? 0
|
||||
'beds' => $snapshotStats['beds'] ?? 0,
|
||||
'percentDead' => $percentDead,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -501,6 +593,13 @@ class ReportService
|
||||
$beds = Department::where('rf_mis_department_id', $misBranch->rf_DepartmentID)
|
||||
->first()->metrikaDefault->where('rf_metrika_item_id', 1)->first();
|
||||
|
||||
if ($outcomeCount == 0) {
|
||||
$percentDead = 0;
|
||||
} else {
|
||||
$percentDead = ($deadCount / $outcomeCount) * 100;
|
||||
$percentDead = round($percentDead, 2);
|
||||
}
|
||||
|
||||
return [
|
||||
'recipientCount' => $recipientCount, // только поступившие сегодня
|
||||
'extractCount' => $outcomeCount,
|
||||
@@ -510,6 +609,7 @@ class ReportService
|
||||
'recipientIds' => $recipientIds, // ID поступивших сегодня
|
||||
'planCount' => $planCount, // плановые (поступившие + уже лечащиеся)
|
||||
'emergencyCount' => $emergencyCount, // экстренные (поступившие + уже лечащиеся)
|
||||
'percentDead' => $percentDead,
|
||||
'beds' => $beds->value
|
||||
];
|
||||
}
|
||||
@@ -517,15 +617,15 @@ class ReportService
|
||||
/**
|
||||
* Получить пациентов из снапшотов
|
||||
*/
|
||||
private function getPatientsFromSnapshots(
|
||||
User $user,
|
||||
public function getPatientsFromSnapshots(
|
||||
Department $department,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
int $branchId,
|
||||
bool $onlyIds = false
|
||||
) {
|
||||
$reports = $this->getReportsForDateRange(
|
||||
$user->rf_department_id,
|
||||
$department->department_id,
|
||||
$dateRange
|
||||
);
|
||||
|
||||
@@ -543,7 +643,7 @@ class ReportService
|
||||
$patientType = $patientTypeMap[$status] ?? null;
|
||||
|
||||
if ($patientType === 'observation') {
|
||||
return $this->patientQueryService->getObservationPatients($user->rf_department_id, $onlyIds); //$this->getObservationPatientsFromSnapshots($user->rf_department_id, $reportIds, $onlyIds);
|
||||
return $this->patientQueryService->getObservationPatients($department->department_id, $onlyIds); //$this->getObservationPatientsFromSnapshots($user->rf_department_id, $reportIds, $onlyIds);
|
||||
}
|
||||
|
||||
return $this->snapshotService->getPatientsFromSnapshots($patientType, $reportIds, $branchId, $onlyIds);
|
||||
@@ -553,6 +653,7 @@ class ReportService
|
||||
* Получить пациентов из реплики БД
|
||||
*/
|
||||
private function getPatientsFromReplica(
|
||||
Department $department,
|
||||
User $user,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
@@ -575,7 +676,7 @@ class ReportService
|
||||
$onlyIds,
|
||||
$includeCurrent
|
||||
),
|
||||
'observation' => $this->patientQueryService->getObservationPatients($user->rf_department_id, $onlyIds),
|
||||
'observation' => $this->patientQueryService->getObservationPatients($department->department_id, $onlyIds),
|
||||
'outcome' => $this->patientQueryService->getOutcomePatients(
|
||||
$branchId,
|
||||
$dateRange,
|
||||
@@ -623,10 +724,10 @@ class ReportService
|
||||
/**
|
||||
* Получить количество пациентов из снапшотов
|
||||
*/
|
||||
private function getPatientsCountFromSnapshots(User $user, string $status, DateRange $dateRange): int
|
||||
private function getPatientsCountFromSnapshots(Department $department, string $status, DateRange $dateRange): int
|
||||
{
|
||||
$reports = $this->getReportsForDateRange(
|
||||
$user->rf_department_id,
|
||||
$department->department_id,
|
||||
$dateRange
|
||||
);
|
||||
|
||||
@@ -669,7 +770,13 @@ class ReportService
|
||||
/**
|
||||
* Получить количество пациентов из реплики БД
|
||||
*/
|
||||
private function getPatientsCountFromReplica(User $user, string $status, DateRange $dateRange, int $branchId): int
|
||||
private function getPatientsCountFromReplica(
|
||||
Department $department,
|
||||
User $user,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
int $branchId
|
||||
): int
|
||||
{
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
|
||||
@@ -680,7 +787,7 @@ class ReportService
|
||||
$branchId,
|
||||
$dateRange,
|
||||
),
|
||||
'observation' => ObservationPatient::where('rf_department_id', $user->rf_department_id)->count(),
|
||||
'observation' => ObservationPatient::where('rf_department_id', $department->department_id)->count(),
|
||||
'outcome' => $this->patientQueryService->getOutcomePatients(
|
||||
$branchId,
|
||||
$dateRange,
|
||||
@@ -708,11 +815,11 @@ class ReportService
|
||||
/**
|
||||
* Получить нежелательные события за дату
|
||||
*/
|
||||
private function getUnwantedEvents(User $user, DateRange $dateRange)
|
||||
public function getUnwantedEvents(Department $department, DateRange $dateRange)
|
||||
{
|
||||
return UnwantedEvent::whereHas('report', function ($query) use ($user, $dateRange) {
|
||||
$query->where('rf_department_id', $user->rf_department_id)
|
||||
->whereDate('created_at', $dateRange->endSql());
|
||||
return UnwantedEvent::whereHas('report', function ($query) use ($department, $dateRange) {
|
||||
$query->where('rf_department_id', $department->department_id)
|
||||
->whereBetween('sent_at', [$dateRange->startSql(), $dateRange->endSql()]);
|
||||
})
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
@@ -733,8 +840,12 @@ class ReportService
|
||||
return $dateRange->isEndDateToday() && !$reportToday;
|
||||
}
|
||||
|
||||
// Для заведующего/админа: если есть отчет и он заполнен текущим пользователем
|
||||
if ($reportToday && $reportToday->rf_lpudoctor_id === intval($fillableUserId)) {
|
||||
// Для заведующего/админа: если есть отчет & он заполнен текущим пользователем & диапазон дат = 1 дню
|
||||
if (
|
||||
$reportToday &&
|
||||
$reportToday->rf_lpudoctor_id === intval($fillableUserId) &&
|
||||
$dateRange->isOneDay
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -761,7 +872,7 @@ class ReportService
|
||||
/**
|
||||
* Получить отчеты за диапазон дат
|
||||
*/
|
||||
private function getReportsForDateRange(int $departmentId, DateRange $dateRange)
|
||||
public function getReportsForDateRange(int $departmentId, DateRange $dateRange)
|
||||
{
|
||||
if ($dateRange->isOneDay) {
|
||||
return Report::where('rf_department_id', $departmentId)
|
||||
|
||||
@@ -12,6 +12,16 @@ use Carbon\Carbon;
|
||||
|
||||
class StatisticsService
|
||||
{
|
||||
/**
|
||||
* Метрики, которые нужно суммировать (целые числа)
|
||||
*/
|
||||
protected array $summableMetrics = [4, 12, 11, 10, 13, 7, 9, 8, 17, 14, 16];
|
||||
|
||||
/**
|
||||
* Метрики, которые нужно усреднять (числа с плавающей точкой)
|
||||
*/
|
||||
protected array $averageMetrics = [18];
|
||||
|
||||
protected array $metricMapping = [
|
||||
4 => 'plan', // Плановые поступления
|
||||
12 => 'emergency', // Экстренные поступления
|
||||
@@ -21,13 +31,24 @@ class StatisticsService
|
||||
7 => 'outcome', // Выбыло
|
||||
9 => 'deceased', // Умерло
|
||||
8 => 'current', // Состоит
|
||||
17 => 'count_staff', // Кол-во мед. персонала
|
||||
14 => 'count_observable', // Кол-во пациентов на контроле
|
||||
16 => 'count_unwanted', // Кол-во нежелательных событий
|
||||
18 => 'average_bed_days' // Средний койко-день
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
protected BedDayService $bedDayService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Получить статистические данные с оптимизацией
|
||||
*/
|
||||
public function getStatisticsData(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||
{
|
||||
// Очищаем кэш памяти перед началом
|
||||
$this->bedDayService->clearMemoryCache();
|
||||
|
||||
// Определяем порог для использования оптимизированного метода
|
||||
$daysDiff = Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate));
|
||||
|
||||
@@ -45,6 +66,63 @@ class StatisticsService
|
||||
return $this->getDetailedStatistics($user, $startDate, $endDate, $isRangeOneDay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить средние койко-дни за период из сохраненных метрик отчетов
|
||||
*/
|
||||
protected function getAverageBedDaysFromReports(array $departmentIds, string $startDate, string $endDate): array
|
||||
{
|
||||
if (empty($departmentIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$results = DB::table('reports as r')
|
||||
->join('metrika_results as mr', 'r.report_id', '=', 'mr.rf_report_id')
|
||||
->whereIn('r.rf_department_id', $departmentIds)
|
||||
->where('mr.rf_metrika_item_id', 18)
|
||||
->whereBetween('r.created_at', [$startDate, $endDate])
|
||||
->select(
|
||||
'r.rf_department_id',
|
||||
DB::raw('AVG(CAST(mr.value AS DECIMAL)) as avg_value')
|
||||
)
|
||||
->groupBy('r.rf_department_id')
|
||||
->get()
|
||||
->keyBy('rf_department_id');
|
||||
|
||||
$averages = [];
|
||||
foreach ($departmentIds as $departmentId) {
|
||||
if (isset($results[$departmentId]) && $results[$departmentId]->avg_value !== null) {
|
||||
$averages[$departmentId] = round((float)$results[$departmentId]->avg_value, 1);
|
||||
} else {
|
||||
$averages[$departmentId] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $averages;
|
||||
} catch (\Exception $e) {
|
||||
\Log::error("Error in getAverageBedDaysFromReports: " . $e->getMessage());
|
||||
return array_fill_keys($departmentIds, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить общий средний койко-день
|
||||
*/
|
||||
protected function getOverallAverageBedDays(array $averages): float
|
||||
{
|
||||
$total = 0;
|
||||
$count = 0;
|
||||
|
||||
foreach ($averages as $avg) {
|
||||
if ($avg > 0) {
|
||||
$total += $avg;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count > 0 ? round($total / $count, 1) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Агрегированный метод для очень больших диапазонов (больше 30 дней)
|
||||
*/
|
||||
@@ -60,7 +138,7 @@ class StatisticsService
|
||||
// Загружаем все отделения
|
||||
$departments = Department::select('department_id', 'rf_department_type', 'name_short')
|
||||
->with(['departmentType'])
|
||||
->orderBy('name_short')
|
||||
->orderBy('rf_department_type')
|
||||
->get()
|
||||
->keyBy('department_id');
|
||||
|
||||
@@ -80,12 +158,24 @@ class StatisticsService
|
||||
$isRangeOneDay ? $dateReport : $dateReport[1]
|
||||
);
|
||||
|
||||
// Получаем средние койко-дни из метрик отчетов
|
||||
$averageBedDays = $this->getAverageBedDaysFromReports(
|
||||
$departments->pluck('department_id')->toArray(),
|
||||
$startDate,
|
||||
$endDate
|
||||
);
|
||||
|
||||
// Общий средний койко-день
|
||||
$overallAverageBedDays = $this->getOverallAverageBedDays($averageBedDays);
|
||||
|
||||
return $this->processAggregatedData(
|
||||
$departments,
|
||||
$defaultMetrics,
|
||||
$aggregatedData,
|
||||
$lastReportsData,
|
||||
$isRangeOneDay
|
||||
$isRangeOneDay,
|
||||
$averageBedDays,
|
||||
$overallAverageBedDays
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,25 +184,52 @@ class StatisticsService
|
||||
*/
|
||||
private function getAggregatedReportData(array $departmentIds, $dateReport, bool $isRangeOneDay): Collection
|
||||
{
|
||||
$query = DB::table('reports as r')
|
||||
// Для суммируемых метрик - SUM
|
||||
$summableQuery = DB::table('reports as r')
|
||||
->join('metrika_results as mr', 'r.report_id', '=', 'mr.rf_report_id')
|
||||
->select(
|
||||
'r.rf_department_id',
|
||||
'mr.rf_metrika_item_id',
|
||||
DB::raw('SUM(CAST(mr.value AS INTEGER)) as total')
|
||||
DB::raw('SUM(CAST(mr.value AS DECIMAL)) as total')
|
||||
)
|
||||
->whereIn('r.rf_department_id', $departmentIds);
|
||||
->whereIn('r.rf_department_id', $departmentIds)
|
||||
->whereIn('mr.rf_metrika_item_id', $this->summableMetrics);
|
||||
|
||||
if ($isRangeOneDay) {
|
||||
$query->whereDate('r.created_at', $dateReport);
|
||||
$summableQuery->whereDate('r.created_at', $dateReport);
|
||||
} else {
|
||||
$query->whereBetween('r.created_at', $dateReport);
|
||||
$summableQuery->whereBetween('r.created_at', $dateReport);
|
||||
}
|
||||
|
||||
return $query->whereIn('mr.rf_metrika_item_id', array_keys($this->metricMapping))
|
||||
$summableResults = $summableQuery
|
||||
->groupBy('r.rf_department_id', 'mr.rf_metrika_item_id')
|
||||
->get()
|
||||
->groupBy('rf_department_id');
|
||||
->get();
|
||||
|
||||
// Для усредняемых метрик - AVG (например, средний койко-день)
|
||||
$averageQuery = DB::table('reports as r')
|
||||
->join('metrika_results as mr', 'r.report_id', '=', 'mr.rf_report_id')
|
||||
->select(
|
||||
'r.rf_department_id',
|
||||
'mr.rf_metrika_item_id',
|
||||
DB::raw('AVG(CAST(mr.value AS DECIMAL)) as total')
|
||||
)
|
||||
->whereIn('r.rf_department_id', $departmentIds)
|
||||
->whereIn('mr.rf_metrika_item_id', $this->averageMetrics);
|
||||
|
||||
if ($isRangeOneDay) {
|
||||
$averageQuery->whereDate('r.created_at', $dateReport);
|
||||
} else {
|
||||
$averageQuery->whereBetween('r.created_at', $dateReport);
|
||||
}
|
||||
|
||||
$averageResults = $averageQuery
|
||||
->groupBy('r.rf_department_id', 'mr.rf_metrika_item_id')
|
||||
->get();
|
||||
|
||||
// Объединяем результаты
|
||||
$allResults = $summableResults->concat($averageResults);
|
||||
|
||||
return $allResults->groupBy('rf_department_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +262,9 @@ class StatisticsService
|
||||
Collection $defaultMetrics,
|
||||
Collection $aggregatedData,
|
||||
Collection $lastReportsData,
|
||||
bool $isRangeOneDay
|
||||
bool $isRangeOneDay,
|
||||
array $averageBedDays,
|
||||
float $overallAverageBedDays
|
||||
): array {
|
||||
$groupedData = [];
|
||||
$totalsByType = [];
|
||||
@@ -182,14 +301,21 @@ class StatisticsService
|
||||
$allCount = $counters['plan'] + $counters['emergency'];
|
||||
$percentLoadedBeds = $bedsCount > 0 ? round($currentCount * 100 / $bedsCount) : 0;
|
||||
|
||||
// Получаем средний койко-день для отделения
|
||||
$departmentAverageBedDays = $averageBedDays[$departmentId] ?? 0;
|
||||
|
||||
// Формируем данные
|
||||
$departmentData = $this->createDepartmentData(
|
||||
$department->name_short,
|
||||
$departmentId,
|
||||
$bedsCount,
|
||||
$allCount,
|
||||
$counters,
|
||||
$percentLoadedBeds,
|
||||
$departmentType
|
||||
$departmentType,
|
||||
null,
|
||||
$departmentAverageBedDays,
|
||||
$overallAverageBedDays
|
||||
);
|
||||
|
||||
$groupedData[$departmentType][] = $departmentData;
|
||||
@@ -214,7 +340,7 @@ class StatisticsService
|
||||
// Загружаем все отделения
|
||||
$departments = Department::select('department_id', 'rf_department_type', 'name_short')
|
||||
->with(['departmentType'])
|
||||
->orderBy('name_short')
|
||||
->orderBy('rf_department_type')
|
||||
->get()
|
||||
->keyBy('department_id');
|
||||
|
||||
@@ -228,13 +354,25 @@ class StatisticsService
|
||||
$reportIds = $reports->flatMap(fn($items) => $items->pluck('report_id'))->toArray();
|
||||
$reportMetrics = $this->getReportMetricsBatch($reportIds);
|
||||
|
||||
// Получаем средние койко-дни из метрик отчетов
|
||||
$averageBedDays = $this->getAverageBedDaysFromReports(
|
||||
$departments->pluck('department_id')->toArray(),
|
||||
$startDate,
|
||||
$endDate
|
||||
);
|
||||
|
||||
// Общий средний койко-день
|
||||
$overallAverageBedDays = $this->getOverallAverageBedDays($averageBedDays);
|
||||
|
||||
return $this->processOptimizedData(
|
||||
$departments,
|
||||
$defaultMetrics,
|
||||
$reports,
|
||||
$reportMetrics,
|
||||
$dateReport,
|
||||
$isRangeOneDay
|
||||
$isRangeOneDay,
|
||||
$averageBedDays,
|
||||
$overallAverageBedDays
|
||||
);
|
||||
}
|
||||
|
||||
@@ -279,12 +417,26 @@ class StatisticsService
|
||||
return collect();
|
||||
}
|
||||
|
||||
return DB::table('metrika_results')
|
||||
// Получаем все метрики, но для 18 не преобразуем в integer
|
||||
$results = DB::table('metrika_results')
|
||||
->whereIn('rf_report_id', $reportIds)
|
||||
->whereIn('rf_metrika_item_id', array_keys($this->metricMapping))
|
||||
->select('rf_report_id', 'rf_metrika_item_id', 'value')
|
||||
->get()
|
||||
->groupBy('rf_report_id');
|
||||
|
||||
// Преобразуем значения в зависимости от типа метрики
|
||||
foreach ($results as $reportId => $metrics) {
|
||||
foreach ($metrics as $metric) {
|
||||
if (in_array($metric->rf_metrika_item_id, $this->summableMetrics)) {
|
||||
$metric->value = (int)$metric->value;
|
||||
} else {
|
||||
$metric->value = (float)$metric->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +448,9 @@ class StatisticsService
|
||||
Collection $reports,
|
||||
Collection $reportMetrics,
|
||||
$dateReport,
|
||||
bool $isRangeOneDay
|
||||
bool $isRangeOneDay,
|
||||
array $averageBedDays,
|
||||
float $overallAverageBedDays
|
||||
): array {
|
||||
$groupedData = [];
|
||||
$totalsByType = [];
|
||||
@@ -362,14 +516,21 @@ class StatisticsService
|
||||
$allCount = $counters['plan'] + $counters['emergency'];
|
||||
$percentLoadedBeds = $bedsCount > 0 ? round($counters['current'] * 100 / $bedsCount) : 0;
|
||||
|
||||
// Получаем средний койко-день для отделения
|
||||
$departmentAverageBedDays = $averageBedDays[$departmentId] ?? 0;
|
||||
|
||||
// Формируем данные отделения
|
||||
$departmentData = $this->createDepartmentData(
|
||||
$department->name_short,
|
||||
$departmentId,
|
||||
$bedsCount,
|
||||
$allCount,
|
||||
$counters,
|
||||
$percentLoadedBeds,
|
||||
$departmentType
|
||||
$departmentType,
|
||||
null,
|
||||
$departmentAverageBedDays,
|
||||
$overallAverageBedDays
|
||||
);
|
||||
|
||||
$groupedData[$departmentType][] = $departmentData;
|
||||
@@ -403,9 +564,19 @@ class StatisticsService
|
||||
}
|
||||
$query->with('metrikaResults');
|
||||
}])
|
||||
->orderBy('name_short')
|
||||
->orderBy('rf_department_type')
|
||||
->get();
|
||||
|
||||
// Получаем средние койко-дни из метрик отчетов
|
||||
$averageBedDays = $this->getAverageBedDaysFromReports(
|
||||
$departments->pluck('department_id')->toArray(),
|
||||
$startDate,
|
||||
$endDate
|
||||
);
|
||||
|
||||
// Общий средний койко-день
|
||||
$overallAverageBedDays = $this->getOverallAverageBedDays($averageBedDays);
|
||||
|
||||
foreach ($departments as $department) {
|
||||
$departmentType = $department->departmentType->name_full;
|
||||
|
||||
@@ -458,14 +629,24 @@ class StatisticsService
|
||||
$allCount = $counters['plan'] + $counters['emergency'];
|
||||
$percentLoadedBeds = $bedsCount > 0 ? round($counters['current'] * 100 / $bedsCount) : 0;
|
||||
|
||||
if ($isRangeOneDay)
|
||||
$isReportToday = !empty($lastReport);
|
||||
else $isReportToday = null;
|
||||
|
||||
$departmentAverageBedDays = $averageBedDays[$department->department_id] ?? 0;
|
||||
|
||||
// Формируем данные отделения
|
||||
$departmentData = $this->createDepartmentData(
|
||||
$department->name_short,
|
||||
$department->department_id,
|
||||
$bedsCount,
|
||||
$allCount,
|
||||
$counters,
|
||||
$percentLoadedBeds,
|
||||
$departmentType
|
||||
$departmentType,
|
||||
$isReportToday,
|
||||
$departmentAverageBedDays,
|
||||
$overallAverageBedDays
|
||||
);
|
||||
|
||||
$groupedData[$departmentType][] = $departmentData;
|
||||
@@ -480,14 +661,19 @@ class StatisticsService
|
||||
*/
|
||||
private function createDepartmentData(
|
||||
string $name,
|
||||
int $departmentId,
|
||||
int $beds,
|
||||
int $allCount,
|
||||
array $counters,
|
||||
int $percentLoadedBeds,
|
||||
string $type
|
||||
string $type,
|
||||
?bool $isReportToday = null,
|
||||
float $departmentAverageBedDays = 0,
|
||||
float $overallAverageBedDays = 0
|
||||
): array {
|
||||
return [
|
||||
'department' => $name,
|
||||
'department_id' => $departmentId,
|
||||
'beds' => $beds,
|
||||
'recipients' => [
|
||||
'all' => $allCount,
|
||||
@@ -503,8 +689,14 @@ class StatisticsService
|
||||
'emergency' => $counters['emergency_surgical']
|
||||
],
|
||||
'deceased' => $counters['deceased'],
|
||||
'countStaff' => $counters['count_staff'],
|
||||
'countObservable' => $counters['count_observable'],
|
||||
'countUnwanted' => $counters['count_unwanted'],
|
||||
'averageBedDays' => $departmentAverageBedDays,
|
||||
'overallAverageBedDays' => $overallAverageBedDays,
|
||||
'type' => $type,
|
||||
'isDepartment' => true
|
||||
'isDepartment' => true,
|
||||
'isReportToday' => $isReportToday,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -527,6 +719,11 @@ class StatisticsService
|
||||
'deceased_sum' => 0,
|
||||
'percentLoadedBeds_total' => 0,
|
||||
'percentLoadedBeds_count' => 0,
|
||||
'staff_sum' => 0,
|
||||
'observable_sum' => 0,
|
||||
'unwanted_sum' => 0,
|
||||
'average_bed_days_total' => 0,
|
||||
'average_bed_days_count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -548,6 +745,11 @@ class StatisticsService
|
||||
$totals['deceased_sum'] += $departmentData['deceased'];
|
||||
$totals['percentLoadedBeds_total'] += $departmentData['percentLoadedBeds'];
|
||||
$totals['percentLoadedBeds_count']++;
|
||||
$totals['staff_sum'] += $departmentData['countStaff'];
|
||||
$totals['observable_sum'] += $departmentData['countObservable'];
|
||||
$totals['unwanted_sum'] += $departmentData['countUnwanted'];
|
||||
$totals['average_bed_days_total'] += $departmentData['averageBedDays'];
|
||||
$totals['average_bed_days_count']++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -563,7 +765,7 @@ class StatisticsService
|
||||
$finalData[] = [
|
||||
'isGroupHeader' => true,
|
||||
'groupName' => $type,
|
||||
'colspan' => 12,
|
||||
'colspan' => 14,
|
||||
'type' => $type
|
||||
];
|
||||
|
||||
@@ -592,7 +794,11 @@ class StatisticsService
|
||||
// ? round($grandTotals['percentLoadedBeds_total'] / $grandTotals['percentLoadedBeds_count'])
|
||||
// : 0;
|
||||
//
|
||||
// $finalData[] = $this->createTotalRow('all', $grandTotals, $avgPercent, true);
|
||||
// $grandAvgBedDays = $grandTotals['averageBedDays_count'] > 0
|
||||
// ? round($grandTotals['averageBedDays_total'] / $grandTotals['averageBedDays_count'], 1)
|
||||
// : $overallAverageBedDays;
|
||||
//
|
||||
// $finalData[] = $this->createTotalRow('all', $grandTotals, $avgPercent, true, $grandAvgBedDays);
|
||||
// }
|
||||
|
||||
return [
|
||||
@@ -605,7 +811,7 @@ class StatisticsService
|
||||
/**
|
||||
* Создать строку итогов
|
||||
*/
|
||||
private function createTotalRow(string $type, array $total, int $avgPercent, bool $isGrandTotal): array
|
||||
private function createTotalRow(string $type, array $total, int $avgPercent, bool $isGrandTotal, float $avgBedDays = 0): array
|
||||
{
|
||||
$row = [
|
||||
'isTotalRow' => !$isGrandTotal,
|
||||
@@ -628,8 +834,12 @@ class StatisticsService
|
||||
'emergency' => $total['emergency_surgical_sum']
|
||||
],
|
||||
'deceased' => $total['deceased_sum'],
|
||||
'averageBedDays' => '—',
|
||||
'type' => $type,
|
||||
'departments_count' => $total['departments_count'],
|
||||
'countStaff' => $total['staff_sum'],
|
||||
'countObservable' => $total['observable_sum'],
|
||||
'countUnwanted' => $total['unwanted_sum'],
|
||||
'isBold' => true
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user