"""子客服专用订单接口(不修改商家原接口)。""" import logging from django.db.models import Q from rest_framework import status from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from backend.utils import pick_leixing_id, pick_order_id from merchant_ops.helpers import proxy_merchant_order_view, staff_can_access_order from merchant_ops.models import MerchantOrderDispatch from merchant_ops.services.authz import StaffAuthError, get_active_staff_member, member_has_perm from merchant_ops.services.audit import audit_staff from merchant_ops.services.dispatch import staff_dispatch_order from merchant_ops.services.stats import bump_cancel_count, bump_penalty_count, update_merchant_and_staff_stats from merchant_ops.constants import STAT_ACTION_REFUND, STAT_ACTION_SETTLE from orders.models import MerchantOrderExt from orders.views import ( ShangjiaDingdanXiangqingView, ShangjiaJiesuanView, ShangjiaChexiaoView, ShangjiaTuikuanShenqingView, ShangjiaFakuanApplyView, ShangjiaFakuaiXiugaiView, ZxsjghdsView, ShangpinLeixingView, ) logger = logging.getLogger(__name__) def _require_staff(request): member = get_active_staff_member(request.user) if not member: raise StaffAuthError(403, '非有效子客服') return member 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}) class StaffOrderListView(APIView): """POST /yonghu/merchant-staff/order/list — 对齐 sjdingdanhq""" permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) if not member_has_perm(member, 'order_view_all') and not member_has_perm(member, 'order_view_self'): raise StaffAuthError(403, '无订单查看权限') data = request.data zhuangtai_list = data.get('zhuangtai_list', []) page = max(1, int(data.get('page', 1))) page_size = min(100, max(1, int(data.get('page_size', 10)))) leixing_id = pick_leixing_id(data) keyword = str(data.get('keyword', '')).strip() if not isinstance(zhuangtai_list, list) or not zhuangtai_list: return Response({'code': 3, 'msg': '状态列表不能为空', 'data': None}) base_qs = MerchantOrderExt.query.filter( MerchantID=member.merchant_id, ).select_related('Order').filter(Order__Status__in=zhuangtai_list) if not member_has_perm(member, 'order_view_all'): own_ids = MerchantOrderDispatch.query.filter( staff_member_id=member.id, ).values_list('order_id', flat=True) base_qs = base_qs.filter(Order__OrderID__in=list(own_ids)) if leixing_id is not None: base_qs = base_qs.filter(Order__ProductTypeID=leixing_id) if keyword: base_qs = base_qs.filter( Q(Order__OrderID__exact=keyword) | Q(Order__Description__icontains=keyword) | Q(Order__Nickname__icontains=keyword) | Q(Order__PlayerID__exact=keyword) ) pending_count = MerchantOrderExt.query.filter( MerchantID=member.merchant_id, Order__Status=8, ).count() total_count = base_qs.count() start = (page - 1) * page_size page_qs = base_qs.order_by('-Order__CreateTime')[start:start + page_size] order_list = [] for ext in page_qs: o = ext.Order order_list.append({ 'dingdan_id': o.OrderID, 'zhuangtai': o.Status, 'jine': float(o.Amount) if o.Amount else 0.0, 'jieshao': o.Description or '', 'beizhu': o.Remark or '', 'nicheng': o.Nickname or '', 'leixing_id': o.ProductTypeID, 'tupian': o.ImageURL or '', 'jiedan_dashou_id': o.PlayerID or '', 'CreateTime': o.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if o.CreateTime else '', }) return Response({ 'code': 0, 'msg': '成功', 'data': { 'list': order_list, 'has_more': len(page_qs) == page_size and start + page_size < total_count, 'pending_count': pending_count, }, }) except Exception as e: return _err(e) class StaffOrderDetailView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) if not member_has_perm(member, 'order_detail'): raise StaffAuthError(403, '无权限') resp = proxy_merchant_order_view( request, member, ShangjiaDingdanXiangqingView, 'order_detail', ) if resp.status_code == 200 and resp.data.get('code') in (0, 200): audit_staff(member, 'ORDER_DETAIL', request, resource_id=pick_order_id(request.data)) return resp except Exception as e: return _err(e) class StaffDispatchView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) if not member_has_perm(member, 'order_dispatch'): raise StaffAuthError(403, '无派单权限') ok, payload = staff_dispatch_order(member, request.user, request.data) if ok: audit_staff(member, 'DISPATCH', request, resource_id=payload.get('data', {}).get('dingdan_id'), amount=request.data.get('jiage')) code = payload.get('code', 500) return Response(payload, status=status.HTTP_200_OK if code in (0, 200) else status.HTTP_400_BAD_REQUEST) except Exception as e: return _err(e) class StaffProductTypesView(APIView): """商品类型 — 代理 sjspleixing""" permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) if not member_has_perm(member, 'order_dispatch'): raise StaffAuthError(403, '无权限') return proxy_merchant_order_view(request, member, ShangpinLeixingView, 'order_dispatch') except Exception as e: return _err(e) class StaffSettleView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) resp = proxy_merchant_order_view(request, member, ShangjiaJiesuanView, 'order_settle') if resp.data.get('code') in (0, 200): audit_staff(member, 'SETTLE', request, resource_id=pick_order_id(request.data)) from orders.models import Order oid = pick_order_id(request.data) o = Order.query.filter(OrderID=oid).first() if o: update_merchant_and_staff_stats(member.merchant_id, member, o.Amount, STAT_ACTION_SETTLE) return resp except Exception as e: return _err(e) class StaffCancelView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) resp = proxy_merchant_order_view(request, member, ShangjiaChexiaoView, 'order_cancel') if resp.data.get('code') in (0, 200): audit_staff(member, 'CANCEL', request, resource_id=pick_order_id(request.data)) bump_cancel_count(member.merchant_id, member) return resp except Exception as e: return _err(e) class StaffRefundView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) resp = proxy_merchant_order_view(request, member, ShangjiaTuikuanShenqingView, 'order_refund_apply') if resp.data.get('code') in (0, 200): audit_staff(member, 'REFUND_APPLY', request, resource_id=pick_order_id(request.data)) return resp except Exception as e: return _err(e) class StaffPenaltyApplyView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) resp = proxy_merchant_order_view(request, member, ShangjiaFakuanApplyView, 'penalty_apply') if resp.data.get('code') in (0, 200): audit_staff(member, 'PENALTY_APPLY', request, resource_id=pick_order_id(request.data)) bump_penalty_count(member.merchant_id, member) return resp except Exception as e: return _err(e) class StaffPenaltyManageView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) resp = proxy_merchant_order_view(request, member, ShangjiaFakuaiXiugaiView, 'penalty_manage') if resp.data.get('code') in (0, 200): audit_staff(member, 'PENALTY_MANAGE', request, resource_id=pick_order_id(request.data)) return resp except Exception as e: return _err(e) class StaffChangePlayerView(APIView): permission_classes = [IsAuthenticated] def post(self, request): try: member = _require_staff(request) resp = proxy_merchant_order_view(request, member, ZxsjghdsView, 'order_change_player') if resp.data.get('code') in (0, 200): audit_staff(member, 'CHANGE_PLAYER', request, resource_id=pick_order_id(request.data)) return resp except Exception as e: return _err(e)