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 products.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}" )