added translation

This commit is contained in:
sdarbinyan
2026-02-20 09:01:02 +04:00
parent 083b270c74
commit 6850a911f3
22 changed files with 1219 additions and 136 deletions

503
API.ru.md Normal file
View 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.
### Каскадное удаление
| Удаляемый объект | Также удаляется |
|---|---|
| Категория | все подкатегории (рекурсивно) и их товары |
| Подкатегория | все вложенные подкатегории (рекурсивно) и их товары |
| Товар | ничего больше |