feat: 商家子客服 merchant_ops 模块与专用 API
This commit is contained in:
256
merchant_ops/views_orders.py
Normal file
256
merchant_ops/views_orders.py
Normal file
@@ -0,0 +1,256 @@
|
||||
"""子客服专用订单接口(不修改商家原接口)。"""
|
||||
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)
|
||||
Reference in New Issue
Block a user