From c8b67a2fc8704536088e9d657979574191c87686 Mon Sep 17 00:00:00 2001 From: XingQue Date: Fri, 26 Jun 2026 20:28:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=95=86=E5=AE=B6=E8=AE=A2=E5=8D=95=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=94=AF=E6=8C=81=E6=97=B6=E9=97=B4=E6=AE=B5=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E5=B9=B6=E4=BF=AE=E6=AD=A3=E5=88=86=E9=A1=B5has=5Fmor?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- merchant_ops/services/order_query.py | 18 +---- merchant_ops/views_orders.py | 2 +- .../services/merchant_order_list_filters.py | 75 +++++++++++++++++++ orders/views.py | 35 ++------- 4 files changed, 85 insertions(+), 45 deletions(-) create mode 100644 orders/services/merchant_order_list_filters.py diff --git a/merchant_ops/services/order_query.py b/merchant_ops/services/order_query.py index b996c15..4db8661 100644 --- a/merchant_ops/services/order_query.py +++ b/merchant_ops/services/order_query.py @@ -1,10 +1,9 @@ """子客服订单查询:按权限隔离,不返回商家全量(除非 order_view_all)。""" -from django.db.models import Q - -from backend.utils import fmt_datetime, pick_leixing_id +from backend.utils import fmt_datetime from merchant_ops.models import MerchantOrderDispatch from merchant_ops.services.authz import member_has_perm from orders.models import MerchantOrderExt +from orders.services.merchant_order_list_filters import apply_merchant_order_list_filters def staff_can_view_all_orders(member): @@ -27,18 +26,7 @@ def staff_order_ext_qs(member, zhuangtai_list=None): def staff_apply_order_filters(qs, data): - leixing_id = pick_leixing_id(data) - keyword = str(data.get('keyword', '')).strip() - if leixing_id is not None: - qs = qs.filter(Order__ProductTypeID=leixing_id) - if keyword: - qs = qs.filter( - Q(Order__OrderID__exact=keyword) - | Q(Order__Description__icontains=keyword) - | Q(Order__Nickname__icontains=keyword) - | Q(Order__PlayerID__exact=keyword) - ) - return qs + return apply_merchant_order_list_filters(qs, data) def staff_order_to_dict(ext): diff --git a/merchant_ops/views_orders.py b/merchant_ops/views_orders.py index 3b0ae19..d14bdee 100644 --- a/merchant_ops/views_orders.py +++ b/merchant_ops/views_orders.py @@ -85,7 +85,7 @@ class StaffOrderListView(APIView): 'msg': '成功', 'data': { 'list': order_list, - 'has_more': len(page_qs) == page_size and start + page_size < total_count, + 'has_more': start + len(page_qs) < total_count, 'total': total_count, 'pending_count': pending_count, 'view_scope': 'all' if staff_can_view_all_orders(member) else 'self', diff --git a/orders/services/merchant_order_list_filters.py b/orders/services/merchant_order_list_filters.py new file mode 100644 index 0000000..33300df --- /dev/null +++ b/orders/services/merchant_order_list_filters.py @@ -0,0 +1,75 @@ +"""商家订单列表通用筛选(商家老板接口 + 子客服列表共用)。""" +from datetime import datetime + +from django.db.models import Q +from django.utils import timezone +from django.utils.dateparse import parse_datetime + +from backend.utils import pick_leixing_id + + +def _aware_dt(dt): + if dt is None: + return None + if timezone.is_naive(dt): + return timezone.make_aware(dt, timezone.get_current_timezone()) + return dt + + +def parse_order_list_bound(value, is_end=False): + """解析列表时间边界;仅日期时开始取 00:00:00,结束取 23:59:59。""" + if not value: + return None + raw = str(value).strip() + if not raw: + return None + if len(raw) <= 10: + try: + base = datetime.strptime(raw[:10], '%Y-%m-%d') + except ValueError: + return None + if is_end: + base = base.replace(hour=23, minute=59, second=59) + return _aware_dt(base) + dt = parse_datetime(raw.replace(' ', 'T', 1) if ' ' in raw and 'T' not in raw else raw) + if dt is None: + try: + base = datetime.strptime(raw[:19], '%Y-%m-%d %H:%M:%S') + return _aware_dt(base) + except ValueError: + return None + return _aware_dt(dt) + + +def apply_merchant_order_list_filters(qs, data): + """在 MerchantOrderExt 查询集上应用类型/关键字/时间段筛选。""" + leixing_id = pick_leixing_id(data) + keyword = str(data.get('keyword', '')).strip() + waibu_dingdan_id = str(data.get('waibuDingdanId') or data.get('waibu_dingdan_id') or '').strip() + + start_raw = data.get('start_time') or data.get('time_start') or data.get('create_time_start') + end_raw = data.get('end_time') or data.get('time_end') or data.get('create_time_end') + + if leixing_id is not None: + qs = qs.filter(Order__ProductTypeID=leixing_id) + + start_dt = parse_order_list_bound(start_raw, is_end=False) + end_dt = parse_order_list_bound(end_raw, is_end=True) + if start_dt and end_dt and start_dt > end_dt: + start_dt, end_dt = end_dt, start_dt + if start_dt: + qs = qs.filter(Order__CreateTime__gte=start_dt) + if end_dt: + qs = qs.filter(Order__CreateTime__lte=end_dt) + + if waibu_dingdan_id: + qs = qs.filter(Order__ExternalOrderID__exact=waibu_dingdan_id) + elif keyword: + qs = qs.filter( + Q(Order__OrderID__exact=keyword) + | Q(Order__Description__icontains=keyword) + | Q(Order__Nickname__icontains=keyword) + | Q(Order__PlayerID__exact=keyword) + | Q(Order__ExternalOrderID__exact=keyword) + ) + return qs diff --git a/orders/views.py b/orders/views.py index fe59d24..24092d4 100644 --- a/orders/views.py +++ b/orders/views.py @@ -2058,12 +2058,7 @@ class ShangjiaDingdanHuoquView(APIView): page = max(1, int(data.get('page', 1))) page_size = min(100, max(1, int(data.get('page_size', 10)))) - # 🆕 商品类型筛选(兼容 leixing_id / ProductTypeID) - leixing_id = pick_leixing_id(data) - - # 🆕 模糊搜索关键字(支持订单ID、订单介绍、游戏昵称、打手ID、拼多多订单号) - keyword = data.get('keyword', '').strip() - waibu_dingdan_id = str(data.get('waibuDingdanId') or '').strip() + # 商品类型 / 关键字 / 时间段等见 apply_merchant_order_list_filters except (ValueError, TypeError): return Response({ 'code': 2, @@ -2080,6 +2075,8 @@ class ShangjiaDingdanHuoquView(APIView): }, status=status.HTTP_400_BAD_REQUEST) try: + from orders.services.merchant_order_list_filters import apply_merchant_order_list_filters + # 4. 构建基础查询(该商家的所有订单) base_qs = MerchantOrderExt.query.filter( MerchantID=yonghuid @@ -2088,28 +2085,8 @@ class ShangjiaDingdanHuoquView(APIView): # 5. 状态过滤 base_qs = base_qs.filter(Order__Status__in=zhuangtai_list) - # 6. 商品类型筛选 - if leixing_id is not None: - base_qs = base_qs.filter(Order__ProductTypeID=leixing_id) - - # 7. 模糊搜索(支持订单ID、订单介绍、游戏昵称、打手ID、拼多多订单号) - if waibu_dingdan_id: - base_qs = base_qs.filter(Order__ExternalOrderID__exact=waibu_dingdan_id) - elif keyword: - # 订单ID精确匹配(dingdan_id有唯一索引,先用精确匹配) - dingdan_id_match = Q(Order__OrderID__exact=keyword) - # 订单介绍模糊匹配 - jieshao_match = Q(Order__Description__icontains=keyword) - # 游戏昵称模糊匹配 - nicheng_match = Q(Order__Nickname__icontains=keyword) - # 打手ID精确匹配 - dashou_match = Q(Order__PlayerID__exact=keyword) - # 拼多多订单号精确匹配 - waibu_match = Q(Order__ExternalOrderID__exact=keyword) - - base_qs = base_qs.filter( - dingdan_id_match | jieshao_match | nicheng_match | dashou_match | waibu_match - ) + # 6–7. 类型 / 关键字 / 时间段 + base_qs = apply_merchant_order_list_filters(base_qs, data) # 8. 计算待结算订单数量(状态为8) pending_count = MerchantOrderExt.query.filter( @@ -2131,7 +2108,7 @@ class ShangjiaDingdanHuoquView(APIView): # 11. 判断是否还有更多 current_page_count = len(current_page_orders) - has_more = current_page_count == page_size and end_index < total_count + has_more = start_index + current_page_count < total_count return Response({ 'code': 0,