Гүлдер мен тағам жеткізуге арналған кептеліске қарсы жүйе: Курьерлік агрегаторлардың тонаушылық комиссияларына қарсы Google Maps Routes API (TSP-оңтайландыру)
Мереке күндері мен Алматының күнделікті кешкі қарбалас уақытында (Әл-Фараби, Сайын, Райымбек) және Астанада (Мәңгілік Ел, Тұран) гүлдер мен дайын мейрамхана тағамдарын жеткізу нағыз стресс-тестке айналады. Тұтынушы балғын букетті немесе ыстық тағамды тура 19:00-ге дейін алуды күтеді, алайда сыртқы агрегаторлар чектің 25–35% комиссиясын ұстап қалады, ал олардың курьерлері мекенжайлардан шатасып, кешігіп келеді.
Бұл мақалада біз Google Cloud Run және Google Maps Routes API v2 негізіндегі серверсіз микросервистің жауынгерлік архитектурасын қарастырамыз. Ол 1 шақыру арқылы Алматыдағы нақты көлік кептелісін ескере отырып, коммерциялық саяхатшы есебін (Traveling Salesperson Problem, TSP) шешеді. Біз шағын бизнеске арналған шынайы экономиканы есептеп, жеткізу уақытын 58.5%-ға қалай қысқартуға және агрегатор комиссиясынан айына 1.3 млн ₸ астам үнемдеуге болатынын көрсетеміз.
1. Мәселе: 28% комиссия тұзағы және нүктелерді ретсіз аралау
Алматыдағы Достық даңғылындағы гүл бутигі немесе кондитерлік цех айына орта есеппен ~450 тапсырыс жеткізеді (жұмыс күндері күніне 15–20 жеткізу, 8 Наурыз бен 14 ақпанда 40–60 тапсырыс) және орташа чегі 14 000 ₸ құрайды. Жеткізу бойынша айлық айналым 6 300 000 ₸ құрайды.
| Жеткізу моделі | Шығындар құрылымы (айына) | Логистика шығыны | Сервис пен мерзімді бақылау |
|---|---|---|---|
| Курьерлік агрегатор (28% комиссия) | 6.3 млн ₸ айналымның 28%-ы | 1 764 000 ₸ | Төмен (курьер қатарлас тапсырыстар алады, букеттер бүлінеді) |
| Бағдарламасыз өз курьері (қолмен таңдау) | Жалақы 380 мың ₸ + Жанармай 70 мың ₸ | 450 000 ₸ | Хаос (34% кешігу, бір рейске 142 мин жұмсау) |
| Жеке штат + Routes API (TSP) | Жалақы 380 мың ₸ + Жанармай/мопед 70 мың ₸ + API ($0 Free Tier) | 450 000 ₸ | 98.2% уақытында, айына 1 314 000 ₸ таза үнем |
2. Шешім архитектурасы: Cloud Run + Google Maps Routes API
Ауыр монолитті логистикалық жүйе құрудың орнына біз Google Cloud Run негізінде жеңіл серверсіз микросервис орнатамыз. Сервис кассалық жүйеден (1C, Poster, Kaspi Pay) жеткізу нүктелерін қабылдап, геокодтау жасайды және optimizeWaypointOrder: true параметрімен Google Maps Routes API (Directions v2) жүйесіне оңтайландыру сұранысын жолдайды.
┌────────────────────────┐ ┌─────────────────────────────┐ ┌──────────────────────────────┐
│ CRM / 1C тапсырыстары │ ===> │ Cloud Run Delivery Engine │ ===> │ Google Maps Routes API v2 │
│ (Poster, Kaspi, 1C) │ │ (Node.js 22 LTS / TS) │ │ (computeRoutes / TSP Solver)│
└────────────────────────┘ └──────────────┬──────────────┘ └──────────────┬───────────────┘
│ │
│ Оңтайлы маршрут (JSON) │ Қайта реттелген waypoints │
▼ ▼ реті + Polyline │
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ Telegram Bot / Web App │ <=== │ Кептеліс (TRAFFIC_AWARE) │
│ (Жүргізуші маршрут парағы)│ │ және көлік түрі (TWO_WHEELER)│
└─────────────────────────────┘ └──────────────────────────────┘3. TypeScript-те TSP-бағыттаушыны іске асыру
Google Maps Routes API v2 жүйесінің басты ерекшелігі — X-Goog-FieldMask тақырыбы. Біз тек оңтайландырылған нүктелер ретін routes.optimizedIntermediateWaypointIndex, жол ұзақтығын routes.duration және сызықты routes.polyline.encodedPolyline сұраймыз.
import axios from 'axios';
export interface LocationPoint {
latitude: number;
longitude: number;
address?: string;
orderId?: string;
}
export interface OptimizedRouteResult {
totalDistanceMeters: number;
totalDurationSeconds: number;
formattedDuration: string;
optimizedOrder: LocationPoint[];
encodedPolyline: string;
}
export class GoogleMapsRoutesOptimizer {
private readonly apiKey: string;
private readonly endpoint = 'https://routes.googleapis.com/directions/v2:computeRoutes';
constructor(apiKey: string) {
if (!apiKey) {
throw new Error('GOOGLE_MAPS_API_KEY is required');
}
this.apiKey = apiKey;
}
async optimizeDeliveryRoute(
origin: LocationPoint,
destinations: LocationPoint[],
vehicleType: 'DRIVE' | 'TWO_WHEELER' = 'DRIVE'
): Promise<OptimizedRouteResult> {
if (destinations.length === 0) {
throw new Error('At least one delivery waypoint required');
}
if (destinations.length > 25) {
throw new Error('Google Maps Routes API supports max 25 intermediate waypoints per call');
}
const payload = {
origin: {
location: {
latLng: { latitude: origin.latitude, longitude: origin.longitude }
}
},
destination: {
location: {
latLng: {
latitude: destinations[destinations.length - 1].latitude,
longitude: destinations[destinations.length - 1].longitude
}
}
},
intermediates: destinations.slice(0, -1).map(point => ({
location: {
latLng: { latitude: point.latitude, longitude: point.longitude }
}
})),
travelMode: vehicleType,
routingPreference: 'TRAFFIC_AWARE_OPTIMAL',
optimizeWaypointOrder: true,
computeAlternativeRoutes: false,
languageCode: 'ru-KZ',
units: 'METRIC'
};
const response = await axios.post(this.endpoint, payload, {
headers: {
'Content-Type': 'application/json',
'X-Goog-Api-Key': this.apiKey,
'X-Goog-FieldMask': 'routes.duration,routes.distanceMeters,routes.optimizedIntermediateWaypointIndex,routes.polyline.encodedPolyline'
}
});
const route = response.data.routes?.[0];
if (!route) {
throw new Error('No route returned by Google Maps API');
}
const permutation: number[] = route.optimizedIntermediateWaypointIndex || [];
const reorderedIntermediates = permutation.map(idx => destinations[idx]);
const finalDest = destinations[destinations.length - 1];
const orderedPoints = [origin, ...reorderedIntermediates, finalDest];
const durationSeconds = parseInt(route.duration.replace('s', ''), 10);
const minutes = Math.round(durationSeconds / 60);
return {
totalDistanceMeters: route.distanceMeters,
totalDurationSeconds: durationSeconds,
formattedDuration: `${minutes} мин`,
optimizedOrder: orderedPoints,
encodedPolyline: route.polyline?.encodedPolyline || ''
};
}
}4. Тиімділікті өлшеу: Уақыт және FinOps
Біз алгоритмді Алматының кешкі қарбалас уақытында (18:30) 8 мекенжай бойынша тексердік (басталуы: Достық д-лындағы гүл салоны -> Самал ы/а -> Тимирязев к-сі -> Розыбакиев к-сі -> Абай д-лы -> Тастақ ы/а -> Төле би к-сі -> аяқталуы: Әл-Фараби д-лы).
Алматының кептелісінде 8 тапсырысты жеткізу уақытын салыстыру (минут)
Routes API арқылы нүктелер ретін оңтайландыру жалпы рейс уақытын 142 минуттан 59 минутқа дейін қысқартты (уақытты −58.5% үнемдеу), ал қашықтық 34.8 шақырымнан 18.2 шақырымға дейін азайды (−47.7%).
450 жеткізуге айлық шығын: Агрегатор vs Жеке флот + Routes API (мың ₸)
Айына 450 жеткізу көлемінде шағын бизнес агрегаторларға 1 764 000 ₸ береді. Автоматтандырылған TSP-маршрутизатор негізіндегі жеке курьер небәрі айына 450 000 ₸ шығады (жалақы мен жанармайды қосқанда), ал Google Maps API $0 тұрады, өйткені 450 шақыру Google Cloud-тың $200 айлық тегін лимитіне толық сыяды. Таза айлық үнем 1 314 000 ₸ құрайды.
5. OZAT Скауттарына нұсқаулық: Шешімді қалай сату және енгізу керек
OZAT Скауттар Академиясының түлектері үшін бөлшек саудадағы жеткізуді автоматтандыру — ең тез өзін-өзі ақтайтын ұсыныстардың бірі:
- Клиентті іріктеу: Қалаңыздан күніне 10-нан астам жеткізуі бар (айына ~300–600) гүл салондарын, кондитерлік дүкендерді, dark kitchen немесе премиум ет дүкендерін табыңыз.
- Агрегатор комиссияларына аудит: Иесіне нақты сандармен қанша жоғалтып жатқанын көрсетіңіз: 450 тапсырыс × 14 000 ₸ × 28% = айына 1.76 млн ₸ тек делдалдарға кетеді.
- Енгізу оффері: Толық кілтпен енгізуді (450 000 – 650 000 ₸) 15 күнге жетпей өзін ақтайтын шартпен ұсыныңыз.
- Техникалық стек: Курьерлерге арналған Telegram-бот триггерімен Cloud Run-да микросервисті орнатыңыз және IP/Referrer бойынша қорғалған Google Maps API кілтін қосыңыз.
6. Шешімнің шынайы шектеулері мен инженерлік компромисстері
Инженерлік компромисстер мен ҚР-дағы жеткізу ерекшелігі
- • Бір сұраныста 25 нүкте шектеуі: Google Maps Routes API бір
computeRoutesсұранысында ең көп дегенде 25 аралық нүктені оңтайландырады. Шағын бизнес үшін (күніне 15–20 жеткізу) бұл артығымен жетеді, ал мерекелерде тапсырыстар 8–12 мекенжайлық рейстерге бөлінеді. - • Аудандарды кластерлеу: Алматыда жоғарғы бөліктегі (Әл-Фарабиден жоғары) және төменгі бөліктегі (Райымбектен төмен) нүктелерді алдын ала гео-кластерлеусіз бір рейске біріктіруге болмайды.
- • Екі дөңгелекті көлік (TWO_WHEELER) шектеулері: Қазақстанда мопедтер мен скутерлерге жоғары жылдамдықты даңғылдарға (ШҚАЖ, Әл-Фараби, Сайын) шығуға тыйым салынған.
TWO_WHEELERрежимін таңдағанда жол ережесін қадағалау маңызды. - • Астанадағы ауа райы буфері: Қыста боран мен -30°C аязда көлікке отыру және аулалардан тұрақ іздеу ұзарады. API есебіне әр мекенжайға +7 минуттық детерминирленген сервистік буфер қосу қажет.
💡 ОЗАТ кеңесі: Енгізуге дайынсыз ба? Архитектура мен бюджетті Scope Builder арқылы есептеңіз немесе тегін ЖИ-аудиттен өтіңіз.

Рустам Шарафутдинов
Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.