隐藏式商品/类型增加归属商家端类型,点单按归属写入抢单字段;抢单池只返回商家端类型。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-22 16:26:43 +08:00
parent bc22bd0df1
commit 04fcfb909b
5 changed files with 176 additions and 13 deletions

View File

@@ -182,7 +182,9 @@ class CreateOrderView(APIView):
if not user_openid: if not user_openid:
return Response({'code': 10, 'msg': '用户openid不存在', 'data': None}) return Response({'code': 10, 'msg': '用户openid不存在', 'data': None})
# 创建订单主表数据 # 创建订单主表数据(隐藏式有归属则用商家端类型的抢单要求)
from utils.shangpin_guishu import resolve_order_qiangdan_fields
qiangdan_fields = resolve_order_qiangdan_fields(shangpin)
order_data = { order_data = {
'dingdan_id': dingdanid, 'dingdan_id': dingdanid,
'zhuangtai': 9, # 未付款状态 'zhuangtai': 9, # 未付款状态
@@ -191,15 +193,15 @@ class CreateOrderView(APIView):
'dashou_fencheng': dashou_fencheng, 'dashou_fencheng': dashou_fencheng,
'zhiding_id': zhiding_id_value, 'zhiding_id': zhiding_id_value,
'shangpin_id': shangpin_id, 'shangpin_id': shangpin_id,
'yaoqiuleixing': getattr(shangpin, 'yaoqiuleixing', None), 'yaoqiuleixing': qiangdan_fields['yaoqiuleixing'],
'huiyuan_id': getattr(shangpin, 'huiyuan_id', ''), 'huiyuan_id': qiangdan_fields['huiyuan_id'],
'yongjin': getattr(shangpin, 'yongjin', 0), 'yongjin': qiangdan_fields['yongjin'],
'jieshao': getattr(shangpin, 'jieshao', ''), 'jieshao': getattr(shangpin, 'jieshao', ''),
'beizhu': beizhu, 'beizhu': beizhu,
'nicheng': nicheng, 'nicheng': nicheng,
'tupian':shangpin_tupian, 'tupian':shangpin_tupian,
'user1_id':f"Boss{request.user.yonghuid}", 'user1_id':f"Boss{request.user.yonghuid}",
'leixing_id':shangpin.leixing_id 'leixing_id': qiangdan_fields['leixing_id'],
} }
# 创建订单记录 # 创建订单记录
@@ -1034,7 +1036,9 @@ class VirtualCreateOrderView(APIView):
with transaction.atomic(): with transaction.atomic():
laoban_yonghuid = getattr(current_user, 'yonghuid', '') laoban_yonghuid = getattr(current_user, 'yonghuid', '')
# 订单主表 # 订单主表(隐藏式有归属则用商家端类型的抢单要求)
from utils.shangpin_guishu import resolve_order_qiangdan_fields
qiangdan_fields = resolve_order_qiangdan_fields(shangpin)
order_data = { order_data = {
'dingdan_id': dingdanid, 'dingdan_id': dingdanid,
'zhuangtai': 9, 'zhuangtai': 9,
@@ -1043,15 +1047,15 @@ class VirtualCreateOrderView(APIView):
'dashou_fencheng': dashou_fencheng, 'dashou_fencheng': dashou_fencheng,
'zhiding_id': zhiding_id_value, 'zhiding_id': zhiding_id_value,
'shangpin_id': shangpin_id, 'shangpin_id': shangpin_id,
'yaoqiuleixing': getattr(shangpin, 'yaoqiuleixing', None), 'yaoqiuleixing': qiangdan_fields['yaoqiuleixing'],
'huiyuan_id': getattr(shangpin, 'huiyuan_id', ''), 'huiyuan_id': qiangdan_fields['huiyuan_id'],
'yongjin': getattr(shangpin, 'yongjin', 0), 'yongjin': qiangdan_fields['yongjin'],
'jieshao': getattr(shangpin, 'jieshao', ''), 'jieshao': getattr(shangpin, 'jieshao', ''),
'beizhu': beizhu, 'beizhu': beizhu,
'nicheng': nicheng, 'nicheng': nicheng,
'tupian': shangpin_tupian, 'tupian': shangpin_tupian,
'user1_id': f"Boss{current_user.yonghuid}", 'user1_id': f"Boss{current_user.yonghuid}",
'leixing_id': shangpin.leixing_id 'leixing_id': qiangdan_fields['leixing_id'],
} }
dingdan = Dingdan.objects.create(**order_data) dingdan = Dingdan.objects.create(**order_data)
@@ -7731,7 +7735,7 @@ class DashouHuoquLeixingView(APIView):
# 条件shenhezhuangtai=1正常状态 # 条件shenhezhuangtai=1正常状态
# 排序:先按 paixu 降序(数值大的在前),再按 id 升序 # 排序:先按 paixu 降序(数值大的在前),再按 id 升序
leixing_queryset = ShangpinLeixing.objects.filter( leixing_queryset = ShangpinLeixing.objects.filter(
shenhezhuangtai__in=[3, 4] # 商家端 + 点单隐藏式(抢单池可见) shenhezhuangtai=3 # 抢单池只展示商家端类型;隐藏式订单经归属映射到商家端类型
).order_by('-paixu', 'id') ).order_by('-paixu', 'id')
'''leixing_queryset = ShangpinLeixing.objects.filter( '''leixing_queryset = ShangpinLeixing.objects.filter(
shenhezhuangtai__in= [1,9] # 🔴 只取正常状态,排除审核专用 shenhezhuangtai__in= [1,9] # 🔴 只取正常状态,排除审核专用

View File

@@ -5373,6 +5373,7 @@ class GetProductBaseDataView(APIView):
'tupian_url': t.tupian_url or '', 'tupian_url': t.tupian_url or '',
'paixu': t.paixu, 'paixu': t.paixu,
'shenhezhuangtai': t.shenhezhuangtai, 'shenhezhuangtai': t.shenhezhuangtai,
'guishu_leixing_id': t.guishu_leixing_id,
'product_count': product_count, 'product_count': product_count,
}) })
@@ -5636,6 +5637,19 @@ class AddProductView(APIView):
ewai_dashou_fencheng_str = request.data.get('ewai_dashou_fencheng', '0') ewai_dashou_fencheng_str = request.data.get('ewai_dashou_fencheng', '0')
jieshao = request.data.get('jieshao', '').strip() jieshao = request.data.get('jieshao', '').strip()
xiadan_xuzhi = request.data.get('xiadan_xuzhi', '').strip() xiadan_xuzhi = request.data.get('xiadan_xuzhi', '').strip()
try:
shenhezhuangtai = int(request.data.get('shenhezhuangtai', 1))
if shenhezhuangtai not in (1, 2, 3, 4):
raise ValueError
except (TypeError, ValueError):
return Response({'code': 400, 'msg': '审核状态无效必须为1/2/3/4'})
from utils.shangpin_guishu import parse_guishu_leixing_id
guishu_leixing_id, guishu_err = parse_guishu_leixing_id(
request.data.get('guishu_leixing_id'),
shenhezhuangtai,
)
if guishu_err:
return Response({'code': 400, 'msg': guishu_err})
# 图片文件 # 图片文件
image_file = request.FILES.get('file') # 前端上传时字段名为 'file' image_file = request.FILES.get('file') # 前端上传时字段名为 'file'
@@ -5767,7 +5781,8 @@ class AddProductView(APIView):
kaioi_ewai_dashou_fencheng=kaioi_ewai, kaioi_ewai_dashou_fencheng=kaioi_ewai,
ewai_dashou_fencheng=ewai_fencheng, ewai_dashou_fencheng=ewai_fencheng,
paixu=0, # 新商品默认排序最后 paixu=0, # 新商品默认排序最后
shenhezhuangtai=1, # 正常状态 shenhezhuangtai=shenhezhuangtai,
guishu_leixing_id=guishu_leixing_id,
) )
except Exception as e: except Exception as e:
logger.error(f"创建商品失败: {str(e)}", exc_info=True) logger.error(f"创建商品失败: {str(e)}", exc_info=True)
@@ -5890,7 +5905,7 @@ class GetProductDetailView(APIView):
# 查询所有商品类型、专区、会员(用于前端下拉选择) # 查询所有商品类型、专区、会员(用于前端下拉选择)
type_list = list(ShangpinLeixing.objects.all().values( type_list = list(ShangpinLeixing.objects.all().values(
'id', 'jieshao', 'tupian_url', 'shenhezhuangtai' 'id', 'jieshao', 'tupian_url', 'shenhezhuangtai', 'guishu_leixing_id'
)) ))
zone_list = list(ShangpinZhuanqu.objects.all().values( zone_list = list(ShangpinZhuanqu.objects.all().values(
'id', 'mingzi', 'leixing_id', 'shenhezhuangtai' 'id', 'mingzi', 'leixing_id', 'shenhezhuangtai'
@@ -5920,6 +5935,7 @@ class GetProductDetailView(APIView):
'ewai_dashou_fencheng': str(product.ewai_dashou_fencheng) if product.ewai_dashou_fencheng is not None else '0', 'ewai_dashou_fencheng': str(product.ewai_dashou_fencheng) if product.ewai_dashou_fencheng is not None else '0',
'create_time': product.create_time.isoformat() if product.create_time else None, 'create_time': product.create_time.isoformat() if product.create_time else None,
'shenhezhuangtai': product.shenhezhuangtai, 'shenhezhuangtai': product.shenhezhuangtai,
'guishu_leixing_id': product.guishu_leixing_id,
} }
return Response({ return Response({
@@ -6016,6 +6032,14 @@ class UpdateProductView(APIView):
except: except:
shenhezhuangtai = 1 shenhezhuangtai = 1
from utils.shangpin_guishu import parse_guishu_leixing_id
guishu_leixing_id, guishu_err = parse_guishu_leixing_id(
request.data.get('guishu_leixing_id'),
shenhezhuangtai,
)
if guishu_err:
return Response({'code': 400, 'msg': guishu_err})
# 校验类型和专区存在性 # 校验类型和专区存在性
if not ShangpinLeixing.objects.filter(id=leixing_id).exists(): if not ShangpinLeixing.objects.filter(id=leixing_id).exists():
return Response({'code': 400, 'msg': '商品类型不存在'}) return Response({'code': 400, 'msg': '商品类型不存在'})
@@ -6108,6 +6132,7 @@ class UpdateProductView(APIView):
product.tupian_url = tupian_url product.tupian_url = tupian_url
product.guize_tupian = guize_tupian product.guize_tupian = guize_tupian
product.shenhezhuangtai = shenhezhuangtai product.shenhezhuangtai = shenhezhuangtai
product.guishu_leixing_id = guishu_leixing_id
product.save() product.save()
return Response({'code': 0, 'msg': '商品修改成功', 'data': {'id': product.id}}) return Response({'code': 0, 'msg': '商品修改成功', 'data': {'id': product.id}})
@@ -7626,6 +7651,7 @@ class GetProductTypeZoneView(APIView):
'huiyuan_id': t.huiyuan_id or '', 'huiyuan_id': t.huiyuan_id or '',
'yongjin': float(t.yongjin) if t.yongjin is not None else None, 'yongjin': float(t.yongjin) if t.yongjin is not None else None,
'shenhezhuangtai': t.shenhezhuangtai, 'shenhezhuangtai': t.shenhezhuangtai,
'guishu_leixing_id': t.guishu_leixing_id,
'is_cross_enabled': t.is_cross_enabled, 'is_cross_enabled': t.is_cross_enabled,
'is_special_type': t.is_special_type, 'is_special_type': t.is_special_type,
'paixu': t.paixu, 'paixu': t.paixu,
@@ -7727,6 +7753,14 @@ class ModifyProductTypeZoneView(APIView):
except (TypeError, ValueError): except (TypeError, ValueError):
return Response({'code': 400, 'msg': '审核状态无效必须为1/2/3/4'}) return Response({'code': 400, 'msg': '审核状态无效必须为1/2/3/4'})
from utils.shangpin_guishu import parse_guishu_leixing_id
guishu_leixing_id, guishu_err = parse_guishu_leixing_id(
request.data.get('guishu_leixing_id'),
shenhezhuangtai,
)
if guishu_err:
return Response({'code': 400, 'msg': guishu_err})
is_cross_enabled = request.data.get('is_cross_enabled', False) is_cross_enabled = request.data.get('is_cross_enabled', False)
is_cross_enabled = is_cross_enabled in [True, 'true', '1', 'on'] is_cross_enabled = is_cross_enabled in [True, 'true', '1', 'on']
is_special_type = request.data.get('is_special_type', False) is_special_type = request.data.get('is_special_type', False)
@@ -7774,6 +7808,7 @@ class ModifyProductTypeZoneView(APIView):
huiyuan_id=huiyuan_id, huiyuan_id=huiyuan_id,
yongjin=yongjin, yongjin=yongjin,
shenhezhuangtai=shenhezhuangtai, shenhezhuangtai=shenhezhuangtai,
guishu_leixing_id=guishu_leixing_id,
is_cross_enabled=is_cross_enabled, is_cross_enabled=is_cross_enabled,
is_special_type=is_special_type, is_special_type=is_special_type,
paixu=0 paixu=0
@@ -7841,6 +7876,19 @@ class ModifyProductTypeZoneView(APIView):
except (TypeError, ValueError): except (TypeError, ValueError):
return Response({'code': 400, 'msg': '审核状态无效必须为1/2/3/4'}) return Response({'code': 400, 'msg': '审核状态无效必须为1/2/3/4'})
# 归属商家端类型(隐藏式才有效;非隐藏式清空)
if 'guishu_leixing_id' in request.data or shenhezhuangtai is not None:
from utils.shangpin_guishu import parse_guishu_leixing_id
target_status = changes.get('shenhezhuangtai', leixing.shenhezhuangtai)
raw_guishu = request.data.get('guishu_leixing_id', leixing.guishu_leixing_id)
if 'guishu_leixing_id' not in request.data and target_status != 4:
raw_guishu = None
guishu_id, guishu_err = parse_guishu_leixing_id(raw_guishu, target_status)
if guishu_err:
return Response({'code': 400, 'msg': guishu_err})
if guishu_id != leixing.guishu_leixing_id:
changes['guishu_leixing_id'] = guishu_id
# 跨平台互通 # 跨平台互通
is_cross = request.data.get('is_cross_enabled') is_cross = request.data.get('is_cross_enabled')
if is_cross is not None: if is_cross is not None:

View File

@@ -0,0 +1,33 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shangpin', '0010_gsfenhong_fenhong_leixing_unique'),
]
operations = [
migrations.AddField(
model_name='shangpin',
name='guishu_leixing_id',
field=models.PositiveIntegerField(
blank=True,
db_index=True,
help_text='仅隐藏式商品使用;指向 shenhezhuangtai=3 的类型',
null=True,
verbose_name='归属商家端类型ID',
),
),
migrations.AddField(
model_name='shangpinleixing',
name='guishu_leixing_id',
field=models.PositiveIntegerField(
blank=True,
db_index=True,
help_text='仅隐藏式类型使用;指向 shenhezhuangtai=3 的类型',
null=True,
verbose_name='归属商家端类型ID',
),
),
]

View File

@@ -40,6 +40,14 @@ class Shangpin(models.Model):
verbose_name='审核状态', verbose_name='审核状态',
help_text='1=点单正常, 2=审核专用, 3=商家端, 4=点单隐藏式', help_text='1=点单正常, 2=审核专用, 3=商家端, 4=点单隐藏式',
) )
# 隐藏式商品归属的商家端类型ID点单写入订单抢单字段时用默认无归属
guishu_leixing_id = models.PositiveIntegerField(
null=True,
blank=True,
db_index=True,
verbose_name='归属商家端类型ID',
help_text='仅隐藏式商品使用;指向 shenhezhuangtai=3 的类型',
)
class Meta: class Meta:
db_table = 'shangpin' db_table = 'shangpin'
@@ -70,6 +78,14 @@ class ShangpinLeixing(models.Model):
verbose_name='审核状态', verbose_name='审核状态',
help_text='1=点单正常, 2=审核专用, 3=商家端, 4=点单隐藏式', help_text='1=点单正常, 2=审核专用, 3=商家端, 4=点单隐藏式',
) )
# 隐藏式类型归属的商家端类型ID默认无归属
guishu_leixing_id = models.PositiveIntegerField(
null=True,
blank=True,
db_index=True,
verbose_name='归属商家端类型ID',
help_text='仅隐藏式类型使用;指向 shenhezhuangtai=3 的类型',
)
bankuai = models.ForeignKey( bankuai = models.ForeignKey(
'dengji.Bankuai', 'dengji.Bankuai',
on_delete=models.SET_NULL, on_delete=models.SET_NULL,

62
utils/shangpin_guishu.py Normal file
View File

@@ -0,0 +1,62 @@
"""
隐藏式商品/类型归属商家端类型:点单写入订单抢单字段时使用。
"""
from shangpin.models import ShangpinLeixing
def parse_guishu_leixing_id(raw, shenhezhuangtai=None):
"""
解析归属商家端类型 ID。
非隐藏式(4)一律视为无归属;有值时必须指向 shenhezhuangtai=3 的类型。
返回 (guishu_leixing_id|None, error_message|None)
"""
try:
status = int(shenhezhuangtai) if shenhezhuangtai is not None else None
except (TypeError, ValueError):
status = None
if status is not None and status != 4:
return None, None
if raw is None or raw == '' or str(raw).lower() in ('null', 'none', 'undefined'):
return None, None
try:
gid = int(raw)
except (TypeError, ValueError):
return None, '归属商家端类型ID无效'
if gid <= 0:
return None, None
if not ShangpinLeixing.objects.filter(id=gid, shenhezhuangtai=3).exists():
return None, '归属类型必须是商家端商品类型(审核状态=3'
return gid, None
def resolve_order_qiangdan_fields(shangpin):
"""
点单写入 Dingdan 的抢单相关字段:
- 商品或所属类型配置了 guishu_leixing_id → 用该商家端类型的 id/yaoqiuleixing/huiyuan_id/yongjin
- 否则沿用商品自身字段
"""
guishu_id = getattr(shangpin, 'guishu_leixing_id', None)
if not guishu_id and getattr(shangpin, 'leixing_id', None):
own_type = ShangpinLeixing.objects.filter(id=shangpin.leixing_id).only(
'guishu_leixing_id',
).first()
if own_type:
guishu_id = own_type.guishu_leixing_id
if guishu_id:
merchant = ShangpinLeixing.objects.filter(id=guishu_id, shenhezhuangtai=3).first()
if merchant:
return {
'leixing_id': merchant.id,
'yaoqiuleixing': merchant.yaoqiuleixing,
'huiyuan_id': merchant.huiyuan_id or '',
'yongjin': merchant.yongjin if merchant.yongjin is not None else 0,
}
return {
'leixing_id': shangpin.leixing_id,
'yaoqiuleixing': getattr(shangpin, 'yaoqiuleixing', None),
'huiyuan_id': getattr(shangpin, 'huiyuan_id', '') or '',
'yongjin': getattr(shangpin, 'yongjin', 0) or 0,
}