dateService = $dateRangeService; } public function index(Request $request) { $user = $request->user(); $queryStartDate = $request->query('startAt'); $queryEndDate = $request->query('endAt'); [$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $queryStartDate, $queryEndDate); $isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate); // Если диапазон содержит сутки if ($isRangeOneDay) { // Устанавливаем дату отчета, как последний день из выборки $dateReport = $endDate; } else { // Устанавливаем дату отчета, как выборку $dateReport = [$startDate, $endDate]; } $groupedData = []; $departments = Department::select('department_id', 'rf_department_type', 'name_short') ->with(['reports']) ->orderBy('name_short')->get(); foreach ($departments as $department) { $departmentType = $department->departmentType->name_full; if (!isset($groupedData[$departmentType])) { $groupedData[$departmentType] = []; } if ($isRangeOneDay) { // Статистика выводится с нарастающим числом $reports = $department->reports() ->whereDate('created_at', $dateReport) ->get(); } else { $reports = $department->reports() ->whereBetween('created_at', $dateReport)->get(); } // Метрики зависищие от отчетов $allCount = 0; $outcomeCount = 0; $currentCount = 0; $occupiedBeds = 0; $planCount = 0; $emergencyCount = 0; $planSurgical = 0; $emergencySurgical = 0; $transferredCount = 0; $deceasedCount = 0; foreach ($reports as $report) { $allCount += $this->getMetrikaResultFromReport($report, 3, $isRangeOneDay); $currentCount += $this->getMetrikaResultFromReport($report, 8, false); $occupiedBeds += $this->getMetrikaResultFromReport($report, 8, $isRangeOneDay); $planCount += $this->getMetrikaResultFromReport($report, 4, $isRangeOneDay); $emergencyCount += $this->getMetrikaResultFromReport($report, 12, $isRangeOneDay); $planSurgical += $this->getMetrikaResultFromReport($report, 11, $isRangeOneDay); $emergencySurgical += $this->getMetrikaResultFromReport($report, 10, $isRangeOneDay); $transferredCount += $this->getMetrikaResultFromReport($report, 13, $isRangeOneDay); $outcomeCount += $this->getMetrikaResultFromReport($report, 7, $isRangeOneDay); $deceasedCount += $this->getMetrikaResultFromReport($report, 9, $isRangeOneDay); } // Независимые метрики (установки по умолчанию и т.п.) $bedsCount = $department->metrikaDefault() ->where('rf_metrika_item_id', 1)->value('value'); $percentLoadedBeds = $bedsCount > 0 ? round($occupiedBeds * 100 / $bedsCount) : 0; $groupedData[$departmentType][] = [ 'department' => $department->name_short, 'beds' => $bedsCount, 'recipients' => [ 'all' => $allCount, 'plan' => $planCount, 'emergency' => $emergencyCount, 'transferred' => $transferredCount, ], 'outcome' => $outcomeCount, 'consist' => $currentCount, 'percentLoadedBeds' => $percentLoadedBeds, 'surgical' => [ 'plan' => $planSurgical, 'emergency' => $emergencySurgical ], 'deceased' => $deceasedCount, 'type' => $departmentType ]; } // Преобразуем группированные данные в плоский массив с заголовками групп $finalData = []; foreach ($groupedData as $type => $departmentsInType) { // Добавляем строку-заголовок группы $finalData[] = [ 'isGroupHeader' => true, 'groupName' => $type, 'colspan' => 12, // Количество колонок в таблице 'type' => $type ]; // Добавляем отделения этой группы foreach ($departmentsInType as $department) { $finalData[] = $department; } } $isHeadOrAdmin = $user->isAdmin() || $user->isHeadOfDepartment(); $date = $isHeadOrAdmin ? [ $this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(), $this->dateService->parseDate($endDate)->getTimestampMs() ] : $this->dateService->parseDate($endDate)->getTimestampMs(); return Inertia::render('Statistic/Index', [ 'data' => $finalData, 'isHeadOrAdmin' => $isHeadOrAdmin, 'date' => $date, 'isOneDay' => $isRangeOneDay ]); } private function getMetrikaResultFromReport(Report $report, int $metrikaItem, bool $sum = true) { if ($sum) { return (int) ($report->metrikaResults() ->where('rf_metrika_item_id', $metrikaItem) ->sum(DB::raw('CAST(value AS INTEGER)')) ?: 0); } return (int) ($report->metrikaResults() ->where('rf_metrika_item_id', $metrikaItem) ->value('value') ?: 0); } public function indexOld(Request $request) { $user = Auth::user(); $validator = Validator::make($request->all(), [ 'sent_at' => 'required|string' ]); $groupId = (int)$request->query('groupId'); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors() ], 422); } $timestamps = explode(',', $request->sent_at); $startAt = intval($timestamps[0] / 1000); $endAt = intval($timestamps[1] / 1000); // Проверяем период (максимум 1 год) $daysDiff = ($endAt - $startAt) / (60 * 60 * 24); if ($daysDiff > 365) { return response()->json([ 'success' => false, 'message' => 'Период не может превышать 1 год' ], 400); } $dateStart = date('Y-m-d', $startAt); $dateEnd = date('Y-m-d', $endAt); $group = MetrikaGroup::findOrFail($groupId); // Оптимизированный агрегированный запрос $aggregatedData = DB::table('metrika_results as mr') ->join('metrika_result_values as mv', 'mr.metrika_result_id', '=', 'mv.rf_metrika_result_id') ->join('reports as r', 'mr.rf_report_id', '=', 'r.report_id') ->where('mr.rf_metrika_group_id', $groupId) ->whereBetween('r.sent_at', [$dateStart, $dateEnd]) ->when(!$user->isAdmin() && !$user->isHeadOfDepartment(), function ($query) use ($user) { return $query->where('r.rf_user_id', $user->id); }) ->select([ 'mv.rf_metrika_item_id', DB::raw('SUM(CAST(mv.value AS DECIMAL(10,2))) as total_sum'), DB::raw('COUNT(DISTINCT r.report_id) as reports_count'), DB::raw('AVG(CAST(mv.value AS DECIMAL(10,2))) as avg_value') ]) ->groupBy('mv.rf_metrika_item_id') ->get() ->keyBy('rf_metrika_item_id'); if ($aggregatedData->isEmpty()) { return response()->json([ 'success' => false, 'message' => 'Данные за указанный период не найдены' ], 404); } // Получаем названия метрик одним запросом $itemIds = $aggregatedData->pluck('rf_metrika_item_id')->toArray(); $items = MetrikaItem::whereIn('metrika_item_id', $itemIds) ->pluck('name', 'metrika_item_id'); // Формируем ответ $formValues = []; foreach ($aggregatedData as $itemId => $data) { $formValues["metrika_item_{$itemId}"] = [ 'sum' => (float) $data->total_sum, 'average' => (float) $data->avg_value, 'reports_count' => $data->reports_count, 'item_name' => $items[$itemId] ?? 'Неизвестный показатель' ]; } // Получаем структуру формы $formData = MetrikaForm::getFormData($groupId); return Inertia::render('Statistic/Index', [ 'is_view_only' => true, 'period' => [ 'start' => $dateStart, 'end' => $dateEnd, 'days' => $daysDiff + 1 ], 'group' => [ 'id' => $group->metrika_group_id, 'name' => $group->name, 'description' => $group->description, ], 'metrics' => [ 'total_items' => count($formValues), 'total_reports' => $aggregatedData->first()->reports_count ?? 0, 'values' => $formValues, 'aggregation' => 'sum_and_average' ], 'form' => [ 'fields' => $formData, 'sections' => $this->groupFieldsBySection($formData) ] ]); } private function groupFieldsBySection($fields) { $sections = []; foreach ($fields as $field) { $section = $field['section'] ?? 'general'; if (!isset($sections[$section])) { $sections[$section] = [ 'name' => $this->getSectionName($section), 'fields' => [] ]; } $sections[$section]['fields'][] = $field; } return array_values($sections); } private function getSectionName($section) { $names = [ 'general' => 'Основные показатели', 'admissions' => 'Поступления', 'discharges' => 'Выписки', 'additional' => 'Дополнительная информация' ]; return $names[$section] ?? ucfirst($section); } }