"""打手端角标计数:抢单池按类型待接单数、组队大厅待加入数。 仅供响应附加字段,不改变原有业务筛选逻辑。 """ from __future__ import annotations import logging from django.db.models import Count logger = logging.getLogger(__name__) def _fake_type_counts(club_id: str) -> dict[str, int]: try: from orders.models import FakeGrabOrder rows = ( FakeGrabOrder.query.filter(ClubID=club_id) .exclude(ProductTypeID__isnull=True) .values('ProductTypeID') .annotate(cnt=Count('OrderID')) ) out = {} for r in rows: lid = r.get('ProductTypeID') if lid is None: continue out[str(int(lid))] = int(r.get('cnt') or 0) return out except Exception as e: logger.warning('_fake_type_counts failed club=%s: %s', club_id, e) return {} def grab_pool_type_pending_counts(club_id: str, user=None) -> dict[str, int]: """ 与 /dingdan/ddhq 同口径:Status in (1,7) + 抢单俱乐部范围,按 ProductTypeID 聚合。 若传入 user:按假单展示模式把假单数计入(mix/fake_only 计入;real_only/off 不计)。 返回 { \"类型id\": 数量 },key 一律 str。 """ club_id = (club_id or '').strip() if not club_id: return {} try: from django.db.models import Q from orders.models import Order from jituan.services.order_grab_share import order_club_q_for_grabber base = Q(Status__in=[1, 7]) & order_club_q_for_grabber(club_id) rows = ( Order.query.filter(base) .exclude(ProductTypeID__isnull=True) .values('ProductTypeID') .annotate(cnt=Count('OrderID')) ) out = {} for r in rows: lid = r.get('ProductTypeID') if lid is None: continue out[str(int(lid))] = int(r.get('cnt') or 0) if user is not None: try: from orders.services.fake_order_pool import ( MODE_FAKE_ONLY, MODE_MIX, resolve_fake_pool_mode, ) fake_counts = _fake_type_counts(club_id) # 所有出现过的类型(真单或假单) all_keys = set(out.keys()) | set(fake_counts.keys()) adjusted = {} for key in all_keys: try: lid = int(key) except (TypeError, ValueError): continue real_n = int(out.get(key) or 0) fake_n = int(fake_counts.get(key) or 0) mode = resolve_fake_pool_mode(user, club_id=club_id, leixing_id=lid) if mode == MODE_FAKE_ONLY: adjusted[key] = fake_n elif mode == MODE_MIX: adjusted[key] = real_n + fake_n else: # off / real_only:不计假单 adjusted[key] = real_n out = adjusted except Exception as e: logger.warning('merge fake into type counts failed: %s', e) return out except Exception as e: logger.warning('grab_pool_type_pending_counts failed club=%s: %s', club_id, e) return {} def team_pending_count(club_id: str) -> int: """ 与 /zudui/dating 同口径:本俱乐部招募中,且关联订单进行中(Status=2)。 """ club_id = (club_id or '').strip() if not club_id: return 0 try: from orders.models import Order, TeamRecruit qs = TeamRecruit.query.filter( club_id=club_id, status=TeamRecruit.STATUS_RECRUITING, ) oids = list(qs.values_list('order_id', flat=True)[:2000]) if not oids: return 0 valid = set( Order.query.filter( OrderID__in=oids, ClubID=club_id, Status=2, ).values_list('OrderID', flat=True) ) return sum(1 for oid in oids if oid in valid) except Exception as e: logger.warning('team_pending_count failed club=%s: %s', club_id, e) return 0 def dashou_badge_fields(club_id: str, user=None) -> dict: """附加到接口 data 的角标字段(旧字段不受影响)。""" type_counts = grab_pool_type_pending_counts(club_id, user=user) total = int(sum(type_counts.values())) return { 'type_pending_counts': type_counts, 'pool_pending_total': total, 'team_pending_count': int(team_pending_count(club_id)), } def attach_pending_to_leixing_list(leixing_list: list, type_counts: dict | None) -> list: """给类型 list 每项附加 pending_count,不改动其它字段。""" counts = type_counts or {} out = [] for item in leixing_list or []: if not isinstance(item, dict): out.append(item) continue row = dict(item) lid = row.get('id') key = str(int(lid)) if lid is not None else '' row['pending_count'] = int(counts.get(key) or 0) out.append(row) return out