86 lines
3.5 KiB
Python
86 lines
3.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
from celery import shared_task
|
||
from django.utils import timezone
|
||
from django.db.models import Q
|
||
import logging
|
||
from django_tenants.utils import get_tenant_model, schema_context
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
@shared_task
|
||
def cleanup_expired_cart_locks():
|
||
"""
|
||
Периодическая задача для очистки истекших блокировок корзины.
|
||
Освобождает витринные комплекты, которые были добавлены в корзину,
|
||
но блокировка истекла (timeout 30 минут).
|
||
|
||
Запускается каждые 5 минут (настроить в celery beat schedule).
|
||
Проходит по всем тенантам.
|
||
|
||
Returns:
|
||
dict: Статистика очистки {
|
||
'released_count': int, # Общее количество освобожденных блокировок
|
||
'details': list # Детали по каждому тенанту
|
||
}
|
||
"""
|
||
Tenant = get_tenant_model()
|
||
overall_stats = {
|
||
'released_count': 0,
|
||
'details': []
|
||
}
|
||
|
||
# Проходим по всем тенантам (кроме public)
|
||
# Public схема не содержит бизнес-данных (корзины, резервы)
|
||
tenants = Tenant.objects.exclude(schema_name='public')
|
||
|
||
for tenant in tenants:
|
||
try:
|
||
with schema_context(tenant.schema_name):
|
||
from inventory.models import Reservation
|
||
|
||
# Находим все резервы с истекшей блокировкой
|
||
expired_locks = Reservation.objects.filter(
|
||
Q(cart_lock_expires_at__lte=timezone.now()) &
|
||
Q(cart_lock_expires_at__isnull=False) &
|
||
Q(status='reserved')
|
||
).select_related('product_kit', 'locked_by_user')
|
||
|
||
count = expired_locks.count()
|
||
|
||
if count > 0:
|
||
affected_kits = list(
|
||
expired_locks.values_list('product_kit_id', flat=True).distinct()
|
||
)
|
||
|
||
logger.info(
|
||
f"[{tenant.schema_name}] Очистка истекших блокировок: {count} резервов"
|
||
)
|
||
|
||
# Очищаем блокировки
|
||
expired_locks.update(
|
||
cart_lock_expires_at=None,
|
||
locked_by_user=None,
|
||
cart_session_id=None
|
||
)
|
||
|
||
overall_stats['released_count'] += count
|
||
overall_stats['details'].append({
|
||
'tenant': tenant.schema_name,
|
||
'released': count,
|
||
'kits': affected_kits
|
||
})
|
||
|
||
except Exception as e:
|
||
# Логируем ошибку, но не прерываем обработку других тенантов
|
||
logger.error(f"[{tenant.schema_name}] Ошибка при очистке блокировок: {e}", exc_info=True)
|
||
overall_stats['details'].append({
|
||
'tenant': tenant.schema_name,
|
||
'error': str(e)
|
||
})
|
||
|
||
if overall_stats['released_count'] > 0:
|
||
logger.info(f"Общая очистка блокировок завершена: {overall_stats}")
|
||
|
||
return overall_stats
|