From 8960744a8190f6e79af71392af91fe1e2804a693 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Thu, 28 May 2026 10:32:33 +0330 Subject: [PATCH] fix update bank status --- backend/order/tasks.py | 77 ++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/backend/order/tasks.py b/backend/order/tasks.py index b645538..405ce3a 100644 --- a/backend/order/tasks.py +++ b/backend/order/tasks.py @@ -10,45 +10,78 @@ from account.models import PushSubscription import ghasedak_sms from product.models import ProductImageModel from celery import shared_task +from django.db import transaction + +logger = logging.getLogger(__name__) @shared_task def udpate_bank_status(): factory = bankfactories.BankFactory() - bank_models.Bank.objects.update_expire_records() + # ۱. بروزرسانی رکوردهای منقضی در یک تراکنش جداگانه + try: + with transaction.atomic(): + bank_models.Bank.objects.update_expire_records() + except Exception as e: + logger.error(f"Error in update_expire_records: {e}") + # ۲. پردازش رکوردهایی که از بانک برگشته‌اند for item in bank_models.Bank.objects.filter_return_from_bank(): - bank = factory.create( - bank_type=item.bank_type, identifier=item.bank_choose_identifier - ) - bank.verify(item.tracking_code) - bank_record = bank_models.Bank.objects.get(tracking_code=item.tracking_code) - if bank_record.is_success and bank_record.order: - bank_record.order.cart.clear_cart() - bank_record.order.is_paid = True - bank_record.order.save() - logging.debug("This record is verify now.", extra={"pk": bank_record.pk}) - elif bank_record.order: - order = bank_record.order - order.rollback_stock() + try: + with transaction.atomic(): + bank = factory.create( + bank_type=item.bank_type, identifier=item.bank_choose_identifier + ) + bank.verify(item.tracking_code) + # استفاده از select_for_update برای جلوگیری از Race Condition + bank_record = bank_models.Bank.objects.select_related('order').select_for_update().get(tracking_code=item.tracking_code) + + if bank_record.is_success and bank_record.order: + bank_record.order.cart.clear_cart() + bank_record.order.is_paid = True + bank_record.order.save(update_fields=['is_paid']) + elif bank_record.order: + bank_record.order.rollback_stock() + except Exception as e: + logger.error(f"Failed to verify bank record {item.tracking_code}: {e}") failed_statuses = [ - PaymentStatus.CANCEL_BY_USER, - PaymentStatus.EXPIRE_GATEWAY_TOKEN, - PaymentStatus.EXPIRE_VERIFY_PAYMENT, - PaymentStatus.ERROR, - ] + PaymentStatus.CANCEL_BY_USER, + PaymentStatus.EXPIRE_GATEWAY_TOKEN, + PaymentStatus.EXPIRE_VERIFY_PAYMENT, + PaymentStatus.ERROR, + ] + + # 1. ابتدا رکوردهای بانکی را بدون select_related پیدا کنید + # از select_for_update اینجا استفاده نکنید چون به Order وصل است failed_records = bank_models.Bank.objects.filter( status__in=failed_statuses, order__isnull=False, order__is_paid=False, order__is_stock_rolled_back=False, - ).select_related('order') + ) + + logger.info(f"Found {failed_records.count()} failed records for rollback.") for bank_record in failed_records: - bank_record.order.rollback_stock() + try: + # استفاده از تراکنش اتمیک برای هر رکورد + with transaction.atomic(): + # 2. حالا خودِ Order مربوطه را با select_for_update قفل کنید + # این کار مانع از تداخل با بقیه تسک‌ها می‌شود + order = bank_record.order + + # بررسی مجدد شرط برای اطمینان در لحظه قفل شدن + if order and not order.is_paid and not order.is_stock_rolled_back: + order.rollback_stock() + logger.info(f"Successfully rolled back stock for bank record {bank_record.id}") + else: + logger.info(f"Order {order.id} already processed or paid, skipping.") + + except Exception as e: + logger.error(f"Failed to rollback stock for bank record {bank_record.id}: {str(e)}") - return 'update bank record is done' + return "update bank record is done" @shared_task