Привет! Меня зовут Данила Соловьёв, я руковожу направлением PHP — самым крупным подразделением в отделе разработки AGIMA. Поделюсь историей о том, как мы встроили новую CRM-систему в два абсолютно незнакомых нам IT-ландшафта и тем самым спасли сейлзов двух крупных интернет-магазинов от бесконечных табличек в почте. Подробно опишу, какие данные мы выгружали, как их дедуплицировали и какие сервисы использовали для их валидации. Поехали!
Предпосылки. Как появилась задача?
Два федеральных интернет-магазина, для удобства назовем их А и Б. В каждом независимо друг от друга велась клиентская база юридических лиц с накопленными данными за весь период работы.
Работа с B2B-клиентами происходила по принципу «всё в почте». Менеджеры по продажам пересылают кучу неактуальных эксель-таблиц. К таким процессам были готовы не все, и менеджеры часто увольнялись в первые рабочие дни.
Бизнес был настроен изменить данный процесс, автоматизировать все возможные процессы, таким образом активно развивать B2B-направление и инвестировать в него.
Ограничения
Основными ограничения стали:
Срок MVP — полгода (с июня-июля до нового года). Заказчик пришел летом. Релиз-фриз перед новым годом.
Бюджет. Лепить огромного, отказоустойчивого и, как следствие, дорогого мастодонта нет возможности. Бизнес хочет тестировать гипотезы, чем быстрее, тем лучше.
Также были сопутствующие ограничения:
Сервис одного окна. У менеджеров по продажам должен быть единый инструмент для ведения клиентов. Отправка почты, IP-телефония, формирование отчетов, просмотр всех клиентов и их заказов.
Низкий порог входа для менеджеров. Интерфейс системы должен быть прост и интуитивно понятен.
Ключевая задача
Создать единую клиентскую базу юридических лиц и обогатить ее данными из двух существующих баз данных (БД) интернет-магазинов для последующих продаж.
Для этого нужно:
Получить необходимый набор данных и сложить в нашу новую систему, чтобы менеджеры могли заходить и работать с ними.
Превратить их в сущности CRM. В качестве CRM выбрали решение от Bitrix24.
Выбор Bitrix24 обусловлен ограничениями, которые описаны выше + коробка предлагает достаточно широкий функционал:
интеграция с AD/LDAP, которая нам и требовалась в качестве SSO;
интеграции с IP-телефониями;
интеграция с почтой;
понятный и анимированный интерфейс.
Данные. Что выгружаем?
Приступаем к сбору информации о том, что мы хотим видеть в нашей CRM-системе. Определяем основные сущности и их свойства, требуемые для заполнения, а также устанавливаем критерии проверки данных. Таким образом, мы формируем желаемый итог нашей работы.
В рамках Bitrix24 нам предстоит взаимодействовать с двумя основными сущностями:
Компания — информация о юридическом лице, включая организационно-правовую форму, а также банковские реквизиты.
Контакт — карточка с данными о конкретном контактном лице, например менеджере по закупкам. Этот контакт привязан к конкретной компании.
Затем определяем бизнес-требования к Bitrix24:
Выгружаются только активные компании.
Исключаются дубликаты контактов и компаний.
Этап технического ППО. Работаем в чужой инфраструктуре
Мы заходили в чужую инфраструктуру двух огромных екомов, в которой мы совсем не ориентируемся. Поэтому на этом этапе нам предстояло ответить на следующие вопросы:
Кто может помочь со стороны интернет-магазинов интегрироваться (конкретные команды и люди)? К кому идти? Кто может рассказать про сетевую связность, про продукты и системы, которые есть внутри каждого из магазинов?
Как будет происходить обмен данными? Realtime? Огромные файлы выгрузки? Брокеры сообщений? REST?
Потребуются ли доработки со стороны интернет-магазинов?
Какой стек технологий потребуется (помимо Bitrix24, PHP и MySQL)?
Как оперативно мы должны и можем получать данные (технические возможности)? Важно! Мы не должны нашими обменами положить ни интернет-магазины, ни себя :)
Чтобы разобраться в инфраструктуре, мы встретились с представителями интернет-магазинов и обсудили важные детали:
Как забирать данные для каждой сущности (REST, брокеры, файлы и т.д.)?
В каком формате (json, xml, csv и т.д.)?
Как оперативно? С какой периодичностью возможен обмен?
Проектирование. Выбор реализации. Архитектура
На этапе ППО мы определили:
Способы интеграции.
Режимы работы выгрузки. Спойлер: их два (полный и дельта).
Способы валидации компаний, чтобы не тащить «мусор» и дубли в новую систему.
Как «сырые» данные преобразовывать в сущности CRM.
Механизм: Извлечение. Преобразование. Загрузка
В этом блоке расскажу о том, как мы выгружали данные, и опишу технические моменты.
Для решения задачи было необходимо реализовать два режима выгрузки данных:
Полная выгрузка — все данные за текущий и предыдущие годы.
Выгрузка обновлений — периодически мы должны получать данные о новых компаниях из интернет-магазинов.
Объем данных получился таким:
В интернет-магазине А — 75 000 контактов, 53 000 компаний.
В интернет-магазине Б — 600 000 контактов, 125 000 компаний.
Отмечу, что эти цифры, если их сложить, с итоговыми не сойдутся. Всё потому, что произошла дедупликация, и мусорные компании тоже были отброшены.
За основу реализации взяли процесс ETL. Аббревиатура ETL (Extract. Transform. Load) означает «Извлечение, Преобразование, Загрузка». Остановимся подробнее на каждом из этапов.
Extract — Извлечение
Это, наверное, самый сложный этап из всех трех, которые нас ждали.
На данном шаге мы должны реализовать интеграцию с интернет-магазинами для получение «сырых» данных. Важно понимать, что «сырые» они только для новой CRM, в то время как для интернет-магазина это полноценный объем данных в его базе.
Полная выгрузка:
ИМ А — slave реплика MySQL (symfony console command)
ИМ Б — json (halaxa/json-machine + symfony console command)
Также мы прикрутили к нашему проекту компонент Symfony Console, чтобы с помощью него запускать полную выгрузку из MySQL и другие фоновые задачи.
Дельта выгрузка:
ИМ А — slave реплика MySQL (cronjob + symfony console command)
ИМ Б — kafka (php extension RdKafka + supervisor + symfony console command)
Отдельно остановлюсь на интернет-магазине Б — у них был SAP CRM, где они хранили всех клиентов. У SAP CRM был веб-сервис, который умел продюсировать свои изменения в Kafka. Нам предоставили отдельный топик. Мы на своей стороне реализовали консюмер, для этого нам пришлось установить PHP-расширение RdKafka. Консюмер представлял собой бесконечный цикл, поэтому сверху мы накрыли его еще Supervisor’ом, чтобы он за ним следил, и если падает — переподнимал его. А в сам Supervisor передали команду через Symfony Console.
Transform — Преобразование
Теперь важно реализовать очистку и преобразование данных для будущих сущностей, чтобы они соответствовали потребностям бизнес-модели. Для этого мы:
удаляли компании, которые приходили без ИНН;
преобразовали кодировку Windows-1251 в UTF-8;
преобразовали номера телефонов в общий формат данных;
удаляли лишние пробелы из текстовых полей;
контакты и компании укладывали в DTO.
Load — Загрузка
Теперь мы получили очищенные данные и готовы сохранять их в БД как сущности CRM (компании и контакты).
Этот шаг был одинаковым для всех трех источников (БД, Kafka, файлы). Так как мы всё грузим в одно хранилище, организовать надо единообразно.
Одновременно с этим происходила валидация компаний, т. к. нужны только действующие. Для реализации этого требования мы воспользовались сервисом ЕГРЮЛ, где присутствуют необходимые данные о компаниях. Отмечу, что у Bitrix24 есть готовый модуль «из коробки» для получения сведений из ЕГРЮЛ по ИНН.
Но данные приходили «сырые». Перед тем как сделать запрос в ЕГРЮЛ, мы должны были убедиться, что ИНН валидный. Такой запрос стоил времени, на него уходило 0,4 секунды, что уменьшало скорость обработки выгрузки. Поэтому для ИНН реализовали стандартную проверку на длину и символы: ИНН должен состоять из 10 или 12 цифр. Также реализовали проверку контрольных чисел, она определяет корректность номера ИНН с помощью математической формулы. Данная формула — унифицированная для всех ИНН.
По итогам проверки в ЕГРЮЛ отправляются запросы только с валидным ИНН, что сократило этап загрузки на 30–40%. Невалидные компании в процессе загрузки складывали в отдельную табличку.
Релизы
Первым релизом мы выпустили выгрузку из интернет-магазина А с общим загрузчиком.
Вторым релизом вышла дельта из интернет-магазина Б.
Третьим — полная выгрузка исторических данных из интернет-магазина Б.
Таким образом, даже на этапе разработки мы непрерывно снабжали новыми данными менеджеров по продажам.
Итоговый стек и архитектура
В ходе проекта нами использовался следующий стек:
B2B CRM — Bitrix 24.
База данных — MySQL.
Брокер сообщений — Kafka.
ETL — PHP-пакет flow-php/etl.
Большие json — PHP-пакет halaxa/json-machine.
Чтение из Slave-реплики — symfony/console + сron.
Консюмеры Kafka — symfony/console + supervisor.
Как видим, у нас было три экстрактора: общий для магазина А и два отдельных для магазина Б (один для Kafka, другой для Json). Два трансформера — для каждого магазина свой, они выдавали одинаковые DTO и передавали их в лоадер. Дальше лоадер закидывал всё в B2B CRM.
Заключение
Bitrix24 был успешно доработан. Нам удалось выгрузить свыше 170 000 активных компаний и более 264 000 контактов из обоих интернет-магазинов.
Менеджерам по продажам была предоставлена обширная клиентская база двух интернет-магазинов. Это позволяет эффективно работать со старыми клиентами и добиваться повторных продаж, используя функционал CRM Bitrix24. Все отчеты и аналитические данные стали доступны в один клик. И больше никаких табличек в почте. Ура!
Только благодаря правильно выстроенному взаимодействию между бизнесом и разработчиками, нам удалось реализовать поставленные цели.
Больше о разработке мы рассказываем в нашем телеграм-канале «Заметки тимлида». Приходите обсудить статью. А еще мы раз в месяц проводим закрытые встречи для еком-директоров разных компаний — обсуждаем там насущные проблемы, слушаем доклады и просто знакомимся. Будем рады новым участникам!