🎯 Введение

API для работы с каталогом офферов позволяет управлять источниками данных и товарными предложениями (офферами). Система поддерживает все базовые CRUD операции, массовые операции, фильтрацию, поиск и статистику.

📦 Базовый URL

Все запросы выполняются к базовому URL: https://gkt.itunity.dev:3000/api/

🔑 Аутентификация

В текущей версии API работает без аутентификации. В продакшн-режиме рекомендуется настроить аутентификацию через DRF.

📊 Формат данных

Все запросы и ответы используют JSON формат. Устанавливайте заголовок Content-Type: application/json для запросов с телом.

📌 Важные замечания

  • Пагинация: Все списки поддерживают пагинацию. По умолчанию 20 элементов на странице.
  • Сортировка: Используйте параметр ordering с именем поля. Префикс - для обратной сортировки.
  • Поиск: Параметр search ищет по текстовым полям модели.
  • Фильтрация: Используйте имена полей для точной фильтрации.

🏢 Источники (Sources)

Управление источниками данных. Источник - это поставщик или площадка, откуда поступают офферы.

📋 Поля модели Source

Поле Тип Описание Обязательное
id Integer Уникальный идентификатор источника Только чтение
name CharField Наименование источника/поставщика Обязательно
url TextField URL сайта источника Опционально
updated_at DateTimeField Дата и время последнего обновления Только чтение

Эндпоинты

GET Получить список источников

https://gkt.itunity.dev:3000/api/sources/

Возвращает список всех источников с поддержкой пагинации и фильтрации.

Параметры запроса:
search Поиск по name и url Опционально
has_offers Фильтр по наличию офферов (true/false) Опционально
ordering Сортировка (name, -name, updated_at, -updated_at) Опционально
page Номер страницы Опционально
page_size Количество элементов на странице (по умолчанию 20) Опционально
Пример запроса:
curl -X GET "https://gkt.itunity.dev:3000/api/sources/?search=example&ordering=name&page=1"
Пример ответа:
{ "count": 42, "next": "https://gkt.itunity.dev:3000/api/sources/?page=2", "previous": null, "results": [ { "id": 1, "name": "Example Supplier", "url": "https://example.com", "updated_at": "2024-01-15T10:30:00Z" } ] }

POST Создать источник

https://gkt.itunity.dev:3000/api/sources/

Создает новый источник данных.

Тело запроса:
{ "name": "Example Supplier", "url": "https://example.com" }
Пример ответа (201 Created):
{ "id": 1, "name": "Example Supplier", "url": "https://example.com", "updated_at": "2024-01-15T10:30:00Z" }

GET Получить источник по ID

https://gkt.itunity.dev:3000/api/sources/{id}/

Возвращает детальную информацию об источнике.

Пример ответа:
{ "id": 1, "name": "Example Supplier", "url": "https://example.com", "updated_at": "2024-01-15T10:30:00Z" }

PUT Полное обновление источника

https://gkt.itunity.dev:3000/api/sources/{id}/

Полностью обновляет информацию об источнике. Требует все обязательные поля.

Пример запроса:
{ "name": "Updated Name", "url": "https://updated.com" }

PATCH Частичное обновление источника

https://gkt.itunity.dev:3000/api/sources/{id}/

Обновляет только указанные поля источника.

Пример запроса:
{ "name": "New Name Only" }

DELETE Удалить источник

https://gkt.itunity.dev:3000/api/sources/{id}/

Удаляет источник по ID. Вместе с источником удаляются все связанные офферы (каскадное удаление).

Пример ответа (204 No Content):

Пустой ответ с кодом 204 при успешном удалении.

📊 Дополнительные эндпоинты для источников

GET https://gkt.itunity.dev:3000/api/sources/stats/ Статистика по источникам (количество офферов, средние цены)
POST https://gkt.itunity.dev:3000/api/sources/bulk-create/ Массовое создание источников
POST https://gkt.itunity.dev:3000/api/sources/bulk-delete/ Массовое удаление источников по списку ID
GET https://gkt.itunity.dev:3000/api/sources/{id}/offers/ Получить все офферы конкретного источника

📦 Офферы (Offers)

Управление товарными предложениями. Оффер - это товарное предложение от источника с уникальной комбинацией source + pn_raw + condition_raw.

📋 Поля модели OfferRaw

Поле Тип Описание Обязательное
id Integer Уникальный идентификатор оффера Только чтение
source ForeignKey (Source) Источник данных (связь с моделью Source) Обязательно
source_identifier CharField (write-only) Идентификатор источника (имя, URL или ID) Обязательно
brand_raw CharField Название бренда в сыром виде Опционально
pn_raw CharField Номер детали/артикул в сыром виде Обязательно
condition_raw CharField Состояние товара (new, used, refurbished и т.д.) Опционально
status_raw CharField Статус оффера (active, inactive, sold и т.д.) Опционально
price_raw DecimalField Цена товара Опционально
price_currency CharField Валюта цены (USD, EUR, GBP и т.д.) Опционально
weight_value DecimalField Вес товара Опционально
weight_unit CharField Единица измерения веса (kg, g, lb и т.д.) Опционально
product_url TextField URL страницы товара у источника Опционально
raw_payload JSONField Сырые данные оффера в формате JSON Опционально
raw_hash CharField SHA256 хеш raw_payload для отслеживания изменений Опционально
created_at DateTimeField Дата и время создания оффера Только чтение

Основные эндпоинты

GET Получить список офферов

https://gkt.itunity.dev:3000/api/offers/

Возвращает список офферов с поддержкой фильтрации, поиска и сортировки.

Параметры запроса:
source ID источника Опционально
brand_raw Фильтр по бренду Опционально
pn_raw Фильтр по номеру детали Опционально
condition_raw Фильтр по состоянию Опционально
price_min Минимальная цена Опционально
price_max Максимальная цена Опционально
currency Фильтр по валюте (USD, EUR, GBP и т.д.) Опционально
search Поиск по brand_raw, pn_raw, condition_raw Опционально
ordering Сортировка (created_at, -price_raw, pn_raw) Опционально
Пример ответа:
{ "count": 156, "next": "https://gkt.itunity.dev:3000/api/offers/?page=2", "previous": null, "results": [ { "id": 1, "source": { "id": 1, "name": "Example Supplier", "url": "https://example.com", "updated_at": "2024-01-15T10:30:00Z" }, "brand_raw": "Bosch", "pn_raw": "0 986 AF2 200", "condition_raw": "new", "price_raw": "45.99", "price_currency": "EUR", "created_at": "2024-01-15T10:35:00Z" } ] }

POST Создать оффер

https://gkt.itunity.dev:3000/api/offers/

Создает новый оффер. Источник можно указать по имени, URL или ID (будет создан если не существует).

Тело запроса:
{ "source_identifier": "Supplier Name", "brand_raw": "Brand A", "pn_raw": "PART-123", "condition_raw": "new", "price_raw": "100.50", "price_currency": "USD", "product_url": "https://example.com/product" }

💡 Важная информация о source_identifier

source_identifier может быть:

  • ✅ Имя существующего источника
  • ✅ URL существующего источника
  • ✅ ID существующего источника (число)
  • ✅ Новое имя (источник будет создан)
  • ✅ Новый URL (источник будет создан с именем из домена)

POST Создать оффер (строгий режим)

https://gkt.itunity.dev:3000/api/offers/create-strict/

Создает оффер только с существующим источником (указывается по ID).

Тело запроса:
{ "source_id": 1, "brand_raw": "Brand B", "pn_raw": "PART-456", "condition_raw": "used" }

GET Получить оффер по ID

https://gkt.itunity.dev:3000/api/offers/{id}/

Возвращает детальную информацию об оффере.

PUT Полное обновление оффера

https://gkt.itunity.dev:3000/api/offers/{id}/

Полностью обновляет оффер. Требует все обязательные поля.

PATCH Частичное обновление оффера

https://gkt.itunity.dev:3000/api/offers/{id}/

Обновляет указанные поля оффера. Поддерживает изменение источника.

Примеры запросов:

Без изменения источника:

{ "price_raw": "120.00", "status_raw": "active" }

С изменением источника:

{ "source_identifier": "New Supplier", "brand_raw": "Updated Brand" }

С изменением источника по ID:

{ "source_id": 2, "pn_raw": "UPDATED-001" }

DELETE Удалить оффер

https://gkt.itunity.dev:3000/api/offers/{id}/

Удаляет оффер по ID.

Специальные эндпоинты

POST Массовое создание офферов

https://gkt.itunity.dev:3000/api/offers/bulk-create/

Создает несколько офферов за один запрос. Возвращает статистику по созданным и пропущенным офферам.

Тело запроса:
{ "offers": [ { "source_identifier": "Supplier 1", "brand_raw": "Brand A", "pn_raw": "PART-001", "condition_raw": "new" }, { "source_identifier": "Supplier 2", "brand_raw": "Brand B", "pn_raw": "PART-002", "condition_raw": "used" } ] }
Пример ответа:
{ "created": 2, "skipped": 0, "errors": [] }

POST Массовое обновление офферов

https://gkt.itunity.dev:3000/api/offers/bulk-update/

Обновляет несколько офферов за один запрос. Каждое обновление должно содержать ID оффера и данные для обновления.

Тело запроса:
{ "updates": [ { "id": 1, "data": { "price_raw": "110.00", "status_raw": "active" } }, { "id": 2, "data": { "brand_raw": "Updated Brand" } } ] }
Пример ответа:
{ "updated": [ { "id": 1, "status": "updated" }, { "id": 2, "status": "updated" } ], "errors": [], "not_found": [] }

PATCH Обновить только цену

https://gkt.itunity.dev:3000/api/offers/{id}/update-price/

Специальный эндпоинт для быстрого обновления цены без изменения других полей.

Тело запроса:
{ "price_raw": "99.99", "price_currency": "EUR" }

PATCH Изменить источник

https://gkt.itunity.dev:3000/api/offers/{id}/change-source/

Специальный эндпоинт для изменения источника оффера.

По имени/URL:
{ "source_identifier": "New Source" }
По ID:
{ "source_id": 3 }

GET Расширенный поиск

https://gkt.itunity.dev:3000/api/offers/search/

Расширенный поиск с дополнительными параметрами фильтрации.

Пример запроса:
GET https://gkt.itunity.dev:3000/api/offers/search/?brand=Bosch&price_min=40&price_max=60¤cy=EUR

GET Офферы по имени источника

https://gkt.itunity.dev:3000/api/offers/by-source/{source_name}/

Получает все офферы от источника по его имени (частичное совпадение).

Пример запроса:
GET https://gkt.itunity.dev:3000/api/offers/by-source/Bosch/

GET Статистика по офферам

https://gkt.itunity.dev:3000/api/offers/stats/

Возвращает подробную статистику по всем офферам.

Пример ответа:
{ "summary": { "total_offers": 156, "total_sources": 12, "offers_with_price": 145, "offers_with_brand": 140 }, "sources": [ { "source_id": 1, "source_name": "Example Supplier", "offers_count": 45, "avg_price": 123.45 } ], "top_brands": [ { "brand_raw": "Bosch", "count": 30 }, { "brand_raw": "Siemens", "count": 25 } ], "currencies": [ { "price_currency": "EUR", "count": 80, "avg_price": 145.67 } ], "price_statistics": { "min_price": 10.99, "max_price": 999.99, "avg_price": 156.78, "total_with_price": 145 } }

POST Массовое удаление офферов

https://gkt.itunity.dev:3000/api/offers/bulk-delete/

Удаляет несколько офферов по списку ID.

Тело запроса:
{ "ids": [1, 2, 3, 4, 5] }
Пример ответа:
{ "deleted_count": 5, "message": "Удалено 5 офферов" }

🚀 Примеры использования

Сценарий 1: Импорт данных от нового поставщика

# 1. Создаем источник (или используем существующий) POST https://gkt.itunity.dev:3000/api/sources/ { "name": "Новый Поставщик", "url": "https://new-supplier.com" } # 2. Импортируем офферы POST https://gkt.itunity.dev:3000/api/offers/bulk-create/ { "offers": [ { "source_identifier": "Новый Поставщик", "brand_raw": "Bosch", "pn_raw": "0 986 AF2 200", "condition_raw": "new", "price_raw": "45.99", "price_currency": "EUR" }, { "source_identifier": "Новый Поставщик", "brand_raw": "Siemens", "pn_raw": "5SJ61217CC20", "condition_raw": "used", "price_raw": "22.50", "price_currency": "EUR" } ] }

Сценарий 2: Ежедневное обновление цен

# 1. Получаем все офферы от поставщика GET https://gkt.itunity.dev:3000/api/offers/?source_name=Поставщик # 2. Обновляем цены массово POST https://gkt.itunity.dev:3000/api/offers/bulk-update/ { "updates": [ { "id": 123, "data": {"price_raw": "47.50"} }, { "id": 124, "data": {"price_raw": "23.00"} } ] }

Сценарий 3: Мониторинг и статистика

# Статистика по источникам GET https://gkt.itunity.dev:3000/api/sources/stats/ # Общая статистика по офферам GET https://gkt.itunity.dev:3000/api/offers/stats/ # Поиск конкретных позиций GET https://gkt.itunity.dev:3000/api/offers/?search=Bosch&price_min=40&price_max=60¤cy=EUR

Сценарий 4: Поиск дубликатов

# Поиск офферов с одинаковым pn_raw от разных источников GET https://gkt.itunity.dev:3000/api/offers/?search=PART-123

🗄️ Модели данных

Модель Source

📝 Описание полей

id (Integer) - Уникальный идентификатор источника

name (CharField) - Наименование источника/поставщика Обязательно Макс. длина: 255

url (TextField) - URL сайта источника

updated_at (DateTimeField) - Дата и время последнего обновления

Модель OfferRaw

📝 Описание полей

id (Integer) - Уникальный идентификатор оффера

source (ForeignKey (Source)) - Источник данных (связь с моделью Source) Обязательно

source_identifier (CharField (write-only)) - Идентификатор источника (имя, URL или ID) Обязательно Макс. длина: 255

brand_raw (CharField) - Название бренда в сыром виде Макс. длина: 255

pn_raw (CharField) - Номер детали/артикул в сыром виде Обязательно Макс. длина: 255

condition_raw (CharField) - Состояние товара (new, used, refurbished и т.д.) Макс. длина: 255

status_raw (CharField) - Статус оффера (active, inactive, sold и т.д.) Макс. длина: 255

price_raw (DecimalField) - Цена товара

price_currency (CharField) - Валюта цены (USD, EUR, GBP и т.д.) Макс. длина: 16

weight_value (DecimalField) - Вес товара (Вес)

weight_unit (CharField) - Единица измерения веса (kg, g, lb и т.д.) Макс. длина: 16 (Единица веса)

product_url (TextField) - URL страницы товара у источника

raw_payload (JSONField) - Сырые данные оффера в формате JSON

raw_hash (CharField) - SHA256 хеш raw_payload для отслеживания изменений Макс. длина: 64

created_at (DateTimeField) - Дата и время создания оффера

🔐 Уникальные ограничения

Офферы имеют уникальное ограничение по комбинации полей: source + pn_raw + condition_raw. Это означает, что не может быть двух офферов с одинаковым источником, номером детали и состоянием.

🗄️ Индексы базы данных

  • offer_raw_src_brand_idx - по source и brand_raw
  • catalog_offer_raw_pn_raw_idx - по pn_raw
  • catalog_sources_name_idx - по name в источниках

⚠️ Коды ошибок

Код Описание Причина
400 Bad Request Некорректный запрос Невалидные данные, отсутствие обязательных полей
401 Unauthorized Не авторизован Отсутствует или недействителен токен авторизации
403 Forbidden Доступ запрещен Недостаточно прав для выполнения операции
404 Not Found Ресурс не найден Несуществующий ID или эндпоинт
409 Conflict Конфликт данных Попытка создать дубликат оффера
500 Internal Server Error Внутренняя ошибка сервера Проблемы с базой данных или логикой приложения

Примеры ошибок

Дубликат оффера (409 Conflict)

{ "non_field_errors": [ "Оффер с такими source (Supplier), pn_raw (PART-123) и condition_raw (new) уже существует" ] }

Невалидные данные (400 Bad Request)

{ "source_identifier": ["Это поле обязательно"], "pn_raw": ["Это поле обязательно"], "price_raw": ["Введите число"] }

Ресурс не найден (404 Not Found)

{ "detail": "Не найдено." }