"""集团后台管理员俱乐部上下文。""" from jituan.constants import ( ADMIN_ROLE_LABELS, DATA_SCOPE_ALL, DATA_SCOPE_SINGLE, CLUB_ID_DEFAULT, SUPER_ADMIN_PHONES, ) from jituan.models import AdminAssignment, Club GROUP_MANAGE_ROLES = frozenset({ 'GROUP_OWNER', 'GROUP_SUPER_ADMIN', }) def get_allowed_club_ids(user): """用户可切换/访问的子公司 club_id 集合(不含集团汇总)。""" if is_system_super_admin(user): return {c.club_id for c in Club.query.filter(status=1)} assignments = AdminAssignment.query.filter(yonghuid=user.UserUID, status=1) has_group = assignments.filter( club_id__isnull=True ).exists() or assignments.filter(data_scope=DATA_SCOPE_ALL).exists() if has_group: return {c.club_id for c in Club.query.filter(status=1)} allowed = { a.club_id for a in assignments if a.club_id } if not allowed: cid = (getattr(user, 'ClubID', None) or '').strip() if cid: allowed = {cid} return allowed def resolve_admin_effective_club(user, request): """ 校验后台用户请求的俱乐部/范围,返回 (club_id, scope, error_msg)。 无权限访问请求的俱乐部时,回退到主任职俱乐部(避免 localStorage 脏数据串俱乐部)。 """ from jituan.services.club_context import resolve_club_id_from_request, resolve_club_scope requested_club = resolve_club_id_from_request(request) requested_scope = resolve_club_scope(request) if is_system_super_admin(user): return requested_club, requested_scope, None ctx = build_admin_club_context(user) allowed = get_allowed_club_ids(user) if requested_scope == DATA_SCOPE_ALL: if not ctx.get('is_group_admin'): primary = ctx.get('club_id') or CLUB_ID_DEFAULT return primary, DATA_SCOPE_SINGLE, None return ctx.get('club_id') or CLUB_ID_DEFAULT, DATA_SCOPE_ALL, None if requested_club not in allowed: primary = ctx.get('club_id') or CLUB_ID_DEFAULT return primary, DATA_SCOPE_SINGLE, None return requested_club, DATA_SCOPE_SINGLE, None def build_admin_club_context(user): """ 根据 admin_assignment + 超管推断后台登录后的俱乐部上下文。 返回 dict 供前端存储与请求头使用。 """ yonghuid = user.UserUID is_super = is_system_super_admin(user) assignments = list( AdminAssignment.query.filter(yonghuid=yonghuid, status=1).order_by('-is_primary', 'club_id') ) clubs_qs = Club.query.filter(status=1).order_by('sort_order', 'club_id') all_clubs = [ {'club_id': c.club_id, 'name': c.name} for c in clubs_qs ] # 系统超管:始终集团视角 + 全部俱乐部(不受任职记录限制) if is_super: role_code = 'GROUP_SUPER_ADMIN' if assignments: primary = next((a for a in assignments if a.is_primary), assignments[0]) role_code = primary.role_code or role_code return _pack( scope=DATA_SCOPE_ALL, club_id=CLUB_ID_DEFAULT, is_group_admin=True, assignments=assignments, clubs=all_clubs, can_switch_club=True, role_code=role_code, role_name=ADMIN_ROLE_LABELS.get(role_code, role_code), ) if not assignments: allowed_ids = get_allowed_club_ids(user) visible_clubs = [c for c in all_clubs if c['club_id'] in allowed_ids] club_id = next(iter(allowed_ids), CLUB_ID_DEFAULT) if allowed_ids else CLUB_ID_DEFAULT return _pack( scope=DATA_SCOPE_SINGLE, club_id=club_id, is_group_admin=False, assignments=[], clubs=visible_clubs, can_switch_club=len(visible_clubs) > 1, role_code='CLUB_ADMIN', role_name='子公司客服(默认)', ) primary = next((a for a in assignments if a.is_primary), assignments[0]) has_group = any( a.club_id is None or a.data_scope == DATA_SCOPE_ALL for a in assignments ) allowed_ids = get_allowed_club_ids(user) if has_group: scope = DATA_SCOPE_ALL club_id = CLUB_ID_DEFAULT visible_clubs = all_clubs else: scope = DATA_SCOPE_SINGLE primary_cid = primary.club_id or CLUB_ID_DEFAULT if primary_cid in allowed_ids: club_id = primary_cid elif allowed_ids: club_id = sorted(allowed_ids)[0] else: club_id = primary_cid visible_clubs = [c for c in all_clubs if c['club_id'] in allowed_ids] role_code = primary.role_code or 'CLUB_ADMIN' return _pack( scope=scope, club_id=club_id, is_group_admin=has_group, assignments=assignments, clubs=visible_clubs, can_switch_club=has_group or len(visible_clubs) > 1, role_code=role_code, role_name=ADMIN_ROLE_LABELS.get(role_code, role_code), ) 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 ) def is_kefu_backend_account(user): """可登录客服后台:超管 / UserType=kefu / 有正常 KefuProfile。""" if is_system_super_admin(user): return True if getattr(user, 'UserType', '') == 'kefu': return True try: return user.KefuProfile.zhuangtai == 1 except Exception: return False def can_manage_admin_assignments(user, permissions=None): """是否可维护数据范围任职、俱乐部密钥等集团级配置(不含俱乐部 000001)。""" if is_system_super_admin(user): return True ctx = build_admin_club_context(user) if ctx.get('is_group_admin') and ctx.get('role_code') in GROUP_MANAGE_ROLES: return True return any( a.role_code in GROUP_MANAGE_ROLES and (a.club_id is None or a.data_scope == DATA_SCOPE_ALL) for a in AdminAssignment.query.filter(yonghuid=user.UserUID, status=1) ) def get_switchable_club_ids(user): """顶栏可切换俱乐部:有任职/数据范围即可切换;菜单按当前俱乐部功能角色展示。""" return get_allowed_club_ids(user) def can_manage_roles_for_club(user, permissions, club_id): """ 是否可为指定俱乐部分配/移除功能角色。 集团高层:全部俱乐部;否则须在该俱乐部拥有 000001 超级管理权限。 """ if can_manage_admin_assignments(user, permissions): return True cid = (club_id or '').strip() if not cid: return False from jituan.services.club_rbac import resolve_permission_codes_for_club codes = resolve_permission_codes_for_club(user, cid) return '000001' in codes def get_manageable_club_ids(user, permissions=None): """当前操作者可管理角色分配的俱乐部集合。""" if can_manage_admin_assignments(user, permissions): return {c.club_id for c in Club.query.filter(status=1)} return { c.club_id for c in Club.query.filter(status=1) if can_manage_roles_for_club(user, permissions, c.club_id) } def _pack(scope, club_id, is_group_admin, assignments, clubs, can_switch_club, role_code, role_name): allowed_club_ids = sorted({ c['club_id'] for c in clubs if c.get('club_id') }) return { 'scope': scope, 'club_id': club_id, 'is_group_admin': is_group_admin, 'role_code': role_code, 'role_name': role_name, 'allowed_club_ids': allowed_club_ids, 'perm_source': 'gvsdsdk', 'perm_note': ( '【功能权限】在「角色管理」为各俱乐部创建角色,在「管理员用户」绑定;' '切换俱乐部后菜单按该俱乐部角色生效。' '【数据范围】在本页添加任职,可分配多个俱乐部,无需集团权限即可切换。' ), 'assignments': [ { 'club_id': a.club_id, 'role_code': a.role_code, 'role_name': ADMIN_ROLE_LABELS.get(a.role_code, a.role_code), 'data_scope': a.data_scope, 'is_primary': a.is_primary, } for a in assignments ], 'clubs': clubs, 'can_switch_club': can_switch_club, }