Files
market-backOfficce/API.ru.md
2026-02-20 09:01:02 +04:00

504 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Документация 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.
### Каскадное удаление
| Удаляемый объект | Также удаляется |
|---|---|
| Категория | все подкатегории (рекурсивно) и их товары |
| Подкатегория | все вложенные подкатегории (рекурсивно) и их товары |
| Товар | ничего больше |