fix: 总榜按无效人数排行;总会员ID匹配与子类型展开修复
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -71,12 +71,16 @@ def get_bundle_included_ids(club_id, bundle_huiyuan_id):
|
||||
|
||||
if not club_id or not bundle_huiyuan_id:
|
||||
return []
|
||||
return list(
|
||||
ClubHuiyuanBundleInclude.query.filter(
|
||||
club_id=club_id,
|
||||
bundle_huiyuan_id=bundle_huiyuan_id,
|
||||
).values_list('included_huiyuan_id', flat=True)
|
||||
)
|
||||
for bid in expand_huiyuan_id_variants(bundle_huiyuan_id):
|
||||
ids = list(
|
||||
ClubHuiyuanBundleInclude.query.filter(
|
||||
club_id=club_id,
|
||||
bundle_huiyuan_id=bid,
|
||||
).values_list('included_huiyuan_id', flat=True)
|
||||
)
|
||||
if ids:
|
||||
return ids
|
||||
return []
|
||||
|
||||
|
||||
def _membership_club_candidates(yonghu_id, club_id=None, record_club_id=None):
|
||||
@@ -123,10 +127,11 @@ def resolve_bundle_included_ids(bundle_huiyuan_id, club_id=None, yonghu_id=None,
|
||||
def _included_huiyuan_id_set(included_ids):
|
||||
included_set = set()
|
||||
for x in included_ids or []:
|
||||
sx = normalize_huiyuan_id(x)
|
||||
if sx:
|
||||
included_set.add(sx)
|
||||
included_set.add(sx.lstrip('0') or '0')
|
||||
for variant in expand_huiyuan_id_variants(x):
|
||||
sx = normalize_huiyuan_id(variant)
|
||||
if sx:
|
||||
included_set.add(sx)
|
||||
included_set.add(sx.lstrip('0') or '0')
|
||||
return included_set
|
||||
|
||||
|
||||
@@ -159,10 +164,13 @@ def _record_club_id(record, club_id=None):
|
||||
|
||||
|
||||
def is_bundle_huiyuan(huiyuan_id):
|
||||
try:
|
||||
return bool(Huiyuan.query.get(huiyuan_id=huiyuan_id).is_bundle)
|
||||
except Huiyuan.DoesNotExist:
|
||||
hid = normalize_huiyuan_id(huiyuan_id)
|
||||
if not hid:
|
||||
return False
|
||||
for bid in Huiyuan.query.filter(is_bundle=True).values_list('huiyuan_id', flat=True):
|
||||
if huiyuan_ids_match(bid, hid):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _membership_record_active(record):
|
||||
@@ -188,18 +196,13 @@ def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
|
||||
def _record_valid(rec):
|
||||
return _membership_record_active(rec)
|
||||
|
||||
req_set = {req, req.lstrip('0') or '0'}
|
||||
for rec in Huiyuangoumai.query.filter(yonghu_id=yonghu_id):
|
||||
if _record_valid(rec) and huiyuan_ids_match(rec.huiyuan_id, req):
|
||||
if not _record_valid(rec):
|
||||
continue
|
||||
if huiyuan_ids_match(rec.huiyuan_id, req):
|
||||
return True
|
||||
|
||||
bundle_ids = set(
|
||||
Huiyuan.query.filter(is_bundle=True).values_list('huiyuan_id', flat=True)
|
||||
)
|
||||
if bundle_ids:
|
||||
req_set = {req, req.lstrip('0') or '0'}
|
||||
for rec in Huiyuangoumai.query.filter(yonghu_id=yonghu_id, huiyuan_id__in=bundle_ids):
|
||||
if not _record_valid(rec):
|
||||
continue
|
||||
if is_bundle_huiyuan(rec.huiyuan_id):
|
||||
included = resolve_bundle_included_ids(
|
||||
rec.huiyuan_id,
|
||||
club_id=club_id,
|
||||
|
||||
@@ -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 []
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -181,6 +181,48 @@ class XzjPhbHqsjView(APIView):
|
||||
})
|
||||
|
||||
try:
|
||||
if riqi == '总榜' and shenfen in ('guanshi', 'zuzhang'):
|
||||
from rank.reward_services import (
|
||||
WUXIAO_TOTAL_RANK_SORT_FIELDS,
|
||||
enrich_rank_stats,
|
||||
query_wuxiao_total_rank_rows,
|
||||
)
|
||||
wuxiao_rows = query_wuxiao_total_rank_rows(shenfen, club_id=None, limit=MAX_RANK)
|
||||
cfg = XZJ_ROLE_CONFIG[shenfen]
|
||||
id_field = cfg['id_field']
|
||||
wuxiao_key = WUXIAO_TOTAL_RANK_SORT_FIELDS[shenfen]
|
||||
pseudo_rows = [{id_field: r['yonghuid']} for r in wuxiao_rows]
|
||||
nick_map, avatar_map = self._load_user_display(pseudo_rows, shenfen)
|
||||
yonghuids = [r['yonghuid'] for r in wuxiao_rows]
|
||||
stats_map = enrich_rank_stats(shenfen, yonghuids, riqi, '')
|
||||
result_list = []
|
||||
for idx, row in enumerate(wuxiao_rows):
|
||||
yonghuid = row['yonghuid']
|
||||
metric = int(row.get('metric') or 0)
|
||||
item = {
|
||||
'mingci': idx + 1,
|
||||
'yonghuid': yonghuid,
|
||||
'nicheng': nick_map.get(yonghuid, '用户'),
|
||||
'touxiang': avatar_map.get(yonghuid, '') or '',
|
||||
wuxiao_key: metric,
|
||||
'sort_field': wuxiao_key,
|
||||
'metric_value': metric,
|
||||
'metric_label': '无效人数',
|
||||
}
|
||||
extra = stats_map.get(yonghuid) or {}
|
||||
for k, v in extra.items():
|
||||
item[k] = v
|
||||
result_list.append(item)
|
||||
return Response({
|
||||
'code': 200,
|
||||
'msg': '获取成功',
|
||||
'data': {
|
||||
'list': result_list,
|
||||
'sort_field': wuxiao_key,
|
||||
'sort_label': '无效人数',
|
||||
},
|
||||
})
|
||||
|
||||
if riqi == '总榜':
|
||||
rows = self._query_rows(shenfen, all_time=True)
|
||||
start_date, end_date = None, None
|
||||
|
||||
Reference in New Issue
Block a user