fix: 订单时间字段别名与子客服鉴权/读权限统一

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-22 16:22:47 +08:00
parent a07bd3d388
commit 45af7e1649
6 changed files with 78 additions and 33 deletions

View File

@@ -503,6 +503,19 @@ def check_fadan_permission(permissions, shenfen):
return False
def fmt_datetime(dt):
"""格式化时间为前端通用字符串。"""
if not dt:
return ''
return dt.strftime('%Y-%m-%d %H:%M:%S')
def datetime_aliases(dt):
"""同时返回 CreateTime / create_time避免前后端字段名不一致。"""
s = fmt_datetime(dt)
return {'CreateTime': s, 'create_time': s}
def pick_order_id(data):
"""兼容前端 dingdan_id 与 SDK 字段 OrderID。"""
return str(data.get('OrderID') or data.get('dingdan_id') or '').strip()

View File

@@ -2,7 +2,8 @@
from rest_framework.exceptions import PermissionDenied, NotFound
from merchant_ops.constants import (
MEMBER_STATUS_ACTIVE, MEMBER_STATUS_REMOVED, OWNER_ALL_PERMISSIONS,
MEMBER_STATUS_ACTIVE, MEMBER_STATUS_DISABLED, MEMBER_STATUS_REMOVED,
OWNER_ALL_PERMISSIONS,
)
from merchant_ops.models import MerchantStaffMember, MerchantStaffMemberPermission
from merchant_ops.services.bootstrap import ensure_merchant_roles
@@ -31,6 +32,24 @@ def get_active_staff_member(user):
).select_related('role').first()
def require_active_staff_member(user):
"""子客服接口鉴权:区分老板误入、未绑定、禁用、移除等场景。"""
if is_merchant_owner(user):
raise StaffAuthError(403, '您是商家老板,请使用商家端原接口')
member = MerchantStaffMember.query.filter(
staff_user_id=user.UserUID,
).select_related('role').order_by('-id').first()
if not member:
raise StaffAuthError(403, '未绑定子客服,请使用商家邀请码绑定')
if member.status == MEMBER_STATUS_DISABLED:
raise StaffAuthError(403, '子客服账号已被禁用,请联系商家老板')
if member.status == MEMBER_STATUS_REMOVED:
raise StaffAuthError(403, '子客服已被移除,请联系商家老板重新邀请')
if member.status != MEMBER_STATUS_ACTIVE:
raise StaffAuthError(403, '非有效子客服')
return member
def get_member_permissions(member):
"""角色权限 + 成员覆盖。"""
perms = set(
@@ -48,6 +67,14 @@ def member_has_perm(member, perm_code):
return perm_code in get_member_permissions(member)
def staff_order_read_perm(member):
"""子客服读订单/商品类型/模板时,任一看单或派单权限即可。"""
for code in ('order_dispatch', 'order_view_all', 'order_view_self', 'order_detail'):
if member_has_perm(member, code):
return code
return None
def assert_staff_perm(member, perm_code):
if not member_has_perm(member, perm_code):
raise StaffAuthError(403, f'无权限: {perm_code}')

View File

@@ -1,7 +1,7 @@
"""子客服订单查询:按权限隔离,不返回商家全量(除非 order_view_all"""
from django.db.models import Q
from backend.utils import pick_leixing_id
from backend.utils import fmt_datetime, pick_leixing_id
from merchant_ops.models import MerchantOrderDispatch
from merchant_ops.services.authz import member_has_perm
from orders.models import MerchantOrderExt
@@ -43,7 +43,7 @@ def staff_apply_order_filters(qs, data):
def staff_order_to_dict(ext):
o = ext.Order
ct = o.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if o.CreateTime else ''
ct = fmt_datetime(o.CreateTime)
return {
'dingdan_id': o.OrderID,
'zhuangtai': o.Status,

View File

@@ -8,7 +8,9 @@ from rest_framework.views import APIView
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.authz import (
StaffAuthError, require_active_staff_member, member_has_perm, staff_order_read_perm,
)
from merchant_ops.services.order_query import (
staff_apply_order_filters,
staff_can_view_all_orders,
@@ -29,10 +31,7 @@ logger = logging.getLogger(__name__)
def _require_staff(request):
member = get_active_staff_member(request.user)
if not member:
raise StaffAuthError(403, '非有效子客服')
return member
return require_active_staff_member(request.user)
def _err(e):
@@ -138,9 +137,10 @@ class StaffProductTypesView(APIView):
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')
perm = staff_order_read_perm(member)
if not perm:
raise StaffAuthError(403, '无权限查看商品类型')
return proxy_merchant_order_view(request, member, ShangpinLeixingView, perm)
except Exception as e:
return _err(e)

View File

@@ -10,7 +10,9 @@ from config.views import (
ShangjiaShanchuMobanView,
)
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.authz import (
StaffAuthError, require_active_staff_member, member_has_perm, staff_order_read_perm,
)
from merchant_ops.services.template_query import list_merchant_templates
from merchant_ops.services.link import staff_generate_link, staff_list_links
from merchant_ops.services.audit import audit_staff
@@ -18,15 +20,12 @@ from merchant_ops.views_manage import _err
def _require_staff(request):
member = get_active_staff_member(request.user)
if not member:
raise StaffAuthError(403, '非有效子客服')
return member
return require_active_staff_member(request.user)
def _can_read_templates(member):
"""派单发常规单需读模板;改模板需 template_manage"""
return member_has_perm(member, 'order_dispatch') or member_has_perm(member, 'template_manage')
"""派单/看单/管模板任一权限即可读模板列表"""
return staff_order_read_perm(member) or member_has_perm(member, 'template_manage')
class StaffTemplateListView(APIView):

View File

@@ -48,7 +48,7 @@ from orders.utils import (
)
from shop.utils import calculate_pingtai_and_dianpu_shouyi, validate_shangpin_and_dianpu, update_dianpu_daily_stat
from products.utils import update_shangpin_daily_stat
from backend.utils import update_shangjia_daily, update_dashou_daily_by_action, pick_leixing_id
from backend.utils import update_shangjia_daily, update_dashou_daily_by_action, pick_leixing_id, datetime_aliases, fmt_datetime
from rank.services import record_dashou_biaoxian
from rank.utils import check_dashou_biaoqian_required
@@ -1097,7 +1097,8 @@ class DingdanHuoquView(APIView):
'jieshao': order.Description or '',
'beizhu': order.Remark or '',
'nicheng': order.Nickname or '',
'chuangjianshijian': order.CreateTime.strftime('%Y-%m-%d %H:%M:%S')
'chuangjianshijian': fmt_datetime(order.CreateTime),
'create_time': fmt_datetime(order.CreateTime),
}
order_list.append(order_data)
@@ -1759,12 +1760,12 @@ class ShangpinLeixingView(APIView):
if getattr(request, '_staff_member', None) is not None:
return True
from merchant_ops.services.authz import (
get_active_staff_member, is_merchant_owner, member_has_perm,
get_active_staff_member, is_merchant_owner, member_has_perm, staff_order_read_perm,
)
if is_merchant_owner(request.user):
return True
member = get_active_staff_member(request.user)
return bool(member and member_has_perm(member, 'order_dispatch'))
return bool(member and staff_order_read_perm(member))
def post(self, request):
@@ -2140,7 +2141,7 @@ class ShangjiaDingdanHuoquView(APIView):
'leixing_id': order.ProductTypeID,
'tupian': order.ImageURL or '',
'jiedan_dashou_id': order.PlayerID or '',
'CreateTime': order.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if order.CreateTime else ''
**datetime_aliases(order.CreateTime),
}
order_list.append(order_data)
@@ -2249,6 +2250,8 @@ class ShangjiaDingdanXiangqingView(APIView):
).first()
fadan_dict = None
if fadan_obj:
ct = fmt_datetime(fadan_obj.CreateTime)
ut = fmt_datetime(fadan_obj.UpdateTime)
fadan_dict = {
'id': fadan_obj.id,
'chufaliyou': fadan_obj.Reason or '',
@@ -2258,8 +2261,10 @@ class ShangjiaDingdanXiangqingView(APIView):
'shensuliyou': fadan_obj.AppealReason or '',
'tongyi_chufaliyou': fadan_obj.ApproveReason or '',
'bohuiliyou': fadan_obj.RejectReason or '',
'CreateTime': fadan_obj.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if fadan_obj.CreateTime else '',
'UpdateTime': fadan_obj.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if fadan_obj.UpdateTime else '',
'CreateTime': ct,
'create_time': ct,
'UpdateTime': ut,
'update_time': ut,
}
# 7. 商家自己的分红利率(用于罚款分红计算)
@@ -2297,7 +2302,7 @@ class ShangjiaDingdanXiangqingView(APIView):
'jieshao': order.Description or '',
'beizhu': order.Remark or '',
'nicheng': order.Nickname or '',
'CreateTime': order.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if order.CreateTime else '',
**datetime_aliases(order.CreateTime),
# 打手信息
'dashou_yonghuid': dashou_yonghuid,
@@ -3174,7 +3179,8 @@ class DashouDingdanHuoquView(APIView):
'yajin': float(order['CommissionReq']) if order['CommissionReq'] else 0.0,
'jieshao': order['Description'] or '',
'beizhu': order['Remark'] or '',
'creat_time': order['CreateTime'].strftime('%Y-%m-%d %H:%M:%S') if order['CreateTime'] else '',
'creat_time': fmt_datetime(order['CreateTime']),
'create_time': fmt_datetime(order['CreateTime']),
'sjnicheng': order['MerchantNickname'] or '',
'zhiding_uid': order['AssignedID'] or '',
'zhiding_avatar': info.get('avatar', ''),
@@ -3792,7 +3798,7 @@ class DashouDingdanHuoquView1(APIView):
'jieshao': order.Description or '',
'tupian': order.ImageURL or '',
'nicheng': order.Nickname or '',
'CreateTime': order.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if order.CreateTime else ''
**datetime_aliases(order.CreateTime),
}
order_list.append(order_data)
@@ -4324,7 +4330,7 @@ class DashouDingdanXiangqingView(APIView):
'tupian': dingdan.ImageURL or '',
'nicheng': dingdan.Nickname or '',
'beizhu': dingdan.Remark or '',
'CreateTime': dingdan.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if dingdan.CreateTime else '',
**datetime_aliases(dingdan.CreateTime),
'tuikuan_liyou':dingdan.RefundReason or '',
'dashou_liuyan': dingdan.PlayerRemark or '',
'fuwudashou_id': dingdan.PlayerID or '',
@@ -4496,10 +4502,10 @@ class AdGetDingdanXiangQing(APIView):
'dashou_liuyan': dingdan_obj.PlayerRemark or '',
# 时间字段
'chuangjianshijian': dingdan_obj.CreateTime.strftime(
'%Y-%m-%d %H:%M:%S') if dingdan_obj.CreateTime else '',
'genggaishijian': dingdan_obj.UpdateTime.strftime(
'%Y-%m-%d %H:%M:%S') if dingdan_obj.UpdateTime else '',
'chuangjianshijian': fmt_datetime(dingdan_obj.CreateTime),
'create_time': fmt_datetime(dingdan_obj.CreateTime),
'genggaishijian': fmt_datetime(dingdan_obj.UpdateTime),
'update_time': fmt_datetime(dingdan_obj.UpdateTime),
}
# 8. 根据发单平台查询扩展表数据