"""俱乐部上下文解析与查询辅助。""" from jituan.constants import ( CLUB_ID_DEFAULT, CLUB_HEADER, CLUB_SCOPE_HEADER, DATA_SCOPE_ALL, DATA_SCOPE_SINGLE, ) from jituan.models import Club def resolve_club_id_from_request(request): """ 从请求解析 club_id。 优先级:已注入的 request.club_id > Header X-Club-Id > 默认 xq。 旧客户端不传时恒为 xq,与现网行为一致。 """ injected = getattr(request, 'club_id', None) if injected: return injected header = request.META.get(f'HTTP_{CLUB_HEADER.upper().replace("-", "_")}', '') if not header: header = request.headers.get(CLUB_HEADER, '') if hasattr(request, 'headers') else '' club_id = (header or '').strip() or CLUB_ID_DEFAULT return club_id def resolve_club_scope(request): """解析数据范围:ALL_CLUBS(集团汇总)或 SINGLE_CLUB。""" scope = getattr(request, 'club_scope', None) if scope: return scope header = request.META.get(f'HTTP_{CLUB_SCOPE_HEADER.upper().replace("-", "_")}', '') if not header: header = request.headers.get(CLUB_SCOPE_HEADER, '') if hasattr(request, 'headers') else '' if (header or '').strip().lower() == 'all': return DATA_SCOPE_ALL return DATA_SCOPE_SINGLE def get_active_club(club_id): try: club = Club.query.get(club_id=club_id, status=1) return club except Club.DoesNotExist: return None def filter_club_char_field(qs, request, field='club_id'): """按 club_id 字符字段过滤;xq 视图兼容历史空值。""" scope = resolve_club_scope(request) if scope == DATA_SCOPE_ALL: return qs if not hasattr(qs, 'filter'): return qs club_id = resolve_club_id_from_request(request) if club_id == CLUB_ID_DEFAULT: from django.db.models import Q return qs.filter( Q(**{field: club_id}) | Q(**{f'{field}__isnull': True}) | Q(**{field: ''}) ) return qs.filter(**{field: club_id}) def filter_queryset_by_club(qs, request, club_field='club_id'): """ 按请求上下文过滤 QuerySet / FluentQuery。 集团 ALL_CLUBS 视图不过滤;子公司 SINGLE_CLUB 按 club_id 过滤。 历史订单 ClubID 为空时视为 xq,避免列表被滤空。 """ scope = resolve_club_scope(request) club_id = resolve_club_id_from_request(request) if scope == DATA_SCOPE_ALL: return qs if not hasattr(qs, 'filter'): return qs if club_field == 'ClubID' and club_id == CLUB_ID_DEFAULT: from django.db.models import Q return qs.filter( Q(ClubID=club_id) | Q(ClubID__isnull=True) | Q(ClubID='') ) return qs.filter(**{club_field: club_id}) def resolve_effective_club_id(request, user=None): """ 小程序/API 实际生效俱乐部(展示价、支付价、分红须一致)。 优先级:Header X-Club-Id > 用户归属 ClubID > 默认 xq。 勿用 middleware 注入的 request.club_id(无 Header 时恒为 xq)。 """ header = request.META.get(f'HTTP_{CLUB_HEADER.upper().replace("-", "_")}', '') if not header and hasattr(request, 'headers'): header = request.headers.get(CLUB_HEADER, '') header = (header or '').strip() if header: return header if user is not None: from jituan.services.club_user import get_user_club_id user_club = get_user_club_id(user) if user_club: return user_club return CLUB_ID_DEFAULT def club_id_for_write(request, user=None): """写入业务流水时使用的 club_id(集团汇总视图写入仍须指定目标 club)。""" return resolve_effective_club_id(request, user) def orders_for_request(request): """按俱乐部过滤的订单 QuerySet。""" from orders.models import Order return filter_queryset_by_club(Order.query.all(), request, club_field='ClubID') def order_scope_qs(request=None): """ 订单列表作用域 QuerySet。 request 为 None 时不按俱乐部过滤(旧 /yonghu 订单列表,与改造前一致)。 """ from orders.models import Order qs = Order.query.all() if request is not None: qs = filter_queryset_by_club(qs, request, club_field='ClubID') return qs def count_order_status_buckets(q_conditions, request=None): """ 统计各状态订单数。 每次从全新 FluentQuery 起链,避免 FluentQuery.filter 原地累积导致列表被滤空。 """ from django.db.models import Q buckets = ( ('1,7', Q(Status__in=[1, 7])), ('2', Q(Status=2)), ('8', Q(Status=8)), ('3', Q(Status=3)), ('4', Q(Status=4)), ('5', Q(Status=5)), ('6', Q(Status=6)), ) # 合并 8 次独立 count → 1 次 aggregate with 条件 Count from django.db.models import Count _base = order_scope_qs(request).filter(q_conditions) _agg_kwargs = {'all': Count('id')} for key, status_q in buckets: _agg_kwargs[key] = Count('id', filter=status_q) return _base.aggregate(**_agg_kwargs) def paginate_fluent_query(fluent_qs, page, page_size): """ FluentQuery 分页。直接切片底层 Django QuerySet,绕过 FluentQuery 缓存。 返回 (total, items)。 """ page = max(1, int(page)) page_size = max(1, min(int(page_size), 100)) django_qs = fluent_qs._qs total = django_qs.count() start = (page - 1) * page_size return total, list(django_qs[start:start + page_size]) def platform_order_base_q(): """平台订单:发单平台=1,兼容历史 Platform 为空。""" from django.db.models import Q return Q(Platform=1) | Q(Platform__isnull=True) def merchant_order_base_q(): """商家订单:发单平台=2。""" from django.db.models import Q return Q(Platform=2) def filter_user_related_by_club(qs, request, user_club_path='user__ClubID'): """按 User.ClubID 过滤打手/管事/商家等关联查询。""" scope = resolve_club_scope(request) if scope == DATA_SCOPE_ALL: return qs return qs.filter(**{user_club_path: resolve_club_id_from_request(request)}) def filter_user_qs_by_club(qs, request): """直接过滤 User 查询集(字段 ClubID)。""" scope = resolve_club_scope(request) if scope == DATA_SCOPE_ALL: return qs return qs.filter(ClubID=resolve_club_id_from_request(request))