Files
GVSDSDK/celery_utils.py
TermiNexus 9be878b84a feat(sdk): 通用查询与工具层下沉,新增 App 体系镜像模型
P1 通用查询:auth/managers.py 新增 20 个查询方法(Role/Company/Department/Position/Menu/Tenant 六个 Manager);auth/ram.py 新增 RAMManager.ListPolicyResourceUUIDs;stats/manager.py 新增 StatsManager.ListAutoResetConfigs。

P2 App 体系:models.py 新增 App/TenantApp/CompanyApp/UserApp/AppConfig 五个 managed=False 镜像模型,字段与业务侧逐一对齐。

P3 工具下沉:新增 config.py 配置注入层与 money/json_encoder/celery_utils/exception_handler/redis_lock/ip_filter/storage/datetime_utils/string_utils 九个工具模块;model_utils.UUIDObj 增加 PascalCase 别名 Str/Bytes/UUID 与模块级转换函数。

同时移除 commission/stats/CashBook 镜像模型的重复索引声明,消除 Django models.E030。
2026-09-13 21:57:45 +08:00

120 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""模块:Celery 任务工具装饰器
提供事务回滚、重试、日志、安全 Decimal 运算等通用装饰器,
统一异步任务的异常处理与可观测性。
依赖 Celery(gvsds/celery.py),无 Celery 时装饰器自动降级为同步执行。
常用 API:
from gvsdsdk.celery_utils import RollbackOnFailure, RetryOnFailure, LogTask, SafeDecimal
@App.task
@RollbackOnFailure
@RetryOnFailure(max_retries=3, delay=5)
@LogTask
def CriticalTask(pk):
...
@SafeDecimal(default=Decimal('0'))
def AggregateStats(qs):
...
"""
import logging
import functools
from decimal import Decimal
logger = logging.getLogger(__name__)
try:
from celery import shared_task
_HAS_CELERY = True
except ImportError:
_HAS_CELERY = False
shared_task = None
def _IsCeleryTask(func):
"""判断函数是否为 Celery 任务对象。"""
return _HAS_CELERY and hasattr(func, 'apply_async')
def RollbackOnFailure(func):
"""事务回滚装饰器:任务异常时自动回滚当前事务。
适用于写操作任务,防止部分写入导致数据不一致。
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception:
try:
from django.db import transaction
transaction.set_rollback(True)
logger.warning('[CeleryUtils] 任务异常,已标记事务回滚: %s', func.__name__, exc_info=True)
except Exception:
pass
raise
return wrapper
def RetryOnFailure(max_retries=3, delay=5, exceptions=(Exception,)):
"""重试装饰器:任务异常时自动重试。
参数:
max_retries 最大重试次数
delay 重试间隔秒
exceptions 触发重试的异常类型
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exc = None
for attempt in range(max_retries + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
last_exc = e
if attempt < max_retries:
logger.info('[CeleryUtils] 任务重试 %d/%d: %s', attempt + 1, max_retries, func.__name__)
import time
time.sleep(delay)
else:
logger.error('[CeleryUtils] 任务重试耗尽: %s', func.__name__, exc_info=True)
raise last_exc
return wrapper
return decorator
def LogTask(func):
"""日志装饰器:记录任务开始/成功/失败。"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
name = func.__name__
logger.info('[CeleryUtils] 任务开始: %s', name)
try:
result = func(*args, **kwargs)
logger.info('[CeleryUtils] 任务成功: %s', name)
return result
except Exception:
logger.error('[CeleryUtils] 任务失败: %s', name, exc_info=True)
raise
return wrapper
def SafeDecimal(default=Decimal('0')):
"""安全 Decimal 运算装饰器:异常时返回 default。
适用于聚合统计任务,单条脏数据不中断整体计算。
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except (ArithmeticError, TypeError, ValueError):
logger.warning('[CeleryUtils] Decimal 运算异常,返回默认值: %s', func.__name__, exc_info=True)
return default
return wrapper
return decorator