Привет! В предыдущих статьях я рассказал, как из хаоса в Telegram-чате родился HTML-прототип, а потом и полноценное React Native‑приложение для выбора лучшей карты по кэшбэку. Оно уже умело подсказывать, какой картой заплатить — но только после того, как я сам вручную выберу категорию. И тут меня осенило: а что, если приложение само поймёт, где я нахожусь? Зашёл в магазин — получи уведомление с готовой рекомендацией. И никакого ручного поиска.
Эта идея казалась сложной, но с помощью DeepSeek я прошёл путь от первых набросков до готового сервиса, который исправно работал на моём телефоне. В этой статье я подробно, шаг за шагом, расскажу, как это было: какие вопросы я задавал, какие варианты рассматривал, с какими трудностями столкнулся и как их преодолевал. Приготовьтесь — будет много кода, диалогов и неожиданных поворотов.
Идея: «Хочу, чтобы приложение само подсказывало карту в магазине»
Всё началось с простого запроса. Я написал в DeepSeek:
«мне необходимо настроить систему уведомлений: уведомления по геолокации, например, при посещении магазина: «Вы рядом с [название сети, например, Перекресток]? Используйте карту xxx с повышенным кэшбеком на "Супермаркеты" прямо сейчас и верните 5%!» подробные данные о компаниях должны храниться локально. придумай оптимальное решение».
ИИ не растерялся и выдал развёрнутый ответ, в котором описал несколько подходов:
- Использование API Яндекс.Карт или Google Maps — мощно, но требует интернета и может быть платным.
- Покупка готовой базы данных у Data.2GIS — быстро, но дорого.
- Создание собственной локальной базы с координатами — бесплатно, автономно, но нужно где-то взять данные.
Я сразу отмёл варианты с внешними API: хотелось, чтобы приложение работало офлайн и не зависело от лимитов. ИИ подтвердил:
«Для вашего случая, где нужен контроль над конкретным списком магазинов, оптимально создать собственную базу координат. Это даёт вам полную независимость от изменений в сторонних API и гарантирует, что в результатах будут только те точки, которые вас интересуют».
Решение было принято: будем искать данные сами.
Где взять координаты тысяч магазинов?
Без координат магазинов приложение не сможет определить, где находится пользователь. А готовой базы у меня не было. Пришлось обратиться к ИИ за советом — как раздобыть данные на тысячи торговых точек?
DeepSeek предложил несколько источников:
- OpenStreetMap (OSM) — открытые данные, но их нужно уметь извлекать.
- 2GIS — у них есть открытое API и парсеры на GitHub.
- Готовые базы — например, Data.2GIS продаёт выгрузки.
Я решил попробовать парсинг 2GIS. На GitHub действительно нашёлся репозиторий с программой для парсинга организаций по категориям. Скачал, настроил — и через несколько часов получил десятки CSV-файлов с тысячами записей по Санкт-Петербургу: супермаркеты, аптеки, АЗС, рестораны, мебельные магазины и т.д.
Проблема была в том, что файлы имели разную структуру, а в некоторых не хватало важных полей. Нужно было привести их к единому формату.
Пишем конвертер: от CSV и XLS к единому JSON
Я попросил DeepSeek написать скрипт на Python, который:
- читает все файлы из папки (CSV и Excel),
- извлекает из каждой строки нужные поля: название, адрес, телефон, координаты, категории, часы работы,
- нормализует названия магазинов и категорий,
- генерирует уникальный ID на основе названия и координат,
- сохраняет результат в JSON и SQL.
ИИ выдал рабочий код. Вот его упрощённая версия:
Конечно, в реальности код был сложнее: пришлось добавить обработку разных кодировок, пропуск дубликатов, нормализацию категорий и многое другое. Но главное — он работал.
После запуска скрипта я получил файл stores.json с почти 4000 записей. Ура!
Интеграция в React Native: SQLite и первый сервис
Теперь нужно было загрузить эти данные в приложение. DeepSeek предложил SQLite — лёгкая, быстрая, с поддержкой индексов — и подсказал структуру таблицы:
Импорт JSON в SQLite я сделал через JavaScript, генерирующий готовый SQL-файл. При первом запуске приложение выполняло этот скрипт и наполняло базу.
Бизнес-логика: как понять, что пользователь в магазине?
Самая сложная часть — написать алгоритм, который не будет спамить уведомлениями, но при этом надёжно определит, что человек действительно зашёл в магазин. Просто входить в радиус и сразу показывать уведомление — плохая идея: GPS прыгает, пользователь может просто проходить мимо.
Я описал требования DeepSeek:
- при входе в радиус 50 метров начинаем отсчёт времени;
- если пользователь остаётся внутри 1,5 минуты — считаем, что он в магазине;
- проверяем, не было ли уведомления за последние 4 часа;
- если есть карта с подходящей категорией — показываем уведомление;
- для торговых центров — особое уведомление без привязки к конкретному магазину.
DeepSeek предложил архитектуру с «активными посещениями»: когда пользователь впервые входит в радиус, магазин добавляется в Map activeVisits с временной меткой. При каждом обновлении геолокации проверяется, прошло ли достаточно времени. Если да — ищем карту и отправляем уведомление.
Вот ключевые куски кода, которые ИИ сгенерировал и которые потом много раз дорабатывались:
Конечно, это только основа. Реальный код включал ещё кучу проверок: на режим работы магазина, на домашние магазины (чтобы не беспокоить, если человек живёт рядом), на ТРЦ, на скорость движения и т.д.
Также для меня было важно, чтобы данные о местоположении пользователей оставались только у них. Поэтому я сделал так, что вся геолокация обрабатывается локально на устройстве, а база магазинов хранится прямо в приложении. Никакая информация не отправляется на серверы — ни мои, ни сторонние. Приложение не требует интернета для определения магазинов, поэтому пользователи могут быть уверены: их перемещения никто не видит.
Торговые центры: особая логика
Когда я протестировал систему на своем рабочем месте (расположено в здании ТРЦ), то обнаружил проблему: приложение начинало спамить уведомлениями от разных магазинов внутри одного здания. Нужно было как-то исправить.
Я написал DeepSeek:
«в базе данных есть магазины с категорией «ТРЦ». нужно, чтобы при попадании в такой магазин (или просто в зону ТРЦ) показывалось одно общее уведомление, а не по каждому магазину».
ИИ предложил два подхода:
- Если у магазина явно указана категория «ТРЦ» — сразу показывать общее уведомление, не проверяя другие магазины.
- Если в радиусе 150 метров находится больше 5 магазинов и они относятся к разным категориям — считать это ТРЦ и показывать общее уведомление.
Я реализовал оба. В результате появился метод checkIfShoppingCenter, который сначала проверял категорию магазина, а если не подходит — запускал кластеризацию.
Это сильно уменьшило количество ложных срабатываний в крупных ТЦ.
Настройка параметров: метод проб и ошибок (или как я тестировал на собственной квартире)
После того как база данных была готова и сервис уведомлений написан, начался самый муторный, но и самый важный этап — настройка параметров. В теории всё выглядело логично: радиус 50 метров, время подтверждения 1,5 минуты, проверка режима работы магазина. Но на практике приложение упорно не хотело отправлять уведомления, хотя я исправно ходил по магазинам. Пришлось разбираться, что пошло не так.
Создаём тестовую точку «Аэроклуб» прямо в квартире
Первая проблема: я не мог сидеть в кафе или магазине часами, ожидая срабатывания. Нужен был способ тестировать систему, не выходя из дома. Тогда я решил создать искусственную торговую точку с координатами… своей квартиры. Взял свои GPS-координаты, добавил в базу данных новую запись с названием «Аэроклуб» и категорией «ТРЦ». Теперь, сидя на диване, я имитировал «посещение» магазина. Гениально? Возможно. Но главное — это сработало.
В переписке с DeepSeek я написал:
«в бд я уже добавил тестовую компанию с моим текущим местоположением»
ИИ одобрил такой подход и посоветовал убедиться, что у тестовой точки заполнены все необходимые поля: координаты, категория, часы работы (я поставил круглосуточно).
Первые логи: магазин найден, но уведомления нет
Запускаю приложение, включаю геолокацию, сижу на диване. Через некоторое время смотрю логи — и вижу:
📍 Проверяем ближайшие магазины...
🏪 Найдено магазинов: 1
🔍 ОБРАБАТЫВАЕМ: "Аэроклуб"
📍 Расстояние: 1м
⏰ Аэроклуб: ОТКРЫТ (00:00-24:00)
🎯 ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ! Добавляем: "Аэроклуб"
⏳ Ожидаем подтверждения посещения...
Казалось бы, всё отлично. Магазин обнаружен, добавлен в активные посещения, ждём подтверждения. Проходит 1,5 минуты, снова проверяю логи — и вижу, что активные посещения пусты, а уведомления так и не было. Почему?
Я скопировал логи и отправил DeepSeek с вопросом:
«по логам найдена точка, но процесс дальше не идет в чем может быть проблема»
ИИ проанализировал код и указал на вероятную причину: в методе checkConfirmedVisits после подтверждения времени мы проверяем, что пользователь всё ещё рядом, но затем… ничего не происходит, потому что забыли вызвать поиск карты! Действительно, в коде была ошибка: магазин добавлялся в активные посещения, но при подтверждении не выполнялся поиск лучшей карты и отправка уведомления.
Исправляем ошибку и добавляем проверку карт
После корректировки кода логи стали другими:
✅ Подтверждено посещение: "Аэроклуб"
🔍 Поиск карт для: "Аэроклуб" (ТРЦ)
✅ Лучшая карта: Сбербанк Premier (1.5%)
🔔 Уведомление отправлено: Аэроклуб
Ура! Уведомление пришло.
Тонкая настройка: радиус, время и защита от спама
Следующая проблема: сидя на диване, я получал уведомление каждые 1,5 минуты, потому что «посещал» магазин непрерывно. Пришлось вводить кулдаун. Сначала поставил 30 минут, потом увеличил до 4 часов — так, чтобы повторное уведомление приходило только при следующем визите.
Кроме того, я заметил, что иногда из-за погрешности GPS расстояние скакало от 1 до 10 метров, и это приводило к тому, что магазин то исчезал из активных, то появлялся снова. Пришлось добавить буферную зону: если пользователь удалился более чем на 50 метров, посещение отменяется, но если остаётся в пределах 50-70 метров — продолжает отсчитываться время.
Все эти изменения я обсуждал с DeepSeek, отправляя запросы вроде:
«изменить радиус до 50 метров и время подтверждения до 2 минут»
«добавить проверку на повторное уведомление в том же магазине, в том числе после перезапуска приложения»
«проблема: если пользователь живёт рядом с магазином, уведомления приходят постоянно. нужно как-то определять «домашние» магазины»
Каждый раз ИИ предлагал изменения в коде, а я тестировал их на своей «квартирной» точке. Так, шаг за шагом, мы пришли к финальной версии сервиса.
Что дало тестирование на собственной квартире?
- Возможность многократно проверять логику без выхода на улицу — бесценно.
- Выявление ошибок в коде, которые были бы незаметны при обычном использовании.
- Подбор оптимальных числовых параметров — радиус, время подтверждения, кулдауны.
- Понимание важности учёта точности GPS и добавление гистерезиса.
Кстати, тестовая точка «Аэроклуб» так и осталась в базе данных бета-версии приложения. Это мой персональный «домашний магазин», на котором я проверял дальнейшие обновления, прежде чем выпустить их в релиз.
Что получилось в итоге
В результате нескольких недель переписки и десятков итераций у меня появился полноценный сервис, который:
- работает полностью офлайн (все данные хранятся локально и никуда не передаются);
- точно определяет посещение магазина (зависит от качества gps-сигнала);
- не надоедает (кулдауны, учёт времени работы, домашние магазины);
- умеет распознавать ТРЦ и выдавать одно общее уведомление;
- легко расширяется (добавление новых магазинов — просто пересобрать базу).
Выводы и советы
- Не бойтесь начинать с простого. Сначала я хотел подключить Яндекс.Карты и сложные API, но остановился на локальной базе — и это было правильное решение.
- ИИ — отличный партнёр для экспериментов. Он генерирует код, предлагает варианты, помогает отлаживать. Но окончательные решения принимаете вы.
- Тестируйте на реальных данных. Симулятор не покажет всех нюансов. Я создал тестовую точку с координатами своей квартиры и внимательно изучал логи — только так удалось настроить параметры.
- Логируйте всё. Без подробных логов вы не поймёте, почему уведомление не пришло или пришло не вовремя.
Так выглядела первая, рабочая версия гео-уведомлений. Но в итоге мне пришлось полностью переписать её с нуля. О том, как я пересобирал сервис и почему новая архитектура оказалась в разы удобнее — расскажу в следующей части. Подписывайтесь, чтобы не пропустить!