From 17b38d07a9803d404726d16ea64b83ac4dc399d3 Mon Sep 17 00:00:00 2001 From: XingQue Date: Mon, 27 Jul 2026 17:19:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8A=A2=E5=8D=95=E6=B1=A0/=E7=BB=84?= =?UTF-8?q?=E9=98=9F=E8=A7=92=E6=A0=87=E8=AE=A1=E6=95=B0=E5=A4=9A=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E9=99=84=E5=8A=A0=E5=AD=97=E6=AE=B5=EF=BC=88=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=97=A7=E5=AE=A2=E6=88=B7=E7=AB=AF=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orders/services/dashou_badges.py | 100 +++++++++++++++++++++++++++++ orders/services/fake_order_pool.py | 3 + orders/views/dashou.py | 36 ++++++++++- orders/views/zudui.py | 7 ++ users/views/user_info.py | 9 +++ 5 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 orders/services/dashou_badges.py diff --git a/orders/services/dashou_badges.py b/orders/services/dashou_badges.py new file mode 100644 index 0000000..417a411 --- /dev/null +++ b/orders/services/dashou_badges.py @@ -0,0 +1,100 @@ +"""打手端角标计数:抢单池按类型待接单数、组队大厅待加入数。 + +仅供响应附加字段,不改变原有业务筛选逻辑。 +""" +from __future__ import annotations + +import logging + +from django.db.models import Count + +logger = logging.getLogger(__name__) + + +def grab_pool_type_pending_counts(club_id: str) -> dict[str, int]: + """ + 与 /dingdan/ddhq 同口径:Status in (1,7) + 抢单俱乐部范围,按 ProductTypeID 聚合。 + 返回 { \"类型id\": 数量 },key 一律 str,方便 JSON / 前端。 + """ + 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) + 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) -> dict: + """附加到接口 data 的角标字段(旧字段不受影响)。""" + type_counts = grab_pool_type_pending_counts(club_id) + 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 diff --git a/orders/services/fake_order_pool.py b/orders/services/fake_order_pool.py index a3844e9..267088b 100644 --- a/orders/services/fake_order_pool.py +++ b/orders/services/fake_order_pool.py @@ -233,5 +233,8 @@ def build_fake_order_pool_response(request): 'current_page': page, 'page_size': page_size, 'total_pages': (total + page_size - 1) // page_size if page_size else 0, + 'type_pending_counts': {}, + 'pool_pending_total': 0, + 'team_pending_count': 0, }, }) diff --git a/orders/views/dashou.py b/orders/views/dashou.py index 3010479..e6d9b5b 100644 --- a/orders/views/dashou.py +++ b/orders/views/dashou.py @@ -382,6 +382,13 @@ class DashouDingdanHuoquView(APIView): has_more = (page * page_size) < total + badge = {} + try: + from orders.services.dashou_badges import dashou_badge_fields + badge = dashou_badge_fields(club_id) + except Exception as e: + logger.warning('ddhq 角标附加失败: %s', e) + return Response({ 'code': 200, 'msg': '获取成功', @@ -393,6 +400,7 @@ class DashouDingdanHuoquView(APIView): 'page_size': page_size, 'total_pages': (total + page_size - 1) // page_size, 'clumber': user_clumber, + **badge, } }) @@ -762,13 +770,24 @@ class DashouDingdanHuoquView1(APIView): if total_count == 0: has_more = False + badge = {} + try: + from jituan.services.club_context import resolve_effective_club_id + from orders.services.dashou_badges import dashou_badge_fields + badge = dashou_badge_fields( + resolve_effective_club_id(request, getattr(request, 'user', None)) + ) + except Exception as e: + logger.warning('dshqdingdan 角标附加失败: %s', e) + return Response({ 'code': 0, 'msg': '成功', 'data': { 'list': order_list, 'has_more': has_more, - 'total': total_count + 'total': total_count, + **badge, } }, status=status.HTTP_200_OK) @@ -1568,15 +1587,26 @@ class DashouHuoquLeixingView(APIView): club_id = resolve_effective_club_id(request, request.user) leixing_list = build_club_leixing_list_for_api(club_id) + badge = {} + try: + from orders.services.dashou_badges import dashou_badge_fields, attach_pending_to_leixing_list + badge = dashou_badge_fields(club_id) + leixing_list = attach_pending_to_leixing_list( + leixing_list, badge.get('type_pending_counts'), + ) + except Exception as e: + logger.warning('dsqdhqddlx 角标附加失败: %s', e) + logger.info(f"返回商品类型数据:{len(leixing_list)}条,俱乐部:{club_id} 用户: {request.user.UserUID}") # 🔴 返回成功响应 - # 注意:保持与前端原数据格式完全一致 + # 注意:保持与前端原数据格式完全一致;角标为新增字段,不影响旧客户端 return Response({ "code": 200, "msg": "成功", "data": { - "list": leixing_list # 🔴 保持原字段名 "list" + "list": leixing_list, # 🔴 保持原字段名 "list" + **badge, } }, status=status.HTTP_200_OK) diff --git a/orders/views/zudui.py b/orders/views/zudui.py index e7aaa27..9cfe897 100644 --- a/orders/views/zudui.py +++ b/orders/views/zudui.py @@ -207,6 +207,12 @@ class ZuduiDatingView(APIView): team_svc.serialize_recruit_public(r, orders.get(r.order_id), include_game_ids=False) for r in rows ] + badge = {} + try: + from orders.services.dashou_badges import dashou_badge_fields + badge = dashou_badge_fields(club) + except Exception: + badge = {} return Response({ 'code': 0, 'msg': 'ok', @@ -216,6 +222,7 @@ class ZuduiDatingView(APIView): 'page': page, 'page_size': page_size, 'has_more': start + len(rows) < total, + **badge, }, }) diff --git a/users/views/user_info.py b/users/views/user_info.py index b5e83bd..2a9f6da 100644 --- a/users/views/user_info.py +++ b/users/views/user_info.py @@ -459,6 +459,15 @@ class DashouXinxiAPIView(APIView): 'zuijinTixian': str(dashou_profile.jinritixian_jine) if dashou_profile.jinritixian_jine is not None else '0.00', # 最近提现金额(今日已提现) }) + # 角标附加字段(不影响旧客户端) + try: + from jituan.services.club_context import resolve_effective_club_id + from orders.services.dashou_badges import dashou_badge_fields + cid = resolve_effective_club_id(request, request.user) + data.update(dashou_badge_fields(cid)) + except Exception: + pass + # 6. 返回成功响应 return Response({ 'code': 200,