fix: 总榜按无效人数排行;总会员ID匹配与子类型展开修复

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-11 01:56:39 +08:00
parent fda4ffb628
commit 5556bbdb61
4 changed files with 172 additions and 31 deletions

View File

@@ -7,7 +7,8 @@ from decimal import Decimal
from typing import Optional
from django.db import IntegrityError, transaction
from django.db.models import F, Sum
from django.db.models import F, Sum, Value
from django.db.models.functions import Coalesce
from django.utils import timezone
from backend.models import (
@@ -51,6 +52,13 @@ DEFAULT_SORT_FIELD = {
'shangjia': 'jiesuan_jine',
}
# 总榜:管事/组长按无效人数排序展示(其它周期不变)
WUXIAO_TOTAL_RANK_SORT_FIELDS = {
'guanshi': 'wuxiao_yaoqing_dashou_shu',
'zuzhang': 'wuxiao_yaoqing_guanshi_shu',
}
WUXIAO_TOTAL_RANK_SORT_LABEL = '无效人数'
# shenfen -> sort_field_key -> (model, id_field, db_field, is_int)
SORT_FIELD_REGISTRY = {
'dashou': {
@@ -171,6 +179,9 @@ def sort_field_options_for_shenfen(shenfen: str) -> list:
def resolve_sort_field_for_display(club_id: str, shenfen: str, riqi: str) -> tuple[str, str]:
"""按后台奖励方案决定展示/排序字段(方案存在即生效,与是否启用奖励无关)。"""
if riqi == '总榜' and shenfen in WUXIAO_TOTAL_RANK_SORT_FIELDS:
sf = WUXIAO_TOTAL_RANK_SORT_FIELDS[shenfen]
return sf, WUXIAO_TOTAL_RANK_SORT_LABEL
period_type = RIQI_TO_PERIOD.get(riqi) or OPEN_RIQI_PERIOD_MAP.get(riqi)
sort_field = None
if period_type:
@@ -246,8 +257,88 @@ def _resolve_date_range_for_display(riqi: str):
return None
def _is_wuxiao_total_rank(riqi: str, shenfen: str, sort_field: str) -> bool:
return (
riqi == '总榜'
and shenfen in WUXIAO_TOTAL_RANK_SORT_FIELDS
and sort_field == WUXIAO_TOTAL_RANK_SORT_FIELDS[shenfen]
)
def query_wuxiao_total_rank_rows(
shenfen: str, club_id: str = None, limit: int = MAX_RANK,
) -> list:
"""总榜专用:管事/组长按无效人数排行(仅展示,不影响其它周期)。"""
from rank.display_utils import apply_zuzhang_wuxiao_fields
member_uids = club_member_uids(club_id) if club_id else None
if club_id and not member_uids:
return []
fetch_limit = min(max(limit * 5, 100), 250)
if shenfen == 'guanshi':
model = ManagerDailyStats
id_field = 'ManagerID'
base_qs = model.objects.all()
if club_id:
base_qs = base_qs.filter(
club_id=club_id, **{f'{id_field}__in': member_uids},
)
rows = (
base_qs.values(id_field)
.annotate(
invited=Coalesce(Sum('InvitedPlayerCount'), Value(0)),
valid=Coalesce(Sum('RechargedPlayerCount'), Value(0)),
)
.annotate(metric=F('invited') - F('valid'))
.order_by('-metric')[:fetch_limit]
)
out = []
for r in rows:
uid = r[id_field]
metric = int(r.get('metric') or 0)
if metric <= 0:
continue
out.append({'yonghuid': uid, 'metric': metric, 'is_int': True})
if club_id:
out = filter_rank_rows_by_user_club(out, club_id)
out.sort(key=lambda x: x['metric'], reverse=True)
return out[:limit]
if shenfen == 'zuzhang':
model = LeaderDailyStats
id_field = 'LeaderID'
base_qs = model.objects.all()
if club_id:
base_qs = base_qs.filter(
club_id=club_id, **{f'{id_field}__in': member_uids},
)
candidates = (
base_qs.values(id_field)
.annotate(invited=Coalesce(Sum('InvitedManagerCount'), Value(0)))
.order_by('-invited')[:fetch_limit]
)
out = []
for r in candidates:
uid = r[id_field]
stats = {'yaoqing_guanshi_shu': int(r.get('invited') or 0)}
apply_zuzhang_wuxiao_fields(stats, uid, None, None)
metric = int(stats.get('wuxiao_yaoqing_guanshi_shu') or 0)
if metric <= 0:
continue
out.append({'yonghuid': uid, 'metric': metric, 'is_int': True})
if club_id:
out = filter_rank_rows_by_user_club(out, club_id)
out.sort(key=lambda x: x['metric'], reverse=True)
return out[:limit]
return []
def query_club_rank_rows(club_id: str, shenfen: str, sort_field: str, riqi: str, limit: int = MAX_RANK):
"""按 club 归属用户 + 日统计 club_id 双过滤(分奖与展示唯一依据)。"""
if _is_wuxiao_total_rank(riqi, shenfen, sort_field):
return query_wuxiao_total_rank_rows(shenfen, club_id=club_id, limit=limit)
reg = SORT_FIELD_REGISTRY.get(shenfen, {}).get(sort_field)
if not reg:
return []
@@ -320,6 +411,8 @@ def query_group_rank_rows(shenfen: str, sort_field: str, riqi: str, limit: int =
def _query_group_rank_rows(shenfen: str, sort_field: str, riqi: str, limit: int = MAX_RANK):
if _is_wuxiao_total_rank(riqi, shenfen, sort_field):
return query_wuxiao_total_rank_rows(shenfen, club_id=None, limit=limit)
reg = SORT_FIELD_REGISTRY.get(shenfen, {}).get(sort_field)
if not reg:
return []

View File

@@ -167,15 +167,18 @@ class PhbJlPhbView(APIView):
if riqi not in RIQI_OPTIONS:
return Response({'code': 400, 'msg': '周期参数错误', 'data': {'list': []}})
from rank.reward_services import SORT_FIELD_REGISTRY
from rank.reward_services import SORT_FIELD_REGISTRY, WUXIAO_TOTAL_RANK_SORT_FIELDS
scheme_sort, sort_label = resolve_sort_field_for_display(club_id, shenfen, riqi)
if not sort_field:
sort_field = scheme_sort
elif sort_field not in (SORT_FIELD_REGISTRY.get(shenfen) or {}):
return Response({'code': 400, 'msg': '排序指标无效', 'data': {'list': []}})
if riqi == '总榜' and shenfen in WUXIAO_TOTAL_RANK_SORT_FIELDS:
sort_field, sort_label = resolve_sort_field_for_display(club_id, shenfen, riqi)
else:
sort_label = SORT_FIELD_LABELS.get(sort_field, sort_field)
scheme_sort, sort_label = resolve_sort_field_for_display(club_id, shenfen, riqi)
if not sort_field:
sort_field = scheme_sort
elif sort_field not in (SORT_FIELD_REGISTRY.get(shenfen) or {}):
return Response({'code': 400, 'msg': '排序指标无效', 'data': {'list': []}})
else:
sort_label = SORT_FIELD_LABELS.get(sort_field, sort_field)
if fanwei == 'club':
rows = query_club_rank_rows(club_id, shenfen, sort_field, riqi)