fix: 订单时间字段别名与子客服鉴权/读权限统一
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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. 根据发单平台查询扩展表数据
|
||||
|
||||
Reference in New Issue
Block a user