Интернет-магазин "День Сурка"

Главные вкладки

Аватар пользователя Andruxa Andruxa 28 июня 2015 в 15:15

http://www.densurka.ru

Я делал бэкенд, дизайном и вёрсткой занимались другие люди.
Плюс перенос всего контента из drupal 6 + ubercart 2.

26000 сущностей product,
3000 product display,
116 типов товаров (отдельная сущность, в которой хранятся настройки атрибутов, отображения и т.д)
1500 цветов,
600 размеров,
90 особенностей товаров - по ним можно делать выборку в каталоге,
225 характеристик товаров, пока выполняют описательную функцию, планируется добавлять их в индекс в зависимости от типа товара

Цвета и размеры приведены к общепонятным.
Например: цвет Alert, у одного производителя он может быть красным, а у другого - оранжевым, и т.п. Все цвета приведены к красному-желтому-зелёному, понятным пользователям.
Один производитель может шить одежду-маломерку, его размер М будет соответствовать 46 размеру, а другой - наоборот: его М - это 50й размер. Это мужские размеры, а есть ещё женские и детские.
Размеры приведены к буквенным S-M-L среднестатистического здорового человека, и к числовым размерам старого доброго ГОСТа.

Из особенностей:

  • сортировка и фильтрация товаров внутри дисплеев,
  • отслеживание остатков товаров по 16 магазинам розничной сети - можно отфильтровать только те товары, которые есть в определенном магазине, если покупатель захочет примерить вещь перед покупкой,
  • соответственно - синхронизация остатков и цен (в т.ч. старая цена - новая цена) с базой данных розничной сети,
  • мультидоменность: пока запущен сайт розничной сети, День Сурка, планируется перенос остальных сайтов с d6+uc2 и добавление новых. Ожидается увеличение кол-ва товаров/дисплеев до 40000/5000 сущностей.
    У каждого сайта может быть отдельный ассортимент (на уровне product и product display), своя ценовая политика (цены синхронизируются с бд сети подоменно), свои маркетинговые активности (хит продаж, товар дня и т.п),
  • общая панель администрирования заказов для всех доменов, в данный момент заказы из нового сайта на d7 синхронизируются со старой админкой остальных сайтов на d6 через services
  • существенно переработан checkout - способы доставки сгруппированы, способы оплаты привязаны к способам доставки, поля для пользовательского ввода минимизированы, в зависимости от выбранных доставки и оплаты

Что не сделано:

  • очень много не свёрстано (вопрос не ко мне)
  • планируется аяксификация фасетов, я бы ещё дополнительно поработал над их внешним видом
  • фильтрация каталога по отдельным характеристикам товаров
  • генерация файлов для Я.Маркета
ВложениеРазмер
Иконка изображения snimok_ekrana_ot_2015-06-28_152315.png1.14 МБ

Комментарии

Аватар пользователя Andruxa Andruxa 28 июня 2015 в 15:32

Там многоуровневое кеширование:

1. Кешируются панели,
2. Кешируются результаты вьюсов, сам вьюс, которым строится каталог, делает запросы к солру, откуда получает готовые значения цен, скидок, акций и т.п. - разгружая БД.
3. Entitycache.

Ну, и весь кеш лежит в Redis.

По железу - http://servers.agava.ru/server_nezavisim.shtml, тариф А1273

Аватар пользователя Valeratal Valeratal 28 июня 2015 в 15:49

хочется кейс-стадии
ну типа доклад на друпал-пати

Как и чем решались разные задачи, траблы. Как удалось не убить логистов Smile

Аватар пользователя Andruxa Andruxa 28 июня 2015 в 16:02

В прошлом году я приводил этот сайт в качестве примера каталога на кэмпе.
Тогда магазин ещё не работал, только каталожная его часть. В этом году можно будет и про магазин рассказать.

Аватар пользователя Andruxa Andruxa 28 июня 2015 в 17:10

Ну, в этом случае тоже была спешка, но не настолько суровая.
Чуть меньше года прошло от начала до запуска, и запускали - как обычно, с недоделками.

Аватар пользователя Andruxa Andruxa 30 июня 2015 в 16:42

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

Это ещё не реализовано на 100%, но заложено в структуру сайта.

Аватар пользователя Andruxa Andruxa 1 июля 2015 в 2:38

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

Например, на сайтах-ИМ, показываются только те дисплеи и товары в них, которые имеются в наличии.
На сайтах-каталогах брендов - только товары-дисплеи определенного бренда, в т.ч. отсутствующие и/или снятые с производства - в качестве архива продукции.
Есть тематические сайты - например, отдельно для экстремалов, где собраны только товары для разного рода фрирайда.
Ну и т.д.

Сегментируем ЦА - повышаем конверсию, джаст бизнес.

Аватар пользователя Andruxa Andruxa 10 ноября 2015 в 11:50

Реализовал генерацию прайс-листа для Яндекс Маркета.
Генерится по крону, раздача обновляется по мере поступления новой выгрузки.

При изменении фильтров - рестартует.

Получается вот такой xml:

Все настройки хранятся подоменно, выгрузка происходит параллельно.

Аватар пользователя Andruxa Andruxa 23 июля 2015 в 10:19

Архив ничем не поможет - сейчас богатый внутренний мир сайта обрабатывается хардкодом, не укладывается его логика в стандартные средства друпала.
Видится мне там отдельный вьюс дисплей, которым тянутся и рендерятся значения, с раскрытыми фильтрами в админке, и какой-нибудь рул для постпроцесса.
Но позже. Осенью.

Аватар пользователя artemrrr artemrrr 9 августа 2015 в 19:32

1.Нажми на ссылку "Вход Регистрация" и как появится окно, посмотри в вверх в право на Корзину
2.На странице checkout
в стиль - #payment-details label, #commerce-shipping-service-details label - добавь...

#payment-details label, #commerce-shipping-service-details label{
display:inline-block:
width:130px;
text-align:right;
}
для поля доп.инфа по доставке ширину лейбла пропиши отдельно(что бы было красиво)
#edit-customer-profile-shipping-msk-field-profile-shipping-info label{
width:100%Important;
}

3. на странице http://www.densurka.ru/news/tovar_nedeli - внизу нажми на кнопку заполнить анкету и посмотри что там с полями в модальной форме...(ужасть, именно в модальном окне а не на странице)

ну и еще конечно просто лень писать прости...

Аватар пользователя Andruxa Andruxa 9 августа 2015 в 23:48

"artemrrr" wrote:

Ок, понятно.
Ну, а что в таком случае, можно оценить на 4 (без плюса), или на 5 ?
Какая, вообще, шкала: 3+ из трёх - это высокая оценка, а из 100 - в пределах погрешности, ни о чём.
Как выглядит весь диапазон, что там на максимуме, и на минимуме?

Аватар пользователя artemrrr artemrrr 11 августа 2015 в 10:14

"Andruxa" wrote:
можно оценить на 4 (без плюса)

нет, 4-
"Andruxa" wrote:
что там на максимуме

максимум 5+

p.s. Все субъективно.

Аватар пользователя Andruxa Andruxa 2 апреля 2016 в 6:10

Доработали систему скидок.
Теперь их 3 типа:
- старая цена новая цена, это изначально синхронизируется с учетным софтом
- накопительная скидка пользователя: поле user_discount типа Integer List с предустановленными значениями
- сущность Акция, которая может быть активирована вручную или по расписанию, распространяется на любой список товаров.


Дело в том, что скидки, реализованные в commerce_discount, влияют на commerce_line_item, а не на продукт, и могут быть применены при создании заказа/корзины, а надо в каталоге.
При изменении Акции - товары, на которые она распространялась/будет распространена, помечаются на переиндексацию, поскольку надо фильтровать и сортировать каталог с учетом результирующей скидки, рассчитанной с поправкой на накопительную скидку юзера.

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

Для Анонимуса:


и для юзера с накопительной скидкой 25%


А можно сделать, например, акцию +5% к имеющейся скидке.

Аватар пользователя Anton Polikarpov Anton Polikarpov 11 июля 2016 в 16:32

Ajax facets + Pretty path в итоге работают для аяксификации выдачи, только сортировка с Search sorts не работает, вроде ее можно с помощью Search API ajax прикрутить, но с этим модулем перестают работать Ajax facets (вероятно из-за того лишь впрочем, что я не настраивал Search API ajax в собственном модуле). Но в целос аякс пашет, даже диапозоны нормально работают, не схлопываясь до одного неизменяемого значения.

Аватар пользователя Anton Polikarpov Anton Polikarpov 11 июля 2016 в 17:01

Кстати хотел спросить, вот кнопка "Добавить в корзину" при использовании панелей добавляется с помощью вьювсов (по крайней мере это наиболее распространенный метод на сколько я заметил), у вас так же? Просто интересно как оно работает со столь сложным типом материала? У вас же там можно выбрать товар на странице дисплея с помощью нескольких селектов (как это кстати реализовано?).

Аватар пользователя Andruxa Andruxa 11 июля 2016 в 20:42

Кнопка добавления в корзину - это самостоятельный content pane, на странице карточки товара их несколько: список товаров с ценами, селекты для выбора атрибутов, изображение товара, превью изображений - тоже отдельный content pane, ну и т.д.

Между ними проброшена логика на js - при смене атрибута товара - перерисовываются панели.
Тут напрашивается backbone, тем более что информация о товарах хранится в js-объектах, максимально приближенных к коллекциям, но пока нет.
Без js, разумеется, работать не будет, но и коробочное решение и так не позволит оформить заказ с отключенным js.
Поэтому на 0.5% юзеров без js - просто забили.

Кнопка добавления в корзину - дёргает аяксом сервис на сервере, которому передаёт id ноды-дисплея, id продукта и его количество.
При изменении атрибутов / перерисовке кнопки - проверяется, что товар доступен для заказа, иначе кнопка недоступна для клика.

Аватар пользователя Anton Polikarpov Anton Polikarpov 13 июля 2016 в 17:09

Здорово получилось! Но вот кстати по поводу backbone наверное не соглашусь, но только от того, что предпочитаю AngularJS, правда вот совместить ангуляр с друпалом пока не пробовал (если не считатать этого) но там от друпала давно ничего не осталось кроме разметки по-сути это чистый ангуляр вставленный в ноду. Как совмещается backbone даже представить не могу.

Аватар пользователя Andruxa Andruxa 13 июля 2016 в 17:23

Я совершенно не против ангуляра, но вот сколько не пытался выяснить у разработчиков, работающих с ним - плюсы/минусы в сравнении с бекбоном - никакой внятной картины.

За backbone у меня сейчас 2 аргумента:
- он в коробке D8
- я нашел к нему замечательную либу Backbone.Facetr можно фильтровать элементы коллекций по их свойствам, фасеты на клиенте - это же прекрасно

Аватар пользователя Anton Polikarpov Anton Polikarpov 13 июля 2016 в 18:12

Вот про то, что он сейчас в коробке D8 - этого я не знал и это уже весомый повод им заняться! Правда я пока в работе D8 не использую, пока все на 7ке, но уже походу пора переходить.
В ангуляре мне более всего понравился простой и внятный синтаксис, двустороннее связывание (очень упрощает верстку местами), система областей видимости и строгость всей структуры приложения, которая заранее определена библиотекой, а так же огромное количество сторонних директив расширяющих функционал (их значительно больше чем модулей в backbone). Ну и например фасеты там организуются элементарно прямо из коробки, это одна из основных фишечек как мне показалось этой библиотеки.
<ul><li ng-repeat="item in array | {item.condition:someCondition}">{{item.attr}}</li></ul>
При изменении someCondition любым способом список тут же переформатируется так, чтобы условие элемента массива (item.condition) совпадало с someCondition. Сам список выводит некий атрибут item'a, но можно понятно выводить все что угодно. При том тут логика уже упрятана в сами директивы, надо только прописать условия изменения someCondition и задать ему дефолтное значение, дальше ангуляр сам...

Аватар пользователя Anton Polikarpov Anton Polikarpov 24 марта 2017 в 17:45

Андрей, день добрый! Я к вам там в скайп постучался (мне его Витя Вильчинский дал, если что) никак не могу найти инфу о том как добавить агрегацию к вьюхе каталога, гугл об это как будто совсем не в курсе (ну т.е. пару человек интересовалось, но никто ответа не получил).

Аватар пользователя Andruxa Andruxa 16 октября 2017 в 12:15

Да, возникли проблемы с производительностью недели 2 назад - пока боремся.
Там и база разрослась, и индекс солра, стало не хватать ресурсов.

Аватар пользователя Andruxa Andruxa 19 октября 2017 в 1:51

Fixed.

Если кратко, то:
- почистили базу
- добавили памяти mysql
- добавили памяти solr
- изменили режим persistence для redis
- выбросили apache2
- перешли на php7-fpm

Аватар пользователя Andruxa Andruxa 16 апреля 2018 в 17:13
2

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

Был доработан плагин Min/Max UI Slider из модуля Search API ranges в связи с тем, что:

  • из коробки он строит фасеты по значениям полей (commerce price в даном случае), но не по entity property
  • цены зависят от скидки пользователя, и поэтому надо альтерить запрос к поисковому серверу, чтобы происходила фильтрация по цене, актуальной для текущего пользователя

Заодно сделал корректное округление диапазонов с учетом шага (он равен 1000р.) и решил проблему с ростом кеша url - из-за значений фильтра по цене, он стал рости очень быстро.

Аватар пользователя Andruxa Andruxa 16 апреля 2018 в 23:37
1

А там, собственно, не патч, а свой кастомный модуль, который наследует виджет из Search API ranges:
class CustomSearchApiRangesWidgetUISlider extends SearchApiRangesWidgetUISlider
самая мякотка в public function _buildUISliderForm() - заменяем search_api_ranges_minmax() на свою cusom_search_api_ranges_minmax()

В исходном коде зачем-то грузится сущность и затем search_api_extract_fields().
У меня это не работало по двум причинам: используется денормализация по доменам с помощью Search API Grouping - там к ID сущности добавляется целое число _0, _1, и т.д.
И цены с учетом пользовательской скидки - являются entity property, а не полями.
Странно, зачем так было сделано - ведь эти значения уже есть в индексе, раз мы строим по ним фасет, и они возвращаются поисковым сервером при запросе минимального-максимального значений.
А что касается альтера запроса - там используется hook_search_api_solr_query_alter(), но в нем хардкод.

Аватар пользователя Andruxa Andruxa 16 апреля 2018 в 23:41
1

Я между делом накатываю "желтые" обновления контриба (обновления безопасности устанавливаются сразу же - это святое), просматриваю diff.
Во многих случаях изменения носят косметический характер - форматирование кода по стандартам, добавляются примечания, легкий рефакторинг, не меняющий логику, но добавляющий удобочитаемости кода.
Семёрка напоминает мне сейчас хорошее выдержанное вино - приятно смотреть на свет через бокал.
Надеюсь, что восьмерку ждет та же участь, но пока я "подожду второго сервиспака".