Files
a_long/shangdian/utils.py
2026-06-14 02:38:05 +08:00

634 lines
25 KiB
Python
Raw 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.
from rest_framework.response import Response
from .models import Dianpu, YonghuPingzheng
from shangpin.models import Shangpin
from shangdian.models import Dianpu, DianpuMorenPeizhi
# shangdian/utils.py 或 tongji/utils.py
import logging
from datetime import date
from decimal import Decimal
from django.db import transaction
from django.db.models import F
from shangdian.models import Dianpu, DianpuShouzhiMeiriTongji
logger = logging.getLogger(__name__)
def verify_shop_permission(request, frontend_user_id=None):
"""
验证商家后台用户身份及店铺状态
参数:
request: DRF request 对象
frontend_user_id: 前端传递的用户ID用于防越权校验可选
返回:
(dianpu_obj, None) # 验证成功,返回店铺对象
(None, Response) # 验证失败,返回错误响应
"""
current_user = request.user
# 1. 从 request.user 获取当前认证用户的 yonghuid
# 注意:您的用户模型可能通过 JWT 的 sub 或其他字段标识,此处假设 request.user.yonghuid 存在
if not hasattr(current_user, 'yonghuid'):
logger.error(f"用户对象缺少 yonghuid 属性: {current_user}")
return None, Response(
{'code': 500, 'msg': '用户身份信息异常'}
)
auth_user_id = current_user.yonghuid
# 2. 越权检测如果前端传了用户ID必须与当前登录用户一致
if frontend_user_id is not None and str(frontend_user_id) != str(auth_user_id):
logger.warning(
f"越权尝试: 登录用户 {auth_user_id} 尝试操作 {frontend_user_id} 的数据"
)
return None, Response(
{'code': 403, 'msg': '无权操作其他用户数据'}
)
# 3. 查询用户凭证表,验证账号是否存在且状态正常
try:
pingzheng = YonghuPingzheng.objects.get(yonghu=auth_user_id)
if not pingzheng.is_active:
return None, Response(
{'code': 403, 'msg': '用户账号已被禁用'}
)
except YonghuPingzheng.DoesNotExist:
return None, Response(
{'code': 404, 'msg': '用户凭证不存在'}
)
# 4. 查询关联的店铺信息,验证店铺状态
try:
dianpu = Dianpu.objects.get(pingzheng=pingzheng)
if dianpu.zhuangtai != 1: # 1 表示正常
return None, Response(
{'code': 403, 'msg': '店铺已被封禁'}
)
except Dianpu.DoesNotExist:
return None, Response(
{'code': 404, 'msg': '店铺信息不存在'}
)
# 验证通过,返回店铺对象
return dianpu, None
'''def update_dianpu_daily_stat(
dianpu_id,
caozuo_leixing,
dingdan_jiage=Decimal('0.00'),
dianpu_shouyi=Decimal('0.00')
):
"""
原子更新店铺每日统计数据,并根据结算操作更新店铺余额与总额(高并发安全)
参数:
dianpu_id: 店铺ID (int)
caozuo_leixing: 操作类型
1 = 下单
2 = 结算(成交)
3 = 退款
dingdan_jiage: 订单金额商品价格默认0.00
dianpu_shouyi: 店铺本次收益金额默认0.00
注意:
- 金额若小于0则自动设为0
- 日期自动取当天
- 使用 select_for_update 配合事务,保证并发时数据绝对准确
- 若店铺不存在,仅记录日志不中断主流程
- 结算操作会同步更新 Dianpu 表的 ketixian_yue 和 zhengqu_zonge
"""
# 参数归一化与合法性处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dianpu_shouyi = Decimal(str(dianpu_shouyi)) if dianpu_shouyi else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if dianpu_shouyi < 0:
dianpu_shouyi = Decimal('0.00')
today = date.today()
# 校验店铺是否存在(不加锁,仅校验)
if not Dianpu.objects.filter(id=dianpu_id).exists():
logger.error(f"店铺ID {dianpu_id} 不存在,统计更新失败")
return
# 开启事务,使用行锁确保并发安全
with transaction.atomic():
# 1. 获取或创建统计记录,并锁定该行
tongji, created = DianpuShouzhiMeiriTongji.objects.select_for_update().get_or_create(
dianpu_id=dianpu_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'xiadan_zongliang': 0,
'xiadan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'shouyi_zonge': Decimal('0.00'),
}
)
# 根据操作类型构建统计更新字段
update_fields = {}
if caozuo_leixing == 1: # 下单
update_fields['xiadan_zongliang'] = F('xiadan_zongliang') + 1
update_fields['xiadan_zonge'] = F('xiadan_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['shouyi_zonge'] = F('shouyi_zonge') + dianpu_shouyi
# 结算时同步更新店铺表的可提现余额和赚取总额(需加锁店铺行)
try:
dianpu = Dianpu.objects.select_for_update().get(id=dianpu_id)
dianpu.ketixian_yue = F('ketixian_yue') + dianpu_shouyi
dianpu.zhengqu_zonge = F('zhengqu_zonge') + dianpu_shouyi
dianpu.save(update_fields=['ketixian_yue', 'zhengqu_zonge', 'update_time'])
except Dianpu.DoesNotExist:
logger.error(f"结算时店铺ID {dianpu_id} 不存在,但已校验过,此处不应发生")
raise
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={dianpu_id}, 操作类型={caozuo_leixing}, "
f"订单金额={dingdan_jiage}, 店铺收益={dianpu_shouyi}, 日期={today}"
)
def calculate_pingtai_and_dianpu_shouyi(shangpin_id, dingdan_jiage, dashou_fencheng):
"""
根据商品价格和打手分成计算平台收益和店铺收益
逻辑:
总收益 = 订单金额 - 打手分成
1. 从商品表获取所属店铺ID
2. 若商品无店铺ID平台收益 = 总收益,店铺收益 = 0
3. 若有店铺ID
- 查询店铺配置:
* 若店铺开启抽成,则费率 = 店铺表.shouyi_choucheng_feilv
* 否则从店铺默认配置表(ID=1)获取默认费率,若记录不存在则自动创建
- 平台收益 = 总收益 * 费率
- 店铺收益 = 总收益 - 平台收益
参数:
shangpin_id: 商品ID (int)
dingdan_jiage: 订单金额(商品价格)(Decimal 或 数字)
dashou_fencheng: 打手分成金额 (Decimal 或 数字)
返回:
(dianpu_id, pingtai_shouyi, dianpu_shouyi) 三元组
dianpu_id 可能为 None
pingtai_shouyi 必定有值
dianpu_shouyi 必定有值
"""
# 类型转换与安全处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dashou_fencheng = Decimal(str(dashou_fencheng)) if dashou_fencheng else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if dashou_fencheng < 0:
dashou_fencheng = Decimal('0.00')
# 计算总收益(可用于抽成的基础)
zong_shouyi = dingdan_jiage - dashou_fencheng
if zong_shouyi < 0:
zong_shouyi = Decimal('0.00')
# 获取商品信息及所属店铺
try:
shangpin = Shangpin.objects.select_related('dianpu').only('id', 'dianpu').get(id=shangpin_id)
except Shangpin.DoesNotExist:
logger.error(f"计算收益失败商品ID {shangpin_id} 不存在")
return None, zong_shouyi, Decimal('0.00') # 无商品时平台收益为总收益店铺0
dianpu_id = shangpin.dianpu_id
# 无店铺ID平台拿走全部总收益店铺收益为0
if dianpu_id is None:
logger.info(f"商品 {shangpin_id} 未关联店铺,平台收益 = 总收益 {zong_shouyi}")
return None, zong_shouyi, Decimal('0.00')
# 有店铺ID查询店铺抽成配置
try:
dianpu = Dianpu.objects.only(
'id', 'kaiqi_shouyi_choucheng', 'shouyi_choucheng_feilv'
).get(id=dianpu_id)
except Dianpu.DoesNotExist:
logger.error(f"计算收益失败店铺ID {dianpu_id} 不存在")
return dianpu_id, zong_shouyi, Decimal('0.00') # 店铺不存在时平台收益也为总收益
if dianpu.kaiqi_shouyi_choucheng and dianpu.shouyi_choucheng_feilv is not None:
feilv = dianpu.shouyi_choucheng_feilv
else:
# 从默认配置表获取或创建
moren_peizhi, created = DianpuMorenPeizhi.objects.get_or_create(
id=1,
defaults={
'moren_choucheng_feilv': Decimal('0.1000'), # 默认10%
'moren_tixian_xiane': Decimal('1000.00')
}
)
feilv = moren_peizhi.moren_choucheng_feilv
# 确保费率在合法范围内
if feilv is None or feilv < 0:
feilv = Decimal('0.0000')
if feilv > 1:
feilv = Decimal('1.0000')
# 计算平台收益(费率 * 总收益)
pingtai_shouyi = zong_shouyi * feilv
# 店铺收益 = 总收益 - 平台收益
dianpu_shouyi = zong_shouyi - pingtai_shouyi
logger.info(
f"收益计算: 商品ID={shangpin_id}, 订单金额={dingdan_jiage}, "
f"打手分成={dashou_fencheng}, 总收益={zong_shouyi}, 费率={feilv}, "
f"平台收益={pingtai_shouyi}, 店铺收益={dianpu_shouyi}"
)
return dianpu_id, pingtai_shouyi, dianpu_shouyi'''
def update_dianpu_daily_stat(
dianpu_id,
caozuo_leixing,
dingdan_jiage=Decimal('0.00'),
dianpu_shouyi=Decimal('0.00'),
shangji_dianpu_id=None, # 新增上级店铺ID
shangji_fenhong=Decimal('0.00') # 新增:该笔订单给上级的分红
):
"""
原子更新店铺每日统计数据,结算时同步更新店铺余额,并安全处理上级分红统计。
参数:
dianpu_id: 本店铺ID
caozuo_leixing: 1=下单, 2=结算, 3=退款
dingdan_jiage: 订单金额
dianpu_shouyi: 店铺自身收益
shangji_dianpu_id: 上级店铺ID可选
shangji_fenhong: 给上级的分红金额(可选)
注意:
- 金额自动归一化负数取0
- 使用 select_for_update 行锁保证并发安全
- 结算时,本店余额和统计先落定,上级分红统计放在 try 块内,失败不影响本店
- 新增字段均包含在统计表中:给上级次数/总额/上级ID以及作为上级获得次数/总额
"""
# 金额安全处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dianpu_shouyi = Decimal(str(dianpu_shouyi)) if dianpu_shouyi else Decimal('0.00')
shangji_fenhong = Decimal(str(shangji_fenhong)) if shangji_fenhong else Decimal('0.00')
if dingdan_jiage < 0: dingdan_jiage = Decimal('0.00')
if dianpu_shouyi < 0: dianpu_shouyi = Decimal('0.00')
if shangji_fenhong < 0: shangji_fenhong = Decimal('0.00')
today = date.today()
# 轻量校验店铺存在性
if not Dianpu.objects.filter(id=dianpu_id).exists():
logger.error(f"店铺ID {dianpu_id} 不存在,统计更新失败")
return
# 开启事务,全部使用行锁
with transaction.atomic():
# 1. 获取或创建本店今日统计记录并锁定
tongji, created = DianpuShouzhiMeiriTongji.objects.select_for_update().get_or_create(
dianpu_id=dianpu_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'xiadan_zongliang': 0,
'xiadan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'shouyi_zonge': Decimal('0.00'),
# 新增字段默认值
'shangji_fenhong_cishu': 0,
'shangji_fenhong_zonge': Decimal('0.00'),
'gei_shangji_cishu': 0,
'gei_shangji_zonge': Decimal('0.00'),
'shangji_id': None,
}
)
# 构建更新字段字典
update_fields_dict = {}
if caozuo_leixing == 1: # 下单
update_fields_dict['xiadan_zongliang'] = F('xiadan_zongliang') + 1
update_fields_dict['xiadan_zonge'] = F('xiadan_zonge') + dingdan_jiage
elif caozuo_leixing == 2: # 结算
# --- 本店成交统计 ---
update_fields_dict['chengjiao_zongliang'] = F('chengjiao_zongliang') + 1
update_fields_dict['chengjiao_zonge'] = F('chengjiao_zonge') + dingdan_jiage
update_fields_dict['shouyi_zonge'] = F('shouyi_zonge') + dianpu_shouyi
# --- 更新本店主表余额(加锁) ---
try:
dianpu_locked = Dianpu.objects.select_for_update().get(id=dianpu_id)
dianpu_locked.ketixian_yue = F('ketixian_yue') + dianpu_shouyi
dianpu_locked.zhengqu_zonge = F('zhengqu_zonge') + dianpu_shouyi
dianpu_locked.save(update_fields=['ketixian_yue', 'zhengqu_zonge', 'update_time'])
except Dianpu.DoesNotExist:
logger.error(f"结算时店铺 {dianpu_id} 不存在")
raise
# --- 处理给上级的分红(本店作为下级)---
if shangji_dianpu_id and shangji_fenhong > 0:
# 更新本店统计的“给上级”字段
update_fields_dict['gei_shangji_cishu'] = F('gei_shangji_cishu') + 1
update_fields_dict['gei_shangji_zonge'] = F('gei_shangji_zonge') + shangji_fenhong
# 设置当天上级店铺ID仅当天第一次结算时写入后续保持不变
current_shangji_id = tongji.shangji_id
if current_shangji_id is None:
update_fields_dict['shangji_id'] = shangji_dianpu_id
elif current_shangji_id != shangji_dianpu_id:
logger.warning(
f"店铺 {dianpu_id} 当天上级ID不一致已有 {current_shangji_id}"
f"新上级 {shangji_dianpu_id}保持原上级ID"
)
# --- 更新上级店铺的“作为上级获得”统计(完全隔离保护) ---
try:
_update_shangji_gain_stat_within_tx(shangji_dianpu_id, shangji_fenhong)
except Exception as e:
logger.error(
f"更新上级店铺 {shangji_dianpu_id} 统计失败,不影响本店: {e}"
)
elif caozuo_leixing == 3: # 退款
update_fields_dict['tuikuan_zongliang'] = F('tuikuan_zongliang') + 1
update_fields_dict['tuikuan_zonge'] = F('tuikuan_zonge') + dingdan_jiage
else:
logger.warning(f"未知操作类型: {caozuo_leixing}")
return
# 原子更新本店统计字段
if update_fields_dict:
for field, value in update_fields_dict.items():
setattr(tongji, field, value)
tongji.save(update_fields=list(update_fields_dict.keys()))
logger.info(
f"店铺统计更新完成: dianpu={dianpu_id}, 操作={caozuo_leixing}, "
f"金额={dingdan_jiage}, 店铺收益={dianpu_shouyi}, "
f"上级ID={shangji_dianpu_id}, 上级分红={shangji_fenhong}"
)
def _update_shangji_gain_stat_within_tx(shangji_dianpu_id, fenhong_jine):
"""
在事务内更新上级店铺的“作为上级获得”统计及主表余额。
注意:本函数必须由处于同一事务中的 update_dianpu_daily_stat 调用,
并且调用方已获取必要的行锁,这里再次加锁不会死锁(按相同顺序加锁)。
"""
today = date.today()
# 获取或创建上级店铺的今日统计记录并加锁
tongji_shangji, _ = DianpuShouzhiMeiriTongji.objects.select_for_update().get_or_create(
dianpu_id=shangji_dianpu_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'xiadan_zongliang': 0,
'xiadan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'shouyi_zonge': Decimal('0.00'),
'shangji_fenhong_cishu': 0,
'shangji_fenhong_zonge': Decimal('0.00'),
'gei_shangji_cishu': 0,
'gei_shangji_zonge': Decimal('0.00'),
'shangji_id': None,
}
)
# 更新上级店铺的“作为上级获得”字段
tongji_shangji.shangji_fenhong_cishu = F('shangji_fenhong_cishu') + 1
tongji_shangji.shangji_fenhong_zonge = F('shangji_fenhong_zonge') + fenhong_jine
tongji_shangji.save(update_fields=['shangji_fenhong_cishu', 'shangji_fenhong_zonge', 'update_time'])
# 更新上级店铺主表的分红余额和累计分红
shangji = Dianpu.objects.select_for_update().get(id=shangji_dianpu_id)
shangji.shangji_fenhong_leiji = F('shangji_fenhong_leiji') + fenhong_jine
shangji.shangji_ketixian_fenhong = F('shangji_ketixian_fenhong') + fenhong_jine
shangji.save(update_fields=['shangji_fenhong_leiji', 'shangji_ketixian_fenhong', 'update_time'])
logger.info(f"上级店铺 {shangji_dianpu_id} 获得分红 +{fenhong_jine}")
def calculate_pingtai_and_dianpu_shouyi(shangpin_id, dingdan_jiage, dashou_fencheng):
"""
计算平台收益、店铺收益、上级分红
分配链路(上级分红完全隔离,不影响平台及店铺核心计算):
总收益 = 订单金额 - 打手分成
平台抽成 = 总收益 * (店铺开启则用店铺费率,否则用平台默认费率)
剩余收益 = 总收益 - 平台抽成
上级分红 = 剩余收益 * 本店设置的上级分红费率(若有上级且满足条件)
店铺收益 = 剩余收益 - 上级分红
参数:
shangpin_id: 商品ID
dingdan_jiage: 订单金额
dashou_fencheng: 打手分成金额
返回:
五元组 (dianpu_id, pingtai_shouyi, dianpu_shouyi, shangji_dianpu_id, shangji_fenhong)
- dianpu_id: 店铺ID可能为None
- pingtai_shouyi: 平台收益
- dianpu_shouyi: 店铺最终收益
- shangji_dianpu_id: 上级店铺ID可能为None
- shangji_fenhong: 上级分红金额默认0.00
"""
# ==================== 金额安全处理 ====================
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dashou_fencheng = Decimal(str(dashou_fencheng)) if dashou_fencheng else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if dashou_fencheng < 0:
dashou_fencheng = Decimal('0.00')
zong_shouyi = dingdan_jiage - dashou_fencheng
if zong_shouyi < 0:
zong_shouyi = Decimal('0.00')
# ==================== 获取商品及店铺 ====================
try:
shangpin = Shangpin.objects.select_related('dianpu').only('id', 'dianpu').get(id=shangpin_id)
except Shangpin.DoesNotExist:
logger.error(f"收益计算失败商品ID {shangpin_id} 不存在")
return None, zong_shouyi, Decimal('0.00'), None, Decimal('0.00')
dianpu_id = shangpin.dianpu_id
if dianpu_id is None:
logger.info(f"商品 {shangpin_id} 未关联店铺,平台收益={zong_shouyi}")
return None, zong_shouyi, Decimal('0.00'), None, Decimal('0.00')
# ==================== 获取店铺配置 ====================
try:
dianpu = Dianpu.objects.only(
'id', 'kaiqi_shouyi_choucheng', 'shouyi_choucheng_feilv',
'yaoqing_dianpu_id', 'fenhong_choucheng_feilv'
).get(id=dianpu_id)
except Dianpu.DoesNotExist:
logger.error(f"收益计算失败店铺ID {dianpu_id} 不存在")
return dianpu_id, zong_shouyi, Decimal('0.00'), None, Decimal('0.00')
# ==================== 平台抽成 ====================
if dianpu.kaiqi_shouyi_choucheng and dianpu.shouyi_choucheng_feilv is not None:
feilv = dianpu.shouyi_choucheng_feilv
else:
moren_peizhi, created = DianpuMorenPeizhi.objects.get_or_create(
id=1,
defaults={
'moren_choucheng_feilv': Decimal('0.1000'),
'moren_tixian_xiane': Decimal('1000.00')
}
)
feilv = moren_peizhi.moren_choucheng_feilv
if feilv is None or feilv < 0:
feilv = Decimal('0.0000')
if feilv > 1:
feilv = Decimal('1.0000')
pingtai_shouyi = zong_shouyi * feilv
shengyu = zong_shouyi - pingtai_shouyi
# ==================== 上级分红(完全隔离,不影响店铺收益) ====================
shangji_dianpu_id = dianpu.yaoqing_dianpu_id
shangji_fenhong = Decimal('0.00')
if shangji_dianpu_id and dianpu.fenhong_choucheng_feilv:
try:
# 检查上级店铺状态
shangji = Dianpu.objects.filter(id=shangji_dianpu_id).only('id', 'zhuangtai').first()
if shangji and shangji.zhuangtai == 1:
fz_rate = dianpu.fenhong_choucheng_feilv
if fz_rate and fz_rate > 0:
shangji_fenhong = shengyu * fz_rate
if shangji_fenhong < 0:
shangji_fenhong = Decimal('0.00')
if shangji_fenhong > shengyu:
shangji_fenhong = shengyu
shengyu -= shangji_fenhong
else:
if shangji:
logger.info(f"上级店铺 {shangji_dianpu_id} 状态非正常,不参与分红")
else:
logger.warning(f"上级店铺ID {shangji_dianpu_id} 不存在,不参与分红")
except Exception as e:
logger.exception(f"上级分红计算异常,忽略: {e}")
shangji_fenhong = Decimal('0.00')
# 注意shengyu 未被修改,店铺收益不变
dianpu_shouyi = shengyu
logger.info(
f"收益计算完成: 商品={shangpin_id}, 总收益={zong_shouyi}, 平台={pingtai_shouyi}, "
f"上级ID={shangji_dianpu_id}, 上级分红={shangji_fenhong}, 店铺={dianpu_shouyi}"
)
return dianpu_id, pingtai_shouyi, dianpu_shouyi, shangji_dianpu_id, shangji_fenhong
def validate_shangpin_and_dianpu(shangpin_id):
"""
校验商品及关联店铺(区分有无店铺)
无店铺:仅校验商品是否上架
有店铺:校验商品审核、封禁、上架,以及店铺是否封禁
返回:
(True, None, shangpin_obj) # 通过
(False, "错误信息", None) # 失败
"""
try:
shangpin = Shangpin.objects.select_related('dianpu').get(id=shangpin_id)
except Shangpin.DoesNotExist:
return False, "商品不存在", None
# 无店铺:只检查上架
if not shangpin.dianpu_id:
if not shangpin.shangjia_zhuangtai:
return False, "商品已下架", None
return True, None, shangpin
# 有店铺:完整校验
if not shangpin.shenhe_zhuangtai:
return False, "商品未通过平台审核", None
if shangpin.fengjin_zhuangtai:
return False, "商品已被封禁", None
if not shangpin.shangjia_zhuangtai:
return False, "商品已下架", None
try:
if shangpin.dianpu.zhuangtai != 1:
return False, "店铺已被封禁", None
except Exception:
return False, "关联店铺异常", None
return True, None, shangpin