""" 阿龙电竞 - Celery工具函数 提供通用的Celery辅助函数,确保数据安全和操作稳定 【修复版】已解决装饰器导致Celery任务无法识别的问题 """ import logging from django.db import transaction from datetime import datetime from decimal import Decimal, InvalidOperation import functools # 【核心修复】必须导入此模块 # 配置日志 logger = logging.getLogger(__name__) def safe_decimal_operation(value, default=Decimal('0.00')): """ 安全处理Decimal操作,防止类型转换错误 :param value: 输入值 :param default: 默认值 :return: Decimal对象 """ try: if value is None: return default if isinstance(value, Decimal): return value if isinstance(value, (int, float)): return Decimal(str(value)) if isinstance(value, str): # 移除可能的空白字符和特殊字符 value = value.strip() if not value: return default return Decimal(value) # 其他类型尝试转换 return Decimal(str(value)) except (InvalidOperation, TypeError, ValueError) as e: logger.warning(f"Decimal转换失败: {e}, 输入值: {value}, 类型: {type(value)}, 使用默认值: {default}") return default except Exception as e: logger.error(f"Decimal转换发生未知错误: {e}, 输入值: {value}, 使用默认值: {default}") return default def log_task_execution(task_name, success=True, details=""): """ 记录任务执行日志 :param task_name: 任务名称 :param success: 是否成功 :param details: 详细信息 """ try: timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') status = "成功" if success else "失败" log_message = f"[{timestamp}] {task_name} - {status}" if details: log_message += f" | {details}" if success: logger.info(log_message) else: logger.error(log_message) return log_message except Exception as e: # 即使日志记录失败也不影响主流程 print(f"日志记录失败: {e}") return f"日志记录失败: {e}" def rollback_on_failure(operation_name): """ 事务回滚装饰器(修复版) :param operation_name: 操作名称 """ def decorator(func): @functools.wraps(func) # 【核心修复】保留原函数元数据,使Celery能正确识别任务 def wrapper(*args, **kwargs): try: with transaction.atomic(): return func(*args, **kwargs) except Exception as e: logger.error(f"{operation_name}事务执行失败: {str(e)}") raise return wrapper return decorator def retry_on_failure(max_retries=3, delay=60): """ 重试装饰器(修复版,可用于Celery任务) :param max_retries: 最大重试次数 :param delay: 重试延迟(秒) """ def decorator(func): @functools.wraps(func) # 【核心修复】保留原函数元数据 def wrapper(*args, **kwargs): retries = 0 while retries < max_retries: try: return func(*args, **kwargs) except Exception as e: retries += 1 if retries == max_retries: logger.error(f"任务{func.__name__}重试{max_retries}次后失败: {str(e)}") raise logger.warning(f"任务{func.__name__}第{retries}次失败,{delay}秒后重试: {str(e)}") import time time.sleep(delay) return wrapper return decorator