Реализация собственной курьерской доставки на сайте
Собственная курьерская служба — логичный шаг для магазинов с высоким объёмом доставок в ограниченной географии. При достижении порога около 50–100 доставок в день затраты на собственных курьеров оказываются ниже, чем комиссии внешних служб. Система управления курьерами — это отдельный продукт внутри платформы с мобильным приложением для курьера, диспетчерской панелью и трекингом в реальном времени.
Архитектура системы
Система состоит из трёх взаимосвязанных компонентов:
Backend: REST API + WebSocket-сервер для реалтайм-координат Диспетчерская: веб-интерфейс для управления курьерами и маршрутами Мобильное приложение курьера: React Native или PWA для прима заданий и отправки GPS-координат
Модель данных
couriers (
id, user_id, name, phone, vehicle_type,
status: offline | available | busy | on_break,
current_lat, current_lng, last_seen_at,
zone_ids (jsonb) -- зоны доставки, за которые отвечает курьер
)
delivery_tasks (
id, order_id, courier_id (nullable),
status: pending | assigned | picked_up | in_transit | delivered | failed,
pickup_address, delivery_address,
scheduled_at, picked_up_at, delivered_at,
proof_photo_url, -- фото при вручении
recipient_signature_url, -- подпись получателя
failure_reason
)
courier_routes (
id, courier_id, date,
task_ids (jsonb, ordered), -- очерёдность доставок
total_distance_km, estimated_duration_min
)
Распределение заказов по курьерам
Ручное назначение — диспетчер перетаскивает заказ на курьера на карте. Подходит для небольших объёмов.
Автоматическая маршрутизация — алгоритм Vehicle Routing Problem (VRP). Для небольших объёмов подходит жадный алгоритм (ближайший незанятый курьер к точке отправления). Для серьёзной оптимизации — библиотеки типа Google OR-Tools или сервисы типа Route4Me API, OptimoRoute.
# Простая эвристика: назначить ближайшего свободного курьера
def assign_courier(task):
available_couriers = Courier.objects.filter(status='available')
nearest = min(available_couriers,
key=lambda c: haversine(c.location, task.pickup_address))
task.assign(nearest)
Мобильное приложение курьера
Основные экраны:
- Список заданий — текущий маршрут, следующая точка
-
Навигация — интеграция с Яндекс.Навигатором через deep link:
yandexnavi://map_search?text={address} - Подтверждение доставки — фото вручения, подпись, или код подтверждения из SMS покупателю
- Недоставлено — выбор причины: не открыли дверь, перенос на другое время, отказ
GPS-координаты отправляются на сервер каждые 30 секунд через WebSocket или MQTT. При потере интернета — буферизуются и отправляются при восстановлении.
// Отправка координат через WebSocket
const ws = new WebSocket(`wss://api.example.com/couriers/${courierId}/location`);
navigator.geolocation.watchPosition((position) => {
ws.send(JSON.stringify({
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: Date.now()
}));
}, null, {enableHighAccuracy: true, maximumAge: 30000});
Трекинг для покупателя
Покупатель получает SMS/push с ссылкой на трекинг-страницу. На странице — карта с отображением курьера в реальном времени и ожидаемое время прибытия. ETA пересчитывается каждые 2 минуты на основе текущих координат курьера и расстояния до точки доставки.
Диспетчерская панель
- Карта с курьерами в реальном времени (Яндекс.Карты или Leaflet)
- Список нераспределённых заказов
- Drag-and-drop назначение курьеров
- Очередь задач каждого курьера с возможностью перестановки
- Мониторинг: курьер не двигается 20 минут → алерт диспетчеру
- История маршрутов и эффективность за день
Зоны доставки
Географические зоны ограничивают, в каком районе работает курьер. Зоны рисуются как полигоны на карте и хранятся в PostgreSQL с расширением PostGIS:
delivery_zones (
id, name, courier_ids (jsonb),
polygon geometry(Polygon, 4326),
min_order_amount, delivery_price, free_delivery_from
)
Запрос для определения, в какой зоне находится адрес:
SELECT * FROM delivery_zones
WHERE ST_Contains(polygon, ST_SetSRID(ST_MakePoint(lng, lat), 4326));
Работа с неудачными доставками
Курьер не застал получателя — ставится статус failed с причиной. Автоматически планируется повторная доставка на следующий день или покупатель получает уведомление с возможностью выбрать новое время.
Срок разработки: 8–12 недель для полной системы: API, диспетчерской панели и мобильного приложения курьера.







