added translation
This commit is contained in:
503
API.ru.md
Normal file
503
API.ru.md
Normal file
@@ -0,0 +1,503 @@
|
||||
# Документация API
|
||||
|
||||
Справочник эндпоинтов для бэкофиса маркетплейса.
|
||||
Базовый URL: `https://your-api-domain.com/api`
|
||||
|
||||
> 🇬🇧 English documentation: [API.md](./API.md)
|
||||
|
||||
---
|
||||
|
||||
## Проекты
|
||||
|
||||
### Получить все проекты
|
||||
```
|
||||
GET /api/projects
|
||||
|
||||
Ответ 200:
|
||||
[
|
||||
{
|
||||
"id": "dexar",
|
||||
"name": "dexar",
|
||||
"displayName": "Dexar Marketplace",
|
||||
"active": true,
|
||||
"logoUrl": "https://..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Категории
|
||||
|
||||
### Получить категории проекта
|
||||
```
|
||||
GET /api/projects/:projectId/categories
|
||||
|
||||
Ответ 200:
|
||||
[
|
||||
{
|
||||
"id": "cat1",
|
||||
"name": "Электроника",
|
||||
"visible": true,
|
||||
"priority": 1,
|
||||
"img": "https://...",
|
||||
"projectId": "dexar",
|
||||
"subcategories": [ ...Subcategory[] ],
|
||||
"translations": {
|
||||
"ru": { "name": "Электроника" }
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Получить категорию по ID
|
||||
```
|
||||
GET /api/categories/:categoryId
|
||||
|
||||
Ответ 200: (объект категории с вложенными подкатегориями)
|
||||
```
|
||||
|
||||
### Создать категорию
|
||||
```
|
||||
POST /api/projects/:projectId/categories
|
||||
|
||||
Тело запроса:
|
||||
{
|
||||
"name": "Новая категория", // обязательно
|
||||
"visible": true,
|
||||
"priority": 10,
|
||||
"img": "https://...",
|
||||
"translations": { // опционально
|
||||
"ru": { "name": "Новая категория" }
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 201: (созданный объект категории)
|
||||
```
|
||||
|
||||
### Обновить категорию
|
||||
```
|
||||
PATCH /api/categories/:categoryId
|
||||
|
||||
Тело запроса: (любое подмножество полей)
|
||||
{
|
||||
"name": "Обновлённое название",
|
||||
"visible": false,
|
||||
"priority": 5,
|
||||
"translations": {
|
||||
"ru": { "name": "Обновлённое название" }
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 200: (обновлённый объект категории)
|
||||
```
|
||||
|
||||
### Удалить категорию
|
||||
```
|
||||
DELETE /api/categories/:categoryId
|
||||
|
||||
Ответ 204 No Content
|
||||
|
||||
Примечание: каскадно удаляет все подкатегории и товары внутри
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Подкатегории
|
||||
|
||||
Подкатегории **рекурсивны** — вложенность неограничена. Единственное ограничение:
|
||||
**подкатегория с товарами не может иметь дочерних подкатегорий** (и наоборот).
|
||||
|
||||
### Объект подкатегории
|
||||
```json
|
||||
{
|
||||
"id": "sub1",
|
||||
"name": "Смартфоны",
|
||||
"visible": true,
|
||||
"priority": 1,
|
||||
"img": "https://...",
|
||||
"categoryId": "cat1", // всегда ID корневой категории
|
||||
"parentId": "cat1", // ID прямого родителя (категория или подкатегория)
|
||||
"itemCount": 15,
|
||||
"hasItems": true,
|
||||
"subcategories": [], // дочерние подкатегории (пусто, если hasItems = true)
|
||||
"translations": {
|
||||
"ru": { "name": "Смартфоны" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> `categoryId` — всегда ID **корневой категории** этого поддерева.
|
||||
> `parentId` — ID **прямого родителя**: может быть ID категории или подкатегории.
|
||||
|
||||
---
|
||||
|
||||
### Получить подкатегории категории
|
||||
```
|
||||
GET /api/categories/:categoryId/subcategories
|
||||
|
||||
Ответ 200: Subcategory[] (вложенные подкатегории рекурсивно заполнены)
|
||||
```
|
||||
|
||||
### Получить подкатегорию по ID
|
||||
```
|
||||
GET /api/subcategories/:subcategoryId
|
||||
|
||||
Ответ 200: объект подкатегории (с вложенными подкатегориями при наличии)
|
||||
```
|
||||
|
||||
### Создать подкатегорию в категории (уровень 1)
|
||||
```
|
||||
POST /api/categories/:categoryId/subcategories
|
||||
|
||||
Тело запроса:
|
||||
{
|
||||
"id": "custom-id", // опционально, генерируется автоматически (используется в URL)
|
||||
"name": "Смартфоны", // обязательно
|
||||
"visible": true,
|
||||
"priority": 10,
|
||||
"translations": {
|
||||
"ru": { "name": "Смартфоны" }
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 201: (созданный объект подкатегории)
|
||||
Ошибка 400: если категория не существует
|
||||
```
|
||||
|
||||
### Создать подкатегорию в подкатегории (уровень 2+, вложенная)
|
||||
```
|
||||
POST /api/subcategories/:parentSubcategoryId/subcategories
|
||||
|
||||
Тело запроса:
|
||||
{
|
||||
"id": "custom-id", // опционально
|
||||
"name": "Apple", // обязательно
|
||||
"visible": true,
|
||||
"priority": 10
|
||||
}
|
||||
|
||||
Ответ 201: (созданный объект подкатегории)
|
||||
Ошибка 400: если родительская подкатегория содержит товары (hasItems = true)
|
||||
Ошибка 404: если родительская подкатегория не найдена
|
||||
```
|
||||
|
||||
### Обновить подкатегорию
|
||||
```
|
||||
PATCH /api/subcategories/:subcategoryId
|
||||
|
||||
Тело запроса: (любое подмножество полей)
|
||||
{
|
||||
"id": "new-slug", // ID редактируется — используется в URL маркетплейса
|
||||
"name": "Новое название",
|
||||
"visible": false,
|
||||
"priority": 3,
|
||||
"translations": {
|
||||
"ru": { "name": "Новое название" }
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 200: (обновлённый объект подкатегории)
|
||||
```
|
||||
|
||||
### Удалить подкатегорию
|
||||
```
|
||||
DELETE /api/subcategories/:subcategoryId
|
||||
|
||||
Ответ 204 No Content
|
||||
|
||||
Примечание: каскадно удаляет все вложенные подкатегории и товары
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Товары
|
||||
|
||||
Товары всегда принадлежат **самой глубокой подкатегории** в иерархии (листовой узел).
|
||||
Подкатегория с хотя бы одним товаром имеет `hasItems: true` и не может принимать дочерние подкатегории.
|
||||
|
||||
### Получить товары (с пагинацией)
|
||||
```
|
||||
GET /api/subcategories/:subcategoryId/items
|
||||
|
||||
Query-параметры:
|
||||
page number (по умолчанию: 1)
|
||||
limit number (по умолчанию: 20)
|
||||
search string опционально — фильтр по названию (без учёта регистра)
|
||||
visible boolean опционально — фильтр по видимости
|
||||
tags string опционально — теги через запятую
|
||||
lang string опционально — код языка (en | ru); влияет на поля names/descriptions в ответе
|
||||
|
||||
Ответ 200:
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "item1",
|
||||
"name": "iPhone 15 Pro",
|
||||
"visible": true,
|
||||
"priority": 1,
|
||||
"quantity": 50,
|
||||
"price": 1299,
|
||||
"currency": "USD",
|
||||
"imgs": ["https://...", "https://..."],
|
||||
"tags": ["new", "featured"],
|
||||
"badges": ["new", "exclusive"],
|
||||
"simpleDescription": "Последний iPhone...",
|
||||
"description": [
|
||||
{ "key": "Цвет", "value": "Чёрный" },
|
||||
{ "key": "Память", "value": "256 ГБ" }
|
||||
],
|
||||
"subcategoryId": "sub1",
|
||||
"translations": {
|
||||
"ru": {
|
||||
"name": "iPhone 15 Pro",
|
||||
"simpleDescription": "Последний iPhone с корпусом из титана",
|
||||
"description": [
|
||||
{ "key": "Цвет", "value": "Чёрный" },
|
||||
{ "key": "Память", "value": "256 ГБ" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"comments": [
|
||||
{
|
||||
"id": "c1",
|
||||
"text": "Отличный товар!",
|
||||
"author": "Иван",
|
||||
"stars": 5,
|
||||
"createdAt": "2024-01-10T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"total": 150,
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"hasMore": true
|
||||
}
|
||||
```
|
||||
|
||||
### Получить товар по ID
|
||||
```
|
||||
GET /api/items/:itemId
|
||||
|
||||
Query-параметры:
|
||||
lang string опционально (en | ru)
|
||||
|
||||
Ответ 200: (полный объект товара)
|
||||
```
|
||||
|
||||
### Создать товар
|
||||
```
|
||||
POST /api/subcategories/:subcategoryId/items
|
||||
|
||||
Тело запроса:
|
||||
{
|
||||
"name": "Новый товар", // обязательно
|
||||
"visible": true,
|
||||
"priority": 10,
|
||||
"quantity": 100,
|
||||
"price": 999,
|
||||
"currency": "USD", // USD | EUR | RUB | GBP | UAH
|
||||
"imgs": ["https://..."],
|
||||
"tags": ["new"],
|
||||
"badges": ["new", "exclusive"], // опционально — стандартные или свои метки
|
||||
"simpleDescription": "Краткое описание",
|
||||
"description": [
|
||||
{ "key": "Размер", "value": "Большой" }
|
||||
],
|
||||
"translations": { // опционально
|
||||
"ru": {
|
||||
"name": "Новый товар",
|
||||
"simpleDescription": "Краткое описание",
|
||||
"description": [
|
||||
{ "key": "Размер", "value": "Большой" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 201: (созданный объект товара)
|
||||
|
||||
Побочный эффект: устанавливает hasItems = true на подкатегории.
|
||||
Подкатегория больше не может принимать дочерние подкатегории.
|
||||
```
|
||||
|
||||
### Обновить товар
|
||||
```
|
||||
PATCH /api/items/:itemId
|
||||
|
||||
Тело запроса: (любое подмножество полей)
|
||||
{
|
||||
"name": "Новое название",
|
||||
"price": 899,
|
||||
"quantity": 80,
|
||||
"visible": false,
|
||||
"translations": {
|
||||
"ru": { "name": "Новое название" }
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 200: (обновлённый объект товара)
|
||||
```
|
||||
|
||||
**Обновление изображений — всегда передавай полный массив:**
|
||||
```
|
||||
PATCH /api/items/:itemId
|
||||
|
||||
Тело запроса:
|
||||
{
|
||||
"imgs": ["https://new1.jpg", "https://new2.jpg"]
|
||||
}
|
||||
|
||||
Ответ 200: (обновлённый объект товара)
|
||||
```
|
||||
|
||||
### Удалить товар
|
||||
```
|
||||
DELETE /api/items/:itemId
|
||||
|
||||
Ответ 204 No Content
|
||||
|
||||
Побочный эффект: если в подкатегории не осталось товаров — hasItems становится false.
|
||||
Подкатегория снова может принимать дочерние подкатегории.
|
||||
```
|
||||
|
||||
### Массовое обновление товаров
|
||||
```
|
||||
PATCH /api/items/bulk
|
||||
|
||||
Тело запроса:
|
||||
{
|
||||
"itemIds": ["item1", "item2", "item3"],
|
||||
"data": {
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
|
||||
Ответ 204 No Content
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Загрузка файлов
|
||||
|
||||
### Загрузить изображение
|
||||
```
|
||||
POST /api/upload
|
||||
|
||||
Тело запроса: multipart/form-data
|
||||
image: File
|
||||
|
||||
Ответ 201:
|
||||
{
|
||||
"url": "https://cdn.example.com/uploads/abc123.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Мультиязычность (i18n)
|
||||
|
||||
Поля `name`, `simpleDescription` и `description` поддерживают переводы через объект `translations`.
|
||||
|
||||
### Структура объекта переводов
|
||||
```json
|
||||
{
|
||||
"translations": {
|
||||
"ru": {
|
||||
"name": "Название на русском",
|
||||
"simpleDescription": "Описание на русском",
|
||||
"description": [
|
||||
{ "key": "Ключ", "value": "Значение" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Основные поля (`name`, `simpleDescription`, `description`) хранятся на **английском** (по умолчанию).
|
||||
> Переводы хранятся в `translations[langCode].*`.
|
||||
|
||||
### Запрос конкретного языка
|
||||
Передай параметр `?lang=ru` в GET-запросах. Бэкенд должен:
|
||||
1. Вернуть `translations.ru.*` там, где перевод заполнен.
|
||||
2. Откатиться к основному полю (английскому), если перевод отсутствует.
|
||||
|
||||
```
|
||||
GET /api/items/:itemId?lang=ru
|
||||
GET /api/subcategories/:subcategoryId/items?lang=ru&page=1
|
||||
```
|
||||
|
||||
Альтернативно — заголовок `Accept-Language: ru`.
|
||||
|
||||
### Поддерживаемые языки
|
||||
| Код | Язык |
|
||||
|-----|----------|
|
||||
| en | Английский (по умолчанию) |
|
||||
| ru | Русский |
|
||||
|
||||
---
|
||||
|
||||
## Примечания
|
||||
|
||||
- Все ответы — JSON.
|
||||
- Используй `PATCH` для частичного обновления — передавай только изменяемые поля.
|
||||
- `priority`: меньшее число — выше в списке.
|
||||
- Поддерживаемые значения `currency`: `USD`, `EUR`, `RUB`, `GBP`, `UAH`.
|
||||
- `badges`: необязательный массив строк. Стандартные значения с цветами в интерфейсе: `new`, `sale`, `exclusive`, `hot`, `limited`, `bestseller`, `featured`. Свои строки тоже допустимы.
|
||||
- `imgs`: при обновлении всегда передавай **полный** массив, не отдельные изображения.
|
||||
- `description`: массив пар `{ key, value }` — свободные атрибуты товара.
|
||||
- Автосохранение из бэкофиса отправляет `PATCH` с одним полем каждые ~500 мс.
|
||||
|
||||
---
|
||||
|
||||
## Бизнес-правила
|
||||
|
||||
### Вложенные подкатегории
|
||||
|
||||
Иерархия выглядит так:
|
||||
|
||||
```
|
||||
Категория (напр. Электроника)
|
||||
Подкатегория L1 (напр. Кухня) <- можно добавлять детей ИЛИ товары, не оба
|
||||
Подкатегория L2 (напр. Большая кухня) <- то же правило
|
||||
Подкатегория L3 (напр. Духовки) <- если есть товары — это листовой узел
|
||||
Товары...
|
||||
```
|
||||
|
||||
Правила:
|
||||
- Категория всегда может принимать новые подкатегории (категории никогда не хранят товары напрямую).
|
||||
- Подкатегория с товарами (`hasItems: true`) **не может** принимать дочерние подкатегории.
|
||||
- `POST /api/subcategories/:id/subcategories` на узел с `hasItems: true` → `400 Bad Request`.
|
||||
- Подкатегория с дочерними подкатегориями не может принимать товары (товары только в листовых узлах).
|
||||
- При создании **первого товара** в подкатегории → `hasItems` становится `true`.
|
||||
- При удалении **последнего товара** → `hasItems` становится `false`; дочерние подкатегории снова можно добавлять.
|
||||
|
||||
### Структура URL (фронтенд маркетплейса)
|
||||
|
||||
Поля `id` подкатегорий и товаров используются напрямую в URL маркетплейса:
|
||||
|
||||
```
|
||||
/{categoryId}/{sub1Id}/{sub2Id}/.../{itemId}
|
||||
|
||||
Примеры:
|
||||
/electronics/smartphones/iphone-15
|
||||
/electronics/smartphones/apple/iphone-15-pro
|
||||
/furniture/living-room/sofas/corner-sofa-modelo
|
||||
```
|
||||
|
||||
Поле `id` подкатегорий редактируется через `PATCH` для переименования слагов.
|
||||
|
||||
### Комментарии
|
||||
|
||||
- `stars` — опционально, целое число от 1 до 5.
|
||||
- `author` — опционально (анонимные комментарии допустимы).
|
||||
- `createdAt` — строка в формате ISO 8601.
|
||||
|
||||
### Каскадное удаление
|
||||
|
||||
| Удаляемый объект | Также удаляется |
|
||||
|---|---|
|
||||
| Категория | все подкатегории (рекурсивно) и их товары |
|
||||
| Подкатегория | все вложенные подкатегории (рекурсивно) и их товары |
|
||||
| Товар | ничего больше |
|
||||
Reference in New Issue
Block a user