Переписал контроллер для KPI

This commit is contained in:
brusnitsyn
2026-06-17 17:39:15 +09:00
parent 839a534bb2
commit 698422e0ba

View File

@@ -266,10 +266,22 @@ class StatisticsService
} }
} }
$grandTotals = $this->calculateGrandTotals($totalsByType);
$bedsTotal = (int) collect($beds)->sum('value');
// Посуточные ряды для фон-спарклайнов KPI-карточек
$grandTotals['sparklines'] = $this->calculateDailySparklines(
$allDeptIds,
$startDate,
$endDate,
$bedsTotal
);
// KPI за предыдущий аналогичный период (для бейджей тренда)
$grandTotals['previous'] = $this->calculatePreviousKpis($allDeptIds, $startDate, $endDate, $bedsTotal);
return [ return [
'data' => $this->buildFinalData($groupedData, $totalsByType), 'data' => $this->buildFinalData($groupedData, $totalsByType),
'totalsByType' => $totalsByType, 'totalsByType' => $totalsByType,
'grandTotals' => $this->calculateGrandTotals($totalsByType), 'grandTotals' => $grandTotals,
'recipientPlanOfYear' => [ 'recipientPlanOfYear' => [
'plan' => $grandRecipientPlan, // Сумма планов по периоду 'plan' => $grandRecipientPlan, // Сумма планов по периоду
'progress' => $grandProgressPlan, // Сумма фактов по периоду 'progress' => $grandProgressPlan, // Сумма фактов по периоду
@@ -277,6 +289,123 @@ class StatisticsService
]; ];
} }
/**
* Посуточные ряды по каждому KPI за период для фоновых спарклайнов.
* Источник суточные дежурные отчёты (report_duties + duty_report_metric_results).
*/
private function calculateDailySparklines(array $deptIds, string $startDate, string $endDate, int $bedsTotal): array
{
if (empty($deptIds)) {
return ['days' => [], 'consist' => [], 'admissions' => [], 'outcome' => [], 'operations' => [], 'deceased' => [], 'load' => []];
}
$rows = DB::table('report_duties as r')
->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id')
->whereIn('r.rf_department_id', $deptIds)
->where('r.period_end', '>=', $startDate)
->where('r.period_end', '<=', $endDate)
->whereIn('mr.rf_metrika_item_id', [4, 7, 8, 9, 10, 11, 12])
->select(
DB::raw('DATE(r.period_end) as day'),
'mr.rf_metrika_item_id as metric',
DB::raw('SUM(CAST(mr.value AS DECIMAL)) as total')
)
->groupBy(DB::raw('DATE(r.period_end)'), 'mr.rf_metrika_item_id')
->orderBy('day')
->get();
// day => [metric_id => total]
$byDay = [];
foreach ($rows as $row) {
$byDay[(string) $row->day][(int) $row->metric] = (float) $row->total;
}
ksort($byDay);
$days = array_keys($byDay);
$consist = $admissions = $outcome = $operations = $deceased = $load = [];
foreach ($byDay as $m) {
$c = (int) ($m[8] ?? 0);
$consist[] = $c;
$admissions[] = (int) (($m[4] ?? 0) + ($m[12] ?? 0));
$outcome[] = (int) ($m[7] ?? 0);
$operations[] = (int) (($m[11] ?? 0) + ($m[10] ?? 0));
$deceased[] = (int) ($m[9] ?? 0);
$load[] = $bedsTotal > 0 ? (int) round($c / $bedsTotal * 100) : 0;
}
return compact('days', 'consist', 'admissions', 'outcome', 'operations', 'deceased', 'load');
}
/**
* KPI за предыдущий аналогичный период: столько же, вплотную перед выбранным.
* Сдвигаем оба конца назад на (длина периода + 1 день) так предыдущий идёт встык,
* без нахлёста (для одних суток сравнение с предыдущими сутками).
*/
private function calculatePreviousKpis(array $deptIds, string $startDate, string $endDate, int $bedsTotal): array
{
$start = Carbon::parse($startDate);
$end = Carbon::parse($endDate);
$shiftSeconds = $start->diffInSeconds($end) + 86400;
$prevStart = $start->copy()->subSeconds($shiftSeconds);
$prevEnd = $end->copy()->subSeconds($shiftSeconds);
return $this->kpiTotalsForRange(
$deptIds,
$prevStart->format('Y-m-d H:i:s'),
$prevEnd->format('Y-m-d H:i:s'),
$bedsTotal
);
}
/**
* Сводные KPI за произвольный диапазон (суммы метрик + состав на конец периода).
*/
private function kpiTotalsForRange(array $deptIds, string $start, string $end, int $bedsTotal): array
{
$empty = ['consist' => 0, 'admissions' => 0, 'outcome' => 0, 'operations' => 0, 'deceased' => 0, 'load' => 0];
if (empty($deptIds)) {
return $empty;
}
// Суммы метрик за период
$sums = DB::table('report_duties as r')
->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id')
->whereIn('r.rf_department_id', $deptIds)
->where('r.period_start', '>=', $start)
->where('r.period_end', '<=', $end)
->whereIn('mr.rf_metrika_item_id', [4, 7, 9, 10, 11, 12])
->select('mr.rf_metrika_item_id as metric', DB::raw('SUM(CAST(mr.value AS DECIMAL)) as total'))
->groupBy('mr.rf_metrika_item_id')
->get()
->keyBy(fn ($r) => (int) $r->metric);
$g = fn ($id) => (int) ($sums[$id]->total ?? 0);
// Состав на конец периода: последний отчёт по каждому отделению ≤ end, суммируем
$consistRows = DB::table('report_duties as r')
->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id')
->whereIn('r.rf_department_id', $deptIds)
->where('mr.rf_metrika_item_id', 8)
->where('r.period_end', '<=', $end)
->select('r.rf_department_id', 'mr.value')
->orderBy('r.rf_department_id')
->orderBy('r.period_end', 'desc')
->distinct('r.rf_department_id')
->get();
$consist = (int) $consistRows->sum(fn ($x) => (float) $x->value);
return [
'consist' => $consist,
'admissions' => $g(4) + $g(12),
'outcome' => $g(7),
'operations' => $g(11) + $g(10),
'deceased' => $g(9),
'load' => $bedsTotal > 0 ? (int) round($consist / $bedsTotal * 100) : 0,
];
}
/** /**
* Инициализация итогов по типу * Инициализация итогов по типу
*/ */