fix: 总榜按邀请总人数排行,组长去重统计;总会员子类型解析增强

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-11 02:00:31 +08:00
parent 5556bbdb61
commit 80cca803f8
4 changed files with 98 additions and 68 deletions

View File

@@ -52,12 +52,12 @@ DEFAULT_SORT_FIELD = {
'shangjia': 'jiesuan_jine',
}
# 总榜:管事/组长按无效人数排序展示(其它周期不变)
WUXIAO_TOTAL_RANK_SORT_FIELDS = {
'guanshi': 'wuxiao_yaoqing_dashou_shu',
'zuzhang': 'wuxiao_yaoqing_guanshi_shu',
# 总榜:管事=邀请打手总人数;组长=管事+打手身份邀请去重总人数(其它周期不变)
TOTAL_RANK_INVITE_SORT_FIELDS = {
'guanshi': 'yaoqing_dashou_shu',
'zuzhang': 'yaoqing_zongren_shu',
}
WUXIAO_TOTAL_RANK_SORT_LABEL = '无效人数'
TOTAL_RANK_INVITE_SORT_LABEL = '邀请总人数'
# shenfen -> sort_field_key -> (model, id_field, db_field, is_int)
SORT_FIELD_REGISTRY = {
@@ -94,6 +94,7 @@ SORT_FIELD_LABELS = {
'shouru_zonge': '收益总额',
'yaoqing_dashou_shu': '邀请打手数',
'yaoqing_guanshi_shu': '邀请管事数',
'yaoqing_zongren_shu': '邀请总人数',
'fenyong_jine': '分佣金额',
'jiesuan_dingdan_shu': '结算单量',
'jiesuan_jine': '结算金额',
@@ -179,9 +180,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
if riqi == '总榜' and shenfen in TOTAL_RANK_INVITE_SORT_FIELDS:
sf = TOTAL_RANK_INVITE_SORT_FIELDS[shenfen]
return sf, TOTAL_RANK_INVITE_SORT_LABEL
period_type = RIQI_TO_PERIOD.get(riqi) or OPEN_RIQI_PERIOD_MAP.get(riqi)
sort_field = None
if period_type:
@@ -257,20 +258,59 @@ def _resolve_date_range_for_display(riqi: str):
return None
def _is_wuxiao_total_rank(riqi: str, shenfen: str, sort_field: str) -> bool:
def _is_total_rank_invite_sort(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]
and shenfen in TOTAL_RANK_INVITE_SORT_FIELDS
and sort_field == TOTAL_RANK_INVITE_SORT_FIELDS[shenfen]
)
def query_wuxiao_total_rank_rows(
def _query_zuzhang_total_unique_invite_rows(club_id: str = None, limit: int = MAX_RANK) -> list:
"""
组长总榜:组长身份邀请的管事 + 管事身份邀请的打手,按 UserUID 去重计数。
"""
from users.models import UserDashou, UserGuanshi, UserZuzhang
zuzhang_uids = set(
UserZuzhang.objects.filter(zhuangtai=1).values_list('user__UserUID', flat=True)
)
if club_id:
zuzhang_uids &= set(club_member_uids(club_id))
if not zuzhang_uids:
return []
invite_sets = {}
zuzhang_list = list(zuzhang_uids)
def _add(inviter, invited_uid):
if inviter not in zuzhang_uids or not invited_uid:
return
invite_sets.setdefault(inviter, set()).add(invited_uid)
for inviter, invited_uid in UserGuanshi.objects.filter(
yaoqingren__in=zuzhang_list,
).values_list('yaoqingren', 'user__UserUID'):
_add(inviter, invited_uid)
for inviter, invited_uid in UserDashou.objects.filter(
yaoqingren__in=zuzhang_list,
).values_list('yaoqingren', 'user__UserUID'):
_add(inviter, invited_uid)
out = [
{'yonghuid': uid, 'metric': len(invited), 'is_int': True}
for uid, invited in invite_sets.items()
if invited
]
out.sort(key=lambda x: x['metric'], reverse=True)
return out[:limit]
def query_total_rank_invite_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 []
@@ -286,11 +326,7 @@ def query_wuxiao_total_rank_rows(
)
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'))
.annotate(metric=Coalesce(Sum('InvitedPlayerCount'), Value(0)))
.order_by('-metric')[:fetch_limit]
)
out = []
@@ -306,39 +342,15 @@ def query_wuxiao_total_rank_rows(
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 _query_zuzhang_total_unique_invite_rows(club_id=club_id, limit=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)
if _is_total_rank_invite_sort(riqi, shenfen, sort_field):
return query_total_rank_invite_rows(shenfen, club_id=club_id, limit=limit)
reg = SORT_FIELD_REGISTRY.get(shenfen, {}).get(sort_field)
if not reg:
return []
@@ -411,8 +423,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)
if _is_total_rank_invite_sort(riqi, shenfen, sort_field):
return query_total_rank_invite_rows(shenfen, club_id=None, limit=limit)
reg = SORT_FIELD_REGISTRY.get(shenfen, {}).get(sort_field)
if not reg:
return []

View File

@@ -167,9 +167,9 @@ class PhbJlPhbView(APIView):
if riqi not in RIQI_OPTIONS:
return Response({'code': 400, 'msg': '周期参数错误', 'data': {'list': []}})
from rank.reward_services import SORT_FIELD_REGISTRY, WUXIAO_TOTAL_RANK_SORT_FIELDS
from rank.reward_services import SORT_FIELD_REGISTRY, TOTAL_RANK_INVITE_SORT_FIELDS
if riqi == '总榜' and shenfen in WUXIAO_TOTAL_RANK_SORT_FIELDS:
if riqi == '总榜' and shenfen in TOTAL_RANK_INVITE_SORT_FIELDS:
sort_field, sort_label = resolve_sort_field_for_display(club_id, shenfen, riqi)
else:
scheme_sort, sort_label = resolve_sort_field_for_display(club_id, shenfen, riqi)