Files
along_django/shangpin/utils.py

113 lines
4.3 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.
# tongji/utils.py 或 shangpin/utils.py
import logging
from datetime import date
from decimal import Decimal
from django.db import transaction
from django.db.models import F
from .models import ShangpinMeiriTongji
from shangpin.models import Shangpin
logger = logging.getLogger(__name__)
def update_shangpin_daily_stat(
shangpin_id,
caozuo_leixing,
dingdan_jiage=Decimal('0.00'),
pingtai_shouyi=Decimal('0.00'),
dianpu_zongshouyi=Decimal('0.00')
):
"""
原子更新商品每日统计数据(高并发安全,无店铺关联)
参数:
shangpin_id: 商品ID (int)
caozuo_leixing: 操作类型
1 = 下单(点单)
2 = 结算(成交)
3 = 退款
dingdan_jiage: 订单金额商品价格默认0.00
pingtai_shouyi: 平台收益金额默认0.00
dianpu_zongshouyi: 店铺总收益金额默认0.00
注意:
- 金额若小于0则自动设为0
- 日期自动取当天
- 使用 select_for_update 配合事务,保证并发时数据绝对准确
- 若商品不存在,仅记录日志不中断主流程
"""
# 参数归一化与合法性处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
pingtai_shouyi = Decimal(str(pingtai_shouyi)) if pingtai_shouyi else Decimal('0.00')
dianpu_zongshouyi = Decimal(str(dianpu_zongshouyi)) if dianpu_zongshouyi else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if pingtai_shouyi < 0:
pingtai_shouyi = Decimal('0.00')
if dianpu_zongshouyi < 0:
dianpu_zongshouyi = Decimal('0.00')
today = date.today()
# 校验商品是否存在(不加锁,仅校验)
if not Shangpin.objects.filter(id=shangpin_id).exists():
logger.error(f"商品ID {shangpin_id} 不存在,统计更新失败")
return
# 开启事务,使用行锁确保并发安全
with transaction.atomic():
# 获取或创建统计记录,并锁定该行
tongji, created = ShangpinMeiriTongji.objects.select_for_update().get_or_create(
shangpin_id=shangpin_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'diandan_zongliang': 0,
'diandan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'pingtai_shouyi': Decimal('0.00'),
'dianpu_zongshouyi': Decimal('0.00'),
}
)
# 根据操作类型构建更新字段(使用 F 表达式累加)
update_fields = {}
if caozuo_leixing == 1: # 下单
update_fields['diandan_zongliang'] = F('diandan_zongliang') + 1
update_fields['diandan_zonge'] = F('diandan_zonge') + dingdan_jiage
elif caozuo_leixing == 2: # 结算(成交)
update_fields['chengjiao_zongliang'] = F('chengjiao_zongliang') + 1
update_fields['chengjiao_zonge'] = F('chengjiao_zonge') + dingdan_jiage
# 收益直接累加传入值
update_fields['pingtai_shouyi'] = F('pingtai_shouyi') + pingtai_shouyi
update_fields['dianpu_zongshouyi'] = F('dianpu_zongshouyi') + dianpu_zongshouyi
elif caozuo_leixing == 3: # 退款
update_fields['tuikuan_zongliang'] = F('tuikuan_zongliang') + 1
update_fields['tuikuan_zonge'] = F('tuikuan_zonge') + dingdan_jiage
# 退款是否应扣减收益?根据业务需求决定,此处暂不处理收益字段
else:
logger.warning(f"未知的操作类型: {caozuo_leixing}")
return
# 原子更新字段
if update_fields:
for field, value in update_fields.items():
setattr(tongji, field, value)
tongji.save(update_fields=list(update_fields.keys()))
logger.info(
f"商品统计更新: 商品ID={shangpin_id}, 操作类型={caozuo_leixing}, "
f"订单金额={dingdan_jiage}, 平台收益={pingtai_shouyi}, "
f"店铺收益={dianpu_zongshouyi}, 日期={today}"
)