From b33bd3eb187d5dd04e3d174adcb942bbede77a71 Mon Sep 17 00:00:00 2001 From: XingQue Date: Wed, 24 Jun 2026 06:18:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AE=A2=E5=8D=95=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E3=80=81=E7=BB=9F=E8=AE=A1=E4=B8=8E=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E4=B8=80=E8=87=B4=E3=80=81admin-assignments=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jituan/services/club_context.py | 17 ++++++ jituan/views.py | 101 +++++++++++++++++--------------- users/views.py | 101 ++++++++++++++++---------------- 3 files changed, 123 insertions(+), 96 deletions(-) diff --git a/jituan/services/club_context.py b/jituan/services/club_context.py index 4dabfdc..e41b320 100644 --- a/jituan/services/club_context.py +++ b/jituan/services/club_context.py @@ -93,6 +93,23 @@ def orders_for_request(request): return filter_queryset_by_club(Order.query.all(), request, club_field='ClubID') +def paginate_fluent_query(fluent_qs, page, page_size): + """ + FluentQuery 分页。禁用会话缓存,避免 count 后 slice 导致列表为空。 + 返回 (total, items)。 + """ + page = max(1, int(page)) + page_size = max(1, min(int(page_size), 100)) + counter = fluent_qs._clone() + counter._cache_enabled = False + total = counter.count() + start = (page - 1) * page_size + page_q = fluent_qs._clone() + page_q._cache_enabled = False + page_q._qs = page_q._qs[start:start + page_size] + return total, page_q.to_list() + + def filter_user_related_by_club(qs, request, user_club_path='user__ClubID'): """按 User.ClubID 过滤打手/管事/商家等关联查询。""" scope = resolve_club_scope(request) diff --git a/jituan/views.py b/jituan/views.py index 5e22dfc..56e4ac5 100644 --- a/jituan/views.py +++ b/jituan/views.py @@ -3,6 +3,7 @@ import logging from django.utils import timezone from rest_framework import status +from rest_framework.parsers import JSONParser from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView @@ -197,59 +198,65 @@ class ClubMeContextView(APIView): class ClubAdminAssignmentListView(APIView): """POST /jituan/houtai/admin-assignments — 查看数据范围任职(非 gvsdsdk 功能权限)。""" permission_classes = [IsAuthenticated] + parser_classes = [JSONParser] def post(self, request): - username = (request.data.get('phone') or request.data.get('username') or '').strip() - if not username: - return Response({'code': 401, 'msg': '缺少账号'}, status=401) - kefu_obj, permissions = verify_kefu_permission(request, username) - if kefu_obj is None: - return permissions + try: + username = (request.data.get('phone') or request.data.get('username') or '').strip() + if not username: + return Response({'code': 401, 'msg': '缺少账号'}, status=401) + kefu_obj, permissions = verify_kefu_permission(request, username) + if kefu_obj is None: + return permissions - user = request.user - is_super = bool(user.IsSuperuser) or user.UserType == 'admin' - if is_super: - qs = AdminAssignment.query.filter(status=1).order_by('yonghuid', '-is_primary', 'club_id') - else: - qs = AdminAssignment.query.filter(yonghuid=user.UserUID, status=1).order_by('-is_primary') + user = request.user + is_super = bool(user.IsSuperuser) or user.UserType == 'admin' + if is_super: + qs = AdminAssignment.query.filter(status=1).order_by('yonghuid', '-is_primary', 'club_id') + else: + qs = AdminAssignment.query.filter(yonghuid=user.UserUID, status=1).order_by('-is_primary') - club_name_map = { - c.club_id: c.name for c in Club.query.filter(status=1) - } - uids = list({a.yonghuid for a in qs}) - user_map = {} - if uids: - for u in User.query.filter(UserUID__in=uids).only('UserUID', 'Phone', 'UserName'): - user_map[u.UserUID] = u.Phone or u.UserName or u.UserUID + assignments = qs.to_list() + club_name_map = { + c.club_id: c.name for c in Club.query.filter(status=1) + } + uids = list({a.yonghuid for a in assignments}) + user_map = {} + if uids: + for u in User.query.filter(UserUID__in=uids).only('UserUID', 'Phone', 'UserName'): + user_map[u.UserUID] = u.Phone or u.UserName or u.UserUID - rows = [] - for a in qs: - club_label = '集团(全部子公司)' - if a.club_id: - club_label = club_name_map.get(a.club_id, a.club_id) - rows.append({ - 'yonghuid': a.yonghuid, - 'phone': user_map.get(a.yonghuid, ''), - 'club_id': a.club_id, - 'club_label': club_label, - 'role_code': a.role_code, - 'role_name': ADMIN_ROLE_LABELS.get(a.role_code, a.role_code), - 'data_scope': a.data_scope, - 'is_primary': a.is_primary, + rows = [] + for a in assignments: + club_label = '集团(全部子公司)' + if a.club_id: + club_label = club_name_map.get(a.club_id, a.club_id) + rows.append({ + 'yonghuid': a.yonghuid, + 'phone': user_map.get(a.yonghuid, ''), + 'club_id': a.club_id, + 'club_label': club_label, + 'role_code': a.role_code, + 'role_name': ADMIN_ROLE_LABELS.get(a.role_code, a.role_code), + 'data_scope': a.data_scope, + 'is_primary': a.is_primary, + }) + + return Response({ + 'code': 0, + 'msg': 'ok', + 'data': { + 'list': rows, + 'perm_note': ( + '功能菜单权限(能进哪些页面)在「角色管理」配置;' + '本表 admin_assignment 仅控制集团/子公司数据范围。' + ), + 'current_context': build_admin_club_context(user), + }, }) - - return Response({ - 'code': 0, - 'msg': 'ok', - 'data': { - 'list': rows, - 'perm_note': ( - '功能菜单权限(能进哪些页面)在「角色管理」配置;' - '本表 admin_assignment 仅控制集团/子公司数据范围。' - ), - 'current_context': build_admin_club_context(user), - }, - }) + except Exception: + logger.exception('ClubAdminAssignmentListView 异常') + return Response({'code': 99, 'msg': '加载任职数据失败,请确认已执行 migrate jituan'}, status=500) class ClubCaiwuView(APIView): diff --git a/users/views.py b/users/views.py index 9ef18cc..03888d3 100644 --- a/users/views.py +++ b/users/views.py @@ -7049,7 +7049,7 @@ class KefuGetOrderListView(APIView): q_conditions = base_q & Q(OrderID=dingdan_id) else: q_conditions = base_q - if leixing is not None: + if leixing is not None and str(leixing).strip() != '': try: leixing_int = int(leixing) q_conditions &= Q(ProductTypeID=leixing_int) @@ -7076,40 +7076,42 @@ class KefuGetOrderListView(APIView): if clkf: q_conditions &= Q(AssignedCS__icontains=clkf) - from jituan.services.club_context import orders_for_request + from jituan.services.club_context import orders_for_request, paginate_fluent_query + from orders.models import PlatformOrderExt - # ---------- 统计(仅按发单平台,非跨平台) ---------- + # ---------- 统计与列表共用同一筛选条件 ---------- club_orders = orders_for_request(request) - platform_orders = club_orders.filter(Platform=1) - stats = platform_orders.aggregate( + filtered_orders = club_orders.filter(q_conditions) + stats = filtered_orders.aggregate( total_orders=Count('id'), completed_orders=Count('id', filter=Q(Status=3)), refund_orders=Count('id', filter=Q(Status=5)), - pending_orders=Count('id', filter=Q(Status__in=[4,8])) + pending_orders=Count('id', filter=Q(Status__in=[4, 8])) ) - # ---------- 状态计数(所有状态都返回,非跨平台) ---------- status_counts = { - 'all': platform_orders.count(), - '1,7': platform_orders.filter(Status__in=[1,7]).count(), - '2': platform_orders.filter(Status=2).count(), - '8': platform_orders.filter(Status=8).count(), - '3': platform_orders.filter(Status=3).count(), - '4': platform_orders.filter(Status=4).count(), - '5': platform_orders.filter(Status=5).count(), - '6': platform_orders.filter(Status=6).count(), + 'all': filtered_orders.count(), + '1,7': filtered_orders.filter(Status__in=[1, 7]).count(), + '2': filtered_orders.filter(Status=2).count(), + '8': filtered_orders.filter(Status=8).count(), + '3': filtered_orders.filter(Status=3).count(), + '4': filtered_orders.filter(Status=4).count(), + '5': filtered_orders.filter(Status=5).count(), + '6': filtered_orders.filter(Status=6).count(), } - # ---------- 分页查询(避免 only+select_related 导致列表为空) ---------- - list_qs = club_orders.filter(q_conditions).select_related('pingtai_kuozhan').order_by('-CreateTime') - total_count = list_qs.count() - orders_page = list_qs[(page - 1) * page_size: page * page_size].to_list() + list_qs = filtered_orders.order_by('-CreateTime') + total_count, orders_page = paginate_fluent_query(list_qs, page, page_size) + + order_ids = [o.OrderID for o in orders_page if o.OrderID] + ext_map = {} + if order_ids: + for ext in PlatformOrderExt.query.filter(Order__OrderID__in=order_ids).select_related('Order'): + if ext.Order: + ext_map[ext.Order.OrderID] = ext - # ---------- 获取老板昵称 ---------- laoban_ids = [ - o.pingtai_kuozhan.BossID - for o in orders_page - if getattr(o, 'pingtai_kuozhan', None) and o.pingtai_kuozhan.BossID + ext.BossID for ext in ext_map.values() if ext.BossID ] nickname_map = {} if laoban_ids: @@ -7121,7 +7123,7 @@ class KefuGetOrderListView(APIView): result_list = [] for order in orders_page: - ext = getattr(order, 'pingtai_kuozhan', None) + ext = ext_map.get(order.OrderID) laoban_id = ext.BossID if ext else '' youxi_nicheng = nickname_map.get(laoban_id, order.Nickname or '') result_list.append({ @@ -7219,7 +7221,7 @@ class KefuGetShangjiaOrderListView(APIView): q_conditions = base_q & Q(OrderID=dingdan_id) else: q_conditions = base_q - if leixing is not None: + if leixing is not None and str(leixing).strip() != '': try: leixing_int = int(leixing) q_conditions &= Q(ProductTypeID=leixing_int) @@ -7250,40 +7252,41 @@ class KefuGetShangjiaOrderListView(APIView): ShopProfile__nicheng__icontains=shangjia_nicheng ).values_list('UserUID', flat=True)) - from jituan.services.club_context import orders_for_request + from jituan.services.club_context import orders_for_request, paginate_fluent_query + from orders.models import MerchantOrderExt - # ---------- 统计(仅按发单平台,非跨平台) ---------- club_orders = orders_for_request(request) - merchant_orders = club_orders.filter(Platform=2) - stats = merchant_orders.aggregate( + filtered_orders = club_orders.filter(q_conditions) + stats = filtered_orders.aggregate( total_orders=Count('id'), completed_orders=Count('id', filter=Q(Status=3)), refund_orders=Count('id', filter=Q(Status=5)), - pending_orders=Count('id', filter=Q(Status__in=[4,8])) + pending_orders=Count('id', filter=Q(Status__in=[4, 8])) ) - # ---------- 状态计数(所有状态都返回,非跨平台) ---------- status_counts = { - 'all': merchant_orders.count(), - '1,7': merchant_orders.filter(Status__in=[1,7]).count(), - '2': merchant_orders.filter(Status=2).count(), - '8': merchant_orders.filter(Status=8).count(), - '3': merchant_orders.filter(Status=3).count(), - '4': merchant_orders.filter(Status=4).count(), - '5': merchant_orders.filter(Status=5).count(), - '6': merchant_orders.filter(Status=6).count(), + 'all': filtered_orders.count(), + '1,7': filtered_orders.filter(Status__in=[1, 7]).count(), + '2': filtered_orders.filter(Status=2).count(), + '8': filtered_orders.filter(Status=8).count(), + '3': filtered_orders.filter(Status=3).count(), + '4': filtered_orders.filter(Status=4).count(), + '5': filtered_orders.filter(Status=5).count(), + '6': filtered_orders.filter(Status=6).count(), } - # ---------- 分页查询 ---------- - list_qs = club_orders.filter(q_conditions).select_related('shangjia_kuozhan').order_by('-CreateTime') - total_count = list_qs.count() - orders_page = list_qs[(page - 1) * page_size: page * page_size].to_list() + list_qs = filtered_orders.order_by('-CreateTime') + total_count, orders_page = paginate_fluent_query(list_qs, page, page_size) + + order_ids = [o.OrderID for o in orders_page if o.OrderID] + ext_map = {} + if order_ids: + for ext in MerchantOrderExt.query.filter(Order__OrderID__in=order_ids).select_related('Order'): + if ext.Order: + ext_map[ext.Order.OrderID] = ext - # ---------- 获取商家昵称 ---------- shangjia_ids = [ - o.shangjia_kuozhan.MerchantID - for o in orders_page - if getattr(o, 'shangjia_kuozhan', None) and o.shangjia_kuozhan.MerchantID + ext.MerchantID for ext in ext_map.values() if ext.MerchantID ] nickname_map = {} if shangjia_ids: @@ -7295,9 +7298,9 @@ class KefuGetShangjiaOrderListView(APIView): result_list = [] for order in orders_page: - ext = getattr(order, 'shangjia_kuozhan', None) + ext = ext_map.get(order.OrderID) shangjia_id = ext.MerchantID if ext else '' - youxi_nicheng = order.Nickname or '' # 商家订单的玩家昵称 + youxi_nicheng = order.Nickname or '' result_list.append({ 'dingdan_id': order.OrderID or '', 'jieshao': order.Description or '',