Files
Django/users/xzj_paihang_views.py

343 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
星之界小程序专用排行榜 — 与 /yonghu/phbhqsj 独立,旧接口零改动。
数据范围与 phbhqsj 相同:集团整体日统计,不按俱乐部拆分。
仅排序/展示规则不同:
- 管事/组长:按邀请人数排序;展示无效邀请、有效人数、收入金额
- 接单员:按成交金额排序
- 商家:按派单流水排序(非成交额)
"""
import logging
from datetime import date
from django.db.models import Sum
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from backend.models import (
LeaderDailyStats,
ManagerDailyStats,
MerchantDailyStats,
PlayerDailyStats,
)
from users.business_models import User
from users.models import UserDashou, UserGuanshi, UserShangjia, UserBoss, UserZuzhang
from users.paihang_views import (
MAX_RANK,
RIQI_OPTIONS,
SHENFEN_OPTIONS,
_fmt_value,
_resolve_date_range,
)
logger = logging.getLogger(__name__)
XZJ_ROLE_CONFIG = {
'dashou': {
'model': PlayerDailyStats,
'id_field': 'PlayerID',
'sort_field': 'CompletedAmount',
'fields': (
'AcceptedOrderTotal', 'CompletedOrderTotal',
'AcceptedAmount', 'CompletedAmount',
),
'int_fields': frozenset({'AcceptedOrderTotal', 'CompletedOrderTotal'}),
'response_field_map': {
'AcceptedOrderTotal': 'jiedan_zongliang',
'CompletedOrderTotal': 'chengjiao_zongliang',
'AcceptedAmount': 'jiedan_zonge',
'CompletedAmount': 'chengjiao_zonge',
},
'sort_response_key': 'chengjiao_zonge',
},
'guanshi': {
'model': ManagerDailyStats,
'id_field': 'ManagerID',
'sort_field': 'RechargedPlayerCount',
'fields': ('InvitedPlayerCount', 'RechargedPlayerCount', 'TotalIncome'),
'int_fields': frozenset({'InvitedPlayerCount', 'RechargedPlayerCount'}),
'response_field_map': {
'InvitedPlayerCount': 'yaoqing_dashou_shu',
'RechargedPlayerCount': 'chongzhi_dashou_shu',
'TotalIncome': 'shouru_zonge',
},
'sort_response_key': 'chongzhi_dashou_shu',
'wuxiao_field': 'wuxiao_yaoqing_dashou_shu',
'youxiao_field': 'chongzhi_dashou_shu',
},
'zuzhang': {
'model': LeaderDailyStats,
'id_field': 'LeaderID',
'sort_field': 'TotalIncome',
'fields': ('InvitedManagerCount', 'CommissionAmount', 'TotalIncome'),
'int_fields': frozenset({'InvitedManagerCount'}),
'response_field_map': {
'InvitedManagerCount': 'yaoqing_guanshi_shu',
'CommissionAmount': 'fenyong_jine',
'TotalIncome': 'shouru_zonge',
},
'sort_response_key': 'shouru_zonge',
'wuxiao_field': 'wuxiao_yaoqing_guanshi_shu',
'youxiao_field': 'youxiao_guanshi_shu',
},
'shangjia': {
'model': MerchantDailyStats,
'id_field': 'MerchantID',
'sort_field': 'AssignedAmount',
'fields': ('AssignedOrderCount', 'AssignedAmount'),
'int_fields': frozenset({'AssignedOrderCount'}),
'response_field_map': {
'AssignedOrderCount': 'paifa_dingdan_shu',
'AssignedAmount': 'paifa_jine',
},
'sort_response_key': 'paifa_jine',
},
}
XZJ_TODAY_PROFILE = {
'dashou': {
'profile_model': UserDashou,
'profile_sort_field': 'jinrishouyi',
'field_map': {
'AcceptedOrderTotal': 'jinrijiedan',
'CompletedOrderTotal': 'jinrijiedan',
'AcceptedAmount': 'jinrishouyi',
'CompletedAmount': 'jinrishouyi',
},
},
'guanshi': {
'profile_model': UserGuanshi,
'profile_sort_field': 'jinrichongzhi',
'field_map': {
'InvitedPlayerCount': 'yaogingshuliang',
'RechargedPlayerCount': 'jinrichongzhi',
'TotalIncome': 'chongzhifenrun',
},
},
'zuzhang': {
'profile_model': UserZuzhang,
'profile_sort_field': 'fenyong_zonge',
'field_map': {
'InvitedManagerCount': 'yaoqing_zongshu',
'CommissionAmount': 'jinri_fenyong',
'TotalIncome': 'fenyong_zonge',
},
},
'shangjia': {
'profile_model': UserShangjia,
'profile_sort_field': 'jinriliushui',
'field_map': {
'AssignedOrderCount': 'jinridingdan',
'AssignedAmount': 'jinriliushui',
},
},
}
# UserZuzhang 用于今日组长 profile 回退
from rank.display_utils import (
apply_guanshi_wuxiao_fields,
apply_zuzhang_wuxiao_fields,
load_rank_nicknames,
)
class XzjPhbHqsjView(APIView):
"""
星之界排行榜
POST /yonghu/xzjphbhqsj
参数同 phbhqsjshenfen, riqi
"""
permission_classes = [IsAuthenticated]
def post(self, request):
shenfen = (request.data.get('shenfen') or '').strip()
riqi = (request.data.get('riqi') or '').strip()
if shenfen not in SHENFEN_OPTIONS:
return Response({
'code': 400,
'msg': '参数错误shenfen 必须为 dashou/guanshi/zuzhang/shangjia',
'data': {'list': []},
})
if riqi not in RIQI_OPTIONS:
return Response({
'code': 400,
'msg': '参数错误riqi 必须为 今日/本周/本月/总榜/昨日/上周/上月',
'data': {'list': []},
})
date_range = None
if riqi != '总榜':
date_range = _resolve_date_range(riqi)
if date_range is None:
return Response({
'code': 400,
'msg': '参数错误:无效的日期范围',
'data': {'list': []},
})
try:
if riqi == '总榜' and shenfen in ('guanshi', 'zuzhang'):
from rank.reward_services import (
TOTAL_RANK_INVITE_SORT_FIELDS,
TOTAL_RANK_INVITE_SORT_LABEL,
enrich_rank_stats,
query_total_rank_invite_rows,
)
rank_rows = query_total_rank_invite_rows(shenfen, club_id=None, limit=MAX_RANK)
cfg = XZJ_ROLE_CONFIG[shenfen]
id_field = cfg['id_field']
sort_key = TOTAL_RANK_INVITE_SORT_FIELDS[shenfen]
pseudo_rows = [{id_field: r['yonghuid']} for r in rank_rows]
nick_map, avatar_map = self._load_user_display(pseudo_rows, shenfen)
yonghuids = [r['yonghuid'] for r in rank_rows]
stats_map = enrich_rank_stats(shenfen, yonghuids, riqi, '')
result_list = []
for idx, row in enumerate(rank_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 '',
sort_key: metric,
'sort_field': sort_key,
'metric_value': metric,
'metric_label': TOTAL_RANK_INVITE_SORT_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': sort_key,
'sort_label': TOTAL_RANK_INVITE_SORT_LABEL,
},
})
if riqi == '总榜':
rows = self._query_rows(shenfen, all_time=True)
start_date, end_date = None, None
else:
start_date, end_date = date_range
rows = self._query_rows(shenfen, start_date, end_date)
nick_map, avatar_map = self._load_user_display(rows, shenfen)
cfg = XZJ_ROLE_CONFIG[shenfen]
int_fields = cfg['int_fields']
id_field = cfg['id_field']
response_field_map = cfg.get('response_field_map', {})
result_list = []
for idx, row in enumerate(rows):
yonghuid = row[id_field]
item = {
'mingci': idx + 1,
'yonghuid': yonghuid,
'nicheng': nick_map.get(yonghuid, '用户'),
'touxiang': avatar_map.get(yonghuid, '') or '',
}
for field in cfg['fields']:
response_key = response_field_map.get(field, field)
item[response_key] = _fmt_value(row.get(field), field in int_fields)
if shenfen == 'guanshi':
apply_guanshi_wuxiao_fields(item)
elif shenfen == 'zuzhang':
apply_zuzhang_wuxiao_fields(item, yonghuid, start_date, end_date)
result_list.append(item)
return Response({
'code': 200,
'msg': '获取成功',
'data': {
'list': result_list,
'sort_field': cfg.get('sort_response_key'),
},
})
except Exception as e:
logger.error('星之界排行榜失败 shenfen=%s riqi=%s: %s', shenfen, riqi, e, exc_info=True)
return Response({
'code': 500,
'msg': '服务器内部错误',
'data': {'list': []},
})
def _query_rows(self, shenfen, start_date=None, end_date=None, all_time=False):
cfg = XZJ_ROLE_CONFIG[shenfen]
model = cfg['model']
sort_field = cfg['sort_field']
fields = cfg['fields']
id_field = cfg['id_field']
annotate_kwargs = {f: Sum(f) for f in fields}
if all_time:
return list(
model.objects.values(id_field)
.annotate(**annotate_kwargs)
.order_by(f'-{sort_field}')[:MAX_RANK]
)
if start_date == end_date:
qs = model.objects.filter(Date=start_date).order_by(f'-{sort_field}')[:MAX_RANK]
rows = [
{id_field: getattr(r, id_field), **{f: getattr(r, f) for f in fields}}
for r in qs
]
if rows:
return rows
if start_date == date.today():
return self._query_today_profiles(shenfen)
return rows
return list(
model.objects.filter(Date__gte=start_date, Date__lte=end_date)
.values(id_field)
.annotate(**annotate_kwargs)
.order_by(f'-{sort_field}')[:MAX_RANK]
)
def _query_today_profiles(self, shenfen):
cfg = XZJ_ROLE_CONFIG[shenfen]
today_cfg = XZJ_TODAY_PROFILE[shenfen]
profile_model = today_cfg['profile_model']
profile_sort = today_cfg['profile_sort_field']
field_map = today_cfg['field_map']
id_field = cfg['id_field']
fields = cfg['fields']
qs = profile_model.objects.filter(
**{f'{profile_sort}__gt': 0}
).select_related('user').order_by(f'-{profile_sort}')[:MAX_RANK]
rows = []
for p in qs:
uid = p.user.UserUID
row = {id_field: uid}
for f in fields:
profile_field = field_map.get(f)
row[f] = getattr(p, profile_field, 0) if profile_field else 0
rows.append(row)
return rows
def _load_user_display(self, rows, shenfen):
id_field = XZJ_ROLE_CONFIG[shenfen]['id_field']
yonghuids = [r[id_field] for r in rows if r.get(id_field)]
if not yonghuids:
return {}, {}
users = User.objects.filter(UserUID__in=yonghuids)
avatar_map = {u.UserUID: u.Avatar or '' for u in users}
nick_map = load_rank_nicknames(shenfen, yonghuids)
return nick_map, avatar_map