From 6fa0e1638d07f336e04f26ac06d60ecfd923fe9f Mon Sep 17 00:00:00 2001 From: XingQue Date: Sat, 20 Jun 2026 03:17:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=A8=A1=E6=9D=BF=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E5=90=8D;=20=E5=AE=A2=E6=9C=8D=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF/=E8=A7=92=E8=89=B2/=E6=9D=83=E9=99=90=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/views.py | 4 +- merchant_ops/helpers.py | 5 ++ merchant_ops/urls.py | 16 +++++- merchant_ops/views_manage.py | 66 ++++++++++++++++++++++++ merchant_ops/views_templates.py | 89 +++++++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 merchant_ops/views_templates.py diff --git a/config/views.py b/config/views.py index 1e72f07..1e9b89b 100644 --- a/config/views.py +++ b/config/views.py @@ -1546,8 +1546,8 @@ class ShangjiaTianjiaMobanView(APIView): TemplateDesc=jieshao, Price=jiage_decimal, PublishedCount=0, - CommissionGrabEnabled=yongjin_qiangdan, - CommissionPrice=yongjin_jiage, + CommissionGrabEnabled=CommissionGrabEnabled, + CommissionPrice=CommissionPrice, TitleID=cleaned_label_id ) diff --git a/merchant_ops/helpers.py b/merchant_ops/helpers.py index 60a8cbb..00f427d 100644 --- a/merchant_ops/helpers.py +++ b/merchant_ops/helpers.py @@ -32,6 +32,11 @@ def staff_can_access_order(member, order_id, perm_all='order_view_all', perm_sel def proxy_merchant_order_view(request, member, view_class, perm_code): """子客服通过商家 User 代理调用现有 View.post。""" + return proxy_as_merchant_user(request, member, view_class, perm_code) + + +def proxy_as_merchant_user(request, member, view_class, perm_code): + """子客服以商家身份代理调用任意商家 View(订单/配置等)。""" if not member_has_perm(member, perm_code): raise StaffAuthError(403, f'无权限: {perm_code}') diff --git a/merchant_ops/urls.py b/merchant_ops/urls.py index 2063896..309d8ef 100644 --- a/merchant_ops/urls.py +++ b/merchant_ops/urls.py @@ -3,7 +3,8 @@ from django.urls import path from merchant_ops.views_manage import ( StaffInviteCreateView, StaffInviteListView, StaffInviteRevokeView, StaffMemberListView, StaffMemberUpdateRoleView, StaffMemberRemoveView, - StaffMemberDisableView, StaffRoleListView, StaffRoleSetPermsView, + StaffMemberDisableView, StaffMemberUpdatePermsView, + StaffRoleListView, StaffRoleCreateView, StaffRoleSetPermsView, StaffWalletAllocateView, StaffWalletLedgerView, StaffAuditListView, StaffRankView, ) @@ -13,6 +14,10 @@ from merchant_ops.views_orders import ( StaffProductTypesView, StaffSettleView, StaffCancelView, StaffRefundView, StaffPenaltyApplyView, StaffPenaltyManageView, StaffChangePlayerView, ) +from merchant_ops.views_templates import ( + StaffTemplateListView, StaffTemplateCreateView, StaffTemplateUpdateView, + StaffTemplateDeleteView, StaffLinkGenerateView, StaffLinkListView, +) urlpatterns = [ # 子客服 @@ -24,9 +29,11 @@ urlpatterns = [ path('invite/revoke', StaffInviteRevokeView.as_view()), path('member/list', StaffMemberListView.as_view()), path('member/update-role', StaffMemberUpdateRoleView.as_view()), + path('member/update-perms', StaffMemberUpdatePermsView.as_view()), path('member/remove', StaffMemberRemoveView.as_view()), path('member/disable', StaffMemberDisableView.as_view()), path('role/list', StaffRoleListView.as_view()), + path('role/create', StaffRoleCreateView.as_view()), path('role/set-perms', StaffRoleSetPermsView.as_view()), path('wallet/allocate', StaffWalletAllocateView.as_view()), path('wallet/ledger', StaffWalletLedgerView.as_view()), @@ -43,4 +50,11 @@ urlpatterns = [ path('order/penalty-apply', StaffPenaltyApplyView.as_view()), path('order/penalty-manage', StaffPenaltyManageView.as_view()), path('order/change-player', StaffChangePlayerView.as_view()), + # 子客服模板 / 链接(独立路径,内部代理商家逻辑 + 权限校验) + path('template/list', StaffTemplateListView.as_view()), + path('template/create', StaffTemplateCreateView.as_view()), + path('template/update', StaffTemplateUpdateView.as_view()), + path('template/delete', StaffTemplateDeleteView.as_view()), + path('link/generate', StaffLinkGenerateView.as_view()), + path('link/list', StaffLinkListView.as_view()), ] diff --git a/merchant_ops/views_manage.py b/merchant_ops/views_manage.py index c1ff49b..700ad4b 100644 --- a/merchant_ops/views_manage.py +++ b/merchant_ops/views_manage.py @@ -252,6 +252,72 @@ class StaffRoleSetPermsView(APIView): return _err(e) +class StaffRoleCreateView(APIView): + """商家自定义角色(自由勾选权限)。""" + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + mid = request.user.UserUID + ensure_owner_merchant(mid, request.user) + ensure_merchant_roles(mid) + role_name = (request.data.get('role_name') or '').strip() + if not role_name: + return Response({'code': 400, 'msg': '角色名称不能为空', 'data': None}) + role_code = (request.data.get('role_code') or '').strip() + if not role_code: + role_code = 'CUSTOM_' + secrets.token_hex(3).upper() + if MerchantStaffRole.query.filter(merchant_id=mid, role_code=role_code).exists(): + return Response({'code': 400, 'msg': '角色码已存在', 'data': None}) + permissions = request.data.get('permissions', []) + role = MerchantStaffRole.objects.create( + club_id='xq', + merchant_id=mid, + role_code=role_code, + role_name=role_name, + is_system=False, + sort_order=50, + status=1, + ) + valid = set(MerchantStaffPermission.query.filter(status=1).values_list('perm_code', flat=True)) + for p in permissions: + if p in valid: + MerchantStaffRolePermission.objects.create(role=role, perm_code=p) + audit_owner(mid, mid, 'ROLE_CREATE', request, resource_id=role.id) + return Response({'code': 0, 'msg': '创建成功', 'data': {'id': role.id, 'role_code': role.role_code}}) + except Exception as e: + return _err(e) + + +class StaffMemberUpdatePermsView(APIView): + """成员级权限加减(覆盖角色默认权限)。""" + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + mid = request.user.UserUID + ensure_owner_merchant(mid, request.user) + member_id = request.data.get('member_id') + overrides = request.data.get('overrides', []) + m = MerchantStaffMember.query.get(id=member_id, merchant_id=mid, status=MEMBER_STATUS_ACTIVE) + MerchantStaffMemberPermission.query.filter(member=m).delete() + valid = set(MerchantStaffPermission.query.filter(status=1).values_list('perm_code', flat=True)) + for item in overrides: + if isinstance(item, dict): + code = item.get('perm_code') + granted = bool(item.get('granted', True)) + else: + code = item + granted = True + if code not in valid: + continue + MerchantStaffMemberPermission.objects.create(member=m, perm_code=code, granted=granted) + audit_owner(mid, mid, 'MEMBER_SET_PERMS', request, resource_id=member_id) + return Response({'code': 0, 'msg': '成员权限已更新', 'data': {'permissions': list(get_member_permissions(m))}}) + except Exception as e: + return _err(e) + + class StaffWalletAllocateView(APIView): permission_classes = [IsAuthenticated] diff --git a/merchant_ops/views_templates.py b/merchant_ops/views_templates.py new file mode 100644 index 0000000..0291146 --- /dev/null +++ b/merchant_ops/views_templates.py @@ -0,0 +1,89 @@ +"""子客服专用:派单模板 / 链接(代理商家 config 接口,校验权限,不修改原 /peizhi/*)。""" +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, + ShangjiaGenerateLinkView, + ShangjiaLianjieListView, +) +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.views_manage import _err + + +def _require_staff(request): + member = get_active_staff_member(request.user) + if not member: + raise StaffAuthError(403, '非有效子客服') + return member + + +class StaffTemplateListView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + member = _require_staff(request) + return proxy_as_merchant_user(request, member, ShangjiaMobanListView, 'template_manage') + except Exception as e: + return _err(e) + + +class StaffTemplateCreateView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + member = _require_staff(request) + return proxy_as_merchant_user(request, member, ShangjiaTianjiaMobanView, 'template_manage') + except Exception as e: + return _err(e) + + +class StaffTemplateUpdateView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + member = _require_staff(request) + return proxy_as_merchant_user(request, member, ShangjiaGengxinMobanView, 'template_manage') + except Exception as e: + return _err(e) + + +class StaffTemplateDeleteView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + member = _require_staff(request) + return proxy_as_merchant_user(request, member, ShangjiaShanchuMobanView, 'template_manage') + except Exception as e: + return _err(e) + + +class StaffLinkGenerateView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + member = _require_staff(request) + return proxy_as_merchant_user(request, member, ShangjiaGenerateLinkView, 'order_dispatch') + except Exception as e: + return _err(e) + + +class StaffLinkListView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + try: + member = _require_staff(request) + return proxy_as_merchant_user(request, member, ShangjiaLianjieListView, 'order_dispatch') + except Exception as e: + return _err(e)