From ebe8bce333a65925f80f6aa03e25e0e6d34b57bd Mon Sep 17 00:00:00 2001 From: XingQue Date: Wed, 29 Jul 2026 06:46:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=81=9C=E7=94=A8=E9=9B=86=E5=9B=A2?= =?UTF-8?q?=E4=BB=BB=E8=81=8C=E5=90=8C=E6=97=B6=E6=91=98=E6=8E=89=20IsSupe?= =?UTF-8?q?ruser=EF=BC=8C=E6=9D=9C=E7=BB=9D=E8=B6=85=E7=AE=A1=E7=BB=95?= =?UTF-8?q?=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- jituan/services/admin_assignments.py | 76 +++++++++++++++++++++++----- jituan/services/admin_context.py | 15 +++--- 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/jituan/services/admin_assignments.py b/jituan/services/admin_assignments.py index 050d0c1..c4709bd 100644 --- a/jituan/services/admin_assignments.py +++ b/jituan/services/admin_assignments.py @@ -1,10 +1,18 @@ -"""admin_assignment 任职 CRUD(修复停用不生效、重复插入)。""" +"""admin_assignment 任职 CRUD(修复停用不生效、重复插入、IsSuperuser 绕过)。""" from django.db.models import Q from django.utils import timezone -from jituan.constants import DATA_SCOPE_ALL, DATA_SCOPE_SINGLE +from jituan.constants import DATA_SCOPE_ALL, DATA_SCOPE_SINGLE, SUPER_ADMIN_PHONES from jituan.models import AdminAssignment +GROUP_ROLE_CODES = frozenset({ + 'GROUP_OWNER', + 'GROUP_SUPER_ADMIN', + 'GROUP_AFTER_SALES', + 'GROUP_FINANCE', + 'GROUP_CONFIG', +}) + def _club_filter(qs, club_id): if club_id is None or club_id == '': @@ -53,7 +61,6 @@ def create_or_reactivate_assignment( if is_primary: _clear_other_primary(yonghuid) - # MySQL 下 club_id=NULL 唯一约束不生效,可能有多条停用重复行,全部复活改只复活最新一条 inactive_qs = _club_filter( AdminAssignment.objects.filter(yonghuid=yonghuid, role_code=role_code).exclude(status=1), club_id, @@ -62,7 +69,6 @@ def create_or_reactivate_assignment( if inactive: if is_primary: _clear_other_primary(yonghuid) - # 同键其它停用重复行保持停用,只启用一条 inactive_qs.exclude(pk=inactive.pk).update(status=0, UpdateTime=timezone.now()) AdminAssignment.objects.filter(pk=inactive.id).update( status=1, @@ -85,10 +91,47 @@ def create_or_reactivate_assignment( return row.id, None +def _strip_system_super_if_needed(yonghuid): + """ + 停用集团权限后:非白名单账号必须摘掉 IsSuperuser。 + 否则 build_admin_club_context 仍把 is_group_admin=True,停用等于没停。 + """ + yonghuid = (yonghuid or '').strip() + if not yonghuid: + return + try: + from users.business_models import User + except Exception: + return + user = User.objects.filter(UserUID=yonghuid).first() + if not user: + return + if (user.Phone or '') in SUPER_ADMIN_PHONES: + return + if user.IsSuperuser: + user.IsSuperuser = False + user.save(update_fields=['IsSuperuser']) + + +def deactivate_all_group_assignments_for_user(yonghuid): + """停用该用户全部集团级任职(空俱乐部 / ALL_CLUBS / GROUP_* 角色)。""" + yonghuid = (yonghuid or '').strip() + if not yonghuid: + return 0 + qs = AdminAssignment.objects.filter(yonghuid=yonghuid, status=1).filter( + Q(club_id__isnull=True) + | Q(club_id='') + | Q(data_scope=DATA_SCOPE_ALL) + | Q(role_code__in=list(GROUP_ROLE_CODES)) + ) + return qs.update(status=0, UpdateTime=timezone.now()) + + def deactivate_assignment(assignment_id): """ - 停用任职。必须连同「同人+同角色+同俱乐部」的全部有效行一起停用: - MySQL 对 club_id=NULL 的 unique 不生效,集团任职常会积多条,只停一条等于没停。 + 停用任职。 + - 同人同角色同俱乐部(含 NULL 重复行)全部停 + - 若是集团级任职:再停掉该人全部集团任职,并摘掉非白名单 IsSuperuser """ try: pk = int(assignment_id) @@ -105,22 +148,31 @@ def deactivate_assignment(assignment_id): status=1, ) qs = _club_filter(qs, row.club_id) - updated = qs.update(status=0, UpdateTime=timezone.now()) - # 目标行若已是停用,仍视为成功(幂等) - if updated or int(row.status or 0) != 1: - return True, None + qs.update(status=0, UpdateTime=timezone.now()) + + is_group_level = ( + not (row.club_id or '').strip() + or (row.data_scope or '') == DATA_SCOPE_ALL + or (row.role_code or '') in GROUP_ROLE_CODES + ) + if is_group_level: + deactivate_all_group_assignments_for_user(row.yonghuid) + _strip_system_super_if_needed(row.yonghuid) + return True, None def deactivate_all_assignments_for_user(yonghuid): - """账号禁用时:停用该用户全部有效任职。""" + """账号禁用时:停用该用户全部有效任职,并摘掉非白名单 IsSuperuser。""" yonghuid = (yonghuid or '').strip() if not yonghuid: return 0 - return AdminAssignment.objects.filter(yonghuid=yonghuid, status=1).update( + n = AdminAssignment.objects.filter(yonghuid=yonghuid, status=1).update( status=0, UpdateTime=timezone.now(), ) + _strip_system_super_if_needed(yonghuid) + return n def dedupe_active_assignments(): diff --git a/jituan/services/admin_context.py b/jituan/services/admin_context.py index f9970b7..cb9a7a0 100644 --- a/jituan/services/admin_context.py +++ b/jituan/services/admin_context.py @@ -149,12 +149,15 @@ def build_admin_club_context(user): def is_system_super_admin(user): - """系统级超管(Django 超管 / admin 账号 / 白名单手机号),非俱乐部 000001 角色。""" - return ( - bool(user.IsSuperuser) - or user.UserType == 'admin' - or getattr(user, 'Phone', '') in SUPER_ADMIN_PHONES - ) + """ + 系统级超管。 + 白名单手机号:始终超管。 + 其它账号:仅看 IsSuperuser / UserType=admin;停用集团任职时会清掉 IsSuperuser, + 避免「任职停了仍是集团上帝」。 + """ + if getattr(user, 'Phone', '') in SUPER_ADMIN_PHONES: + return True + return bool(user.IsSuperuser) or user.UserType == 'admin' def is_hardcoded_super_phone(user):