client = new CachingHttpClient($client, $store); } /** * @Route("/anonymous-reserve", methods={"POST"}) */ public function anonymousReserve(Request $request): Response { try { $timezone = Region::getTimezone(); if (!empty($request->request->get('timezone'))) { $timezone = (int) $request->request->get('timezone'); } $reserve = [ 'date' => date('Ymd', strtotime($request->request->get('workDate'))), 'st' => explode('-', $request->request->get('time'))[0], 'en' => explode('-', $request->request->get('time'))[1], 'services' => [], 'filial' => (int) $request->request->get('filial'), 'timezone' => $timezone, 'schedident' => (int) $request->request->get('schedident'), 'rnum' => $request->request->get('rnum') === 'undefined' ? null : $request->request->get('rnum'), 'dcode' => (int) $request->request->get('specialist') ]; $requestData = [ 'accept' => 'true', 'fio' => $request->request->get('fio'), 'captcha' => $request->request->get('captcha'), 'email' => $request->request->get('email'), 'phone' => $request->request->get('phone'), 'reserve' => json_encode($reserve, JSON_UNESCAPED_SLASHES) ]; $referer = $request->headers->get('referer'); $response = $this->client->request('POST', '/api/reservation/anonymous-reserve', [ 'verify_peer' => false, 'verify_host' => false, 'base_uri' => $_ENV['MIS'], 'headers' => [ 'Referer' => $referer, 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0', 'Accept-Language' => 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3', 'Accept' => 'application/json, text/javascript, */*; q=0.01', 'Content-Type' => 'application/json; charset=UTF-8', 'X-Requested-With' => 'XMLHttpRequest', 'X-Integration-Type' => 'WEBSDK' ], 'body' => json_encode($requestData) ]); // Проверяем статус ответа $statusCode = $response->getStatusCode(); if ($statusCode !== 200) { throw new \Exception("External API returned status: {$statusCode}"); } $intervals = $response->toArray(); // Сохраняем запись $entityManager = $this->getDoctrine()->getManager(); $record = new Record(); $record ->setSpecialistId((int) $request->request->get('specialist')) ->setPhone($request->request->get('phone')) ->setHash($request->request->get('phone')) ->setReserve($reserve) ->setCreateAt(new \DateTime('NOW')); $entityManager->persist($record); $entityManager->flush(); return $this->json([ 'success' => true, 'data' => [ 'intervals' => $intervals, 'hash' => md5($request->request->get('phone')), 'phone' => $request->request->get('phone'), 'recordId' => $record->getId(), ] ]); } catch (\Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface $e) { // Ошибка 4xx return $this->json([ 'success' => false, 'error' => 'Client error: ' . $e->getMessage() ], 400); } catch (\Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface $e) { // Ошибка 5xx return $this->json([ 'success' => false, 'error' => 'Server error: ' . $e->getMessage() ], 502); } catch (\Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface $e) { // Ошибки сети return $this->json([ 'success' => false, 'error' => 'Network error: ' . $e->getMessage() ], 503); } catch (\Exception $e) { // Другие ошибки return $this->json([ 'success' => false, 'error' => 'Internal error: ' . $e->getMessage() ], 500); } } /** * @OA\Get( * tags= {"Расписание врача"}, * path="/interval", * summary="Получение сетки расписания", * @OA\Parameter( * name="startInterval", * description="Начальная дата (Y-m-d)", * in="query", * required=true, * @OA\Schema( * type="string", * format="Y-m-d" * * ) * ), * @OA\Parameter( * name="endInterval", * description="Конечна дата (Y-m-d)", * in="query", * required=true, * @OA\Schema( * type="string", * format="Y-m-d" * * ) * ), * @OA\Parameter( * name="department", * description="ID отделения", * in="query", * required=true, * @OA\Schema( * type="string" * ) * ), * @OA\Parameter( * name="doctor", * description="ID врача", * in="query", * required=true, * @OA\Schema( * type="string" * ) * ), * @OA\Parameter( * name="filial", * description="ID филиала", * in="query", * required=true, * @OA\Schema( * type="string" * ) * ), * @OA\Response( * response=200, * description="json response" * ) * ) * * @Route("/interval", methods={"GET"}) */ public function interval(Request $request): Response { $dateFormat = $request->query->get('dateFormat'); if (empty($dateFormat)) { $dateFormat = 'Y-m-d'; } $startInterval = $request->query->get('startInterval'); $endInterval = $request->query->get('endInterval'); $doctor = $request->query->get('doctor'); $department = $request->query->get('department'); $filial = $request->query->get('filial'); $onlineMode = OnlineMode::isOnline($request->query->get('onlineMode')); $isFree = true; $nearestDate = NULL; if (empty($doctor) || empty($startInterval) || empty($endInterval) || empty($department) || empty($filial)) { throw new BadRequestHttpException('Bad request'); } $schedules = $this->getSchedule($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval); $intervals = $this->getInterval($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval); $findInterval = function ($schedident, $workDate) use($intervals, $onlineMode, $isFree, $nearestDate, $dateFormat) { $intervalsData = []; if (!empty($intervals)) { foreach ($intervals[date('Ymd', strtotime($workDate))] as $key => $interval) { if ($interval['schedident'] == $schedident) { $intervalsData[$key]['time'] = $interval['time']; $intervalsData[$key]['rNum'] = isset($interval['rNum'])? $interval['rNum']: null; $intervalsData[$key]['startTime'] = explode('-', $interval['time'])[0]; $intervalsData[$key]['endTime'] = explode('-', $interval['time'])[1]; $intervalsData[$key]['schedident'] = $interval['schedident']; $intervalsData[$key]['isFree'] = $interval['isFree']; $intervalsData[$key]['onlineMode'] = $onlineMode; $intervalsData[$key]['workDate'] = $interval['workDate']->format($dateFormat); if ($interval['isFree'] && $isFree && is_null($nearestDate)) { $nearestDate = $interval['workDate']->format('Y-m-d'); $intervalsData[$key]['nearestDate'] = $interval['workDate']->format($dateFormat); $isFree = false; } } } } return $intervalsData; }; $dataResponse = []; $i = 0; if (isset($schedules['success'])) { if ($schedules['success'] == true) { $uniqueIntervals = []; foreach ($schedules['data'] as $key => $data) { uasort($data['intervals'], function ($a, $b) { if ($a['workDate'] == $b['workDate']) { return $a['startInterval'] <=> $b['startInterval']; } return 0; }); foreach($data['intervals'] as $interval) { if ($interval['isFree'] === true) { $workDate = date($dateFormat, strtotime($interval['workDate'])); $uniqueKey = $interval['schedident'] . '-' . $workDate . '-' . $interval['startInterval'] . '-' . $interval['endInterval']; if (!isset($uniqueIntervals[$uniqueKey])) { $dataIntervals = $findInterval($interval['schedident'], $workDate); if ($dataIntervals) { $uniqueIntervals[$uniqueKey] = [ 'workDate' => $workDate, 'isFree' => $interval['isFree'], 'startInterval' => $interval['startInterval'], 'endInterval' => $interval['endInterval'], 'intervals' => $dataIntervals ]; } } } } } $dataResponse = array_values($uniqueIntervals); } } $uid = false; if (! is_null($this->getUser())) { $uid = $this->getUser()->getUid(); } return $this->json(['data' => ['userInfo' => $uid, 'intervalsData' => $dataResponse]]); } private function getInterval($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval) { $response = $this->client->request('GET', '/api/reservation/intervals', [ 'verify_peer' => false, 'verify_host' => false, 'base_uri' => $_ENV['MIS'], 'headers' => [ 'Content-Type' => 'application/json', 'User-Agent' => 'sovamed_bot' ], 'query' => [ 'dcode' => $doctor, 'spec' => $department, 'onlineMode' => ($onlineMode ? 1 : 0), 'st' => \date("Ymd", strtotime($startInterval)), 'en' => \date("Ymd", strtotime($endInterval)), 'filialId' => $filial, 'inFilials' => $filial ] ]); $intervals = $response->toArray(); $dataResponse = []; if (isset($intervals['data'])) { foreach ($intervals['data'] as $data) { if (isset($data['workdates'])) { foreach ($data['workdates'] as $key => $workdates) { foreach ($workdates as $workdate => $item) { $workDate = \DateTime::createFromFormat( 'Ymd', $workdate ); $intervalKey = 0; for ($i=0; $i < count($item); $i++) { foreach ($item[$i]['intervals'] as $intervaldata) { $dataResponse[$workdate][$intervalKey]['workDate'] = $workDate; $dataResponse[$workdate][$intervalKey]['schedident'] = $item[$i]['schedident']; $dataResponse[$workdate][$intervalKey]['time'] = $intervaldata['time']; $dataResponse[$workdate][$intervalKey]['isFree'] = $intervaldata['isFree']; $dataResponse[$workdate][$intervalKey]['rNum'] = isset($item[$i]['rnum'])? $item[$i]['rnum']: null; $intervalKey++; } } } } } } } return $dataResponse; } private function getSchedule($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval) { $response = $this->client->request('GET', '/api/reservation/schedule', [ 'verify_peer' => false, 'verify_host' => false, 'base_uri' => $_ENV['MIS'], 'headers' => [ 'Content-Type' => 'application/json', 'User-Agent' => 'sovamed_bot' ], 'query' => [ 'doctor' => $doctor, 'department' => $department, 'onlineMode' => ($onlineMode ? 1 : 0), 'st' => date("Ymd", strtotime($startInterval)), 'en' => date("Ymd", strtotime($endInterval)), 'filialId' => $filial ] ]); return $response->toArray(); } /** * @Route("/userInfo", methods={"GET"}) */ public function user(): Response { $uid = false; if (! is_null($this->getUser())) { $uid = $this->getUser()->getUid(); } return $this->json(['data' => $uid]); } /** * @OA\Get( * tags= {"Услуги и цены"}, * path="/pricelist/departments", * summary="Получение списка отделений", * @OA\Response( * response=200, * description="json response" * ) * ) * * @Route("/pricelist/departments", methods={"GET"}) */ public function pricelistDepartments(Request $request): Response { $response = []; $entityManager = $this->getDoctrine()->getManager(); $departments = $entityManager->getRepository(PriceDepartment::class) ->findAll(); if ($departments) { foreach ($departments as $key => $item) { $item = $item->toArray(); unset($item['__initializer__']); unset($item['__isInitialized__']); unset($item['__cloner__']); unset($item['id']); $response[$key] = $item; } } return $this->json(['data' => $response]); } /** * @OA\Get( * tags= {"Услуги и цены"}, * path="/pricelist", * summary="Получение списка услуг и цен", * @OA\Parameter( * name="depnum", * description="ID отделения", * in="query", * required=true, * @OA\Schema( * type="string" * ) * ), * @OA\Parameter( * name="filial", * description="ID филиала", * in="query", * required=false, * @OA\Schema( * type="string" * ) * ), * @OA\Parameter( * name="active", * description="Только активные", * in="query", * required=false, * @OA\Schema( * type="boolean" * ) * ), * @OA\Response( * response=200, * description="json response" * ) * ) * * @Route("/pricelist", methods={"GET"}) */ public function pricelist(Request $request, PaginatorInterface $paginator, PriceListService $priceListService): JsonResponse { $params = [ 'kodoper' => $request->query->get('kodoper'), 'groupId' => $request->query->get('depnum'), 'filial' => $request->query->get('filial'), 'actual' => $request->query->get('active') ]; $priceListQuery = $priceListService->getPriceListQuery($params); $pagination = $paginator->paginate( $priceListQuery->getQuery(), $request->query->getInt('page', 1), 1000 ); $totalItems = $pagination->getTotalItemCount(); // Общее количество элементов $itemCount = $pagination->count(); // Количество элементов на текущей странице $currentPage = $pagination->getCurrentPageNumber(); // Текущая страница $totalPages = ceil($totalItems / 1000); // Общее количество страниц return $this->json([ 'items' => $pagination, 'totalItems' => $totalItems, 'totalPages' => $totalPages, 'currentPage' => $currentPage, 'itemCount' => $itemCount ]); } private function getSpecialistResponse($specialist) { $response = []; if ($specialist) { $response = $specialist->toArray(); unset($response['pecialistMore']); $response['img'] = 'https://api.sovamed.ru/specialist/picture/' . $specialist->getId(); if (!empty($response['kinder'])) { $response['kinder'] = $response['kinder'] . ' ' . $this->textYear($response['kinder'], false); } if (!empty($response['experience'])) { $response['experience'] = $response['experience'] . ' ' . $this->textYear($response['experience'], true); } $specialistMore = $specialist->getSpecialistMore(); if ($defaultLocation = $specialistMore->defaultLocation()) { $response['nearestDate'] = $defaultLocation['nearestDate']; $response['filial'] = [ 'id' => $defaultLocation['filial'], 'address' => $defaultLocation['address'], ]; $response['department'] = [ 'id' => $defaultLocation['department'], 'name' => $defaultLocation['name'], ]; } $response['reviews'] = $specialistMore->getReviews(); $response['prices'] = $specialistMore->getPrices(); } return $response; } private function textYear($year, $exp = true) { $t1 = 0; $t2 = 0; $year = abs($year); $t1 = $year % 10; $t2 = $year % 100; if ($exp) { return ($t1 == 1 && $t2 != 11 ? "год" : ($t1 >= 2 && $t1 <= 4 && ($t2 < 10 || $t2 >= 20) ? "года" : "лет")); } else { return ($t1 == 1 ? "года" : "лет"); } } /** * @OA\Get( * tags= {"Врачи"}, * path="/doctor", * summary="Получение данных о враче", * @OA\Parameter( * name="sid", * description="ID врача", * in="query", * required=true, * @OA\Schema( * type="string" * ) * ), * @OA\Parameter( * name="reviews", * description="Показывать отзывы", * in="query", * required=false, * @OA\Schema( * type="boolean", * default=false * ) * ), * @OA\Response( * response=200, * description="json response" * ) * ) * * @Route("/doctor", methods={"GET"}) */ public function doctor(SpecialistService $specialistService, Request $request): Response { if (empty($request->query->getInt('sid'))) { return $this->json(['data' => false]); } $specialist = $specialistService->show([ 'dcode' => $request->query->get('sid') ]); return $this->json(['data' => $this->getSpecialistResponse($specialist)]); } /** * @OA\Get( * tags= {"Врачи"}, * path="/doctors/{region}", * summary="Получение данных врачей по регионам", * @OA\Parameter( * name="region", * description="Название города", * in="path", * required=true, * @OA\Schema( * type="string", * default="saratov" * ) * ), * @OA\Parameter( * name="reviews", * description="Показывать отзывы", * in="query", * required=false, * @OA\Schema( * type="boolean", * default=true * ) * ), * @OA\Response( * response=200, * description="json response" * ) * ) * * @Route("/doctors/{region}", methods={"GET"}) */ public function index(SpecialistService $specialistService, Request $request, $region = 'saratov'): Response { $regionId = match($region) { 'krasnodar' => 94, 'voronej' => 93, 'volgograd' => 92, default => 91 }; $pagination = $specialistService->listPaginated( ['regionId' => $regionId], $request->query->getInt('page', 1), 500 ); $totalItems = $pagination->getTotalItemCount(); // Общее количество элементов $itemCount = $pagination->count(); // Количество элементов на текущей странице $currentPage = $pagination->getCurrentPageNumber(); // Текущая страница $totalPages = ceil($totalItems / 1000); // Общее количество страниц $response = []; foreach ($pagination as $key => $specialist) { $response[$key] = $this->getSpecialistResponse($specialist); } return $this->json([ 'data' => $response, 'totalItems' => $totalItems, 'totalPages' => $totalPages, 'currentPage' => $currentPage, 'itemCount' => $itemCount ]); } }