fix: 排行榜返回完整统计字段并跟随后台排序方案

This commit is contained in:
XingQue
2026-07-01 19:32:29 +08:00
parent 3394fef79b
commit 7f7859c2a4
2 changed files with 148 additions and 7 deletions

View File

@@ -109,14 +109,14 @@ 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]: def resolve_sort_field_for_display(club_id: str, shenfen: str, riqi: str) -> tuple[str, str]:
"""按后台奖励方案决定展示/排序字段(进行中与已结束周期均适用)。""" """按后台奖励方案决定展示/排序字段(方案存在即生效,与是否启用奖励无关)。"""
period_type = RIQI_TO_PERIOD.get(riqi) or OPEN_RIQI_PERIOD_MAP.get(riqi) period_type = RIQI_TO_PERIOD.get(riqi) or OPEN_RIQI_PERIOD_MAP.get(riqi)
sort_field = None sort_field = None
if period_type: if period_type:
scheme = RankRewardScheme.query.filter( scheme = RankRewardScheme.objects.filter(
club_id=club_id, shenfen=shenfen, period_type=period_type, club_id=club_id, shenfen=shenfen, period_type=period_type,
).first() ).first()
if scheme and scheme.enabled: if scheme:
reg = SORT_FIELD_REGISTRY.get(shenfen) or {} reg = SORT_FIELD_REGISTRY.get(shenfen) or {}
if scheme.sort_field in reg: if scheme.sort_field in reg:
sort_field = scheme.sort_field sort_field = scheme.sort_field
@@ -299,6 +299,137 @@ def _query_group_rank_rows(shenfen: str, sort_field: str, riqi: str, limit: int
] ]
def _fmt_stat_value(val, is_int: bool):
if val is None:
return 0 if is_int else 0.0
if is_int:
return int(val)
if isinstance(val, Decimal):
return float(val.quantize(Decimal('0.01')))
return float(val)
def _enrich_today_profiles(shenfen: str, yonghuids: list) -> dict:
"""今日日统计为空时,从 Profile 补全展示字段(与 phbhqsj 回退一致)。"""
from users.models import UserDashou, UserGuanshi, UserShangjia, UserZuzhang
from users.paihang_views import ROLE_CONFIG
cfg = ROLE_CONFIG[shenfen]
response_field_map = cfg['response_field_map']
int_fields = cfg['int_fields']
id_field = cfg['id_field']
profile_cfg = {
'dashou': (UserDashou, {
'AcceptedOrderTotal': 'jinrijiedan',
'CompletedOrderTotal': 'jinrijiedan',
'AcceptedAmount': 'jinrishouyi',
'CompletedAmount': 'jinrishouyi',
}),
'guanshi': (UserGuanshi, {
'InvitedPlayerCount': None,
'RechargedPlayerCount': 'jinrichongzhi',
'TotalIncome': None,
}),
'zuzhang': (UserZuzhang, {
'InvitedManagerCount': None,
'CommissionAmount': 'jinri_fenyong',
'TotalIncome': 'jinri_fenyong',
}),
'shangjia': (UserShangjia, {
'AssignedOrderCount': 'jinridingdan',
'AssignedAmount': 'jinriliushui',
'SettledOrderCount': None,
'SettledAmount': None,
}),
}
model_cls, field_map = profile_cfg.get(shenfen, (None, {}))
if not model_cls:
return {}
out = {}
qs = model_cls.objects.filter(user__UserUID__in=yonghuids).select_related('user')
for p in qs:
uid = p.user.UserUID
row = {}
for db_field, profile_field in field_map.items():
key = response_field_map.get(db_field, db_field)
if profile_field:
val = getattr(p, profile_field, 0) or 0
else:
val = 0
row[key] = _fmt_stat_value(val, db_field in int_fields)
out[uid] = row
return out
def enrich_rank_stats(shenfen: str, yonghuids: list, riqi: str, club_id: str = '') -> dict:
"""加载榜单行完整统计(排序指标 + 其它单量/金额),避免前端只看到一个数。"""
if not yonghuids:
return {}
from users.paihang_views import ROLE_CONFIG
cfg = ROLE_CONFIG[shenfen]
model = cfg['model']
id_field = cfg['id_field']
fields = cfg['fields']
int_fields = cfg['int_fields']
response_field_map = cfg['response_field_map']
base_filter = {f'{id_field}__in': yonghuids}
if club_id and hasattr(model, 'club_id'):
base_filter['club_id'] = club_id
result = {uid: {} for uid in yonghuids}
def _fill_from_row(uid, row_data):
for f in fields:
key = response_field_map.get(f, f)
result[uid][key] = _fmt_stat_value(row_data.get(f), f in int_fields)
if riqi == '总榜':
rows = (
model.objects.filter(**base_filter)
.values(id_field)
.annotate(**{f: Sum(f) for f in fields})
)
for r in rows:
uid = r[id_field]
if uid in result:
_fill_from_row(uid, r)
return result
date_range = _resolve_date_range_for_display(riqi)
if not date_range:
return result
start_date, end_date = date_range
if start_date == end_date:
qs = model.objects.filter(Date=start_date, **base_filter)
if qs.exists():
for r in qs:
uid = getattr(r, id_field)
if uid not in result:
continue
row_data = {f: getattr(r, f) for f in fields}
_fill_from_row(uid, row_data)
return result
if riqi == '今日':
return _enrich_today_profiles(shenfen, yonghuids)
return result
rows = (
model.objects.filter(Date__gte=start_date, Date__lte=end_date, **base_filter)
.values(id_field)
.annotate(**{f: Sum(f) for f in fields})
)
for r in rows:
uid = r[id_field]
if uid in result:
_fill_from_row(uid, r)
return result
def load_user_club_map(yonghuids: list) -> dict: def load_user_club_map(yonghuids: list) -> dict:
if not yonghuids: if not yonghuids:
return {} return {}
@@ -327,7 +458,7 @@ def tier_amount_for_rank(tiers, mingci: int) -> Optional[Decimal]:
def build_tier_rewards_preview(scheme) -> dict: def build_tier_rewards_preview(scheme) -> dict:
"""按方案档位生成各名次预计奖金(进行中周期展示用)。""" """按方案档位生成各名次预计奖金(进行中周期展示用)。"""
if not scheme or not scheme.enabled: if not scheme:
return {} return {}
tiers = list(RankRewardTier.query.filter(scheme_id=scheme.id).order_by('rank_from')) tiers = list(RankRewardTier.query.filter(scheme_id=scheme.id).order_by('rank_from'))
if not tiers: if not tiers:

View File

@@ -13,6 +13,7 @@ from rank.reward_services import (
SORT_FIELD_LABELS, SORT_FIELD_LABELS,
build_reward_payload, build_reward_payload,
claim_reward, claim_reward,
enrich_rank_stats,
list_my_pending_claims, list_my_pending_claims,
query_club_rank_rows, query_club_rank_rows,
query_group_rank_rows, query_group_rank_rows,
@@ -53,9 +54,10 @@ def _load_display_maps(yonghuids, shenfen):
return nick_map, avatar_map return nick_map, avatar_map
def _format_rank_list(rows, shenfen, sort_field, sort_label): def _format_rank_list(rows, shenfen, sort_field, sort_label, riqi='', club_id=''):
yonghuids = [r['yonghuid'] for r in rows] yonghuids = [r['yonghuid'] for r in rows]
nick_map, avatar_map = _load_display_maps(yonghuids, shenfen) nick_map, avatar_map = _load_display_maps(yonghuids, shenfen)
stats_map = enrich_rank_stats(shenfen, yonghuids, riqi, club_id or '')
club_map = {} club_map = {}
try: try:
from rank.reward_services import load_user_club_map from rank.reward_services import load_user_club_map
@@ -86,6 +88,9 @@ def _format_rank_list(rows, shenfen, sort_field, sort_label):
'metric_label': sort_label, 'metric_label': sort_label,
'metric_is_int': is_int, 'metric_is_int': is_int,
} }
extra = stats_map.get(uid) or {}
for k, v in extra.items():
item[k] = v
item[sort_field] = item['metric_value'] item[sort_field] = item['metric_value']
result.append(item) result.append(item)
return result return result
@@ -180,16 +185,21 @@ class PhbJlPhbView(APIView):
if fanwei == 'club': if fanwei == 'club':
rows = query_club_rank_rows(club_id, shenfen, sort_field, riqi) rows = query_club_rank_rows(club_id, shenfen, sort_field, riqi)
enrich_club = club_id
else: else:
rows = query_group_rank_rows(shenfen, sort_field, riqi) rows = query_group_rank_rows(shenfen, sort_field, riqi)
enrich_club = ''
result_list = _format_rank_list(rows, shenfen, sort_field, sort_label) result_list = _format_rank_list(
rows, shenfen, sort_field, sort_label, riqi=riqi, club_id=enrich_club,
)
reward = build_reward_payload(club_id, shenfen, riqi, request.user.UserUID) reward = build_reward_payload(club_id, shenfen, riqi, request.user.UserUID)
# 仅俱乐部榜展示各行奖金(集团总榜名次与俱乐部分奖名次不同,不可混用) # 仅俱乐部榜展示各行奖金(集团总榜名次与俱乐部分奖名次不同,不可混用)
rank_rewards = reward.get('rank_rewards') or {} rank_rewards = reward.get('rank_rewards') or {}
tier_rewards = reward.get('tier_rewards') or {} tier_rewards = reward.get('tier_rewards') or {}
if fanwei == 'club' and reward.get('scheme_enabled'): tiers = reward.get('tiers') or []
if fanwei == 'club' and (reward.get('scheme_enabled') or tiers):
for item in result_list: for item in result_list:
mc = item['mingci'] mc = item['mingci']
settled = rank_rewards.get(mc) settled = rank_rewards.get(mc)