"""权限校验与成员解析。""" from rest_framework.exceptions import PermissionDenied, NotFound from merchant_ops.constants import ( 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 from users.models import UserShangjia class StaffAuthError(Exception): def __init__(self, code, msg): self.code = code self.msg = msg super().__init__(msg) def is_merchant_owner(user): try: profile = user.ShopProfile return profile.zhuangtai == 1 except UserShangjia.DoesNotExist: return False def get_active_staff_member(user): return MerchantStaffMember.query.filter( staff_user_id=user.UserUID, status=MEMBER_STATUS_ACTIVE, ).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( member.role.role_permissions.values_list('perm_code', flat=True) ) for mp in member.member_permissions.all(): if mp.granted: perms.add(mp.perm_code) else: perms.discard(mp.perm_code) return perms 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}') def staff_fields_for_login(user): """登录/注册响应附带子客服身份(与 shangjiastatus 并列,互斥)。""" ctx = resolve_staff_context(user) return { 'staffstatus': 1 if ctx else 0, 'staff_context': ctx, } def resolve_staff_context(user): """子客服上下文;非客服返回 None。""" member = get_active_staff_member(user) if not member: return None wallet = member.wallets.first() perms = list(get_member_permissions(member)) return { 'active': True, 'merchant_id': member.merchant_id, 'member_id': member.id, 'role_id': member.role_id, 'role_code': member.role.role_code, 'role_name': member.role.role_name, 'display_name': member.display_name or '', 'permissions': perms, 'wallet': { 'quota_total': str(wallet.quota_total if wallet else 0), 'quota_used': str(wallet.quota_used if wallet else 0), 'quota_frozen': str(wallet.quota_frozen if wallet else 0), 'quota_available': str(wallet.quota_available if wallet else 0), }, } def resolve_owner_or_staff(request, perm_code=None): """ 返回 (mode, merchant_id, member, operator_id) mode: 'owner' | 'staff' """ user = request.user if is_merchant_owner(user): return 'owner', user.UserUID, None, user.UserUID member = get_active_staff_member(user) if not member: raise StaffAuthError(403, '非商家老板或有效子客服') if perm_code: assert_staff_perm(member, perm_code) return 'staff', member.merchant_id, member, user.UserUID def assert_not_merchant_when_bind(user): if is_merchant_owner(user): raise StaffAuthError(403, '您已是商家,无法成为子客服') def assert_no_active_staff_when_merchant_register(user): if MerchantStaffMember.query.filter( staff_user_id=user.UserUID, status=MEMBER_STATUS_ACTIVE ).exists(): raise StaffAuthError(403, '您当前是商家客服,请先联系商家解除后再认证商家') def assert_can_bind_staff(user): assert_not_merchant_when_bind(user) if MerchantStaffMember.query.filter( staff_user_id=user.UserUID, status=MEMBER_STATUS_ACTIVE ).exists(): raise StaffAuthError(403, '您已绑定商家客服,无法重复绑定') def get_owner_permissions(): return list(OWNER_ALL_PERMISSIONS) def ensure_owner_merchant(merchant_id, user): if user.UserUID != merchant_id or not is_merchant_owner(user): raise StaffAuthError(403, '仅商家老板可操作') ensure_merchant_roles(merchant_id)