Обратная сторона авиабилета. Как Туту.ру помогает подобрать оптимальный тариф

в 7:54, , рубрики: Алгоритмы, Анализ и проектирование систем, Блог компании Туту.ру, поисковые технологии, Программирование, Проектирование и рефакторинг, разработка, разработка алгоритмов

image

Весной 2014 года были приняты поправки к Воздушному кодексу РФ, позволяющие авиакомпаниям заключать договор на перевозку без возврата платы за проезд в случае расторжения договора. Иными словами, на рынке авиаперевозок появились невозвратные тарифы. До этих изменений авиакомпании могли лишь удерживать штраф в размере не более 25% от стоимости билета, если пассажир сдавал билет позднее, чем за сутки до вылета. Новые поправки позволили авиакомпаниям предложить пассажирам более дешевые, но невозвратные билеты.

В это же время появились бюджетные «безбагажные тарифы». На самом деле, полностью безбагажными их назвать нельзя: по закону РФ, пассажир имеет право провезти с собой до 10 кг личных вещей. И здесь есть интересный момент: закон не регулирует, каким образом пассажир перевозит эти 10 кг — в салоне самолета или в багажном отсеке. Как известно, в салон нельзя брать множество вещей: например, жидкость более 100 мл, маникюрные ножницы, пилочку и некоторые гаджеты. Даже если тариф включает провоз багажа, каждая авиакомпания сама определяет максимальный вес и размеры багажа и ручной клади на одного пассажира.

Пассажиры путались в тарифах, возникало много вопросов, связанных с провозом багажа и доплатой перевеса. В этой статье я хочу рассказать, что и как мы сделали, чтобы облегчить пользователям Tutu.ru поиск наиболее подходящих авиабилетов среди десятков доступных вариантов.

Цены на билеты многих авиакомпаний не выглядели привлекательно на фоне экономического кризиса, отнюдь не дешевеющих тарифов на поезда и появления «Победы», которая действительно сбила цены на многих направлениях. Например, до появления лоукостера я летал в Чебоксары и обратно за 8–10 тысяч рублей, а сейчас полет обойдется в 3–6 тысяч.

Осенью 2015 года авиакомпании с целью снижения цен начали активно внедрять безбагажные и невозвратные тарифы. Для нас было очень важно предоставить пользователю возможность самостоятельно выбирать интересующие его опции. Люди сталкивались с тем, что в аэропорту требуют доплатить за багаж, или что билет нельзя сдать или обменять. Мы хотели дать человеку возможность выбрать именно то, что ему нужно, максимально облегчив и ускорив поиск билетов. С другой стороны, и сами авиакомпании выдвинули своим агентам, в том числе и Туту.ру, требование показывать информацию об особенностях тарифа уже на этапе поиска.

К тому моменту мы уже показывали клиентам, можно ли сдать или обменять билеты. С багажом все оказалось сложнее.

На вкус и цвет GDS’a нет

Данные по багажу мы получаем от систем бронирования (GDS — Global Distribution System), что накладывает некоторые ограничения. Во-первых, мы работаем с несколькими GDS, а также с агрегаторами GDS, и это требует поддержки решения для каждой из систем.

Во-вторых, исторически сложились два типа авиакомпаний. Одни ограничивают максимальный багаж по весу, а другие — по числу мест. Например, «Аэрофлот» дает ограничение по числу мест, и из GDS нам поступают малоинформативные данные о багаже: «1 piece» без информации о максимальном весе.

И, наконец, самое главное. Информация об ограничениях на багаж приходит из GDS подчас тогда, когда клиент уже выбрал конкретное предложение и приступил к оформлению заказа. Мы же стремились дать клиенту возможность сравнить предложения еще на этапе выбора. Посылать уточняющие запросы к GDS, пока клиент выбирает, было бы слишком накладно и по деньгам, и по времени: на одном направлении в день могут быть десятки и сотни предложений.

Конечно, GDS не стоят на месте, и в новых версиях API интересующей нас информации приходит больше, а для некоторых GDS она доступна уже на этапе поиска. Но в большей степени это относится к крупным авиакомпаниям, которые перешли на систему брендированных тарифов (о ней речь пойдет позже). Мы используем эту информацию, но всё же этого недостаточно, чтобы сделать действительно удобный сервис. GDS не всегда следят за тем, чтобы авиакомпании вносили информацию о багаже, и уж тем более не несут ответственности, если правила изменились, а информацию в GDS ещё не внесли, или если при внесении была допущена ошибка. Поэтому для многих небольших и региональных авиакомпаний этой информации в GDS нет. Данные об ограничениях на ручную кладь не вносит практически ни одна авиакомпания.

Есть и совсем сложные случаи, например, авиакомпания Оренбуржье заносит в GDS информацию, что разрешен багаж весом не более 10 кг. При этом рейсы выполняются на самолётах Ан-2, у которых нет багажного отделения, поэтому взять запрещенные к провозу в салоне вещи не получится. В данном случае мы показываем пользователям, что на рейсе нет багажа, но разрешена ручная кладь весом до 10кг.

С возможностью обмена и возврата на тот момент все было немного лучше, но лишь немного. Информация о возможности возврата и обмена билета (как и о багаже) нередко была доступна только по выбранному предложению, а не на этапе поиска. Причем данные из GDS приходили нам не в виде удобного для обработки XML, а в виде «простыни» текста, в которой единственное форматирование — разное количество пробелов в начале строки.

Пример текста тарифных правил

UNLESS OTHERWISE SPECIFIED
  CHANGES
    ANY TIME
      CHANGES PERMITTED.
         NOTE -
          CHARGE EUR 50.00 FOR REISSUE/REVALIDATION
          ----------
          NO CHILD DISCOUNT APPLIES / INFANT WITHOUT A
          SEAT - NO CHARGE.
          ----------
          CHANGES PERMITTED BEFORE ORIGINALY SCHEDULED
          FLIGHT
          ----------
          THE TICKET MUST BE REVALIDATED OR REISSUED AT THE
          SAME TIME WHEN THE BOOKING IS CHANGED.
          ----------
          THE CHANGE FEE APPLIES PER TRANSACTION. THE
          CHANGE FEE MUST BE COLLECTED AT THE SAME TIME
          WHEN THE BOOKING IS CHANGED
          ----------
          WHEN COMBINING ON A HALF ROUNDTRIP BASIS THE
          PENALTY RULES FOR EACH FARE COMPONENT APPLY.
          CHARGE HIGHEST PENALTY FEE OF ALL CHANGED FARE
          COMPONENTS.
          ----------
          IN CASE OF REISSUE-REROUTING/UPGRADE-
          WHEN THE FIRST FARE COMPONENT IS CHANGED CURRENT
          FARES VALID AT THE TIME OF REISSUE MUST BE USED.
          OTHERWISE HISTORICAL FARES VALID AT THE TIME OF
          ISSUANCE OF PREVIOUS TICKET MUST BE USED.
          -----------
          IN CASE OF RE-ROUTING -
          NEW BASE FARE MUST BE EQUAL OR HIGHER THAN
          PREVIOUS FARE AND FARE DIFFERENCE HAS TO BE
          COLLECTED. WHEN NEW ITINERARY RESULTS IN LOWER
          FARE - UPGRADE TO A LEVEL WHICH IS AT LEAST EQUAL
          AS THE BASE FARE OF PREVIOUS TICKET.
          ----------
          FARE DIFFERENCE AND A CHANGE FEE WILL BE
          COLLECTED.
          ----------
          CHANGES PERMITTED ONLY WITHIN SAME OR HIGHER
          BRAND.
          ----------
          ALL PROVISIONS OF THE NEW FARE MUST BE COMPLIED
          WITH.
          ----------
          ONCE A FARE COMPONENT HAS BEEN COMPLETED FARE
          BREAK POINTS CAN NOT BE CHANGED.
          ----------
          THE NON-REFUNDABLE AMOUNT -FARE/YQ/YR - OF THE
          ORIGINAL TICKET WILL BE NON-REFUNDABLE AND HAS TO
          BE INSERTED IN THE ENDORSEMENT BOX OF THE NEW
          TICKET.
          ----------
          NO SHOW - NOT PERMITTED
          ----------
          IF PASSENGER IS NO SHOW - FINNAIR HAS A RIGHT
          TO CANCEL ONWARD OR RETURN RESERVATION.
          ----------
          NAME CHANGES PERMITTED. APPLIES ONLY TO ITINERARY
          WITH FLIGHTS MARKETED AND OPERATED BY AY.
  CANCELLATIONS
    BEFORE DEPARTURE
      CHARGE 50 PERCENT FOR CANCEL/REFUND.
         NOTE -
          NO CHILD DISCOUNT APPLIES / INFANT WITHOUT A
          SEAT - NO CHARGE.
          ----------
          UNUSED YR/YQ FEES WILL BE REFUNDED.
          ----------
          THE MOST RESTRICTIVE REFUND CONDITIONS APPLY WHEN
          COMBINING ON A HALF ROUNDTRIP BASIS.
          ----------
          FULL REFUND PERMITTED IN CASE OF-
          - REJECTION OF VISA. EMBASSY STATEMENT REQUIRED.
          - DEATH OF PASSENGER OR FAMILY MEMBER.
          WAIVERS MUST BE EVIDENCED BY DEATH CERTIFICATE.
          ----------
          NO SHOW - NOT PERMITTED.
    AFTER DEPARTURE
      TICKET IS NON-REFUNDABLE.
         NOTE -
          UNUSED YR/YQ FEES ARE REFUNDABLE.
          ----------
          THE MOST RESTRICTIVE REFUND CONDITIONS APPLY WHEN
          COMBINING ON A HALF ROUNDTRIP BASIS.
          ----------
          NO SHOW - NOT PERMITTED.

Мы уже знали, как определять по таким текстам условия обмена и возврата и даже до какого срока они возможны: у одних компаний до окончания регистрации, у других — не позднее, чем за 48 часов до вылета и т.д. Для этого мы сохраняли тексты тарифных правил по всем заказам, которые сделали наши пользователи. Затем простая, но автоматическая система находила в сохраненных текстах меняющиеся от заказа к заказу части, вроде суммы штрафа за возврат, и вырезала их. От полученного текста с пропусками вычислялся некоторый хеш, и затем исходные, т.е. без пропусков, правила группировались по этому хешу. Практически каждый заказ после оплаты просматривался операторами нашего контакт-центра. Операторы читали конкретный текст правил, определяли в нем условия обмена и возврата, и ставили в соответствии с хешем этого текста удобоваримое описание Например, «Возврат возможен не более чем за 24 часа до вылета». Впоследствии, когда очередной клиент делал заказ, и для текста тарифных правил вычислялся хеш, мы уже могли показать понятные условия обмена и возврата. Но на этапе поиска это решение опять-таки не подходило: информация приходила уже на этапе оформления заказа.

Новая система тарифов

Разработку нового решения мы начали с простейшей системы, умеющей показывать ограничения только для авиакомпаний S7 и Уральские Авиалинии. Они первыми в России перешли на новую систему тарифов. Как работала старая система: допустим, авиакомпания открывает продажу на рейс, который будет выполнять самолет на 300 человек. Эти 300 мест разбиваются на 10-20 так называемых классов бронирования (не путать с классами обслуживания: Эконом, Бизнес, Первый и т.д.). Каждый класс фактически определяет услуги, предлагаемые авиакомпанией: норму провоза багажа, включенное питание, возможность выбора места, а также обмена и возврата билета и т.д. В зависимости от набора услуг меняется и стоимость тарифов в этом классе. Причем на разных рейсах внутри одного класса бронирования набор услуг может отличаться.

Новая система тарифных семейств (еще их называют брендированными тарифами) проще. Классы бронирования остаются, но за доступные опции отвечает код тарифа, а именно его название; конкретный класс бронирования и рейс на это уже не влияют. Например, у S7 есть 4 тарифных семейства: Эконом Базовый — невозвратный и только с ручной кладью, Эконом Гибкий — возвратный и с включенным багажом 23 кг, Бизнес Базовый — невозвратный, без доступа в бизнес-зал и с одним местом багажа до 32 кг и Бизнес Гибкий — возвратный, с доступом в бизнес-зал и двумя местами по 32 кг. Отличить их друг от друга очень просто: в коде тарифов семейства Базовый есть подстрока BS, а в коде тарифов семейства Гибкий — подстрока FL. Класс бронирования уже не отвечает за доступные опции. Например, если осталось 9 мест по классу бронирования N, то на эти места можно купить билеты и с багажом, и без него.

Вскоре еще несколько крупных российских авиакомпаний перешли на тарифные семейства. Мы поставили перед собой цель предоставить пользователю возможность выбора в подавляющем большинстве случаев (даже когда нужно долететь из Якутска в Белую Гору). Кроме того, с тарифными семействами все просто только до тех пор, пока клиент летит собственными рейсами авиакомпании, которой эти тарифы принадлежат. Как только возникает пересадка со сменой авиакомпании, начинаются исключения.

Разработка собственного решения

Разработку собственного решения мы начали с тщательного анализа. Мы пошли к операторам нашего контакт-центра и долго выпытывали у них, какая авиакомпания отвечает за предоставление клиенту тех или иных опций. Немного отвлекаясь, отмечу, что в процессе покупки билета участвует до трех авиакомпаний сразу: та, которой принадлежит самолет (мы называем её оперирующей), та, чей номер рейса указан на вашем билете (маркетинговая), и та, которая продала вам билет на своем бланке и будет распределять деньги между остальными (валидирующая).

image

Например, вы летите из Челябинска в Рим с пересадкам в Москве и Дюссельдорфе. На сегменте Челябинск — Москва вы летите собственным рейсом авиакомпании S7 — S7-8. На сегменте Москва — Дюссельдорф самолет принадлежит S7, но на билете у вас указан рейс авиакомпании AirBerlin и номер AB-5911. Значит, на этом сегменте оперирующий перевозчик — S7, а маркетинговый — AirBerlin и такая комбинация называется codeshare-соглашением. Дальше из Дюссельдорфа до Рима вы летите собственным рейсом AB-8718 AirBerlin. При этом весь перелет из Челябинска в Рим оформлен одним электронным билетом, который продала вам авиакомпания AirBerlin несмотря на то, что на первом сегменте вы летите собственным рейсом S7. В данном случае AirBerlin является валидирующим перевозчиком, т.е. авиакомпанией, которая по взаимному соглашению с другими авиакомпаниями-участниками перелета оформляет билет и будет осуществлять взаиморасчеты. Такое соглашение называется interline. Чаще всего валидирующая компания является и маркетинговой на каком-нибудь из сегментов, однако бывают случаи, когда отличаются все три авиакомпании — оперирующая, маркетинговая и валидирующая.

Так вот, на этапе сбора информации мы выяснили, что обычно опции задает маркетинговая авиакомпания — та, чей номер рейса указан у вас в билете и которой принадлежит тариф. Правда, может оказаться, что самолет оперирующей авиакомпании не рассчитан под нормы маркетинговой. Это относится в основном к багажу. Например, в самолете может не быть багажного отсека, как, например, в Ан-2. Кроме того, на рейсах с пересадками на разных сегментах могут быть разные правила провоза багажа и сомнительно, что пассажир будет рад где-нибудь в промежуточном пункте избавляться от лишних 2 кг багажа. С другой стороны, «Аэрофлот» чаще всего гарантирует своим клиентам свои нормы багажа, даже если у оперирующей авиакомпании нормы меньше.

Даже в рамках одной авиакомпании часто встречаются исключения на конкретном направлении и даже по конкретному аэропорту. Например, у Qatar Airways норма бесплатного провоза багажа в эконом-классе для рейсов из России — 1 место весом не более 30 кг, а для рейсов из США — 2 места не более чем по 23 кг каждое. У многих компаний меняются нормы багажа на рейсах на Ближний Восток — в Турцию и Египет. Есть и совсем исключительные случаи, когда на каком-нибудь отдельном направлении право устанавливать нормы багажа и взимать плату за их превышение выкупает не относящаяся к авиакомпании фирма.

Параллельно с накоплением знаний о проблеме мы начали собирать ту информацию, которая нам приходит из GDS, в надежде вытащить из нее закономерности. Нам нужно было поддерживать нашу базу знаний, поскольку тарифные правила авиакомпаний меняются постоянно. Идея не сработала: только для одного «Аэрофлота» с довольно стандартизированными правилами мы получили дерево решений, занявшее лист A4 очень мелким шрифтом.

Внедряем механизм FRule

В итоге мы решили использовать механизм FRule, который уже давно использовался в нашей команде и изначально представлял собой механизм гибкого конфигурирования. Позднее мы успешно применили его в нескольких прикладных задачах.

FRule — это система гибкого конфигурирования, которую мы разработали для наших сервисов. Впоследствии названием FRule стали называть архитектурный подход для описания системы, в которой необходимо гибко задавать и выбирать частный случай среди множества общих случаев.

Представляет он из себя вот что. Допустим, у нас есть функция от N переменных. Сначала мы задаем

$F(x_1, x_2, ldots, x_N)=C=Const$

Затем фиксируем какую-нибудь переменную $x_i=z$ и задаем

$F(x_1, x_2, ldots, x_{i-1}, z, x_{i+1}, ldots , x_N)=C_1$

А, например, для $x_i=w$ задаем

$F(x_1, x_2, ldots, x_{i-1}, w, x_{i+1}, ldots , x_N)=C_2$

Дальше фиксируем $x_j$ и продолжаем процесс, по сути переходя от общего к частному. Затем на вход этой функции подаются какие-либо данные. В случае с определением нормы багажа это данные полетного сегмента: откуда, куда, когда, какие авиакомпании, код тарифа и т. д. Здесь есть один нюанс: переменные в функции $F$ могут быть неравнозначны между собой. Рассмотрим простейший случай функции двух переменных $F(x_1, x_2)$. Пусть мы задали

$ F(x_1, x_2)=left{ begin{array}{ll} 1 & textrm{если } x_1=0\ 2 & textrm{если } x_2=0\ 3 & textrm{если } x_1=1, x_2=1\ 4 & textrm{если } x_1=1, x_2=2\ 5 & textrm{если } x_1=2, x_2=1\ 6 & textrm{иначе} end{array} right. $

Для заданной таким образом функции $F(0, 0)$ не определено. Чтобы избавиться от таких неопределенностей мы используем предустановленную систему приоритетов: мы говорим, что значения определенные для набора, в котором фиксирована только $x_1$, приоритетнее, чем значения, определенные для набора, в котором фиксирована только $x_2$. При этом значения, определенные для набора, в котором фиксированы и $x_1$, и $x_2$, приоритетнее значений, определенных для наборов, в которых фиксирована только одна из переменных. Таким образом, $F(0, 0)=1$. Эти приоритеты обычно проистекают из предметной области и бизнес-логики, например, очевидно, что указание города приоритетнее указания страны.

Прежде чем разрабатывать решение для продакшена, мы решили проверить его «на коленке», и не зря. Физически механизм FRule представляет собой набор соответствий «параметры — значение» функции $F$, хранящийся в MySQL. Исходя из приоритетов наборов при обращении к FRule, выполняются нехитрые запросы до того момента, пока не будет найден результат, который затем кэшируется. Для первоначальных задач конфигурирования этого более чем хватало. Выяснилось, что задачу определения опций необходимо дорабатывать. Оказалось, что для расчета багажа всех предложений по маршруту Москва — Санкт-Петербург на один день потребовалось 20 минут. Пересмотрев систему приоритетов и убрав оттуда избыточные и неиспользуемые наборы, мы сократили его до 3 минут. Однако это все равно было много.

Узким местом оказалась MySQL, а точнее, количество и накладные расходы на запросы, которые мы ей посылали в том случае, если результата для конкретного предложения нет в кэше. Для каждого предложения мы начинали искать совпадения: сначала по наиболее частному набору, затем переходили ко все более и более общим наборам. Приведем пример, чтобы представить масштаб бедствия. Сейчас у нас 288 наборов с разным приоритетом. Предложений по поиску на одно направление могут быть сотни и тысячи, и несмотря на то, что не все они будут показаны пользователю, обрабатывать требуется все.

Для решения этой проблемы мы решили перенести вычисления на бэкенд. Вычисление значений состоит из двух частей. Первая — это построение некой специальным образом организованной структуры. Вторая — вычисление значений исходя из этой структуры.

Рассмотрим, что за структуру мы строим. Вернемся к нашему примеру функции $F$ от двух переменных. Для нее мы определили такой приоритет наборов:

$[x_1, x_2], [x_1], [x_2], []$

Напомню, что самый верхний набор является самым частным — если для всех входящих в него полей есть явно заданное значение функции, то оно и будет использовано. По сути, мы преобразуем плоскую табличку, хранящуюся в базе данных, в набор деревьев для каждого набора переменных. Рассмотрим процесс построения набора деревьев для набора $[x_1, x_2]$. Сначала запросом типа

SELECT * FROM t WHERE x1 IS NOT NULL AND x2 IS NOT NULL

Мы получаем из базы данных все строки, для которых определены и $x_1$, и $x_2$. Затем группируем их по значению переменной $x_1$. Затем правила в каждой группе группируем по переменной $x_2$, и т.д. Таким образом, для наиболее частного набора $[x_1, x_2]$ строим такой набор деревьев:

[
    [1] =>  [
        [1] => 3,
        [2] => 4
    ],
    [2] =>  [
        [1] => 5
    ]
]

Эту процедуру повторяем для каждого набора переменных. Например, для набора $[x_1]$ мы выберем правила запросом

SELECT * FROM t WHERE x1 IS NOT NULL AND x2 IS NULL

Неиспользуемые в наборе переменные помечаем значением NOT_SPECIFIED. После того, как получены наборы деревьев для каждого набора параметров, складываем их в один массив с сохранением порядка приоритетов наборов.

В итоге для функции $F$ получаем такой набор деревьев:

[
    [
        [1] =>  [
            [1] => 3,
            [2] => 4
        ],
        [2] =>  [
            [1] => 5
        ]
    ],
    [
        [0] => [
            [NOT_SPECIFIED] => 1
        ]
    ],
    [
        [NOT_SPECIFIED] => [
            [0] => 2
        ]
    ],
    [
        [NOT_SPECIFIED] => [
            [NOT_SPECIFIED] => 6
        ]
    ]
]

Структура для проверки построена, остается лишь пропустить через нее входные данные функции. Перебираем деревья сверху вниз и обходим каждое их них, для каждой переменной выбирая ту ветвь, которая соответствует её значению. Если же такой ветви нет, то пробуем пройти по ветви NOT_SPECIFIED. Если значение найдено, то возвращаем его.

Обратная сторона авиабилета. Как Туту.ру помогает подобрать оптимальный тариф - 29

Кэширование данных

На самом деле, строить набор деревьев при каждом вызове FRule было бы странно — его можно один раз посчитать и запомнить. Кроме того, у нас несколько серверов, на которых может выполняться эта задача, так что неплохо было бы еще и передавать набор деревьев между ними. Для кэширования и синхронизации между серверами вычисленных значений мы используем memcache. Однако обращаться каждый раз к memcache, чтобы забрать из него набор, дорого — реальные наборы занимают несколько мегабайт и нет никакого смысла гонять их по сети. Поэтому кроме memcache мы используем еще и APCu (а до перехода на PHP7 — xCache). Если сервер построил или получил из memcache набор деревьев, то он сохранит его в APCu и в следующий раз возьмет оттуда.

Возникает еще проблема с синхронизацией изменений — ситуация, когда правила были изменены и на одном севере они применились, а на другом всё считается по-прежнему из-за старых данных в APCu, недопустима. Для решения этой проблемы мы храним в memcache текущий номер версии структуры. Он состоит из временной метки и версии кода, который построил структуру. Такая же метка хранится также в APCu каждого из бэкендов. Перед тем, как взять деревья из APCu, бэкенд проверяет, совпадает ли номер версии в его APCu с сохраненным в memcache. И если не совпадает, то перестраивает структуру и устанавливает в memcache и свой APCu новый номер. При редактировании правил из админки номер версии в memcache просто сбрасывается: таким образом все бэкенды узнают о том, что надо перестроить структуру.

Что мы в итоге получили

Обратная сторона авиабилета. Как Туту.ру помогает подобрать оптимальный тариф - 30

У пользователя, для выдачи которого нет данных в кэше, вычисление данных о тарифных опциях занимает от долей секунды до 15-20 секунд на очень больших выдачах, где много рейсов и много пересадок. Вскоре после выхода бэкенд-части на бой наши фронтенд-разработчики сделали фильтры по багажу, возможности обмена и возврата, благодаря чему клиенты могут сразу отсечь неинтересные им предложения. По статистике, новыми фильтрами пользуются порядка 10% посетителей, а интересуются параметрами багажа и возвратности 15%. Теперь пользователям не приходится долго искать тот же рейс, но с багажом или возможностью возврата. Мы научились отображать краевые случаи, например, авиакомпания «Полярные авиалинии» суммарно разрешает взять 20 кг багажа, из которых не более 5 — в ручную кладь.

Вот пример результатов нашей работы. Мы помогаем купить билеты не только по традиционным маршрутам в Санкт-Петербург, Сочи или Симферополь, но у нас довольно популярны и региональные направления. Посмотрим на рейс авиакомпании Алроса из Красноярска в Мирный:

Обратная сторона авиабилета. Как Туту.ру помогает подобрать оптимальный тариф - 31

Сразу при поиске можно посмотреть все подробности: и возможность обмена или возврата, и норму багажа, и норму ручной клади, и даже особую схему их расчета, когда пассажир может взять именно суммарно 20 кг багажа. Зачастую такую информацию можно получить или на сайте авиакомпании, или только из личного общения с представителем авиакомпании. Стоит отметить, что это не только наше желание показать всю информацию честно и четко — все эти параметры очень важны самим покупателям при выборе, и без них мы получаем обратную связь от пользователей, что этого не хватает.

С технической точки зрения, мы получили базу знаний, содержащую порядка 10 000 правил, что в значительной мере покрывает наш ассортимент. Конечно, остаются еще предложения, для которых у нас пока не заведены правила — особенно это касается тех направлений и предложений, по которым у нас редко ищут и покупают. Для того, чтобы контент-менеджеры могли оперативно узнавать о таких предложениях, мы разработали специальную систему логирования: стоит прийти значению той или иной опции, которая отличается от значения в нашей базе знаний или которой в ней нет, в течение часа контент-менеджеры об этом узнают. Для поддержания базы в актуальном состоянии наши контент-менеджеры постоянно взаимодействуют с авиакомпаниями и проверяют, нет ли изменений на официальных сайтах, созваниваются с их представителями.

Созданный механизм достаточно легко расширяется: мы можем начать показывать информацию о размере доплаты за багаж, возможности провоза спортивного снаряжения, количестве начисляемых бонусных миль и т. д.

Автор: greblin

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js