"""排行榜奖励 — 后台 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}})