Добавлен асинхронный импорт товаров с параллельной загрузкой фото + исправлен баг со счётчиком SKU
- Реализован импорт Product из CSV/XLSX через Celery с прогресс-баром - Параллельная загрузка фото товаров с внешних URL (масштабируемость до 500+ товаров) - Добавлена модель ProductImportJob для отслеживания статуса импорта - Создан таск download_product_photo_async для загрузки фото в фоне - Интеграция с существующим ImageProcessor (синхронная обработка через use_async=False) - Добавлены view и template для импорта с real-time обновлением через AJAX FIX: Исправлен баг со счётчиком SKU - инкремент только после успешного сохранения - Добавлен SKUCounter.peek_next_value() - возвращает следующий номер БЕЗ инкремента - Добавлен SKUCounter.increment_counter() - инкрементирует счётчик - generate_product_sku() использует peek_next_value() вместо get_next_value() - Добавлен post_save сигнал increment_sku_counter_after_save() для инкремента после создания - Предотвращает пропуски номеров при ошибках валидации (например cost_price NULL) FIX: Исправлена ошибка с is_main в ProductPhoto - ProductPhoto не имеет поля is_main, используется только order - Первое фото (order=0) автоматически считается главным - Удалён параметр is_main из download_product_photo_async и _collect_photo_tasks Изменены файлы: - products/models/base.py - методы для управления счётчиком SKU - products/models/import_job.py - модель для отслеживания импорта - products/services/import_export.py - сервис импорта с поддержкой Celery - products/tasks.py - таски для асинхронного импорта и загрузки фото - products/signals.py - сигнал для инкремента счётчика после сохранения - products/utils/sku_generator.py - использование peek_next_value() - products/views/product_import_views.py - view для импорта - products/templates/products/product_import*.html - UI для импорта - docker/entrypoint.sh - настройка Celery worker (concurrency=4) - requirements.txt - добавлен requests для загрузки фото
This commit is contained in:
@@ -53,6 +53,55 @@ class SKUCounter(models.Model):
|
||||
"""
|
||||
Получить следующее значение счетчика (thread-safe).
|
||||
Использует select_for_update для предотвращения race conditions.
|
||||
|
||||
DEPRECATED: Используйте peek_next_value() + increment_counter() вместо этого метода.
|
||||
Этот метод инкрементирует счётчик немедленно, что может привести к пропускам номеров
|
||||
при ошибках сохранения объекта.
|
||||
"""
|
||||
with transaction.atomic():
|
||||
counter, created = cls.objects.select_for_update().get_or_create(
|
||||
counter_type=counter_type,
|
||||
defaults={'current_value': 0}
|
||||
)
|
||||
counter.current_value += 1
|
||||
counter.save()
|
||||
return counter.current_value
|
||||
|
||||
@classmethod
|
||||
def peek_next_value(cls, counter_type):
|
||||
"""
|
||||
FIX: SKU counter bug - increment only after successful save
|
||||
|
||||
Получить следующее значение счетчика БЕЗ инкремента (thread-safe).
|
||||
Используется для генерации SKU перед сохранением объекта.
|
||||
Фактический инкремент выполняется в post_save сигнале после успешного создания.
|
||||
|
||||
Args:
|
||||
counter_type: Тип счётчика ('product', 'kit', 'category', 'configurable')
|
||||
|
||||
Returns:
|
||||
int: Следующее значение счётчика (current_value + 1)
|
||||
"""
|
||||
with transaction.atomic():
|
||||
counter, created = cls.objects.select_for_update().get_or_create(
|
||||
counter_type=counter_type,
|
||||
defaults={'current_value': 0}
|
||||
)
|
||||
return counter.current_value + 1
|
||||
|
||||
@classmethod
|
||||
def increment_counter(cls, counter_type):
|
||||
"""
|
||||
FIX: SKU counter bug - increment only after successful save
|
||||
|
||||
Инкрементировать счётчик (thread-safe).
|
||||
Вызывается в post_save сигнале после успешного создания объекта с автогенерированным SKU.
|
||||
|
||||
Args:
|
||||
counter_type: Тип счётчика ('product', 'kit', 'category', 'configurable')
|
||||
|
||||
Returns:
|
||||
int: Новое значение счётчика после инкремента
|
||||
"""
|
||||
with transaction.atomic():
|
||||
counter, created = cls.objects.select_for_update().get_or_create(
|
||||
|
||||
Reference in New Issue
Block a user