修复了商家客服接口

This commit is contained in:
XingQue
2026-06-20 03:25:24 +08:00
parent 6fa0e1638d
commit 1ac05494d6
4 changed files with 205 additions and 47 deletions

View File

@@ -0,0 +1,59 @@
"""子客服订单查询:按权限隔离,不返回商家全量(除非 order_view_all"""
from django.db.models import Q
from backend.utils import pick_leixing_id
from merchant_ops.models import MerchantOrderDispatch
from merchant_ops.services.authz import member_has_perm
from orders.models import MerchantOrderExt
def staff_can_view_all_orders(member):
return member_has_perm(member, 'order_view_all')
def staff_order_ext_qs(member, zhuangtai_list=None):
"""商家名下订单;无 order_view_all 时仅本人派单记录。"""
qs = MerchantOrderExt.query.filter(
MerchantID=member.merchant_id,
).select_related('Order')
if zhuangtai_list is not None:
qs = qs.filter(Order__Status__in=zhuangtai_list)
if not staff_can_view_all_orders(member):
own_ids = list(
MerchantOrderDispatch.query.filter(staff_member_id=member.id).values_list('order_id', flat=True)
)
qs = qs.filter(Order__OrderID__in=own_ids)
return qs
def staff_apply_order_filters(qs, data):
leixing_id = pick_leixing_id(data)
keyword = str(data.get('keyword', '')).strip()
if leixing_id is not None:
qs = qs.filter(Order__ProductTypeID=leixing_id)
if keyword:
qs = qs.filter(
Q(Order__OrderID__exact=keyword)
| Q(Order__Description__icontains=keyword)
| Q(Order__Nickname__icontains=keyword)
| Q(Order__PlayerID__exact=keyword)
)
return qs
def staff_order_to_dict(ext):
o = ext.Order
ct = o.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if o.CreateTime else ''
return {
'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': ct,
'create_time': ct,
}

View File

@@ -0,0 +1,103 @@
"""子客服读取商家模板:按 merchant_id 查 ShangjiaMoban与老板同一批数据"""
from decimal import Decimal, InvalidOperation
from django.core.paginator import Paginator
from django.db.models import Q
from config.models import Chenghao, ShangjiaMoban
def _format_moban(moban):
texiao_json = ''
label_name = ''
if moban.TitleID:
try:
ch = Chenghao.query.get(id=moban.TitleID)
label_name = ch.mingcheng
texiao_json = ch.texiao_miaoshu or ''
except Chenghao.DoesNotExist:
pass
return {
'mobanId': moban.id,
'shangpinTypeId': moban.ProductTypeID,
'jieshao': moban.TemplateDesc,
'jiage': str(moban.Price),
'fabushuliang': moban.PublishedCount,
'commissionEnabled': moban.CommissionGrabEnabled,
'commissionValue': str(moban.CommissionPrice) if moban.CommissionPrice is not None else None,
'labelId': moban.TitleID,
'labelName': label_name,
'texiaoJson': texiao_json,
'createTime': moban.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if moban.CreateTime else '',
}
def list_merchant_templates(merchant_id, data):
"""
查询某商家全部模板UserID=merchant_id
返回 (payload_dict, http_status) 与 /peizhi/sjddmblb 一致code=200。
"""
leixing_id = data.get('shangpinTypeId')
if not leixing_id:
return {'code': 400, 'msg': '商品类型ID不能为空', 'data': {}}, 400
get_type = int(data.get('getType', 2))
page = int(data.get('page', 1))
page_size = int(data.get('pageSize', 50))
keyword = data.get('keyword', '')
min_price = data.get('minPrice', '')
label_id = data.get('labelId', None)
if label_id is not None:
try:
label_id = int(label_id)
except (ValueError, TypeError):
label_id = None
query = Q(UserID=merchant_id, ProductTypeID=leixing_id)
if get_type == 1:
if keyword:
keyword = str(keyword).strip()
try:
mid = int(keyword)
query &= Q(id=mid) | Q(TemplateDesc__icontains=keyword)
except ValueError:
query &= Q(TemplateDesc__icontains=keyword)
if min_price:
try:
query &= Q(Price__gte=Decimal(str(min_price)))
except (InvalidOperation, ValueError, TypeError):
pass
if label_id:
query &= Q(TitleID=label_id)
mobans = ShangjiaMoban.query.filter(query).order_by('-id')
formatted = [_format_moban(m) for m in mobans]
return {
'code': 200,
'msg': '搜索成功',
'data': {'list': formatted, 'total': len(formatted), 'hasMore': False},
}, 200
queryset = ShangjiaMoban.query.filter(query).order_by('-id')
paginator = Paginator(queryset, page_size)
if page < 1 or page > max(paginator.num_pages, 1):
return {'code': 400, 'msg': '页码超出范围', 'data': {}}, 400
current_page = paginator.page(page)
formatted = [_format_moban(m) for m in current_page.object_list]
has_more = current_page.has_next()
if len(formatted) < page_size:
has_more = False
return {
'code': 200,
'msg': '获取成功',
'data': {
'list': formatted,
'total': paginator.count,
'hasMore': has_more,
'currentPage': page,
'pageSize': page_size,
'totalPages': paginator.num_pages,
},
}, 200

View File

@@ -1,16 +1,20 @@
"""子客服专用订单接口(不修改商家原接口)。"""
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 backend.utils import pick_order_id
from merchant_ops.helpers import proxy_merchant_order_view
from merchant_ops.services.authz import StaffAuthError, get_active_staff_member, member_has_perm
from merchant_ops.services.order_query import (
staff_apply_order_filters,
staff_can_view_all_orders,
staff_order_ext_qs,
staff_order_to_dict,
)
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
@@ -40,7 +44,11 @@ def _err(e):
class StaffOrderListView(APIView):
"""POST /yonghu/merchant-staff/order/list — 对齐 sjdingdanhq"""
"""
POST /yonghu/merchant-staff/order/list
- 有 order_view_all该商家全部订单
- 否则:仅 merchant_order_dispatch 中 staff_member_id=本人的订单
"""
permission_classes = [IsAuthenticated]
def post(self, request):
@@ -53,61 +61,33 @@ class StaffOrderListView(APIView):
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)
base_qs = staff_apply_order_filters(
staff_order_ext_qs(member, zhuangtai_list),
data,
)
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))
pending_qs = staff_order_ext_qs(member, [8])
pending_count = pending_qs.count()
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 '',
})
order_list = [staff_order_to_dict(ext) for ext in page_qs]
return Response({
'code': 0, 'msg': '成功',
'code': 0,
'msg': '成功',
'data': {
'list': order_list,
'has_more': len(page_qs) == page_size and start + page_size < total_count,
'total': total_count,
'pending_count': pending_count,
'view_scope': 'all' if staff_can_view_all_orders(member) else 'self',
},
})
except Exception as e:

View File

@@ -1,10 +1,9 @@
"""子客服专用:派单模板 / 链接(代理商家 config 接口,校验权限,不修改原 /peizhi/*)。"""
"""子客服专用:派单模板 / 链接(独立路径;读模板用 merchant_id写模板需 template_manage)。"""
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from config.views import (
ShangjiaMobanListView,
ShangjiaTianjiaMobanView,
ShangjiaGengxinMobanView,
ShangjiaShanchuMobanView,
@@ -13,6 +12,7 @@ from config.views import (
)
from merchant_ops.helpers import proxy_as_merchant_user
from merchant_ops.services.authz import StaffAuthError, get_active_staff_member, member_has_perm
from merchant_ops.services.template_query import list_merchant_templates
from merchant_ops.views_manage import _err
@@ -23,13 +23,23 @@ def _require_staff(request):
return member
def _can_read_templates(member):
"""派单发常规单需读模板;改模板需 template_manage。"""
return member_has_perm(member, 'order_dispatch') or member_has_perm(member, 'template_manage')
class StaffTemplateListView(APIView):
"""读商家全部模板UserID=merchant_id有 order_dispatch 即可。"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
member = _require_staff(request)
return proxy_as_merchant_user(request, member, ShangjiaMobanListView, 'template_manage')
if not _can_read_templates(member):
raise StaffAuthError(403, '无权限查看模板')
payload, http_status = list_merchant_templates(member.merchant_id, request.data)
return Response(payload, status=http_status)
except Exception as e:
return _err(e)
@@ -40,6 +50,8 @@ class StaffTemplateCreateView(APIView):
def post(self, request):
try:
member = _require_staff(request)
if not member_has_perm(member, 'template_manage'):
raise StaffAuthError(403, '无权限添加模板')
return proxy_as_merchant_user(request, member, ShangjiaTianjiaMobanView, 'template_manage')
except Exception as e:
return _err(e)
@@ -51,6 +63,8 @@ class StaffTemplateUpdateView(APIView):
def post(self, request):
try:
member = _require_staff(request)
if not member_has_perm(member, 'template_manage'):
raise StaffAuthError(403, '无权限修改模板')
return proxy_as_merchant_user(request, member, ShangjiaGengxinMobanView, 'template_manage')
except Exception as e:
return _err(e)
@@ -62,6 +76,8 @@ class StaffTemplateDeleteView(APIView):
def post(self, request):
try:
member = _require_staff(request)
if not member_has_perm(member, 'template_manage'):
raise StaffAuthError(403, '无权限删除模板')
return proxy_as_merchant_user(request, member, ShangjiaShanchuMobanView, 'template_manage')
except Exception as e:
return _err(e)