Files
Django/rank/reward_houtai_views.py
T
TermiNexus f1c8633345 perf: P1 性能优化批量修复 + kefu 视图拆分
P1 性能优化(避免循环内 N+1 / N 次 DB 查询):
- shop_order_views._batch_images: 移除 ThreadPoolExecutor 掩盖的 N 次查询,改单次批量 + Python 侧分组
- product_query.DashouHuiyuanList: 5N 查询 -> 3 次批量预取 + 本地闭包判断
- roles.GetRolePermissionView / 用户列表: 循环内 RolePermission/Permission/UserRole/Role -> 批量 __in 预取
- guanli / zuzhang 杜次分红 4.2/4.3: 循环内 update_or_create + 单条 delete -> bulk_create + bulk_update + 批量 delete
- admin_config QQ 群配置: 循环内 exists/first/save/create -> 批量预取 + bulk_create + bulk_update
- jituan 服务层多处合并统计查询、批量预取 map
- rank 多处循环 N+1 改批量预取
- backend 多处循环内 count/create 改批量
- config/orders/merchant_ops 多处循环 N+1 改批量预取

其他改动:
- users/views/kefu.py 拆分为 kefu_base/kefu_dashou/kefu_orders/kefu_punishment/kefu_withdraw 5 个文件
- 删除遗留脚本 check_prod_uid.py / create_rbac_tables.sql
2026-07-05 23:17:38 +08:00

207 lines
7.9 KiB
Python

"""排行榜奖励 — 后台 kefu API。"""
import logging
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from backend.utils import verify_kefu_permission
from jituan.services.club_context import resolve_club_id_from_request
from jituan.services.display_config import forbid_display_write_in_all_scope
from rank.reward_models import RankRewardClaim, RankRewardScheme, RankRewardTier
from rank.reward_services import SORT_FIELD_REGISTRY, sort_field_options_for_shenfen
logger = logging.getLogger(__name__)
REWARD_PERMS = ('phbj666', '8080a')
def _parse_date(value):
if not value:
return None
from datetime import date
if isinstance(value, date):
return value
text = str(value).strip()[:10]
if not text:
return None
try:
return date.fromisoformat(text)
except ValueError:
return None
def _has_perm(permissions):
return any(p in permissions for p in REWARD_PERMS)
def _serialize_scheme(scheme, tiers=None):
if tiers is None:
tiers = RankRewardTier.query.filter(scheme_id=scheme.id).order_by('rank_from')
return {
'id': scheme.id,
'club_id': scheme.club_id,
'shenfen': scheme.shenfen,
'period_type': scheme.period_type,
'sort_field': scheme.sort_field,
'sort_options': sort_field_options_for_shenfen(scheme.shenfen),
'title': scheme.title,
'description': scheme.description,
'enabled': scheme.enabled,
'claim_days': scheme.claim_days,
'min_metric': float(scheme.min_metric or 0),
'activity_start': scheme.activity_start.isoformat() if scheme.activity_start else '',
'activity_end': scheme.activity_end.isoformat() if scheme.activity_end else '',
'tiers': [
{
'id': t.id,
'rank_from': t.rank_from,
'rank_to': t.rank_to,
'reward_amount': float(t.reward_amount),
'label': t.label,
}
for t in tiers
],
}
class RankRewardHoutaiListView(APIView):
"""POST /houtai/phbjhq"""
permission_classes = [IsAuthenticated]
def post(self, request):
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败'})
if not _has_perm(permissions):
return Response({'code': 403, 'msg': '无排行榜奖励配置权限'})
club_id = resolve_club_id_from_request(request)
schemes = RankRewardScheme.query.filter(club_id=club_id).order_by('shenfen', 'period_type')
# 批量预取所有 tiers,避免列表推导内 N+1
schemes_list = list(schemes)
_scheme_ids = [s.id for s in schemes_list]
_tiers_map = {}
if _scheme_ids:
for t in RankRewardTier.query.filter(scheme_id__in=_scheme_ids).order_by('scheme_id', 'rank_from'):
_tiers_map.setdefault(t.scheme_id, []).append(t)
return Response({
'code': 0,
'data': {
'club_id': club_id,
'schemes': [_serialize_scheme(s, _tiers_map.get(s.id, [])) for s in schemes_list],
'shenfen_options': list(SORT_FIELD_REGISTRY.keys()),
'period_options': [
{'key': RankRewardScheme.PERIOD_DAY, 'label': '日榜(昨日)'},
{'key': RankRewardScheme.PERIOD_WEEK, 'label': '周榜(上周)'},
{'key': RankRewardScheme.PERIOD_MONTH, 'label': '月榜(上月)'},
],
},
})
class RankRewardHoutaiSaveView(APIView):
"""POST /houtai/phbjbc"""
permission_classes = [IsAuthenticated]
def post(self, request):
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败'})
if not _has_perm(permissions):
return Response({'code': 403, 'msg': '无权限'})
deny = forbid_display_write_in_all_scope(request)
if deny:
return deny
club_id = resolve_club_id_from_request(request)
data = request.data
shenfen = (data.get('shenfen') or '').strip()
period_type = (data.get('period_type') or '').strip()
sort_field = (data.get('sort_field') or '').strip()
if shenfen not in SORT_FIELD_REGISTRY:
return Response({'code': 400, 'msg': '身份无效'})
if period_type not in (RankRewardScheme.PERIOD_DAY, RankRewardScheme.PERIOD_WEEK, RankRewardScheme.PERIOD_MONTH):
return Response({'code': 400, 'msg': '周期无效'})
if sort_field not in SORT_FIELD_REGISTRY[shenfen]:
return Response({'code': 400, 'msg': '排序指标无效'})
tiers = data.get('tiers') or []
if not tiers:
return Response({'code': 400, 'msg': '请配置至少一个奖励档位'})
activity_start = _parse_date(data.get('activity_start'))
activity_end = _parse_date(data.get('activity_end'))
if activity_start and activity_end and activity_start > activity_end:
return Response({'code': 400, 'msg': '活动开始日期不能晚于结束日期'})
scheme, _ = RankRewardScheme.objects.update_or_create(
club_id=club_id,
shenfen=shenfen,
period_type=period_type,
defaults={
'sort_field': sort_field,
'title': (data.get('title') or '').strip(),
'description': (data.get('description') or '').strip(),
'enabled': bool(data.get('enabled', False)),
'claim_days': int(data.get('claim_days') or 30),
'min_metric': data.get('min_metric') or 0,
'activity_start': activity_start,
'activity_end': activity_end,
},
)
RankRewardTier.query.filter(scheme_id=scheme.id).delete()
RankRewardTier.objects.bulk_create([
RankRewardTier(
scheme_id=scheme.id,
rank_from=int(t['rank_from']),
rank_to=int(t['rank_to']),
reward_amount=t['reward_amount'],
label=(t.get('label') or '').strip(),
)
for t in tiers
])
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_scheme(scheme)})
class RankRewardHoutaiClaimsView(APIView):
"""POST /houtai/phbjjl 领取明细"""
permission_classes = [IsAuthenticated]
def post(self, request):
username = request.data.get('username')
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return Response({'code': 403, 'msg': '身份验证失败'})
if not _has_perm(permissions):
return Response({'code': 403, 'msg': '无权限'})
club_id = resolve_club_id_from_request(request)
status = request.data.get('status')
qs = RankRewardClaim.query.filter(club_id=club_id).order_by('-CreateTime')[:100]
if status is not None:
qs = qs.filter(status=int(status))
status_map = {0: '待领取', 1: '已领取', 2: '已过期', 3: '已取消'}
items = [
{
'id': c.id,
'yonghuid': c.yonghuid,
'shenfen': c.shenfen,
'mingci': c.mingci,
'amount': float(c.reward_amount),
'status': c.status,
'status_text': status_map.get(c.status, ''),
'claimed_at': c.claimed_at.isoformat() if c.claimed_at else '',
'CreateTime': c.CreateTime.isoformat() if c.CreateTime else '',
}
for c in qs
]
return Response({'code': 0, 'data': {'list': items}})