"""商家订单操作人统计 API(新接口,不影响旧统计)。""" import logging from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from merchant_ops.services.authz import ( StaffAuthError, ensure_owner_merchant, get_active_staff_member, is_merchant_owner, member_has_perm, ) from merchant_ops.services.action_stats import ( build_operator_action_stats, list_operator_candidates, ) from merchant_ops.services.bootstrap import ensure_merchant_roles logger = logging.getLogger(__name__) def _resolve_stats_merchant(request): """商家老板,或具备 stats_view/finance_view/audit_view 的子客服。""" 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 not ( member_has_perm(member, 'stats_view') or member_has_perm(member, 'finance_view') or member_has_perm(member, 'audit_view') ): raise StaffAuthError(403, '无统计权限') ensure_merchant_roles(member.merchant_id) return member.merchant_id, member def _parse_filters(data): action_codes = data.get('action_codes') or data.get('action_code_list') if isinstance(action_codes, str): action_codes = [x.strip() for x in action_codes.split(',') if x.strip()] return { 'operator_user_id': (data.get('operator_user_id') or data.get('operator_id') or '').strip() or None, 'operator_type': (data.get('operator_type') or '').strip() or None, 'role_code': (data.get('role_code') or '').strip() or None, 'action_code': data.get('action_code'), 'action_codes': action_codes, 'leixing_id': data.get('leixing_id'), 'start_time': data.get('start_time') or data.get('start') or data.get('begin_time'), 'end_time': data.get('end_time') or data.get('end'), } class StaffOrderActionStatsView(APIView): """ POST /yonghu/merchant-staff/order/action-stats 按操作人/行为/时间/游戏类型统计结算·撤单·退款·罚单等操作量与金额。 """ permission_classes = [IsAuthenticated] def post(self, request): try: merchant_id, _member = _resolve_stats_merchant(request) filters = _parse_filters(request.data or {}) data = build_operator_action_stats(merchant_id, filters) data['merchant_id'] = merchant_id data['filters'] = {k: v for k, v in filters.items() if v not in (None, '', [])} return Response({'code': 0, 'msg': 'ok', 'data': data}) except StaffAuthError as e: return Response({'code': e.code, 'msg': e.msg, 'data': None}) except Exception as e: logger.error('操作人统计失败: %s', e, exc_info=True) return Response({'code': 99, 'msg': '统计失败', 'data': None}) class StaffOrderActionOperatorsView(APIView): """ POST /yonghu/merchant-staff/order/action-operators 返回可筛选的操作人候选(商家本人 + 子客服)。 """ permission_classes = [IsAuthenticated] def post(self, request): try: merchant_id, _member = _resolve_stats_merchant(request) return Response({ 'code': 0, 'msg': 'ok', 'data': { 'merchant_id': merchant_id, 'operators': list_operator_candidates(merchant_id), }, }) except StaffAuthError as e: return Response({'code': e.code, 'msg': e.msg, 'data': None}) except Exception as e: logger.error('操作人候选列表失败: %s', e, exc_info=True) return Response({'code': 99, 'msg': '获取失败', 'data': None})