Внутренняя CRM для операционной команды: один экран вместо пяти вкладок
Общий почтовый ящик, живые данные Shopify, отгрузки, звонки, ответы опроса и возвраты по клиенту — в одном окне. Написан на стандартной библиотеке Python и работает на арендованном сервере с 961 МБ памяти.
Ответ на вопрос «где мой заказ» собирался из пяти систем
Бренд товаров для активного отдыха из США, два магазина на Shopify. Двое операционистов вели клиентов, переключаясь между двумя админками Shopify, почтовым клиентом, облачной телефонией и складскими таблицами.
Самый частый вопрос покупателя — «где мой заказ». Чтобы на него ответить, нужно было найти письмо, вытащить из него номер заказа, вспомнить, в каком из двух магазинов этот заказ лежит, открыть нужную админку, найти отгрузку, вернуться в почту и написать ответ. Пять переключений контекста на одну строчку текста.
Чарджбеки одного из магазинов не были видны вообще: они приходили в интерфейс, в который операционная команда просто не заходила. Узнавали о них постфактум.
Первая версия карточки клиента, когда её собрали, грузилась 10 секунд — и операционист сказал об этом прямо. Это и стало главным техническим требованием: инструмент, который открывается дольше, чем ручное переключение вкладок, никто использовать не будет.
Своя CRM на стандартной библиотеке Python
HTTP-сервер на
http.server
из стандартной библиотеки, без веб-фреймворка. Не из эстетики: так он ставится на сервер,
где нет pip и нет права его туда принести. Ни одной внешней зависимости — значит, нечему
сломаться при обновлении и нечего чинить через полгода.
Проверка ящиков каждые 15 минут: два корпоративных адреса, более 100 тыс. писем
Письма собираются в переписки, сортируются и пишутся на диск по мере чтения
Адрес → смещения строк; отдельный индекс номеров заказов по телу писем
Шесть источников параллельно: заказы обоих магазинов, отгрузки, звонки, опрос, возвраты
Шаблоны, подписи, AI-черновик, отправка и пересылка с вложениями через очередь
Шесть источников параллельно — и честная деградация
Карточка клиента собирает шесть независимых источников одновременно: заказы первого магазина, заказы второго, отгрузки, историю звонков из облачной телефонии, ответы на послепродажный опрос и возвраты со спорами по платежам.
Ключевая деталь — счётчик сбоев внутри каждого потока, который передаётся в главный. Без него отвалившийся источник просто не показывался бы, и неполная карточка молча считалась бы полной. Операционист видел бы «заказов нет» там, где на самом деле «магазин сейчас не ответил». Это разные вещи, и клиенту про них говорят разное.
Индексы вместо стрима всего корпуса
Почта двух ящиков — более 100 тыс. писем, корпус в сотни мегабайт. Первая версия на каждый просмотр карточки читала его целиком: отсюда 5,8 секунды на поиск писем по клиенту.
Построил два индекса:
- Адрес → смещения строк — открыть корпус, прыгнуть по offset, прочитать только нужные письма
- Номер заказа → клиент — индекс номеров, вытащенных из тел писем: покупатель пишет с личного адреса, а заказ оформлен на рабочий — карточка всё равно находится
Потоковый разбор почты: иначе не хватает памяти
Разбор почты изначально держал весь массив писем в памяти — на сервере с 961 МБ это заканчивалось тем, что система убивала процесс из-за нехватки памяти. Переписал на потоковый разбор: письма пишутся на диск по одному, в памяти остаётся только лёгкая проекция заголовков. Пик памяти упал с 985 МБ до 256 МБ (на самом сервере — 215 МБ), при этом результат побайтово идентичен: то же количество писем и переписок, контрольная сумма содержимого (sha256) совпала.
Всё, что нужно, чтобы не выходить из окна
- Отправка и пересылка писем с вложениями через очередь — интерфейс не блокируется на SMTP
- Персональные шаблоны и подписи у каждого сотрудника
- AI-черновик ответа — по контексту треда и данным карточки; редактируется перед отправкой
- Персональные аккаунты с памятью устройства — не логиниться заново каждое утро
- Аудит-лог «кто / что / чем кончилось» — по любому действию видно, кто его сделал и чем оно завершилось
- Блок складских остатков из Lark — прямо в карточке, без отдельной таблицы
- Полноценная мобильная вёрстка — проверено на 390px, отвечать можно с телефона
Переезд на выделенный сервер — за один день
Сначала CRM жила на общем сервере вместе с трекером — соседство, при котором любой всплеск нагрузки бьёт по обоим. Вынес на отдельный сервер за nginx с сертификатами Let’s Encrypt: почта проверяется каждые 15 минут, монитор раз в час, копии по ночам. Переезд проверен по счётчикам: письма, переписки и заказы на экране сошлись один в один со старой установкой.
Ноль внешних зависимостей — осознанно
http.server, imaplib, sqlite3, urllib — ставится даже на сервер, где нельзя доустанавливать пакеты
Живые заказы и отгрузки двух магазинов, без промежуточной базы
Собственный разбор, сортировка и индексация почты
Черновик ответа по всей переписке — сотрудник правит и отправляет
Отдельный сервер, шифрование через certbot, сверки и мониторы по расписанию
454 собираемых теста, 28 тестовых файлов при 15 модулях кода
Замеры до и после
шесть источников, собранные параллельно
индекс смещений вместо чтения всего корпуса
на самом сервере 215 МБ; результат побайтово идентичен
Операционная команда работает в одном окне: письмо, заказы обоих магазинов, отгрузка, звонки, ответы опроса и возвраты — на одном экране, с уже подготовленным текстом ответа. Чарджбеки перестали быть слепой зоной — они лежат в той же карточке, что и всё остальное по клиенту.
Экономию памяти проверяли не «на глаз»: после переписывания разбора сверили количество писем, количество переписок и контрольную сумму содержимого — совпало. Это тот случай, когда ускорение не имеет права поменять данные, и это надо доказать, а не заявить.
Переезд на выделенный сервер занял один день и был принят по паритету со старой инсталляцией. На проекте — 120 коммитов в модуле CRM и 454 собираемых теста (28 тестовых файлов при 15 модулях кода): внутренний инструмент, но не «скрипт на коленке».
Где ещё ложится та же методология
Это не «CRM для интернет-магазина». Это типовая задача «склеить N систем в один экран вокруг одной сущности» — там, где сотрудник каждый день руками сводит данные из разных вкладок:
- → Поддержка на общем ящике — почта + учётная система + доставка в одном треде вместо трёх вкладок
- → Мультиканальные продажи — несколько витрин и маркетплейсов, у каждого своя админка, а клиент один
- → Сервис и выездные работы — заявка, история звонков, склад запчастей и статус наряда по одному клиенту
- → Архивы переписки как источник данных — индексация корпуса писем и поиск по номерам документов внутри тел
- → Замена готовой системы поддержки — когда плата за каждого сотрудника растёт, а нужны две-три специфичные вкладки, которых в коробке нет
- Параллельный сбор карточки с честным учётом сбоев — неполные данные никогда не выдаются за полные
- Индексация всего архива почты: адрес → позиция в файле, номер документа → клиент
- Потоковая обработка вместо «загрузить всё в память» — работает на самых дешёвых серверах
- Проверка миграции паритетом счётчиков и хешей, а не «вроде открылось»
- Журнал действий сотрудников по схеме «кто / что / чем кончилось»
Если сотрудник делает join между вкладками руками — это собирается в один экран
Начинаем с самого частого вопроса, на который команда отвечает каждый день, и собираем ровно тот экран, который на него отвечает. Первая рабочая версия — недели, а не кварталы, и её сразу проверяет тот, кто в ней будет сидеть.
Разбор по теме:Синхронизация остатков 1С с Wildberries и Ozon: откуда берутся расхождения и что сводить в одном окне
Похожие кейсы
Генератор промо-страниц: описание или ссылка → готовая страница
Python + tkinter: программа сама считывает логотип, значок сайта и тексты с источника, шаблонизатор собирает…
Платформа автоматизации маркетинга: десктоп → Telegram → веб
Платформа управления рекламными кампаниями. Выросла из программы на Tkinter (80+ функций, пакетные операции…
Swift/SwiftUI: первая версия за 9 недель — от старта до App Store
Приложение для iOS 17+: расписание и напоминания (24 ч / 1 ч / 10 мин), Speech Framework проверяет…
Аудит за 5 000 ₽ — с конкретным отчётом и сметой
Расскажу что внедрить в вашем бизнесе в первую очередь, какая будет окупаемость, и нужен ли вообще AI для вашей задачи (иногда — нет).
Или просто напишите свой вопрос — отвечу в течение 2 часов