""" 星之界小程序专用排行榜 — 与 /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 参数同 phbhqsj:shenfen, 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