Сервис для поиска шеф-поваров и заказа у них еды.
- Интеграция OpenStreetMap API. Для поиска по адресу использует одну строку (например, "ул. Пушкина 23, Москва"). Через OpenStreetMap API происходит поиск подходящих вариантов и возвращает результат на клиент. Пользователь выбирает нужный вариант (также расчитывается расстояние до адреса) и отправляет координаты (longitude/latitude) в эндпоинт для сохранения адреса.
- Реализована кастомная аутентификации через заголовки X-Device-Id и X-Access-Code с использованием Spring Security и фильтров. Заказчик (пользователь) передаёт в заголовке device_id (например, X-Device-Id), по которому его можно идентифицировать. Повар передаёт в заголовке access_code (например, X-Access-Code) вместе device_id.
- Используются миграции через Liquibase для контроля изменений схемы БД (PostgreSQL)
- SSE (Server-Sent Events): реализована потоковая передача данных через Spring WebFlux. После создания нового заказа со стороны пользователя, сервер отправляет SSE-сообщение всем подписанным поварам. Повар также может слушать SSE-эндпоинт для получения новых заказов в режиме реального времени
- Интеграция Redis: настроено кэширование результатов OpenStreetMap API
1.1 Эндпоинт: Поиск координат по адресу (одна строка)
POST /addresses/search
Заголовки: X-Device-Id: <device_id>
Тело (Request Body):
{ "query": "пр. Пушкина 23, Москва" }
Описание:
Вызывает OpenStreetMap API для поиска координат и возвращает список подходящих адресов.
Пример ответа:
{
"results": [
{
"formatted_address": "пр. Пушкина 23, Москва, Россия",
"longitude": 76.945,
"latitude": 43.256,
"place_id": "ChIJN1..."
},
{
"formatted_address": "ул. Пушкина 23/1, Москва, Россия",
"longitude": 76.947,
"latitude": 43.257,
"place_id": "ChIJN2..."
}
]
}1.2. Эндпоинт: Сохранить (добавить/обновить) адрес
POST /addresses
Заголовки: X-Device-Id: <device_id>
Тело (Request Body):
{
“Place_id”: “fdsfdaj”
"entrance": "1",
"floor": "3",
"apartment": "12"
}Описание:
Сохраняет (или обновляет) адрес пользователя и его координаты в БД.(long, lat берется с place_id)
Ответ:
{
"message": "Address updated successfully"
}1.3. Эндпоинт: Добавление/изменение персональных данных
POST /users/personal-data
Заголовки: X-Device-Id: <device_id>\
Тело (Request Body):
{
"name": "Иван",
"phone": "+77001112233"
}Описание:
Добавляет/обновляет имя и номер телефона пользователя (заказчика).
Ответ:
{
"message": "Personal data updated successfully"
}1.4. Эндпоинт: Получение списка ближайших поваров
GET /chefs
Заголовки: X-Device-Id: <device_id>
Описание:
Получает из БД координаты пользователя и выводит список поваров, которые:\
- Находятся в радиусе 5 км от координат пользователя.\
- У которых is_working = true.
Ответ (пример):
[
{
"chef_id": 123,
"photo_url": "https://example.com/photo.jpg",
"description": "Готовлю традиционные блюда",
"phone": "+77001112233",
"is_working": true,
"foods": [
{
"food_id": 456,
"name": "Плов",
"description": "Настоящий узбекский плов",
"price": 1500,
"photo_url": "https://example.com/food.jpg"
}
]
}
]1.5. Эндпоинт: Получение детальной информации о конкретном поваре
GET /chefs/{chef_id}
Заголовки: X-Device-Id: <device_id>
Описание:
Возвращает данные конкретного повара (при условии, что is_working = true или, по бизнес-логике, даже если is_working = false, но пользователь может просматривать).
Ответ (пример):
{
"chef_id": 123,
"photo_url": "https://example.com/photo.jpg",
"description": "Готовлю традиционные блюда",
"phone": "+77001112233",
"is_working": true,
"foods": [
{
"food_id": 456,
"name": "Плов",
"description": "Настоящий узбекский плов",
"ingredients": ["лук", "сыр"],
"price": 1500,
"photo_url": "https://example.com/food.jpg"
}
]
}1.6. Эндпоинт: Создание заказа
POST /orders
Заголовки: X-Device-Id: <device_id>
Тело (Request Body):
{
"items": [
{ "food_id": 456, "quantity": 2 },
{ "food_id": 789, "quantity": 1 }
],
"comment": "Положите, пожалуйста, салфетки"
}Описание:
- Сервис проверяет, заполнены ли у пользователя персональные данные и есть ли адрес.
- Рассчитывается цена (сумма price * quantity по всем блюдам).
- Создаётся заказ со статусом по умолчанию (in_cooking).
- После успешного создания заказа сервер отправляет SSE-событие для поваров (либо конкретному повару, если известно, к кому относится еда). Ответ:
{
"order_id": 101112,
"status": "created",
"total_price": 2500
}1.7. Эндпоинт: Получение цены
POST /orders/price
Заголовки: X-Device-Id: <device_id>
Тело (Request Body):
{
"items": [
{ "food_id": 456, "quantity": 2 },
{ "food_id": 789, "quantity": 1 }
],
}Ответ:
{
"total_price": 2500
}1.8. Эндпоинт: Получение текущих заказов пользователя
GET /orders/current
Заголовки: X-Device-Id: <device_id>
Описание:
Возвращает активные (не завершённые) заказы пользователя.
Ответ (пример):
[
{
"order_id": 101112,
"chef_name": "Повар Ахмет",
"chef_phone": "+77001112233",
"status": "in_cooking",
"created_at": "2025-01-01T12:00:00Z",
"total_price": 2500,
"items": [
{ "food_id": 456, "name": "Плов", "quantity": 2, "price": 1500 }
]
}
]1.9. Эндпоинт: Получение истории заказов пользователя
GET /orders/history
Заголовки: X-Device-Id: <device_id>
Описание: Возвращает завершённые или отменённые заказы пользователя.
Ответ (пример):
[
{
"order_id": 101100,
"chef_name": "Повар Ахмет",
"status": "completed",
"created_at": "2024-12-31T18:00:00Z",
"total_price": 2000,
"items": [
{ "food_id": 789, "name": "Манты", "quantity": 3, "price": 700 }
]
}
]2.1. Эндпоинт: Проверка (валидация) access-кода
POST /chefs/validate-access
Заголовки: X-Access-Code: <access_code>
Описание:
Проверяет, существует ли повар в базе с таким access_code. При успехе возвращает идентификатор повара или успех.
Ответ (пример):
{
"chef_id": 123,
"message": "Access code is valid"
}Ошибки:
401 Unauthorized: неверный или отсутствующий access_code.
2.2. Эндпоинт: Смена рабочего статуса повара
POST /chefs/working-status
Заголовки: X-Access-Code: <access_code>
Тело (Request Body):
{
"is_working": true
}Описание:
Устанавливает повару рабочий статус (is_working = true/false).
Ответ:
{
"message": "Working status updated successfully",
"is_working": true
}2.3. SSE-эндпоинт: Просмотр поступающих заказов в реальном времени
GET /chefs/orders/stream
Заголовки: X-Access-Code: <access_code>
Описание: Открывает SSE-подключение (тип text/event-stream), по которому повар получает информацию о новых заказах.
Пример события (SSE):
event: new_order
data: {"order_id": 101112, "items": [...], "customer_id": 55, ... }
2.4. Эндпоинт: Принятие или отказ от заказа
POST /chefs/orders/{order_id}/response
Заголовки: X-Access-Code: <access_code>
Тело (Request Body):
{
"action": "accept" // или "reject"
"reject_comment": "Извините, я не успеваю"
}Если action = "reject", то поле reject_comment — обязательное.
Описание:
Позволяет повару принять или отклонить заказ.
- При принятии заказа статус может меняться на in_cooking.
- При отказе заказ становится canceled (или “rejected” — в зависимости от бизнес-логики), а в поле комментария сохраняется причина.
Ответ (пример):
{
"order_id": 101112,
"status": "in_cooking",
"message": "Order accepted"
}либо
{
"order_id": 101112,
"status": "canceled",
"message": "Order rejected: Извините, я не успеваю"
}2.5. Эндпоинт: Смена статуса заказа
POST /chefs/orders/{order_id}/status
Заголовки: X-Access-Code: <access_code>
Тело (Request Body):
{
"status": "delivering" // или in_cooking, completed, canceled
}Описание:
Изменяет статус уже принятого заказа. Например, повар поменял статус на delivering, когда заказ передан курьеру.
Ответ (пример):
{
"order_id": 101112,
"status": "delivering",
"message": "Order status updated"
}Примечание
- При создании заказа (эндпоинт /orders):
- Проверяет наличие device_id (customer_id).
- Проверяет валидность адреса и персональных данных.
- Определяет повара для каждого блюда по food.chef_id.
- Сохраняет заказ.
- Отправляет SSE-событие с деталями нового заказа, чтобы повар(ы) могли увидеть поступление.
- При SSE-подписке повар получает новые заказы или обновления статусов. Пример события:
event: new_order
data: {"order_id":123, "items":[...] }
Или:
event: order_status_update
data: {"order_id":123, "new_status":"delivering" }