diff --git a/dingdan/views.py b/dingdan/views.py index 7feb2e4..0498d96 100644 --- a/dingdan/views.py +++ b/dingdan/views.py @@ -182,7 +182,9 @@ class CreateOrderView(APIView): if not user_openid: 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 = { 'dingdan_id': dingdanid, 'zhuangtai': 9, # 未付款状态 @@ -191,15 +193,15 @@ class CreateOrderView(APIView): 'dashou_fencheng': dashou_fencheng, 'zhiding_id': zhiding_id_value, 'shangpin_id': shangpin_id, - 'yaoqiuleixing': getattr(shangpin, 'yaoqiuleixing', None), - 'huiyuan_id': getattr(shangpin, 'huiyuan_id', ''), - 'yongjin': getattr(shangpin, 'yongjin', 0), + 'yaoqiuleixing': qiangdan_fields['yaoqiuleixing'], + 'huiyuan_id': qiangdan_fields['huiyuan_id'], + 'yongjin': qiangdan_fields['yongjin'], 'jieshao': getattr(shangpin, 'jieshao', ''), 'beizhu': beizhu, 'nicheng': nicheng, 'tupian':shangpin_tupian, '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(): laoban_yonghuid = getattr(current_user, 'yonghuid', '') - # 订单主表 + # 订单主表(隐藏式有归属则用商家端类型的抢单要求) + from utils.shangpin_guishu import resolve_order_qiangdan_fields + qiangdan_fields = resolve_order_qiangdan_fields(shangpin) order_data = { 'dingdan_id': dingdanid, 'zhuangtai': 9, @@ -1043,15 +1047,15 @@ class VirtualCreateOrderView(APIView): 'dashou_fencheng': dashou_fencheng, 'zhiding_id': zhiding_id_value, 'shangpin_id': shangpin_id, - 'yaoqiuleixing': getattr(shangpin, 'yaoqiuleixing', None), - 'huiyuan_id': getattr(shangpin, 'huiyuan_id', ''), - 'yongjin': getattr(shangpin, 'yongjin', 0), + 'yaoqiuleixing': qiangdan_fields['yaoqiuleixing'], + 'huiyuan_id': qiangdan_fields['huiyuan_id'], + 'yongjin': qiangdan_fields['yongjin'], 'jieshao': getattr(shangpin, 'jieshao', ''), 'beizhu': beizhu, 'nicheng': nicheng, 'tupian': shangpin_tupian, 'user1_id': f"Boss{current_user.yonghuid}", - 'leixing_id': shangpin.leixing_id + 'leixing_id': qiangdan_fields['leixing_id'], } dingdan = Dingdan.objects.create(**order_data) @@ -7731,7 +7735,7 @@ class DashouHuoquLeixingView(APIView): # 条件:shenhezhuangtai=1(正常状态) # 排序:先按 paixu 降序(数值大的在前),再按 id 升序 leixing_queryset = ShangpinLeixing.objects.filter( - shenhezhuangtai__in=[3, 4] # 商家端 + 点单隐藏式(抢单池可见) + shenhezhuangtai=3 # 抢单池只展示商家端类型;隐藏式订单经归属映射到商家端类型 ).order_by('-paixu', 'id') '''leixing_queryset = ShangpinLeixing.objects.filter( shenhezhuangtai__in= [1,9] # 🔴 只取正常状态,排除审核专用 diff --git a/houtai/view.py b/houtai/view.py index 598e802..a567500 100644 --- a/houtai/view.py +++ b/houtai/view.py @@ -5373,6 +5373,7 @@ class GetProductBaseDataView(APIView): 'tupian_url': t.tupian_url or '', 'paixu': t.paixu, 'shenhezhuangtai': t.shenhezhuangtai, + 'guishu_leixing_id': t.guishu_leixing_id, 'product_count': product_count, }) @@ -5636,6 +5637,19 @@ class AddProductView(APIView): ewai_dashou_fencheng_str = request.data.get('ewai_dashou_fencheng', '0') jieshao = request.data.get('jieshao', '').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' @@ -5767,7 +5781,8 @@ class AddProductView(APIView): kaioi_ewai_dashou_fencheng=kaioi_ewai, ewai_dashou_fencheng=ewai_fencheng, paixu=0, # 新商品默认排序最后 - shenhezhuangtai=1, # 正常状态 + shenhezhuangtai=shenhezhuangtai, + guishu_leixing_id=guishu_leixing_id, ) except Exception as e: logger.error(f"创建商品失败: {str(e)}", exc_info=True) @@ -5890,7 +5905,7 @@ class GetProductDetailView(APIView): # 查询所有商品类型、专区、会员(用于前端下拉选择) 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( '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', 'create_time': product.create_time.isoformat() if product.create_time else None, 'shenhezhuangtai': product.shenhezhuangtai, + 'guishu_leixing_id': product.guishu_leixing_id, } return Response({ @@ -6016,6 +6032,14 @@ class UpdateProductView(APIView): except: 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(): return Response({'code': 400, 'msg': '商品类型不存在'}) @@ -6108,6 +6132,7 @@ class UpdateProductView(APIView): product.tupian_url = tupian_url product.guize_tupian = guize_tupian product.shenhezhuangtai = shenhezhuangtai + product.guishu_leixing_id = guishu_leixing_id product.save() return Response({'code': 0, 'msg': '商品修改成功', 'data': {'id': product.id}}) @@ -7626,6 +7651,7 @@ class GetProductTypeZoneView(APIView): 'huiyuan_id': t.huiyuan_id or '', 'yongjin': float(t.yongjin) if t.yongjin is not None else None, 'shenhezhuangtai': t.shenhezhuangtai, + 'guishu_leixing_id': t.guishu_leixing_id, 'is_cross_enabled': t.is_cross_enabled, 'is_special_type': t.is_special_type, 'paixu': t.paixu, @@ -7727,6 +7753,14 @@ class ModifyProductTypeZoneView(APIView): 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}) + is_cross_enabled = request.data.get('is_cross_enabled', False) is_cross_enabled = is_cross_enabled in [True, 'true', '1', 'on'] is_special_type = request.data.get('is_special_type', False) @@ -7774,6 +7808,7 @@ class ModifyProductTypeZoneView(APIView): huiyuan_id=huiyuan_id, yongjin=yongjin, shenhezhuangtai=shenhezhuangtai, + guishu_leixing_id=guishu_leixing_id, is_cross_enabled=is_cross_enabled, is_special_type=is_special_type, paixu=0 @@ -7841,6 +7876,19 @@ class ModifyProductTypeZoneView(APIView): except (TypeError, ValueError): 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') if is_cross is not None: diff --git a/shangpin/migrations/0011_guishu_leixing_id.py b/shangpin/migrations/0011_guishu_leixing_id.py new file mode 100644 index 0000000..bb0d706 --- /dev/null +++ b/shangpin/migrations/0011_guishu_leixing_id.py @@ -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', + ), + ), + ] diff --git a/shangpin/models.py b/shangpin/models.py index df1f8a8..54ae723 100644 --- a/shangpin/models.py +++ b/shangpin/models.py @@ -40,6 +40,14 @@ class Shangpin(models.Model): verbose_name='审核状态', 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: db_table = 'shangpin' @@ -70,6 +78,14 @@ class ShangpinLeixing(models.Model): verbose_name='审核状态', 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( 'dengji.Bankuai', on_delete=models.SET_NULL, diff --git a/utils/shangpin_guishu.py b/utils/shangpin_guishu.py new file mode 100644 index 0000000..a67e754 --- /dev/null +++ b/utils/shangpin_guishu.py @@ -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, + }