Files
Django/users/xzj_paihang_views.py
XingQue 550061435e fix: xzjphbhqsj 与 phbhqsj 同用集团整体数据
去掉 club_id 过滤,仅保留邀请人数/派单流水等排序差异
2026-06-26 03:04:45 +08:00

340 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': 'InvitedPlayerCount',
'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': 'yaoqing_dashou_shu',
'wuxiao_field': 'wuxiao_yaoqing_dashou_shu',
'youxiao_field': 'chongzhi_dashou_shu',
},
'zuzhang': {
'model': LeaderDailyStats,
'id_field': 'LeaderID',
'sort_field': 'InvitedManagerCount',
'fields': ('InvitedManagerCount', 'CommissionAmount', 'TotalIncome'),
'int_fields': frozenset({'InvitedManagerCount'}),
'response_field_map': {
'InvitedManagerCount': 'yaoqing_guanshi_shu',
'CommissionAmount': 'fenyong_jine',
'TotalIncome': 'shouru_zonge',
},
'sort_response_key': 'yaoqing_guanshi_shu',
'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': 'yaogingshuliang',
'field_map': {
'InvitedPlayerCount': 'yaogingshuliang',
'RechargedPlayerCount': 'jinrichongzhi',
'TotalIncome': 'chongzhifenrun',
},
},
'zuzhang': {
'profile_model': UserZuzhang,
'profile_sort_field': 'yaoqing_zongshu',
'field_map': {
'InvitedManagerCount': 'yaoqing_zongshu',
'CommissionAmount': 'jinri_fenyong',
'TotalIncome': 'jinri_fenyong',
},
},
'shangjia': {
'profile_model': UserShangjia,
'profile_sort_field': 'jinriliushui',
'field_map': {
'AssignedOrderCount': 'jinridingdan',
'AssignedAmount': 'jinriliushui',
},
},
}
# UserZuzhang 用于今日组长 profile 回退
def _apply_guanshi_wuxiao(item):
invited = int(item.get('yaoqing_dashou_shu') or 0)
valid = int(item.get('chongzhi_dashou_shu') or 0)
item['wuxiao_yaoqing_dashou_shu'] = max(0, invited - valid)
return item
def _count_youxiao_guanshi_for_zuzhang(zuzhang_uid, start_date=None, end_date=None):
"""组长旗下有充值打手记录的管事人数。"""
guanshi_uids = list(
UserGuanshi.objects.filter(yaoqingren=zuzhang_uid)
.values_list('user__UserUID', flat=True)
)
if not guanshi_uids:
return 0
qs = ManagerDailyStats.objects.filter(ManagerID__in=guanshi_uids)
if start_date and end_date:
qs = qs.filter(Date__gte=start_date, Date__lte=end_date)
qs = (
qs.values('ManagerID')
.annotate(total_rc=Sum('RechargedPlayerCount'))
.filter(total_rc__gt=0)
)
return qs.count()
def _apply_zuzhang_wuxiao(item, start_date=None, end_date=None):
zuzhang_uid = item.get('yonghuid')
invited = int(item.get('yaoqing_guanshi_shu') or 0)
youxiao = _count_youxiao_guanshi_for_zuzhang(zuzhang_uid, start_date, end_date)
item['youxiao_guanshi_shu'] = youxiao
item['wuxiao_yaoqing_guanshi_shu'] = max(0, invited - youxiao)
return item
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 == '总榜':
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(item)
elif shenfen == 'zuzhang':
_apply_zuzhang_wuxiao(item, 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 = {}
if shenfen == 'dashou':
for p in UserDashou.objects.filter(user__UserUID__in=yonghuids).select_related('user'):
nick_map[p.user.UserUID] = p.nicheng or '用户'
elif shenfen == 'shangjia':
for p in UserShangjia.objects.filter(user__UserUID__in=yonghuids).select_related('user'):
nick_map[p.user.UserUID] = p.nicheng or '用户'
else:
for b in UserBoss.objects.filter(user__UserUID__in=yonghuids).select_related('user'):
nick_map[b.user.UserUID] = b.nickname or '用户'
return nick_map, avatar_map