"""商家老板：邀请码、成员、角色、额度、日志、排行。"""
import logging
import secrets
import string
from datetime import timedelta
from decimal import Decimal

from django.db.models import Sum, Q
from django.utils import timezone
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from merchant_ops.constants import (
    INVITE_STATUS_REVOKED, INVITE_STATUS_UNUSED, INVITE_STATUS_USED,
    MEMBER_STATUS_ACTIVE, MEMBER_STATUS_DISABLED, MEMBER_STATUS_REMOVED,
)
from merchant_ops.models import (
    MerchantStaffInvite, MerchantStaffMember, MerchantStaffRole,
    MerchantStaffRolePermission, MerchantStaffMemberPermission,
    MerchantStaffWallet, MerchantStaffWalletLedger, MerchantStaffAuditLog,
    MerchantStaffDailyStats, MerchantStaffPermission,
)
from merchant_ops.services.authz import (
    StaffAuthError, ensure_owner_merchant, get_member_permissions,
    is_merchant_owner, resolve_staff_context,
    get_active_staff_member, member_has_perm as _member_has_perm,
)
from merchant_ops.services.order_enrich import staff_display_label, user_avatar_relative
from merchant_ops.services.audit import audit_owner
from merchant_ops.services.bootstrap import ensure_merchant_roles
from merchant_ops.services.wallet import allocate_quota, get_or_create_wallet, revoke_quota
from users.models import UserShangjia

logger = logging.getLogger(__name__)


def _err(e):
    if isinstance(e, StaffAuthError):
        return Response({'code': e.code, 'msg': e.msg, 'data': None})
    logger.exception(e)
    return Response({'code': 500, 'msg': '服务器错误', 'data': None})


def _role_prefix(role_code):
    m = {'CHIEF': 'ZG', 'FINANCE': 'CW', 'DISPATCHER': 'PD'}
    return m.get(role_code, 'KF')


def _role_hint(role_code, tpl_desc=None):
    hints = {
        'CHIEF': '总管：除老板专属外，订单/派单/客服/额度/罚单等均可操作',
        'FINANCE': '财务：查看余额与额度、划额度、看全部订单与统计，不可派单结单',
        'DISPATCHER': '派单员：派单发单、只看自己派的单，不可结单退款',
    }
    if role_code in hints:
        return hints[role_code]
    name = (tpl_desc or {}).get(role_code) or role_code
    return f'自定义角色：{name}'


def _resolve_manage_context(request, need_staff_manage=False):
    """商家老板，或具备 staff_manage 的总管等子客服。"""
    if is_merchant_owner(request.user):
        ensure_owner_merchant(request.user.UserUID, request.user)
        return request.user.UserUID, None
    member = get_active_staff_member(request.user)
    if not member:
        raise StaffAuthError(403, '无权限')
    if need_staff_manage and not _member_has_perm(member, 'staff_manage'):
        raise StaffAuthError(403, '无客服管理权限')
    ensure_merchant_roles(member.merchant_id)
    return member.merchant_id, member


def _resolve_wallet_operator(request, perm_code):
    """商家老板，或具备 wallet_allocate / wallet_revoke 的子客服（总管/财务）。"""
    if is_merchant_owner(request.user):
        ensure_owner_merchant(request.user.UserUID, request.user)
        return request.user.UserUID, None
    member = get_active_staff_member(request.user)
    if not member or not _member_has_perm(member, perm_code):
        raise StaffAuthError(403, f'无{perm_code}权限')
    ensure_merchant_roles(member.merchant_id)
    return member.merchant_id, member


def _assert_not_self_target(operator_member, target_member):
    """子客服不得对自己做管理类操作（划收额度、换角色、封禁等）。"""
    if operator_member and target_member.id == operator_member.id:
        raise StaffAuthError(400, '不能对本人执行此操作')


class StaffInviteCreateView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            ensure_owner_merchant(request.user.UserUID, request.user)
            role_id = request.data.get('role_id')
            if not role_id:
                return Response({'code': 400, 'msg': '请选择角色', 'data': None})
            role = MerchantStaffRole.query.get(id=role_id, merchant_id=request.user.UserUID)
            prefix = _role_prefix(role.role_code)
            code = f"{prefix}-{secrets.token_hex(3).upper()}"
            invite = MerchantStaffInvite.objects.create(
                merchant_id=request.user.UserUID,
                invite_code=code,
                role=role,
                status=INVITE_STATUS_UNUSED,
                created_by=request.user.UserUID,
                expire_at=timezone.now() + timedelta(days=30),
            )
            audit_owner(request.user.UserUID, request.user.UserUID, 'INVITE_CREATE', request,
                        resource_type='invite', resource_id=code)
            return Response({'code': 0, 'msg': '成功', 'data': {'invite_code': code, 'id': invite.id}})
        except Exception as e:
            return _err(e)


class StaffInviteListView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            ensure_owner_merchant(request.user.UserUID, request.user)
            status_filter = request.data.get('status')  # None=all, 0,1,2
            qs = MerchantStaffInvite.query.filter(merchant_id=request.user.UserUID).select_related('role')
            if status_filter is not None:
                qs = qs.filter(status=int(status_filter))
            invites = list(qs.order_by('-CreateTime')[:200])
            # 批量预取已使用邀请码对应的用户，避免循环内 N+1
            from users.business_models import User
            _used_uids = [inv.used_by for inv in invites if inv.used_by]
            _user_map = {
                u.UserUID: u
                for u in User.query.filter(UserUID__in=_used_uids).only('UserUID', 'Nickname')
            } if _used_uids else {}
            items = []
            for inv in invites:
                item = {
                    'id': inv.id,
                    'code': inv.invite_code,
                    'typeName': inv.role.role_name if inv.role else '',
                    'status': inv.status,
                    'adddate': inv.CreateTime.strftime('%Y-%m-%d') if inv.CreateTime else '',
                    'usedate': inv.used_at.strftime('%Y-%m-%d') if inv.used_at else '',
                    'user': None,
                }
                if inv.used_by:
                    u = _user_map.get(inv.used_by)
                    item['user'] = {'nickname': getattr(u, 'Nickname', '') or inv.used_by, 'uid': inv.used_by}
                items.append(item)
            return Response({'code': 0, 'msg': '成功', 'data': {'list': items, 'gudingCode': 'XQKF2026'}})
        except Exception as e:
            return _err(e)


class StaffInviteRevokeView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            ensure_owner_merchant(request.user.UserUID, request.user)
            invite_id = request.data.get('id')
            inv = MerchantStaffInvite.query.get(id=invite_id, merchant_id=request.user.UserUID, status=INVITE_STATUS_UNUSED)
            inv.status = INVITE_STATUS_REVOKED
            inv.save(update_fields=['status'])
            audit_owner(request.user.UserUID, request.user.UserUID, 'INVITE_REVOKE', request, resource_id=inv.invite_code)
            return Response({'code': 0, 'msg': '已作废', 'data': None})
        except MerchantStaffInvite.DoesNotExist:
            return Response({'code': 404, 'msg': '邀请码不存在或已使用', 'data': None})
        except Exception as e:
            return _err(e)


class StaffMemberListView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            from datetime import date
            from users.business_models import User

            mid, _ = _resolve_manage_context(request, need_staff_manage=True)
            today = date.today()
            members = MerchantStaffMember.query.filter(
                merchant_id=mid,
                status__in=[MEMBER_STATUS_ACTIVE, MEMBER_STATUS_DISABLED],
            ).select_related('role')
            members_list = list(members)
            _member_ids = [m.id for m in members_list]
            _staff_uids = [m.staff_user_id for m in members_list if m.staff_user_id]

            # 批量预取聚合统计，避免循环内 N+1
            _total_stats_map = {}
            _today_stat_map = {}
            if _member_ids:
                from django.db.models import Count
                for item in MerchantStaffDailyStats.query.filter(
                    member_id__in=_member_ids
                ).values('member_id').annotate(
                    dispatch_count=Sum('dispatch_count'),
                    dispatch_amount=Sum('dispatch_amount'),
                    settle_count=Sum('settle_count'),
                    settle_amount=Sum('settle_amount'),
                    refund_count=Sum('refund_count'),
                    refund_amount=Sum('refund_amount'),
                ):
                    _total_stats_map[item['member_id']] = item
                for s in MerchantStaffDailyStats.query.filter(
                    member_id__in=_member_ids, stat_date=today,
                ):
                    _today_stat_map[s.member_id] = s

            # 批量预取用户头像
            _user_map = {
                u.UserUID: u
                for u in User.query.filter(UserUID__in=_staff_uids).only('UserUID', 'Avatar')
            } if _staff_uids else {}

            # 批量预取钱包
            _wallet_map = {
                w.member_id: w
                for w in MerchantStaffWallet.objects.filter(member_id__in=_member_ids)
            } if _member_ids else {}

            result = []
            for m in members_list:
                total_stats = _total_stats_map.get(m.id, {})
                today_stat = _today_stat_map.get(m.id)
                wallet = _wallet_map.get(m.id)
                if wallet is None:
                    wallet = get_or_create_wallet(m)
                u = _user_map.get(m.staff_user_id)
                result.append({
                    'id': m.id,
                    'uid': m.staff_user_id,
                    'nickname': staff_display_label(m),
                    'merchant_remark': m.merchant_remark or '',
                    'display_name': m.display_name or '',
                    'avatar': user_avatar_relative(u),
                    'role_name': m.role.role_name if m.role else '',
                    'isChief': m.role.role_code == 'CHIEF' if m.role else False,
                    'isFinance': m.role.role_code == 'FINANCE' if m.role else False,
                    'status': m.status,
                    'isDisabled': m.status == MEMBER_STATUS_DISABLED,
                    'todayDispatchCount': today_stat.dispatch_count if today_stat else int(m.today_dispatch_count or 0),
                    'todayDispatchAmount': str(today_stat.dispatch_amount if today_stat else (m.today_dispatch_amount or '0.00')),
                    'dispatchCount': total_stats.get('dispatch_count') or 0,
                    'dispatchAmount': str(total_stats.get('dispatch_amount') or '0.00'),
                    'orderCount': total_stats.get('dispatch_count') or 0,
                    'settleCount': total_stats.get('settle_count') or 0,
                    'settleAmount': str(total_stats.get('settle_amount') or '0.00'),
                    'refundCount': total_stats.get('refund_count') or 0,
                    'refundAmount': str(total_stats.get('refund_amount') or '0.00'),
                    'quota_available': str(wallet.quota_available),
                    'quota_total': str(wallet.quota_total),
                    'quota_used': str(wallet.quota_used),
                    'isSelf': m.staff_user_id == request.user.UserUID,
                })
            return Response({'code': 0, 'msg': '成功', 'data': {'list': result}})
        except Exception as e:
            return _err(e)


class StaffMemberRemarkUpdateView(APIView):
    """商家老板/总管：设置客服备注"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid, operator = _resolve_manage_context(request, need_staff_manage=True)
            member_id = request.data.get('member_id')
            remark = str(request.data.get('merchant_remark', '')).strip()[:64]
            if not member_id:
                return Response({'code': 400, 'msg': '缺少 member_id', 'data': None})
            m = MerchantStaffMember.query.get(id=member_id, merchant_id=mid)
            m.merchant_remark = remark
            m.save(update_fields=['merchant_remark'])
            audit_owner(mid, request.user.UserUID, 'MEMBER_UPDATE_REMARK', request,
                        resource_type='member', resource_id=member_id,
                        value_after={'merchant_remark': remark})
            return Response({
                'code': 0,
                'msg': '备注已保存',
                'data': {'member_id': m.id, 'merchant_remark': remark, 'nickname': staff_display_label(m)},
            })
        except MerchantStaffMember.DoesNotExist:
            return Response({'code': 404, 'msg': '客服不存在', 'data': None})
        except Exception as e:
            return _err(e)


class StaffMemberUpdateRoleView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid, operator = _resolve_manage_context(request, need_staff_manage=True)
            member_id = request.data.get('member_id')
            role_id = request.data.get('role_id')
            m = MerchantStaffMember.query.get(
                id=member_id, merchant_id=mid, status=MEMBER_STATUS_ACTIVE,
            )
            _assert_not_self_target(operator, m)
            role = MerchantStaffRole.query.get(id=role_id, merchant_id=mid, status=1)
            old = m.role_id
            m.role = role
            m.save(update_fields=['role'])
            audit_owner(mid, request.user.UserUID, 'MEMBER_UPDATE_ROLE', request,
                        resource_type='member', resource_id=member_id,
                        value_before={'role_id': old}, value_after={'role_id': role_id})
            perms = list(role.role_permissions.values_list('perm_code', flat=True))
            return Response({'code': 0, 'msg': '角色已更新', 'data': {
                'member_id': m.id,
                'role_id': role.id,
                'role_code': role.role_code,
                'role_name': role.role_name,
                'permissions': perms,
            }})
        except Exception as e:
            return _err(e)


class StaffMemberRemoveView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            ensure_owner_merchant(request.user.UserUID, request.user)
            member_id = request.data.get('member_id')
            m = MerchantStaffMember.query.get(id=member_id, merchant_id=request.user.UserUID)
            m.status = MEMBER_STATUS_REMOVED
            m.revoked_at = timezone.now()
            m.save(update_fields=['status', 'revoked_at'])
            audit_owner(request.user.UserUID, request.user.UserUID, 'MEMBER_REMOVE', request,
                        resource_type='member', resource_id=member_id)
            return Response({'code': 0, 'msg': '已移除', 'data': None})
        except Exception as e:
            return _err(e)


class StaffMemberDisableView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid, operator = _resolve_manage_context(request, need_staff_manage=True)
            member_id = request.data.get('member_id')
            disable = request.data.get('disable', True)
            m = MerchantStaffMember.query.get(id=member_id, merchant_id=mid)
            _assert_not_self_target(operator, m)
            m.status = MEMBER_STATUS_DISABLED if disable else MEMBER_STATUS_ACTIVE
            m.save(update_fields=['status'])
            action = 'MEMBER_DISABLE' if disable else 'MEMBER_ENABLE'
            audit_owner(mid, request.user.UserUID, action, request, resource_id=member_id)
            return Response({'code': 0, 'msg': '成功', 'data': None})
        except Exception as e:
            return _err(e)


class StaffRoleListView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            from merchant_ops.constants import PERMISSION_DEFINITIONS, SYSTEM_ROLE_TEMPLATES
            from merchant_ops.services.bootstrap import seed_global_permissions
            seed_global_permissions()
            if is_merchant_owner(request.user):
                mid = request.user.UserUID
                ensure_owner_merchant(mid, request.user)
            else:
                mid, _ = _resolve_manage_context(request, need_staff_manage=True)
            roles = MerchantStaffRole.query.filter(merchant_id=mid, status=1)
            tpl_desc = {t['role_code']: t['role_name'] for t in SYSTEM_ROLE_TEMPLATES}
            data = []
            for r in roles:
                perms = list(r.role_permissions.values_list('perm_code', flat=True))
                perm_labels = [
                    dict(PERMISSION_DEFINITIONS).get(p, p) for p in perms
                ]
                data.append({
                    'id': r.id, 'role_code': r.role_code, 'role_name': r.role_name,
                    'is_system': r.is_system, 'permissions': perms,
                    'permission_labels': perm_labels,
                    'role_hint': _role_hint(r.role_code, tpl_desc),
                })
            all_perms = list(MerchantStaffPermission.query.filter(status=1).order_by('sort_order').values(
                'perm_code', 'perm_name'))
            if not all_perms:
                all_perms = [{'perm_code': c, 'perm_name': n} for c, n in PERMISSION_DEFINITIONS]
            return Response({'code': 0, 'msg': '成功', 'data': {'roles': data, 'all_permissions': all_perms}})
        except Exception as e:
            return _err(e)


class StaffRoleSetPermsView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            ensure_owner_merchant(request.user.UserUID, request.user)
            role_id = request.data.get('role_id')
            permissions = request.data.get('permissions', [])
            role = MerchantStaffRole.query.get(id=role_id, merchant_id=request.user.UserUID)
            MerchantStaffRolePermission.query.filter(role=role).delete()
            for p in permissions:
                MerchantStaffRolePermission.objects.create(role=role, perm_code=p)
            audit_owner(request.user.UserUID, request.user.UserUID, 'ROLE_SET_PERMS', request, resource_id=role_id)
            return Response({'code': 0, 'msg': '成功', 'data': None})
        except Exception as e:
            return _err(e)


class StaffRoleCreateView(APIView):
    """商家自定义角色（自由勾选权限）。"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid = request.user.UserUID
            ensure_owner_merchant(mid, request.user)
            ensure_merchant_roles(mid)
            role_name = (request.data.get('role_name') or '').strip()
            if not role_name:
                return Response({'code': 400, 'msg': '角色名称不能为空', 'data': None})
            role_code = (request.data.get('role_code') or '').strip()
            if not role_code:
                role_code = 'CUSTOM_' + secrets.token_hex(3).upper()
            if MerchantStaffRole.query.filter(merchant_id=mid, role_code=role_code).exists():
                return Response({'code': 400, 'msg': '角色码已存在', 'data': None})
            permissions = request.data.get('permissions', [])
            role = MerchantStaffRole.objects.create(
                club_id='xq',
                merchant_id=mid,
                role_code=role_code,
                role_name=role_name,
                is_system=False,
                sort_order=50,
                status=1,
            )
            valid = set(MerchantStaffPermission.query.filter(status=1).values_list('perm_code', flat=True))
            for p in permissions:
                if p in valid:
                    MerchantStaffRolePermission.objects.create(role=role, perm_code=p)
            audit_owner(mid, mid, 'ROLE_CREATE', request, resource_id=role.id)
            return Response({'code': 0, 'msg': '创建成功', 'data': {'id': role.id, 'role_code': role.role_code}})
        except Exception as e:
            return _err(e)


class StaffMemberUpdatePermsView(APIView):
    """成员级权限加减（覆盖角色默认权限）。"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid = request.user.UserUID
            ensure_owner_merchant(mid, request.user)
            member_id = request.data.get('member_id')
            overrides = request.data.get('overrides', [])
            m = MerchantStaffMember.query.get(id=member_id, merchant_id=mid, status=MEMBER_STATUS_ACTIVE)
            MerchantStaffMemberPermission.query.filter(member=m).delete()
            valid = set(MerchantStaffPermission.query.filter(status=1).values_list('perm_code', flat=True))
            for item in overrides:
                if isinstance(item, dict):
                    code = item.get('perm_code')
                    granted = bool(item.get('granted', True))
                else:
                    code = item
                    granted = True
                if code not in valid:
                    continue
                MerchantStaffMemberPermission.objects.create(member=m, perm_code=code, granted=granted)
            audit_owner(mid, mid, 'MEMBER_SET_PERMS', request, resource_id=member_id)
            return Response({'code': 0, 'msg': '成员权限已更新', 'data': {'permissions': list(get_member_permissions(m))}})
        except Exception as e:
            return _err(e)


class StaffWalletAllocateView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid, operator = _resolve_wallet_operator(request, 'wallet_allocate')
            member_id = request.data.get('member_id')
            amount = request.data.get('amount')
            m = MerchantStaffMember.query.get(
                id=member_id, merchant_id=mid, status=MEMBER_STATUS_ACTIVE,
            )
            _assert_not_self_target(operator, m)
            get_or_create_wallet(m)
            wallet = allocate_quota(m, amount, request.user.UserUID, remark='划额度')
            audit_owner(mid, request.user.UserUID, 'WALLET_ALLOCATE', request,
                        resource_type='wallet', resource_id=member_id, amount=amount)
            shangjia = UserShangjia.query.filter(user__UserUID=mid).first()
            return Response({'code': 0, 'msg': '划额度成功', 'data': {
                'quota_available': str(wallet.quota_available),
                'merchant_yue': str(shangjia.yue) if shangjia else '0',
            }})
        except ValueError as e:
            return Response({'code': 400, 'msg': str(e), 'data': None})
        except Exception as e:
            return _err(e)


class StaffWalletRevokeView(APIView):
    """收回客服未使用额度，退回商家余额。"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid, operator = _resolve_wallet_operator(request, 'wallet_revoke')
            member_id = request.data.get('member_id')
            amount = request.data.get('amount')
            remark = (request.data.get('remark') or '').strip() or '收回未使用额度'
            m = MerchantStaffMember.query.get(
                id=member_id,
                merchant_id=mid,
                status__in=[MEMBER_STATUS_ACTIVE, MEMBER_STATUS_DISABLED],
            )
            _assert_not_self_target(operator, m)
            wallet = revoke_quota(m, amount, request.user.UserUID, remark=remark)
            audit_owner(mid, request.user.UserUID, 'WALLET_REVOKE', request,
                        resource_type='wallet', resource_id=member_id, amount=amount)
            shangjia = UserShangjia.query.filter(user__UserUID=mid).first()
            return Response({'code': 0, 'msg': '收回成功', 'data': {
                'quota_available': str(wallet.quota_available),
                'merchant_yue': str(shangjia.yue) if shangjia else '0',
            }})
        except ValueError as e:
            return Response({'code': 400, 'msg': str(e), 'data': None})
        except Exception as e:
            return _err(e)


class StaffWalletLedgerView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            mid = request.user.UserUID
            if is_merchant_owner(request.user):
                member_id = request.data.get('member_id')
                qs = MerchantStaffWalletLedger.query.filter(merchant_id=mid)
                if member_id:
                    m = MerchantStaffMember.query.get(id=member_id, merchant_id=mid)
                    qs = qs.filter(staff_user_id=m.staff_user_id)
            else:
                ctx = resolve_staff_context(request.user)
                if not ctx:
                    return Response({'code': 403, 'msg': '无权限', 'data': None})
                qs = MerchantStaffWalletLedger.query.filter(staff_user_id=request.user.UserUID)
            items = [{
                'change_type': x.change_type,
                'amount': str(x.amount),
                'balance_after': str(x.balance_after),
                'related_order_id': x.related_order_id or '',
                'remark': x.remark,
                'time': x.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if x.CreateTime else '',
            } for x in qs.order_by('-CreateTime')[:100]]
            return Response({'code': 0, 'msg': '成功', 'data': {'list': items}})
        except Exception as e:
            return _err(e)


class StaffAuditListView(APIView):
    """POST /yonghu/merchant-staff/audit/list — 商家老板或有 audit_view 权限的客服可查看重要操作日志。"""

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            from merchant_ops.constants import IMPORTANT_AUDIT_ACTIONS
            from merchant_ops.services.audit import serialize_audit_row

            mid = request.user.UserUID
            member = None
            if is_merchant_owner(request.user):
                ensure_owner_merchant(mid, request.user)
            else:
                member = get_active_staff_member(request.user)
                if not member or not _member_has_perm(member, 'audit_view'):
                    return Response({'code': 403, 'msg': '无权限', 'data': None})
                mid = member.merchant_id
            page = max(1, int(request.data.get('page', 1)))
            page_size = min(50, int(request.data.get('page_size', 20)))
            qs = MerchantStaffAuditLog.query.filter(
                merchant_id=mid,
                action__in=IMPORTANT_AUDIT_ACTIONS,
            )
            staff_filter = request.data.get('staff_user_id')
            if staff_filter:
                qs = qs.filter(operator_user_id=staff_filter)
            total = qs.count()
            rows = list(qs.order_by('-CreateTime')[(page - 1) * page_size: page * page_size])
            member_ids = {r.staff_member_id for r in rows if r.staff_member_id}
            member_map = {}
            if member_ids:
                for m in MerchantStaffMember.query.filter(id__in=member_ids).select_related('role'):
                    member_map[m.id] = m
            items = [serialize_audit_row(r, member_map) for r in rows]
            return Response({'code': 0, 'msg': '成功', 'data': {
                'list': items, 'total': total, 'has_more': page * page_size < total,
            }})
        except Exception as e:
            return _err(e)


class StaffRankView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            from datetime import date, timedelta
            from django.db.models import Sum

            if is_merchant_owner(request.user):
                mid = request.user.UserUID
            else:
                member = get_active_staff_member(request.user)
                if not member:
                    return Response({'code': 403, 'msg': '无权限', 'data': None})
                mid = member.merchant_id
            period = request.data.get('period', 'today')
            sort_by = request.data.get('sort_by', 'dispatch_count')
            today = date.today()
            if period == 'today':
                start = today
            elif period == 'yesterday':
                start = today - timedelta(days=1)
            elif period == 'week':
                start = today - timedelta(days=7)
            elif period == 'month':
                start = today - timedelta(days=30)
            else:
                start = None

            qs = MerchantStaffDailyStats.query.filter(merchant_id=mid)
            if start:
                if period == 'yesterday':
                    qs = qs.filter(stat_date=start)
                else:
                    qs = qs.filter(stat_date__gte=start)

            agg = qs.values('staff_user_id', 'member_id').annotate(
                dispatch_count=Sum('dispatch_count'),
                dispatch_amount=Sum('dispatch_amount'),
                settle_count=Sum('settle_count'),
                settle_amount=Sum('settle_amount'),
            )
            sort_field = sort_by if sort_by in ('dispatch_count', 'dispatch_amount', 'settle_count', 'settle_amount') else 'dispatch_count'
            agg = sorted(agg, key=lambda x: x[sort_field] or 0, reverse=True)[:50]

            # 批量预取 member 信息，避免循环内 N+1
            _member_ids = [row['member_id'] for row in agg]
            _members_map = {
                m.id: m
                for m in MerchantStaffMember.query.filter(id__in=_member_ids).only('id', 'display_name')
            } if _member_ids else {}
            result = []
            for idx, row in enumerate(agg, 1):
                m = _members_map.get(row['member_id'])
                result.append({
                    'rank': idx,
                    'member_id': row['member_id'],
                    'staff_user_id': row['staff_user_id'],
                    'display_name': (m.display_name if m else '') or row['staff_user_id'],
                    'dispatch_count': row['dispatch_count'] or 0,
                    'dispatch_amount': str(row['dispatch_amount'] or 0),
                    'settle_count': row['settle_count'] or 0,
                    'settle_amount': str(row['settle_amount'] or 0),
                })
            return Response({'code': 0, 'msg': '成功', 'data': {'list': result, 'period': period}})
        except Exception as e:
            return _err(e)
