diff --git a/rank/display_utils.py b/rank/display_utils.py new file mode 100644 index 0000000..2a8c741 --- /dev/null +++ b/rank/display_utils.py @@ -0,0 +1,124 @@ +"""排行榜展示名与星之界扩展统计字段。""" +from __future__ import annotations + +from django.db.models import Sum + +from backend.models import ManagerDailyStats +from users.business_models import User +from users.models import UserBoss, UserDashou, UserGuanshi, UserShangjia + + +def is_openid_like(value: str) -> bool: + if not value or not isinstance(value, str): + return False + s = value.strip() + # 微信 openid 常见形态 + return s.startswith('o') and len(s) >= 20 + + +def resolve_rank_nickname(shenfen: str, user: User, profile_nick: str = '') -> str: + """展示名:优先业务昵称,禁止把 openid 当昵称。""" + nick = (profile_nick or '').strip() + if nick and not is_openid_like(nick): + return nick + phone = (user.Phone or '').strip() + if phone: + return phone + uname = (user.UserName or '').strip() + if uname and not is_openid_like(uname): + return uname + defaults = { + 'dashou': '打手', + 'guanshi': '管事', + 'zuzhang': '组长', + 'shangjia': '商家', + } + return defaults.get(shenfen, '用户') + + +def load_rank_nicknames(shenfen: str, yonghuids: list) -> dict: + if not yonghuids: + return {} + users = {u.UserUID: u for u in User.objects.filter(UserUID__in=yonghuids)} + nick_map = {} + + if shenfen == 'dashou': + for p in UserDashou.objects.filter(user__UserUID__in=yonghuids).select_related('user'): + u = p.user + nick_map[u.UserUID] = resolve_rank_nickname('dashou', u, p.nicheng) + elif shenfen == 'shangjia': + for p in UserShangjia.objects.filter(user__UserUID__in=yonghuids).select_related('user'): + u = p.user + nick_map[u.UserUID] = resolve_rank_nickname('shangjia', u, p.nicheng) + elif shenfen in ('guanshi', 'zuzhang'): + boss_map = {} + for b in UserBoss.objects.filter(user__UserUID__in=yonghuids).select_related('user'): + boss_map[b.user.UserUID] = b.nickname + for uid in yonghuids: + u = users.get(uid) + if not u: + nick_map[uid] = '用户' + continue + nick_map[uid] = resolve_rank_nickname(shenfen, u, boss_map.get(uid, '')) + else: + for uid in yonghuids: + u = users.get(uid) + nick_map[uid] = resolve_rank_nickname(shenfen, u) if u else '用户' + + for uid in yonghuids: + nick_map.setdefault(uid, '用户') + return nick_map + + +def apply_guanshi_wuxiao_fields(stats: dict) -> dict: + invited = int(stats.get('yaoqing_dashou_shu') or 0) + valid = int(stats.get('chongzhi_dashou_shu') or 0) + stats['wuxiao_yaoqing_dashou_shu'] = max(0, invited - valid) + return stats + + +def count_youxiao_guanshi_for_zuzhang(zuzhang_uid, start_date=None, end_date=None) -> int: + """组长旗下有充值打手记录的管事人数。""" + 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_fields(stats: dict, zuzhang_uid, start_date=None, end_date=None) -> dict: + invited = int(stats.get('yaoqing_guanshi_shu') or 0) + youxiao = count_youxiao_guanshi_for_zuzhang(zuzhang_uid, start_date, end_date) + stats['youxiao_guanshi_shu'] = youxiao + stats['wuxiao_yaoqing_guanshi_shu'] = max(0, invited - youxiao) + return stats + + +def enrich_extra_rank_fields(shenfen: str, stats_map: dict, riqi: str = '') -> dict: + """为管事/组长补充无效/有效邀请等展示字段。""" + from rank.reward_services import _resolve_date_range_for_display + + start_date, end_date = None, None + if riqi and riqi != '总榜': + dr = _resolve_date_range_for_display(riqi) + if dr: + start_date, end_date = dr + + for uid, stats in stats_map.items(): + if not stats: + stats_map[uid] = stats = {} + if shenfen == 'guanshi': + apply_guanshi_wuxiao_fields(stats) + elif shenfen == 'zuzhang': + apply_zuzhang_wuxiao_fields(stats, uid, start_date, end_date) + return stats_map diff --git a/rank/reward_services.py b/rank/reward_services.py index f026644..c081237 100644 --- a/rank/reward_services.py +++ b/rank/reward_services.py @@ -47,7 +47,7 @@ OPEN_RIQI_PERIOD_MAP = { DEFAULT_SORT_FIELD = { 'dashou': 'chengjiao_zongliang', 'guanshi': 'chongzhi_dashou_shu', - 'zuzhang': 'yaoqing_guanshi_shu', + 'zuzhang': 'shouru_zonge', 'shangjia': 'jiesuan_jine', } @@ -82,7 +82,7 @@ SORT_FIELD_LABELS = { 'chengjiao_zonge': '成交总额', 'jiedan_zongliang': '接单量', 'jiedan_zonge': '接单总额', - 'chongzhi_dashou_shu': '有效打手数', + 'chongzhi_dashou_shu': '有效人数', 'shouru_zonge': '收益总额', 'yaoqing_dashou_shu': '邀请打手数', 'yaoqing_guanshi_shu': '邀请管事数', @@ -388,14 +388,14 @@ def _enrich_today_profiles(shenfen: str, yonghuids: list) -> dict: 'CompletedAmount': 'jinrishouyi', }), 'guanshi': (UserGuanshi, { - 'InvitedPlayerCount': None, + 'InvitedPlayerCount': 'yaogingshuliang', 'RechargedPlayerCount': 'jinrichongzhi', - 'TotalIncome': None, + 'TotalIncome': 'chongzhifenrun', }), 'zuzhang': (UserZuzhang, { - 'InvitedManagerCount': None, + 'InvitedManagerCount': 'yaoqing_zongshu', 'CommissionAmount': 'jinri_fenyong', - 'TotalIncome': 'jinri_fenyong', + 'TotalIncome': 'fenyong_zonge', }), 'shangjia': (UserShangjia, { 'AssignedOrderCount': 'jinridingdan', @@ -443,6 +443,11 @@ def enrich_rank_stats(shenfen: str, yonghuids: list, riqi: str, club_id: str = ' result = {uid: {} for uid in yonghuids} + def _finish(): + from rank.display_utils import enrich_extra_rank_fields + enrich_extra_rank_fields(shenfen, result, riqi) + return result + def _fill_from_row(uid, row_data): for f in fields: key = response_field_map.get(f, f) @@ -458,11 +463,11 @@ def enrich_rank_stats(shenfen: str, yonghuids: list, riqi: str, club_id: str = ' uid = r[id_field] if uid in result: _fill_from_row(uid, r) - return result + return _finish() date_range = _resolve_date_range_for_display(riqi) if not date_range: - return result + return _finish() start_date, end_date = date_range if start_date == end_date: @@ -474,10 +479,13 @@ def enrich_rank_stats(shenfen: str, yonghuids: list, riqi: str, club_id: str = ' continue row_data = {f: getattr(r, f) for f in fields} _fill_from_row(uid, row_data) - return result + return _finish() if riqi == '今日': - return _enrich_today_profiles(shenfen, yonghuids) - return result + today = _enrich_today_profiles(shenfen, yonghuids) + for uid, row in today.items(): + result[uid] = {**result.get(uid, {}), **row} + return _finish() + return _finish() rows = ( model.objects.filter(Date__gte=start_date, Date__lte=end_date, **base_filter) @@ -488,7 +496,7 @@ def enrich_rank_stats(shenfen: str, yonghuids: list, riqi: str, club_id: str = ' uid = r[id_field] if uid in result: _fill_from_row(uid, r) - return result + return _finish() def load_user_club_map(yonghuids: list) -> dict: diff --git a/rank/reward_views.py b/rank/reward_views.py index 6dbae61..2731d66 100644 --- a/rank/reward_views.py +++ b/rank/reward_views.py @@ -22,6 +22,7 @@ from rank.reward_services import ( ) from users.business_models import User from users.models import UserDashou, UserShangjia +from rank.display_utils import load_rank_nicknames logger = logging.getLogger(__name__) @@ -41,16 +42,8 @@ def _resolve_club_id(request, data_club_id=''): def _load_display_maps(yonghuids, shenfen): users = User.objects.filter(UserUID__in=yonghuids) - nick_map = {u.UserUID: (u.UserName or u.Phone or u.UserUID) for u in users} + nick_map = load_rank_nicknames(shenfen, yonghuids) avatar_map = {u.UserUID: u.Avatar or '' for u in users} - if shenfen == 'dashou': - for p in UserDashou.objects.filter(user__UserUID__in=yonghuids).select_related('user'): - if p.nicheng: - nick_map[p.user.UserUID] = p.nicheng - elif shenfen == 'shangjia': - for p in UserShangjia.objects.filter(user__UserUID__in=yonghuids).select_related('user'): - if p.nicheng: - nick_map[p.user.UserUID] = p.nicheng return nick_map, avatar_map diff --git a/users/paihang_views.py b/users/paihang_views.py index 63b2f20..d2902f6 100644 --- a/users/paihang_views.py +++ b/users/paihang_views.py @@ -19,6 +19,7 @@ from backend.models import ( ) from users.models import UserDashou, UserShangjia, UserGuanshi, UserZuzhang from users.business_models import User +from rank.display_utils import load_rank_nicknames logger = logging.getLogger(__name__) @@ -96,18 +97,18 @@ TODAY_PROFILE_CONFIG = { 'profile_model': UserGuanshi, 'profile_sort_field': 'jinrichongzhi', 'field_map': { - 'InvitedPlayerCount': None, + 'InvitedPlayerCount': 'yaogingshuliang', 'RechargedPlayerCount': 'jinrichongzhi', - 'TotalIncome': None, + 'TotalIncome': 'chongzhifenrun', }, }, 'zuzhang': { 'profile_model': UserZuzhang, - 'profile_sort_field': 'jinri_fenyong', + 'profile_sort_field': 'fenyong_zonge', 'field_map': { - 'InvitedManagerCount': None, + 'InvitedManagerCount': 'yaoqing_zongshu', 'CommissionAmount': 'jinri_fenyong', - 'TotalIncome': 'jinri_fenyong', + 'TotalIncome': 'fenyong_zonge', }, }, 'shangjia': { @@ -313,16 +314,5 @@ class PhbHqsjView(APIView): users = User.objects.filter(UserUID__in=yonghuids) avatar_map = {u.UserUID: u.Avatar or '' for u in users} - - nick_map = {u.UserUID: u.UserName or u.Phone or '用户' for u in users} - - if shenfen == 'dashou': - for p in UserDashou.objects.filter(user__UserUID__in=yonghuids).select_related('user'): - if p.nicheng: - nick_map[p.user.UserUID] = p.nicheng - elif shenfen == 'shangjia': - for p in UserShangjia.objects.filter(user__UserUID__in=yonghuids).select_related('user'): - if p.nicheng: - nick_map[p.user.UserUID] = p.nicheng - + nick_map = load_rank_nicknames(shenfen, yonghuids) return nick_map, avatar_map diff --git a/users/xzj_paihang_views.py b/users/xzj_paihang_views.py index c87d8ce..0686f51 100644 --- a/users/xzj_paihang_views.py +++ b/users/xzj_paihang_views.py @@ -55,7 +55,7 @@ XZJ_ROLE_CONFIG = { 'guanshi': { 'model': ManagerDailyStats, 'id_field': 'ManagerID', - 'sort_field': 'InvitedPlayerCount', + 'sort_field': 'RechargedPlayerCount', 'fields': ('InvitedPlayerCount', 'RechargedPlayerCount', 'TotalIncome'), 'int_fields': frozenset({'InvitedPlayerCount', 'RechargedPlayerCount'}), 'response_field_map': { @@ -63,14 +63,14 @@ XZJ_ROLE_CONFIG = { 'RechargedPlayerCount': 'chongzhi_dashou_shu', 'TotalIncome': 'shouru_zonge', }, - 'sort_response_key': 'yaoqing_dashou_shu', + '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': 'InvitedManagerCount', + 'sort_field': 'TotalIncome', 'fields': ('InvitedManagerCount', 'CommissionAmount', 'TotalIncome'), 'int_fields': frozenset({'InvitedManagerCount'}), 'response_field_map': { @@ -78,7 +78,7 @@ XZJ_ROLE_CONFIG = { 'CommissionAmount': 'fenyong_jine', 'TotalIncome': 'shouru_zonge', }, - 'sort_response_key': 'yaoqing_guanshi_shu', + 'sort_response_key': 'shouru_zonge', 'wuxiao_field': 'wuxiao_yaoqing_guanshi_shu', 'youxiao_field': 'youxiao_guanshi_shu', }, @@ -109,7 +109,7 @@ XZJ_TODAY_PROFILE = { }, 'guanshi': { 'profile_model': UserGuanshi, - 'profile_sort_field': 'yaogingshuliang', + 'profile_sort_field': 'jinrichongzhi', 'field_map': { 'InvitedPlayerCount': 'yaogingshuliang', 'RechargedPlayerCount': 'jinrichongzhi', @@ -118,11 +118,11 @@ XZJ_TODAY_PROFILE = { }, 'zuzhang': { 'profile_model': UserZuzhang, - 'profile_sort_field': 'yaoqing_zongshu', + 'profile_sort_field': 'fenyong_zonge', 'field_map': { 'InvitedManagerCount': 'yaoqing_zongshu', 'CommissionAmount': 'jinri_fenyong', - 'TotalIncome': 'jinri_fenyong', + 'TotalIncome': 'fenyong_zonge', }, }, 'shangjia': { @@ -138,40 +138,11 @@ XZJ_TODAY_PROFILE = { # 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 - +from rank.display_utils import ( + apply_guanshi_wuxiao_fields, + apply_zuzhang_wuxiao_fields, + load_rank_nicknames, +) class XzjPhbHqsjView(APIView): """ @@ -237,9 +208,9 @@ class XzjPhbHqsjView(APIView): item[response_key] = _fmt_value(row.get(field), field in int_fields) if shenfen == 'guanshi': - _apply_guanshi_wuxiao(item) + apply_guanshi_wuxiao_fields(item) elif shenfen == 'zuzhang': - _apply_zuzhang_wuxiao(item, start_date, end_date) + apply_zuzhang_wuxiao_fields(item, yonghuid, start_date, end_date) result_list.append(item) @@ -324,16 +295,5 @@ class XzjPhbHqsjView(APIView): 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 '用户' - + nick_map = load_rank_nicknames(shenfen, yonghuids) return nick_map, avatar_map