<?php
namespace Slivki\Controller\MobileApi;
use Slivki\Controller\SiteController;
use Slivki\Dao\Statistic\OfferMonthlyPurchaseCountDaoInterface;
use Slivki\Dao\TireFilter\TireFilterDaoInterface;
use Slivki\Dto\Filter\PaginatorFilterDto;
use Slivki\Dto\Filter\PriceFilterDto;
use Slivki\Dto\Filter\SortFilterDto;
use Slivki\Entity\City;
use Slivki\Entity\CreditCard;
use Slivki\Entity\OfferOrder;
use Slivki\Entity\User;
use Slivki\Entity\Visit;
use Slivki\Enum\DeviceTypeEnum;
use Slivki\Enum\LocalizationLanguage;
use Slivki\Enum\Location\DefaultCoordinates;
use Slivki\Enum\Offer\OfferApiSort;
use Slivki\Helpers\CalculationConversionHelper;
use Slivki\Message\Query\Tire\GetTireOffersQuery;
use Slivki\Paginator\Offer\OfferIdsByGiftCertificateFilterPaginatorInterface;
use Slivki\Repository\PurchaseCount\PurchaseCountRepositoryInterface;
use Slivki\Services\CacheService;
use Slivki\Services\Category\CategoryDeliveryFoodOffersCustomSorter;
use Slivki\Services\FoodCourt\DeliveryWorkingTimeService;
use Slivki\Services\Localization\TranslateService;
use Slivki\Services\MainMenu\MainMenuAppCacheService;
use Slivki\Services\MainMenu\MainMenuOplatiCacheService;
use Slivki\Services\MobApiCacheService;
use Slivki\Services\Offer\GeoLocationService;
use Slivki\Services\Offer\OfferResponseCacheService;
use Slivki\Services\Offer\PhoneService;
use Slivki\Services\Payment\PaymentService;
use Slivki\Services\Subscription\SubscriptionService;
use Slivki\Services\UserGetter;
use Slivki\ValueObject\Coordinate;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Slivki\Entity\Category;
use Slivki\Entity\GeoLocation;
use Slivki\Entity\MainMenu;
use Slivki\Entity\Offer;
use Slivki\Services\Offer\OfferCacheService;
use Slivki\Services\ImageService;
use Slivki\Util\SoftCache;
use Symfony\Component\HttpFoundation\Request;
use Slivki\Util\Logger;
use OpenApi\Annotations as OA;
use Nelmio\ApiDocBundle\Annotation\Model;
use Slivki\Entity\EntityOption;
use Slivki\Services\BePaidService;
use Slivki\Dto\Offer\GiftCertificate\FilterDto;
use function array_map;
use function array_slice;
use function count;
class OfferController extends MobileApiController
{
/**
* Список категорий по городам
* @Route("/mobile/api/v2/city/{cityID}/categories/{pageNumber}", methods={"GET"});
* @OA\Response(
* response = 200,
* description = "Список категорий",
* @OA\Schema(
* @OA\Property(property = "ID", type = "integer", description = "ID категории"),
* @OA\Property(property = "name", type = "varchar", description = "название категории"),
* @OA\Property(property = "entityCount", type = "integer", description = "количество акций"),
* @OA\Property(property = "iconURL", type = "varchar", description = "изображение категории"),
* @OA\Property(property = "offers", type = "object", description = "акции",
* @OA\Property(property = "ID", type = "integer", description = "ID акции"),
* @OA\Property(property = "name", type = "varchar", description = "название акции"),
* @OA\Property(property = "regularPrice", type = "decimal", description = "обычная цена"),
* @OA\Property(property = "offerPrice", type = "decimal", description = "цена со скидкой"),
* @OA\Property(property = "discountPercent", type = "varchar", description = "процент скидки"),
* @OA\Property(property = "saleCount", type = "integer", description = "количество проданных кодов"),
* @OA\Property(property = "imageURL", type = "varchar", description = "URL изображения для тизера"),
* @OA\Property(property = "verticalImageUrl", type = "varchar", description = "URL вертикального изображения для тизера"),
* @OA\Property(property = "activeTill", type = "datetime", description = "дата окончания акции"),
* @OA\Property(property = "rating", type = "decimal", description = "рейтинг акции"),
* @OA\Property(property = "codeCost", type = "varchar", description = "цена кода"),
* @OA\Property(property = "offerType", type = "integer", description = "тип оффера. 0 обычный, 1 - онлайн ордер, 2 оноайл ордер без возможности купить код отдельно, 3 трайпл, 4 шм, 5 сертификаты"),
* @OA\Property(property = "phoneNumbersWithoutLocation", type = "object", description = "номера телефонов без местоположения",
* @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* ),
* @OA\Property(property = "locations", type = "object", description = "местоположение",
* @OA\Property(property = "address", type = "varchar", description = "адрес"),
* @OA\Property(property = "workingHours", type = "text", description = "время роботы"),
* @OA\Property(property = "phoneNumbers", type = "object", description = "номера телефонов",
* @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* ),
* @OA\Property(property = "description", type = "varchar", description = "описание"),
* @OA\Property(property = "geoLocation", type = "varchar", description = "массив координат"),
* ),
* @OA\Property(property = "visitCount", type = "integer", description = "количество посещений"),
* @OA\Property(property = "address", type = "varchar", description = "адрес"),
* @OA\Property(property = "isFreeCode", type = "boolean", description = "бесплатный ли код"),
* @OA\Property(property = "companyLogoImage", type = "varchar", description = "URL лого компании"),
* @OA\Property(property = "buyButtonLabel", type = "varchar", description = "текст кнопки покупки кода"),
* @OA\Property(property = "openOnlineOrderButtonLabel", type = "varchar", description = "текст кнопки онлайн заказа"),
* ),
* @OA\Property(property = "categoryCount", type = "integer", description = "количество категорий"),
* @OA\Property(property = "isLast", type = "boolean", description = "последняя ли страница"),
* @OA\Property(property = "userBalance", type = "varchar", description = "баланс пользователя"),
* type="object",
* )
* )
* @OA\Parameter(
* name = "cityID",
* in = "path",
* description = "ID города",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "pageNumber",
* in = "path",
* description = "номер страницы",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Category")
*/
public function getCategoriesAction(MobApiCacheService $mobApiCacheService, OfferCacheService $offerCacheService, ImageService $imageService, $cityID, $pageNumber)
{
ini_set('memory_limit', '4g');
$categories = $this->getCategories(
$mobApiCacheService,
$offerCacheService,
$imageService,
0,
$pageNumber,
$cityID,
true,
$this->getUser(),
);
return $this->getResponse($categories, 200);
}
/**
* Список категорий и субкатегорий
* @Route("/mobile/api/v2/city/{cityID}/categories-tree", methods={"GET"});
* @OA\Response(
* response=Response::HTTP_OK,
* description="Список категорий и субкатегорий",
* @OA\JsonContent(
* @OA\Property(property="categories", type="array", description="Категории",
* @OA\Items(
* @OA\Property(property="ID", type="string", description="ID категории"),
* @OA\Property(property="topParentCategoryId", type="integer", nullable=true, description="Id главной родительской категории"),
* @OA\Property(property="name", type="string", description="название категории"),
* @OA\Property(property="entityCount", type="integer", description="количество акций"),
* @OA\Property(property="iconURL", type="string", description="изображение категории"),
* @OA\Property(property="workExamplesCount", type="integer", description="количество примеров работ"),
* @OA\Property(property="beautyMasterCount", type="integer", description="количество мастеров"),
* @OA\Property(property="interiorGalleryOffersCount", type="integer", description="количество акций с галереей интерьеров"),
* @OA\Property(property="hideWorkExamples", type="boolean", description="Не показывать примеры работ"),
* @OA\Property(property="tireFilterCategories", type="array", description="доступные категории для фильтра шиномонтажа",
* @OA\Items(
* @OA\Property(property="id", type="integer", description="ID категории"),
* @OA\Property(property="name", type="string", description="название категории"),
* ),
* ),
* @OA\Property(property="subcategories", type="array", description="субкатегории",
* @OA\Items(
* @OA\Property(property="ID", type="integer", description="ID субкатегории"),
* @OA\Property(property="topParentCategoryId", type="integer", nullable=true, description="Id главной родительской категории"),
* @OA\Property(property="name", type="string", description="название субкатегории"),
* @OA\Property(property="entityCount", type="integer", description="количество акций субкатегории"),
* @OA\Property(property="iconURL", type="string", description="изображение субкатегории"),
* @OA\Property(property="workExamplesCount", type="integer", description="количество примеров работ"),
* @OA\Property(property="beautyMasterCount", type="integer", description="количество мастеров"),
* @OA\Property(property="interiorGalleryOffersCount", type="integer", description="количество акций с галереей интерьеров"),
* @OA\Property(property="hideWorkExamples", type="boolean", description="Не показывать примеры работ"),
* @OA\Property(property="tireFilterCategories", type="array", description="доступные категории для фильтра шиномонтажа",
* @OA\Items(
* @OA\Property(property="id", type="integer", description="ID категории"),
* @OA\Property(property="name", type="string", description="название категории"),
* ),
* ),
* @OA\Property(property="tireFilterDiscountCategories", type="array", description="доступные категории cо скидками для фильтра шиномонтажа",
* @OA\Items(
* @OA\Property(property="id", type="integer", description="ID категории"),
* @OA\Property(property="name", type="string", description="название категории"),
* ),
* ),
* @OA\Property(property="giftCertificateFilterCategories", type="array", description="доступные категории для фильтра по сертификатам",
* @OA\Items(
* @OA\Property(property="id", type="integer", description="ID категории"),
* @OA\Property(property="name", type="string", description="название категории"),
* ),
* ),
* ),
* ),
* ),
* ),
* @OA\Property(property="categoryCount", type="integer", description="количество категорий"),
* type="object",
* ),
* ),
* @OA\Parameter(
* name = "cityID",
* in = "path",
* description = "ID города",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Category")
*/
public function getCategoriesTreeAction(
Request $request,
MainMenuAppCacheService $mainMenuAppCacheService,
MainMenuOplatiCacheService $mainMenuOplatiCacheService,
TranslateService $translateService,
int $cityID
): JsonResponse {
$locale = LocalizationLanguage::language($request->query->get('language'));
$mainMenu = User::OPLATI_PARTNER_TOKEN === $request->headers->get('HTTP-SLIVKi-PARTNER-TOKEN')
? $mainMenuOplatiCacheService->getMainMenuCached($cityID)
: $mainMenuAppCacheService->getMainMenuCached($cityID);
foreach ($mainMenu['categories'] as $key => $category) {
$mainMenu['categories'][$key]['name'] = $translateService->getCategoryTranslationForField(
$category['ID'],
'name',
$locale,
$category['name']
);
}
return $this->getResponseWithoutUser($mainMenu, Response::HTTP_OK);
}
public function getCategories(MobApiCacheService $mobApiCacheService, OfferCacheService $offerCacheService, ImageService $imageService, $parentID, $pageNumber, $cityID = null, $withOffers = false, $user = null) {
$categories = $this->getCategoriesCached($mobApiCacheService, $imageService, $parentID, $pageNumber, $cityID, $withOffers, $user);
if (!$withOffers) {
return $categories;
}
$offerRepository = $this->getDoctrine()->getRepository(Offer::class);
$result = [];
foreach ($categories['categories'] as $key => $category) {
foreach ($category['offers']['offers'] as $offerKey => $offerArray) {
$offer = $offerCacheService->getOffer($offerArray['ID']);
$categories['categories'][$key]['offers']['offers'][$offerKey]['isFreeCode'] = false;
if ($offer) {
$categories['categories'][$key]['offers']['offers'][$offerKey]['isFreeCode'] = $offerRepository->isOfferFreeForUser($offer, $user);
if ($categories['categories'][$key]['offers']['offers'][$offerKey]['isFreeCode'] && !$offer->isFree()
&& $offer->getID() != Offer::PETROL_OFFER_ID) {
$categories['categories'][$key]['offers']['offers'][$offerKey]['freeCodeCount'] = 1;
}
}
}
$result['categories'][] = $categories['categories'][$key];
}
$result['categoryCount'] = $categories['categoryCount'];
$result['isLast'] = $categories['isLast'];
return $result;
}
private function getCategoriesCached(MobApiCacheService $mobApiCacheService, ImageService $imageService, $parentID, $pageNumber, $cityID = null, $withOffers = false, $user = null) {
$softCache = new SoftCache('MOBILE-1-');
$cacheKey = 'categories-19-' . $parentID;
if ($pageNumber) {
$cacheKey .= '-page-' . $pageNumber;
}
if ($cityID) {
$cacheKey .= '-city-' . $cityID;
}
if ($withOffers) {
$cacheKey .= '-offers';
}
$result = $softCache->get($cacheKey);
if ($result) {
return $result;
}
Logger::instance('CACHEDEBUG')->info('category not in cache ' . $parentID);
$result = [];
$entityManager = $this->getDoctrine()->getManager();
$itemList = $entityManager->getRepository(MainMenu::class)->getItemList(MainMenu::MENU_ID_MAIN, $cityID);
$categoryIDs = [];
/** @var MainMenu $item */
foreach ($itemList as $item) {
if ($item->getType() == MainMenu::TYPE_OFFER_CATEGORY) {
$categoryID = $item->getEntityID();
$category = $entityManager->getRepository(Category::class)->getCategoryForMobileApi($categoryID);
if (!$category) {
continue;
}
$categoryIDs[] = $item->getEntityID();
}
}
$categoryCount = count($categoryIDs);
if ($cityID == City::DEFAULT_CITY_ID) {
array_unshift($categoryIDs, Category::FOOD_DELIVERY_CATEGORY_ID);
}
$perPage = 3;
$isLast = true;
if ($pageNumber) {
$offset = ($pageNumber - 1) * $perPage;
$isLast = count($categoryIDs) <= $offset + $perPage;
$categoryIDs = array_slice($categoryIDs, $offset, $perPage);
}
/** @var Category $category */
foreach ($categoryIDs as $categoryID) {
$category = $entityManager->getRepository(Category::class)->getCategoryForMobileApi($categoryID);
if (!$category) {
continue;
}
$iconURL = 'https://www.slivki.by';
$media = $category->getAppIconMedia();
if (!$media) {
$media = $category->getMobileMenuIconMedia();
}
$iconURL .= $media ? $imageService->getImageURL($media, 232, 232) : ImageService::FALLBACK_IMAGE;
$item = [
'ID' => $category->getID(),
'name' => $category->getName(),
'entityCount' => $category->getEntityCount(),
'iconURL' => $iconURL
];
if ($withOffers) {
$item['offers'] = $mobApiCacheService->getOffersByCategoryIDCached($category->getID(), 1, 0, false);
}
$result[] = $item;
}
$data = [];
$data['categoryCount'] = $categoryCount;
if ($parentID == 0) {
$data['categories'] = $result;
} else {
$data = $result;
}
if ($pageNumber) {
$data['isLast'] = $isLast;
}
$softCache->set($cacheKey, $data, 60 * 60);
return $data;
}
/**
* Список акций по категории
* @Route("/mobile/api/v2/category/{categoryID}/{pageNumber}", methods={"GET"});
* @OA\Response(
* response = 200,
* description = "Список акций по категории",
* @OA\Schema(
* @OA\Property(property = "offers", type = "object", description = "акции",
* @OA\Property(property = "ID", type = "integer", description = "ID акции"),
* @OA\Property(property = "name", type = "varchar", description = "название акции"),
* @OA\Property(property = "regularPrice", type = "decimal", description = "обычная цена"),
* @OA\Property(property = "offerPrice", type = "decimal", description = "цена со скидкой"),
* @OA\Property(property = "discountPercent", type = "varchar", description = "процент скидки"),
* @OA\Property(property = "saleCount", type = "integer", description = "количество проданных кодов"),
* @OA\Property(property = "imageURL", type = "varchar", description = "URL изображения для тизера"),
* @OA\Property(property = "verticalImageUrl", type = "varchar", description = "URL вертикального изображения для тизера"),
* @OA\Property(property = "activeTill", type = "datetime", description = "дата окончания акции"),
* @OA\Property(property = "rating", type = "decimal", description = "рейтинг акции"),
* @OA\Property(property = "codeCost", type = "varchar", description = "цена кода"),
* @OA\Property(property = "phoneNumbersWithoutLocation", type = "object", description = "номера телефонов без местоположения",
* @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* ),
* @OA\Property(property = "locations", type = "object", description = "местоположение",
* @OA\Property(property = "address", type = "varchar", description = "адрес"),
* @OA\Property(property = "workingHours", type = "text", description = "время роботы"),
* @OA\Property(property = "phoneNumbers", type = "object", description = "номера телефонов",
* @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* ),
* @OA\Property(property = "description", type = "varchar", description = "описание"),
* @OA\Property(property = "geoLocation", type = "varchar", description = "массив координат"),
* ),
* @OA\Property(property = "visitCount", type = "integer", description = "количество посещений"),
* @OA\Property(property = "address", type = "varchar", description = "адрес"),
* @OA\Property(property = "offerType", type = "integer", description = "тип акции"),
* @OA\Property(property = "isFreeCode", type = "boolean", description = "бесплатный ли код"),
* @OA\Property(property = "companyLogoImage", type = "object", description = "URL лого компании"),
* @OA\Property(property="buyButtonLabel", type="string", description="текст кнопки покупки кода"),
* @OA\Property(property="openOnlineOrderButtonLabel", type="string", description="текст кнопки онлайн заказа"),
* ),
* @OA\Property(property = "isLast", type = "boolean", description = "последняя ли страница"),
* @OA\Property(property = "offersCount", type = "integer", description = "количество акций"),
* @OA\Property(property = "userBalance", type = "varchar", description = "баланс пользователя"),
* type="object",
* )
* )
* @OA\Parameter(
* name = "categoryID",
* in = "path",
* description = "ID категории",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "pageNumber",
* in = "path",
* description = "номер страницы",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Category")
*/
public function getOffersAction(
OfferCacheService $offerCacheService,
MobApiCacheService $mobApiCacheService,
$categoryID,
$pageNumber
): JsonResponse {
ini_set('memory_limit', '4g');
$user = $this->getUser();
$result = $mobApiCacheService->getOffersByCategoryIDCached($categoryID, $pageNumber);
$offerRepository = $this->getDoctrine()->getRepository(Offer::class);
foreach ($result['offers'] as $key => $offerArray) {
$offer = $offerCacheService->getOffer($offerArray['ID']);
if ($offer) {
if ($offer->isBuyCodeDisable() || $offer->isHideInApp()) {
unset($result['offers'][$key]);
}
$result['offers'][$key]['isFreeCode'] = $offerRepository->isOfferFreeForUser($offer, $user);
}
}
return $this->getResponse($result, 200);
}
/**
* Отсортированные акции категории
* @Route("/mobile/api/v2/category-sorted", methods={"POST"});
* @OA\Response(
* response = 200,
* description = "Отсортированные акции категории",
* @OA\JsonContent(
* @OA\Property(property="offers", type="object", description="акции",
* @OA\Property(property="ID", type="integer", description="ID акции"),
* @OA\Property(property="name", type="string", description="название акции"),
* @OA\Property(property="regularPrice", type="number", format="decimal", description="обычная цена"),
* @OA\Property(property="offerPrice", type="number", format="decimal", description="цена со скидкой"),
* @OA\Property(property="discountPercent", type="string", description="процент скидки"),
* @OA\Property(property="isWithoutCodes", type="boolean", description="Акция без промокодов"),
* @OA\Property(property="saleCount", type="integer", description="количество проданных кодов"),
* @OA\Property(property="imageURL", type="string", description="URL изображения для тизера"),
* @OA\Property(property="verticalImageUrl", type="string", description="URL вертикального изображения для тизера"),
* @OA\Property(property="activeTill", type="datetime", description="дата окончания акции"),
* @OA\Property(property="rating", type="number", format="decimal", description="рейтинг акции"),
* @OA\Property(property="codeCost", type="string", description="цена кода"),
* @OA\Property(property="conversion", type="number", description="Конверсия"),
* @OA\Property(property="phoneNumbersWithoutLocation", type="object", description="номера телефонов без местоположения",
* @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
* @OA\Property(property="label", type="string", description="метка"),
* ),
* @OA\Property(property="locations", type="object", description="местоположение",
* @OA\Property(property="address", type="string", description="адрес"),
* @OA\Property(property="workingHours", type="string", description="время работы"),
* @OA\Property(property="phoneNumbers", type="object", description="номера телефонов",
* @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
* @OA\Property(property="label", type="string", description="метка"),
* ),
* @OA\Property(property="description", type="string", description="описание"),
* @OA\Property(property="geoLocation", type="string", description="массив координат"),
* ),
* @OA\Property(property="visitCount", type="integer", description="количество посещений"),
* @OA\Property(property="address", type="string", description="адрес"),
* @OA\Property(property="offerType", type="integer", description="тип акции"),
* @OA\Property(property="allowedOnlineOrderTypes", type="array", description="Типы онлайн заказа",
* @OA\Items(type="integer", description="1 - Food, 2 - Gift-certificate, 3 - tire"),
* ),
* @OA\Property(property="isFreeCode", type="boolean", description="бесплатный ли код"),
* @OA\Property(property="companyLogoImage", type="object", description="URL лого компании"),
* @OA\Property(property="buyButtonLabel", type="string", description="текст кнопки покупки кода"),
* @OA\Property(property="openOnlineOrderButtonLabel", type="string", description="текст кнопки онлайн заказа"),
* @OA\Property(property="deliveryTime", type="object", nullable="true", description="Время работы доставки",
* @OA\Property(property="text", type="string", description="Текст доставки"),
* @OA\Property(property="isOpen", type="boolean", description="Открыто заведение"),
* ),
* ),
* @OA\Property(property="isLast", type="boolean", description="последняя ли страница"),
* @OA\Property(property="offersCount", type="integer", description="количество акций"),
* @OA\Property(property="showItemsCatalog", type="boolean", description="выводить ли в категории товарный каталог"),
* @OA\Property(property="userBalance", type="string", description="баланс пользователя"),
* @OA\Property(property="currency", type="object", nullable="true", description="Курс для перевода в валюту",
* @OA\Property(property="code", type="string", description="Название валюты (GEL)"),
* @OA\Property(property="rate", type="number", description="Курс конверсии в бел рубль"),
* ),
* type="object",
* )
* )
* @OA\RequestBody(
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* required={"categoryID", "longitude", "latitude", "pageNumber", "sortBy"},
* @OA\Property(property="categoryID", description="ID категории", type="integer"),
* @OA\Property(property="longitude", description="долгота", type="float"),
* @OA\Property(property="latitude", description="широта", type="float"),
* @OA\Property(property="pageNumber", description="номер страницы", type="integer"),
* @OA\Property(property="sortBy", description="сортировка", type="integer"),
* @OA\Property(
* property="tireFilter",
* description="Фильтр онлайн заказа",
* type="object",
* nullable=true,
* @OA\Property(property="weekday", type="integer", description="День недели 0 - воскресенье", example="0"),
* @OA\Property(property="time", type="string", description="Время в формате HH:MM", example="12:30"),
* ),
* @OA\Property(
* property="giftCertificateFilter",
* type="object",
* nullable=true,
* description="Фильтр по сертификатам",
* @OA\Property(
* property="price",
* nullable=true,
* type="object",
* description="Фильтр по цене",
* ref=@Model(type=PriceFilterDto::class),
* ),
* @OA\Property(
* property="paginator",
* type="object",
* description="Пагинация",
* ref=@Model(type=PaginatorFilterDto::class),
* ),
* @OA\Property(
* property="sort",
* type="object",
* description="Сортировка",
* ref=@Model(type=SortFilterDto::class),
* ),
* @OA\Property(property="giftCertificateCategoryId", type="integer", description="Время в формате HH:MM"),
* ),
* ),
* ),
* ),
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function getOffersSortedAction(
Request $request,
OfferCacheService $offerCacheService,
MobApiCacheService $mobApiCacheService,
TireFilterDaoInterface $tireFilterDao,
OfferIdsByGiftCertificateFilterPaginatorInterface $offerIdsByGiftCertificateFilterPaginator,
CalculationConversionHelper $calculationConversionHelper,
CategoryDeliveryFoodOffersCustomSorter $categoryDeliveryFoodOffersCustomSorter,
DeliveryWorkingTimeService $deliveryWorkingTimeService,
TranslateService $translateService,
PurchaseCountRepositoryInterface $purchaseCountRepository
) {
$user = $this->getUser();
$data = json_decode($request->getContent());
$categoryID = $data->categoryID;
$pageNumber = $data->pageNumber;
$sortBy = $data->sortBy;
$location = [];
if (OfferApiSort::DISTANCE === $data->sortBy) {
$location['latitude'] = $data->latitude ?: DefaultCoordinates::LATITUDE;
$location['longitude'] = $data->longitude ?: DefaultCoordinates::LONGITUDE;
}
$tireFilter = $data->tireFilter ?? null;
$giftCertificateFilter = $data->giftCertificateFilter ?? null;
if (null !== $tireFilter) {
$offersData = $tireFilterDao->getOffersByTireFilter(new GetTireOffersQuery($categoryID, $tireFilter->weekday, $tireFilter->time));
$offers = $offerCacheService->getOffers(array_keys($offersData), true, true);
$result = [
'offers' => $mobApiCacheService->createOfferResponse($offers),
'isLast' => true,
'offersCount' => count($offers),
'offerAvailableAddress' => $offersData
];
} elseif (null !== $giftCertificateFilter) {
$offerIds = array_map(
static fn (array $offer): int => (int) $offer['id'],
(array) $offerIdsByGiftCertificateFilterPaginator->findOfferIdsByGiftCertificateFilter(new FilterDto(
true,
new SortFilterDto($giftCertificateFilter->sort->by, $giftCertificateFilter->sort->direction),
new PaginatorFilterDto($giftCertificateFilter->paginator->page, $giftCertificateFilter->paginator->perPage),
$categoryID,
$giftCertificateFilter->giftCertificateCategoryId,
property_exists($giftCertificateFilter, 'price')
? new PriceFilterDto($giftCertificateFilter->price->minPrice, $giftCertificateFilter->price->maxPrice)
: null,
null !== $location['latitude'] && null !== $location['longitude']
? new Coordinate($location['latitude'], $location['longitude'])
: null,
))->getItems(),
);
$offers = $offerCacheService->getOffers($offerIds, true, true);
$offersCount = count($offers);
$result = [
'offers' => $mobApiCacheService->createOfferResponse($offers),
'isLast' => $offersCount < $giftCertificateFilter->paginator->perPage,
'offersCount' => $offersCount,
];
} elseif ($sortBy !== OfferApiSort::TIMETABLE && $categoryDeliveryFoodOffersCustomSorter->support($categoryID)) {
$offers = $mobApiCacheService->getAllOffersByCategoryIdCached($categoryID);
$offset = ($pageNumber - 1) * MobApiCacheService::OFFERS_PER_PAGE;
$offerCount = count($offers);
$result = [
'isLast' => $offset + MobApiCacheService::OFFERS_PER_PAGE >= $offerCount,
'offersCount' => $offerCount,
'offers' => array_slice(
$categoryDeliveryFoodOffersCustomSorter->sort($offers),
$offset,
MobApiCacheService::OFFERS_PER_PAGE,
),
];
} else {
$result = $mobApiCacheService->getOffersByCategoryIDCached($categoryID, $pageNumber, $sortBy, $location);
}
$category = $mobApiCacheService->findCachedCategory($categoryID);
$result['showItemsCatalog'] = null !== $category && $category->getShowItemsCatalog();
$offerRepository = $this->getDoctrine()->getRepository(Offer::class);
foreach ($result['offers'] as $key => $offerArray) {
$offer = $offerCacheService->getOffer($offerArray['ID']);
if (!$offer) {
Logger::instance('debug-getOffersSortedAction')->info($offerArray['ID']);
}
$result['offers'][$key]['isFreeCode'] = false;
$result['offers'][$key]['conversion'] = $calculationConversionHelper->getConversion(
(int) $offerArray['visitCount'],
(int) $offerArray['saleCount'],
);
if ($offer) {
$result['offers'][$key]['isFreeCode'] = $offerRepository->isOfferFreeForUser($offer, $user);
if ($result['offers'][$key]['isFreeCode'] && !$offer->isFree()
&& $offer->getID() != Offer::PETROL_OFFER_ID) {
$result['offers'][$key]['freeCodeCount'] = 1;
}
}
$purchaseCount = $purchaseCountRepository->findByOfferId($offerArray['ID']);
$result['offers'][$key]['numberOfPromocodesPerDay'] = null !== $purchaseCount ? $purchaseCount->getPurchaseCountLastDayWithCorrection() : 0;
$result['offers'][$key]['allowedOnlineOrderTypes'] = false !== $offer ? $offer->getAllowedOnlineOrderTypesOnApp() : [];
$result['offers'][$key]['deliveryTime'] = $this->shouldShowDeliveryTimeText($offer, $category)
? $deliveryWorkingTimeService->getDeliveryTimeText($offer)
: null;
$result['offers'][$key]['currency'] =
$offer->isActiveCurrencyCalculator()
? [
'code' => $offer->getBankCurrency()->getCurrency(),
'rate' => $offer->getBankCurrency()->getRate(),
]
: null;
$result['offers'][$key]['name'] = $translateService->getOfferTranslationForField(
$offerArray['ID'],
'name',
LocalizationLanguage::language($data->language ?? null),
$offer->getTitle(),
);
}
return $this->getResponse($result, 200);
}
private function shouldShowDeliveryTimeText($offer, ?Category $category): bool
{
if (false === $offer || null === $category) {
return false;
}
if (Category::FOOD_DELIVERY_CATEGORY_ID === $category->getID()) {
return true;
}
return $category->isChildOfRecursive(Category::FOOD_DELIVERY_CATEGORY_ID);
}
/**
* Акция
* @Route("/mobile/api/v2/offer", methods={"POST"});
* @OA\Response(
* response=Response::HTTP_OK,
* description="Описание акции",
* content={
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* @OA\Property(property="ID", type="integer", description="ID акции"),
* @OA\Property(property="name", type="string", description="название акции"),
* @OA\Property(property="cityId", type="integer", description="ID города"),
* @OA\Property(property="saleCount", type="integer", description="количество проданных кодов"),
* @OA\Property(property="lastMonthSaleCount", type="integer", description="количество проданных кодов за последний месяц"),
* @OA\Property(property="visitCount", type="integer", description="Количество посещений"),
* @OA\Property(property="rating", type="float", description="рейтинг акции"),
* @OA\Property(property="active", type="boolean", description="акция активна"),
* @OA\Property(property="isFreeCode", type="boolean", description="платный/бесплатный код"),
* @OA\Property(property="codeCost", type="number", description="цена за промокод"),
* @OA\Property(property="phoneNumbersWithoutLocation", type="object", description="номера телефонов без локаций",
* @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
* @OA\Property(property="label", type="string", description="примечание"),
* ),
* @OA\Property(property="offerUrl", type="string", description="ссылка на акцию"),
* @OA\Property(property="offerType", type="integer", description="тип оффера"),
* @OA\Property(property="isWithoutCodes", type="boolean", description="Акция без промокодов"),
* @OA\Property(property="allowedOnlineOrderTypes", type="array", description="Типы онлайн заказа",
* @OA\Items(type="integer", description="1 - Food, 2 - Gift-certificate, 3 - tire"),
* ),
* @OA\Property(property="activeTill", type="datetime", description="дата окончания акции"),
* @OA\Property(property="conditions", type="string", description="условия акции"),
* @OA\Property(property="description", type="string", description="описание акции"),
* @OA\Property(property="features", type="string", description="особенности"),
* @OA\Property(property="locations", type="object", description="адреса акции",
* @OA\Property(property="address", type="string", description="адрес. г. Минск, ул. Захарова, 40"),
* @OA\Property(property="workingHours", type="string", description="часы работы заведения"),
* @OA\Property(property="phoneNumbers", type="object", description="номера телефонов в акции",
* @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
* @OA\Property(property="label", type="string", description="примечание")
* ),
* @OA\Property(property="description", type="string", description="описание"),
* @OA\Property(property="geoLocation", type="string", description="координаты заведения"),
* ),
* @OA\Property(property="discountPercent", type="string", description="процент скидки"),
* @OA\Property(property="images", type="object", description="тизеры акциии",
* @OA\Property(property="imageURL", type="string", description="URL изображения"),
* ),
* @OA\Property(property="shopImages", type="array", description="Изображения заведения",
* @OA\Items(type="string", description="URL изображения"),
* ),
* @OA\Property(property="galleryVideos", type="array", description="Видео в галерее акции",
* @OA\Items(type="object", description="Видео",
* @OA\Property(property="title", type="string", description="Заголовок видео"),
* @OA\Property(property="url", type="string", description="URL видео"),
* ),
* ),
* @OA\Property(property="galleryVideoPackage", type="object", description="Видео пакет в галерее акции",
* @OA\Property(property="title", type="string", description="Заголовок пакета"),
* @OA\Property(property="previewImageUrl", type="string", description="URL превью изображения"),
* ),
* @OA\Property(property="regularPrice", type="string", description="обычная цена (может быть строка от 50 руб.)"),
* @OA\Property(property="offerPrice", type="string", description="цена со скидкой"),
* @OA\Property(property="companyID", type="integer", description="ID компании"),
* @OA\Property(property="companyLogoImage", type="object", description="URL лого компании"),
* @OA\Property(property="buyButtonLabel", type="string", description="текст кнопки покупки кода"),
* @OA\Property(property="openOnlineOrderButtonLabel", type="string", description="текст кнопки онлайн заказа"),
* @OA\Property(property="conversion", type="number", description="Конверсия"),
* @OA\Property(property="numberOfPromocodesPerDay", type="number", description="Количество покупок за сутки"),
* @OA\Property(property="onlineAutoOpened", type="boolean", description="Открывать онлайн в апп автоматически"),
* @OA\Property(property="onlineOrderGiftEnabled", type="boolean", description="Заказ в подарок"),
* @OA\Property(property="userBalance", type="string", description="баланс пользователя"),
* @OA\Property(property="availableOnFood", type="boolean", description="Доступна в еде"),
* @OA\Property(property="workExamplesCount", type="integer", description="Количество примеров работ"),
* @OA\Property(property="beautyMasterCount", type="integer", description="количество мастеров"),
* @OA\Property(property="titleFontColor", type="string", description="Цвет шрифта названия акции"),
* @OA\Property(property="separateTabForCertificatesInApp", type="string", description="Вывод сертификатов в отдельном табе"),
* @OA\Property(property="telegram", type="string", description="Ник в телеграм через @"),
* @OA\Property(property="viber", type="string", description="Номер телефона в вайбер"),
* @OA\Property(property="foodOfferDeliveryZones", type="array", description="Зоны доставки еды акции",
* @OA\Items(type="object", description="Зона",
* @OA\Property(property="position", type="integer", description="Позиция зоны"),
* @OA\Property(property="name", type="string", description="Название зоны"),
* ),
* ),
* @OA\Property(property="messengerCallBack", type="string", description="Обратный звонок (телеграм/вайбер)"),
* @OA\Property(property="commentBanners", type="array", description="Баннеры в комментах",
* @OA\Items(type="object", description="Баннеры в комментах: 100% и 50%",
* @OA\Property(property="id", type="integer", description="Id баннера"),
* @OA\Property(property="type", type="integer", description="Тип баннера"),
* @OA\Property(property="title", type="string", description="Название баннера"),
* @OA\Property(property="url", type="string", description="Внешняя ссылка"),
* @OA\Property(property="filePath", type="string", description="Путь к картинке"),
* @OA\Property(property="position", type="integer", description="Позиция для отображения в списке"),
* ),
* ),
* type="object",
* ),
* ),
* }
* )
* )
* @OA\Response(
* response = Response::HTTP_NOT_FOUND,
* description = "Акция не найдена"
* )
* @OA\RequestBody(
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="object",
* @OA\Property(
* property = "longitude",
* description = "долгота",
* type="number",
* example="27.557008",
* ),
* @OA\Property(
* property = "latitude",
* description = "широта",
* type="number",
* example="53.911724",
* ),
* @OA\Property(
* property = "offerID",
* description = "id акции",
* type="integer",
* example="1",
* ),
* @OA\Property(
* property = "language",
* description = "язык контента",
* type="string",
* example="ru",
* ),
* ),
* ),
* ),
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function offerDetailsAction(
Request $request,
OfferResponseCacheService $offerResponseCacheService
): JsonResponse {
$data = json_decode($request->getContent());
$language = $data->language ?? null;
$user = $this->getUser();
$offerID = (int) $data->offerID;
$isPartner = $request->headers->get(User::HTTP_PARTNER_TOKEN_HEADER_NAME) === User::OPLATI_PARTNER_TOKEN;
$offerResponse = $offerResponseCacheService->getOfferResponseByOfferId($offerID, $isPartner, $language, $user);
$this->addVisit($offerID, Visit::TYPE_OFFER, DeviceTypeEnum::APP, $user, $request->getClientIp());
return $this->getResponse($offerResponse->toArray(), Response::HTTP_OK);
}
/**
* Список локаций акции по категории
* @Route("/mobile/api/v2/offer/locations",methods={"POST"})
* @OA\Response(
* response = 200,
* description = "Список локаций акции",
* @OA\JsonContent(
* @OA\Property(property = "offerID", type = "integer", description = "ID акции"),
* @OA\Property(property = "name", type = "varchar", description = "название акции"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* @OA\Property(property = "imageURL", type = "varchar", description = "тизер акции"),
* @OA\Property(property = "locations", type = "object", description = "местоположение",
* @OA\Property(property = "latitude", type = "varchar", description = "широта"),
* @OA\Property(property = "longitude", type = "varchar", description = "долгота"),
* ),
* type="object",
* )
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Schema (
* type="object",
* @OA\Property(
* property="categoryID",
* required=true,
* description="id категори",
* @OA\Schema(type="integer"),
* ),
* )
* )
* )
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function offerLocationsAction(Request $request, ImageService $imageService) {
ini_set('memory_limit', '8g');
$data = json_decode($request->getContent());
$softCache = new SoftCache('mobapi-7');
$cacheKey = 'locations-3-' . $data->categoryID;
$result = $softCache->get($cacheKey);
if ($result) {
return $this->getResponse($result, 200);
}
$entityManager = $this->getDoctrine()->getManager();
if ($data->categoryID == 0) {
$offerList = $entityManager->getRepository(Offer::class)->getAllActiveOffersCached();
} else {
$offerList = $entityManager->getRepository(Offer::class)->getActiveOffersByCategoryID($data->categoryID);
}
$result = [];
/** @var Offer $offer */
foreach ($offerList as $offer) {
if (!$offer || $offer->isHideInApp()) {
continue;
}
$item = [
'offerID' => $offer->getID(),
'name' => $offer->getTitle(),
'label' => $offer->getCompanyName(),
'imageURL' => 'https://www.slivki.by' . $imageService->getImageURLCached($offer->getTeaserMedia() ?: null, 500, 324)
];
/** @var GeoLocation $location */
foreach ($offer->getGeoLocations() as $location) {
$item['locations'][] = [
'latitude' => $location->getLatitude(),
'longitude' => $location->getLongitude()
];
}
$result[] = $item;
}
$softCache->set($cacheKey, $result, 12 * 60 * 60);
return $this->getResponseWithoutUser($result, 200);
}
/**
* Геолокация активных акций
* @Route("/mobile/api/offers/geo-locations", methods={"GET"});
* @OA\Response(
* response=200,
* description = "Геолокация активных акций",
* @OA\Schema(
* @OA\Property(property = "type", type = "varchar", description = "тип колекции"),
* @OA\Property(property = "features", type = "object", description = "содержание коллекции",
* @OA\Property(property = "type", type = "varchar", description = "тип локации"),
* @OA\Property(property = "id", type = "integer", description = "ID локации"),
* @OA\Property(property = "geometry", type = "object", description = "местоположение",
* @OA\Property(property = "type", type = "varchar", description = "тип маркера"),
* @OA\Property(property = "coordinates", type = "object", description = "массив координат [53.914176,27.589859]"),
* ),
* @OA\Property(property = "properties", type = "object", description = "свойства",
* @OA\Property(property = "iconClass", type = "varchar", description = "класс маркера"),
* @OA\Property(property = "iconContent", type = "varchar", description = "содержание маркера"),
* @OA\Property(property = "locationID", type = "integer", description = "ID локации"),
* @OA\Property(property = "offerID", type = "integer", description = "ID акции"),
* @OA\Property(property = "offerTitle", type = "varchar", description = "название акции"),
* @OA\Property(property = "teaserURL", type = "varchar", description = "тизер акции"),
* @OA\Property(property = "url", type = "varchar", description = "url акции"),
* @OA\Property(property = "hintContent", type = "varchar", description = "текст маркера"),
* ),
* ),
* type="object",
* )
* )
* @OA\Tag(name="Offer")
*/
public function getOffersGeoLocations(ImageService $imageService) {
ini_set('memory_limit', '8g');
$softCache = new SoftCache('mobapi-7');
$cacheKey = 'geo-locations';
$result = $softCache->get($cacheKey);
if ($result) {
return $this->getResponse($result, 200);
}
$entityManager = $this->getDoctrine()->getManager();
$result = [];
$offerList = $entityManager->getRepository(Offer::class)->getAllActiveOffersNoCache();
$offerRepository = $this->getOfferRepository();
foreach ($offerList as $key=>$offer) {
if (!$offer->isHideInApp()) {
$result[$key] = $offerRepository->getOfferGeoLocationData($offer, [], $imageService, false, $this->getParameter('base_url'));
}
}
$softCache->set($cacheKey, $result, 12 * 60 * 60);
return $this->getResponseWithoutUser($result, 200);
}
/**
* Геолокация активных акций по категории
* @Route("/mobile/api/offers/geo-locations/{categoryID}", methods={"GET"});
* @OA\Response(
* response=200,
* description = "Геолокация активных акций",
* @OA\Schema(
* @OA\Property(property = "type", type = "varchar", description = "тип колекции"),
* @OA\Property(property = "features", type = "object", description = "содержание коллекции",
* @OA\Property(property = "type", type = "varchar", description = "тип локации"),
* @OA\Property(property = "id", type = "integer", description = "ID локации"),
* @OA\Property(property = "geometry", type = "object", description = "местоположение",
* @OA\Property(property = "type", type = "varchar", description = "тип маркера"),
* @OA\Property(property = "coordinates", type = "object", description = "массив координат [53.914176,27.589859]"),
* ),
* @OA\Property(property = "properties", type = "object", description = "свойства",
* @OA\Property(property = "iconClass", type = "varchar", description = "класс маркера"),
* @OA\Property(property = "iconContent", type = "varchar", description = "содержание маркера"),
* @OA\Property(property = "locationID", type = "integer", description = "ID локации"),
* @OA\Property(property = "offerID", type = "integer", description = "ID акции"),
* @OA\Property(property = "offerTitle", type = "varchar", description = "название акции"),
* @OA\Property(property = "teaserURL", type = "varchar", description = "тизер акции"),
* @OA\Property(property = "offerType", type = "integer", description = "тип оффера. 0 обычный, 1 - онлайн ордер, 2 оноайл ордер без возможности купить код отдельно, 3 трайпл, 4 шм, 5 сертификаты"),
* @OA\Property(property = "url", type = "varchar", description = "url акции"),
* @OA\Property(property = "hintContent", type = "varchar", description = "текст маркера"),
* ),
* ),
* type="object",
* )
* )
* @OA\Tag(name="Offer")
*/
public function getOffersGeoLocationsByCategory(
$categoryID,
ImageService $imageService,
CacheService $cacheService
): JsonResponse {
ini_set('memory_limit', '8g');
$softCache = new SoftCache('mobapi-7');
$cacheKey = 'geo-locations-by-category' . $categoryID;
$result = $softCache->get($cacheKey);
if ($result) {
return $this->getResponseWithoutUser($result, 200);
}
$entityManager = $this->getDoctrine()->getManager();
$result = [];
$offerList = $entityManager->getRepository(Offer::class)->getActiveOffersByCategoryID($categoryID);
$offerRepository = $this->getOfferRepository();
foreach ($offerList as $key=>$offer) {
if (!$offer->isHideInApp()) {
$offer->setGeoLocation($cacheService->getGeoLocations(GeoLocation::TYPE, GeoLocation::class, $offer->getID()));
$result[$key] = $offerRepository->getOfferGeoLocationData($offer, [], $imageService, false, $this->getParameter('base_url'));
}
}
$softCache->set($cacheKey, $result, 12 * 60 * 60);
return $this->getResponseWithoutUser($result, 200);
}
/**
* Получение бесплатного кода трайпл
* @Route("/mobile/api/azs-triple/get-code", methods={"POST"});
* @OA\Response(
* response=200,
* description = "Получение бесплатного кода трайпл",
* @OA\Schema(
* @OA\Property(property = "code", type = "varchar", description = "Промокод"),
* @OA\Property(property = "enoughBalance", type = "boolean", description = "достаточно ли средств на счету для покупки еще одного кода"),
* @OA\Property(property = "moreFree", type = "bool", description = "Доступен ли еще халявный код"),
* type="object",
* example = { "message": "Ваш промокод: <span>52-467</span><br>", "enoughBalance": true, "moreFree": true }
* )
* )
* @OA\Response(
* response = 400,
* description = "Wrong data"
* )
* @OA\Response(
* response = 403,
* description = "Лимит бесплатных кодов исчерпан"
* )
* @OA\Response(
* response = 404,
* description = "Юзер не найден"
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Schema (
* type="object",
* @OA\Property(
* property="carNumber",
* required=true,
* description="Номер авто",
* @OA\Schema(type="string"),
* ),
* @OA\Property(
* property="phoneNumber",
* required=true,
* description="Номер телефона",
* @OA\Schema(type="string"),
* ),
* ),
* ),
* ),
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function getPetrolCodeAction(
Request $request,
OfferCacheService $offerCacheService,
PaymentService $paymentService,
UserGetter $userGetter
) {
$user = $userGetter->get();
$data = json_decode($request->getContent());
$carNumber = $data->carNumber;
$phoneNumber = $data->phoneNumber;
$codesCount = 1;
$result = [];
if (mb_strlen($carNumber) != 4) {
$result['error'] = true;
$result['message'] = 'Введите номер авто';
return $this->getResponse($result, 400, $result['message']);
}
if (mb_strlen($phoneNumber) != 12) {
$result['error'] = true;
$result['message'] = 'Введите номер телефона';
return $this->getResponse($result, 400, $result['message']);
}
$offer = $offerCacheService->getOffer(Offer::PETROL_OFFER_ID);
$entityManager = $this->getDoctrine()->getManager();
$offerRepository = $entityManager->getRepository(Offer::class);
if (!$offerRepository->isOfferFreeForUser($offer, $user)) {
return $this->getResponse(['error' => true, 'message' => 'Вы не можете получить еще один бесплатный код сегодня.'], 403, 'Вы не можете получить еще один бесплатный код сегодня.');
}
$offerOrder = $this->createNewOfferOrder(
$request,
Offer::PETROL_OFFER_ID,
$codesCount,
$user
);
if (!$offerOrder) {
return $this->getResponse([
'error' => true,
'message' => 'Акция не действительна.'
], 403, 'Акция не действительна.');
}
$orderOptionList = [
['name' => EntityOption::OPTION_CAR_NUMBER, 'value' => $carNumber],
['name' => EntityOption::OPTION_PHONE_NUMBER, 'value' => $phoneNumber]
];
foreach ($orderOptionList as $item) {
$orderOption = new EntityOption();
$orderOption->setEntityTypeID(EntityOption::ORDER_ENTITY_TYPE);
$orderOption->setEntityID($offerOrder->getID());
$orderOption->setName($item['name']);
$orderOption->setValue($item['value']);
$entityManager->persist($orderOption);
}
$entityManager->flush();
$codeCost = $offerRepository->getCodeCost($offer);
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
if ($offerFreeForUser || $user->getFullBalance() >= $codeCost * $codesCount) {
$codeList = $paymentService->createCode($offerOrder, $codesCount, true);
if(empty($codeList)) {
return $this->getResponse([], 404);
}
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
return $this->getResponse([
'code' => $codeList[0],
'enoughBalance' => $user->getFullBalance() >= $codeCost,
'moreFree' => $offerFreeForUser
], 200);
}
return $this->getResponse([], 403);
}
/**
* Покупка кода трайпл через баланс
* @Route("/mobile/api/azs-triple/buy/code/balance", methods={"POST"});
* @OA\Response(
* response=200,
* description = "Покупка кода трайпл",
* @OA\Schema(
* @OA\Property(property = "code", type = "varchar", description = "Промокод"),
* @OA\Property(property = "enoughBalance", type = "boolean", description = "достаточно ли средств на счету для покупки еще одного кода"),
* @OA\Property(property = "moreFree", type = "bool", description = "Доступен ли еще халявный код"),
* type="object",
* example = { "message": "Ваш промокод: <span>52-467</span><br>", "enoughBalance": true, "moreFree": true }
* )
* )
* @OA\Response(
* response = 400,
* description = "Wrong data"
* )
* @OA\Response(
* response = 403,
* description = "Недостаточно денег на балансе"
* )
* @OA\Response(
* response = 404,
* description = "Юзер не найден"
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Schema (
* type="object",
* @OA\Property(
* property="carNumber",
* required=true,
* description="Номер авто",
* @OA\Schema(type="string"),
* ),
* @OA\Property(
* property="phoneNumber",
* required=true,
* description="Номер телефона",
* @OA\Schema(type="string"),
* ),
* ),
* ),
* ),
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function buyPetrolCodeBalanceAction(
Request $request,
OfferCacheService $offerCacheService,
PaymentService $paymentService,
SubscriptionService $subscriptionService,
UserGetter $userGetter
) {
$user = $userGetter->get();
$data = json_decode($request->getContent());
$carNumber = $data->carNumber;
$phoneNumber = $data->phoneNumber;
$codesCount = 1;
$result = [];
if (mb_strlen($carNumber) != 4) {
$result['error'] = true;
$result['message'] = 'Введите номер авто';
return $this->getResponse($result, 400, $result['message']);
}
if (mb_strlen($phoneNumber) != 12) {
$result['error'] = true;
$result['message'] = 'Введите номер телефона';
return $this->getResponse($result, 400, $result['message']);
}
$offer = $offerCacheService->getOffer(Offer::PETROL_OFFER_ID);
$entityManager = $this->getDoctrine()->getManager();
$offerRepository = $entityManager->getRepository(Offer::class);
$codeCost = $offerRepository->getCodeCost($offer);
$amount = $codeCost * $codesCount;
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
$offerOrder = $this->createNewOfferOrder(
$request,
Offer::PETROL_OFFER_ID,
$codesCount,
$user,
SiteController::DEVICE_TYPE_MOBILE_APP,
OfferOrder::METHOD_BALANCE
);
if (!$offerOrder) {
return $this->getResponse([
'error' => true,
'message' => 'Акция не действительна.'
], 403, 'Акция не действительна.');
}
$isSubscriber = $subscriptionService->isSubscriber($user);
if (!$offerFreeForUser && !$isSubscriber && $amount > $user->getFullBalance()) {
return $this->getResponse([
'error' => true,
'message' => 'Недостаточно денег на балансе.'
], 403, 'Недостаточно денег на балансе.');
}
$orderOptionList = [
['name' => EntityOption::OPTION_CAR_NUMBER, 'value' => $carNumber],
['name' => EntityOption::OPTION_PHONE_NUMBER, 'value' => $phoneNumber]
];
foreach ($orderOptionList as $item) {
$orderOption = new EntityOption();
$orderOption->setEntityTypeID(EntityOption::ORDER_ENTITY_TYPE);
$orderOption->setEntityID($offerOrder->getID());
$orderOption->setName($item['name']);
$orderOption->setValue($item['value']);
$entityManager->persist($orderOption);
}
$entityManager->flush();
$codeList = $paymentService->createCode($offerOrder, $codesCount, true);
if (empty($codeList)) {
return $this->getResponse([], 405);
}
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
return $this->getResponse([
'code' => $codeList[0],
'enoughBalance' => $user->getFullBalance() >= $codeCost,
'moreFree' => $offerFreeForUser
], 200);
}
/**
* Покупка кода трайпл через bepaid
* @Route("/mobile/api/azs-triple/buy/code/card", methods={"POST"});
* @OA\Response(
* response=200,
* description = "Покупка кода трайпл",
* @OA\Schema(
* @OA\Property(property = "code", type = "varchar", description = "Промокод"),
* @OA\Property(property = "enoughBalance", type = "boolean", description = "достаточно ли средств на счету для покупки еще одного кода"),
* @OA\Property(property = "moreFree", type = "bool", description = "Доступен ли еще халявный код"),
* type="object",
* example = { "message": "Ваш промокод: <span>52-467</span><br>", "enoughBalance": true, "moreFree": true }
* )
* )
* @OA\Response(
* response = 400,
* description = "Wrong data"
* )
* @OA\Response(
* response = 401,
* description = "Ошибка оплаты"
* )
* @OA\Response(
* response = 402,
* description = "Карта не найдена"
* )
* @OA\Response(
* response = 403,
* description = "Акция не действительна"
* )
* @OA\Response(
* response = 404,
* description = "Юзер не найден"
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Schema (
* type="object",
* @OA\Property(
* property="carNumber",
* required=true,
* description="Номер авто",
* @OA\Schema(type="string"),
* ),
* @OA\Property(
* property="phoneNumber",
* required=true,
* description="Номер телефона",
* @OA\Schema(type="string"),
* ),
* ),
* ),
* ),
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function buyPetrolCodeCardAction(
Request $request,
OfferCacheService $offerCacheService,
BePaidService $bePaidService,
PaymentService $paymentService,
UserGetter $userGetter
) {
$user = $userGetter->get();
$data = json_decode($request->getContent());
$carNumber = $data->carNumber;
$phoneNumber = $data->phoneNumber;
$codesCount = 1;
$result = [];
if (mb_strlen($carNumber) != 4) {
$result['error'] = true;
$result['message'] = 'Введите номер авто';
return $this->getResponse($result, 400, $result['message']);
}
if (mb_strlen($phoneNumber) != 12) {
$result['error'] = true;
$result['message'] = 'Введите номер телефона';
return $this->getResponse($result, 400, $result['message']);
}
$offer = $offerCacheService->getOffer(Offer::PETROL_OFFER_ID);
$entityManager = $this->getDoctrine()->getManager();
$offerRepository = $entityManager->getRepository(Offer::class);
$codeCost = $offerRepository->getCodeCost($offer);
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
$offerOrder = $this->createNewOfferOrder(
$request,
Offer::PETROL_OFFER_ID,
$codesCount,
$user,
SiteController::DEVICE_TYPE_MOBILE_APP
);
if (!$offerOrder) {
return $this->getResponse(['error' => true, 'message' => 'Акция не действительна.'], 403, 'Акция не действительна.');
}
$orderOptionList = [
['name' => EntityOption::OPTION_CAR_NUMBER, 'value' => $carNumber],
['name' => EntityOption::OPTION_PHONE_NUMBER, 'value' => $phoneNumber]
];
foreach ($orderOptionList as $item) {
$orderOption = new EntityOption();
$orderOption->setEntityTypeID(EntityOption::ORDER_ENTITY_TYPE);
$orderOption->setEntityID($offerOrder->getID());
$orderOption->setName($item['name']);
$orderOption->setValue($item['value']);
$entityManager->persist($orderOption);
}
$entityManager->flush();
if (!$offerFreeForUser) {
$creditCard = $entityManager->find(CreditCard::class, $data->cardID);
if (!$creditCard || !$creditCard->isOwner($user->getID())) {
return $this->getResponse([],402);
}
$result = $bePaidService->checkoutByToken($offerOrder, $creditCard->getID());
if (!$result) {
return $this->getResponse([],401);
}
$bePaidService->createBePaidPaiment($offerOrder, $result);
$entityManager->flush();
}
$codeList = $paymentService->createCode($offerOrder, $codesCount,false);
if (empty($codeList)) {
return $this->getResponse([], 405);
}
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
return $this->getResponse([
'code' => $codeList[0],
'enoughBalance' => $user->getFullBalance() >= $codeCost,
'moreFree' => $offerFreeForUser
], 200);
}
/**
* Бесплатный ли код
* @Route("/mobile/api/offer/is-free", methods={"POST"});
* @OA\Response(
* response=200,
* description = "Бесплатный ли код для юзера",
* @OA\Schema(
* @OA\Property(property = "isOfferFree", type = "boolean", description = "Бесплатный ли код для юзера"),
* type="object",
* )
* )
* @OA\Response(
* response = 404,
* description = "Юзер не найден"
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Schema (
* type="object",
* @OA\Property(
* property="offerID",
* required=true,
* description="id акции",
* @OA\Schema(type="integer"),
* ),
* ),
* ),
* ),
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Offer")
*/
public function isOfferFreeAction(
Request $request,
OfferCacheService $offerCacheService,
UserGetter $userGetter
): JsonResponse {
$user = $userGetter->get();
$data = json_decode($request->getContent());
$offerID = $data->offerID;
$offer = $offerCacheService->getOffer($offerID);
$entityManager = $this->getDoctrine()->getManager();
$offerRepository = $entityManager->getRepository(Offer::class);
$offerFreeForUser = $offerRepository->isOfferFreeForUser($offer, $user);
return $this->getResponse(['isOfferFree' => $offerFreeForUser], 200);
}
/**
* Список адресов
* @Route("/mobile/api/v2/geo-locations/{offerID}", methods={"GET"});
* @OA\Tag(name="Geo locations")
* @OA\Response(
* response=200,
* description = "Список адресов",
* @OA\Schema(
* @OA\Property(property = "places", type = "object", description = "Список адресов",
* @OA\Property(property = "description", type = "string", description = "Описание"),
* @OA\Property(property = "city", type = "string", description = "Город"),
* @OA\Property(property = "street", type = "string", description = "Улица"),
* @OA\Property(property = "house", type = "string", description = "Дом"),
* @OA\Property(property = "latitude", type = "string", description = "Широта"),
* @OA\Property(property = "longitude", type = "string", description = "Долгота"),
* @OA\Property(property = "workingHours", type = "string", description = "Время работы"),
* @OA\Property(property = "id", type = "iteger", description = "ID"),
* ),
* )
* )
* @OA\Parameter(
* name="offerID",
* in="path",
* description="ID акции",
* @OA\Schema(type="string"),
* )
*/
public function getGeoLocationsInfoAction(
OfferCacheService $offerCacheService,
GeoLocationService $geoLocationService,
PhoneService $phoneService,
$offerID
) {
$entityManager = $this->getDoctrine()->getManager();
$offer = $offerCacheService->getOffer($offerID, false, true);
if (!$offer) {
$offer = $entityManager->find(Offer::class, $offerID);
}
return $this->getResponseWithoutUser([
'places' => $geoLocationService->getPlace($offer),
], 200);
}
/**
* Список телефонов
* @Route("/mobile/api/v2/phone-numbers/{offerID}", methods={"GET"});
* @OA\Tag(name="Phone numbers")
* @OA\Response(
* response=200,
* description = "Список телефонов",
* @OA\Schema(
* @OA\Property(property = "phones", type = "object", description = "Список телефонов",
* @OA\Property(property = "number", type = "string", description = "Номер"),
* @OA\Property(property = "link", type = "string", description = "Линк"),
* @OA\Property(property = "label", type = "string", description = "Ярлык"),
* @OA\Property(property = "id", type = "integer", description = "ID"),
* @OA\Property(property = "geoLocationId", type = "integer", description = "geo location id"),
* ),
* )
* )
* @OA\Parameter(
* name="offerID",
* in="path",
* description="ID акции",
* @OA\Schema(type="string"),
* )
*/
public function getPhoneNumbersInfoAction(
OfferCacheService $offerCacheService,
PhoneService $phoneService,
$offerID
) {
$entityManager = $this->getDoctrine()->getManager();
$offer = $offerCacheService->getOffer($offerID, false, true);
if (!$offer) {
$offer = $entityManager->find(Offer::class, $offerID);
}
return $this->getResponseWithoutUser([
'phones' => $phoneService->getPhone($offer),
], 200);
}
/**
* Список акций по категории
* @Route("/mobile/api/v2/oplati/category/{categoryID}/{pageNumber}", methods={"GET"});
* @OA\Response(
* response = 200,
* description = "Список акций по категории",
* @OA\Schema(
* @OA\Property(property = "offers", type = "object", description = "акции",
* @OA\Property(property = "ID", type = "integer", description = "ID акции"),
* @OA\Property(property = "name", type = "varchar", description = "название акции"),
* @OA\Property(property = "regularPrice", type = "decimal", description = "обычная цена"),
* @OA\Property(property = "offerPrice", type = "decimal", description = "цена со скидкой"),
* @OA\Property(property = "discountPercent", type = "varchar", description = "процент скидки"),
* @OA\Property(property = "saleCount", type = "integer", description = "количество проданных кодов"),
* @OA\Property(property = "imageURL", type = "varchar", description = "URL изображения для тизера"),
* @OA\Property(property = "verticalImageUrl", type = "varchar", description = "URL вертикального изображения для тизера"),
* @OA\Property(property = "activeTill", type = "datetime", description = "дата окончания акции"),
* @OA\Property(property = "rating", type = "decimal", description = "рейтинг акции"),
* @OA\Property(property = "codeCost", type = "varchar", description = "цена кода"),
* @OA\Property(property = "phoneNumbersWithoutLocation", type = "object", description = "номера телефонов без местоположения",
* @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* ),
* @OA\Property(property = "locations", type = "object", description = "местоположение",
* @OA\Property(property = "address", type = "varchar", description = "адрес"),
* @OA\Property(property = "workingHours", type = "text", description = "время роботы"),
* @OA\Property(property = "phoneNumbers", type = "object", description = "номера телефонов",
* @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
* @OA\Property(property = "label", type = "varchar", description = "метка"),
* ),
* @OA\Property(property = "description", type = "varchar", description = "описание"),
* @OA\Property(property = "geoLocation", type = "varchar", description = "массив координат"),
* ),
* @OA\Property(property = "visitCount", type = "integer", description = "количество посещений"),
* @OA\Property(property = "address", type = "varchar", description = "адрес"),
* @OA\Property(property = "offerType", type = "integer", description = "тип акции"),
* @OA\Property(property = "isFreeCode", type = "boolean", description = "бесплатный ли код"),
* @OA\Property(property = "companyLogoImage", type = "object", description = "URL лого компании"),
* ),
* @OA\Property(property = "isLast", type = "boolean", description = "последняя ли страница"),
* @OA\Property(property = "offersCount", type = "integer", description = "количество акций"),
* @OA\Property(property = "userBalance", type = "varchar", description = "баланс пользователя"),
* type="object",
* )
* )
* @OA\Parameter(
* name = "categoryID",
* in = "path",
* description = "ID категории",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "pageNumber",
* in = "path",
* description = "номер страницы",
* required = true,
* @OA\Schema(type="integer"),
*)
* @OA\Parameter(
* name = "HTTP-SLIVKi-USER-TOKEN",
* in = "header",
* description = "Токен юзера",
* required = true,
* @OA\Schema(type="string"),
*)
* @OA\Tag(name="Category")
*/
public function getOffersForOplatiAction(
OfferCacheService $offerCacheService,
MobApiCacheService $mobApiCacheService,
$categoryID,
$pageNumber
) {
ini_set('memory_limit', '4g');
$offers = [];
$tmpPageNumber = 1;
do {
$result = $mobApiCacheService->getOffersByCategoryIDCached($categoryID, $tmpPageNumber);
$offers = array_merge($offers, $result['offers']);
$tmpPageNumber++;
} while (!$result['isLast']);
$offerRepository = $this->getDoctrine()->getRepository(Offer::class);
foreach ($offers as $key => &$offerArray) {
$offer = $offerCacheService->getOffer($offerArray['ID']);
if ($offer) {
if ($offer->isBuyCodeDisable() || $offer->isHideInApp() || !$offer->isInActivePeriod()) {
unset($offers[$key]);
continue;
}
$isFreeCode = $offerRepository->isOfferFreeForUser($offer, $this->getUser());
if ($isFreeCode) {
unset($offers[$key]);
continue;
}
$offerArray['isFreeCode'] = $isFreeCode;
}
}
$offersCount = count($offers);
$offset = ($pageNumber - 1) * MobApiCacheService::OFFERS_PER_PAGE;
$offersSlice = array_slice($offers, $offset, MobApiCacheService::OFFERS_PER_PAGE);
return $this->getResponse([
'offersCount' => $offersCount,
'isLast' => ($offset + MobApiCacheService::OFFERS_PER_PAGE) >= $offersCount,
'offers' => $offersSlice
], 200);
}
}