feat: 星之界排行榜新接口 xzjphbhqsj
管事/组长按邀请人数排序;商家按派单流水;旧 phbhqsj 不变
This commit is contained in:
@@ -19,6 +19,7 @@ from .views import WechatMiniProgramLoginView, UserInfoUpdateView, DashouXinxiAP
|
|||||||
FaKuanShenSuView, FaKuanPayView,FaKuanHuitiaoView, FaKuanPayPollView, FaKuanPayFailView,KaohePayView,KaohePayCallbackView, KaohePayPollView,KaohePayFailView, TixianAssetView, GuanshiContactView,TixianQueRenAutoView, TixianShenqingV3View, TixianCallbackV3View
|
FaKuanShenSuView, FaKuanPayView,FaKuanHuitiaoView, FaKuanPayPollView, FaKuanPayFailView,KaohePayView,KaohePayCallbackView, KaohePayPollView,KaohePayFailView, TixianAssetView, GuanshiContactView,TixianQueRenAutoView, TixianShenqingV3View, TixianCallbackV3View
|
||||||
from .tixian_shenhe_views import TixianZddkshApplyView
|
from .tixian_shenhe_views import TixianZddkshApplyView
|
||||||
from .paihang_views import PhbHqsjView
|
from .paihang_views import PhbHqsjView
|
||||||
|
from .xzj_paihang_views import XzjPhbHqsjView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@@ -157,5 +158,6 @@ urlpatterns = [
|
|||||||
path('tixianqr', TixianQueRenAutoView.as_view(), name='自动提现确认'),
|
path('tixianqr', TixianQueRenAutoView.as_view(), name='自动提现确认'),
|
||||||
path('tixiancallback', csrf_exempt(TixianCallbackV3View.as_view()), name='提现回调'),
|
path('tixiancallback', csrf_exempt(TixianCallbackV3View.as_view()), name='提现回调'),
|
||||||
path('phbhqsj', PhbHqsjView.as_view(), name='排行榜数据'),
|
path('phbhqsj', PhbHqsjView.as_view(), name='排行榜数据'),
|
||||||
|
path('xzjphbhqsj', XzjPhbHqsjView.as_view(), name='星之界排行榜数据'),
|
||||||
path('merchant-staff/', include('merchant_ops.urls')),
|
path('merchant-staff/', include('merchant_ops.urls')),
|
||||||
]
|
]
|
||||||
344
users/xzj_paihang_views.py
Normal file
344
users/xzj_paihang_views.py
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
"""
|
||||||
|
星之界(xzj) 排行榜 — 与 /yonghu/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_CLUB_ID = 'xzj'
|
||||||
|
|
||||||
|
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(
|
||||||
|
club_id=XZJ_CLUB_ID,
|
||||||
|
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
|
||||||
|
参数同 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 == '总榜':
|
||||||
|
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,
|
||||||
|
'club_id': XZJ_CLUB_ID,
|
||||||
|
'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}
|
||||||
|
base_qs = model.objects.filter(club_id=XZJ_CLUB_ID)
|
||||||
|
|
||||||
|
if all_time:
|
||||||
|
return list(
|
||||||
|
base_qs.values(id_field)
|
||||||
|
.annotate(**annotate_kwargs)
|
||||||
|
.order_by(f'-{sort_field}')[:MAX_RANK]
|
||||||
|
)
|
||||||
|
|
||||||
|
if start_date == end_date:
|
||||||
|
qs = base_qs.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(
|
||||||
|
base_qs.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
|
||||||
Reference in New Issue
Block a user