278 lines
9.5 KiB
Python
278 lines
9.5 KiB
Python
"""集团后台管理员俱乐部上下文。"""
|
||
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)}
|
||
return {
|
||
a.club_id for a in assignments if a.club_id
|
||
}
|
||
|
||
|
||
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_switchable_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:
|
||
switchable = get_switchable_club_ids(user)
|
||
visible_clubs = [c for c in all_clubs if c['club_id'] in switchable]
|
||
club_id = next(iter(switchable), CLUB_ID_DEFAULT) if switchable 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
|
||
)
|
||
switchable_ids = get_switchable_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 switchable_ids:
|
||
club_id = primary_cid
|
||
elif switchable_ids:
|
||
club_id = sorted(switchable_ids)[0]
|
||
else:
|
||
club_id = primary_cid
|
||
visible_clubs = [c for c in all_clubs if c['club_id'] in switchable_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):
|
||
"""
|
||
顶栏可切换俱乐部:须已分配该俱乐部,且在该俱乐部下至少有一个功能角色。
|
||
无角色则不可切换,避免切过去后菜单空白却误显全部页面。
|
||
"""
|
||
from gvsdsdk.models import UserRole
|
||
from jituan.services.club_rbac import filter_role_uuids_for_club_scope
|
||
from jituan.constants import DATA_SCOPE_SINGLE
|
||
|
||
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 = get_allowed_club_ids(user)
|
||
if not allowed:
|
||
cid = (getattr(user, 'ClubID', None) or '').strip()
|
||
if cid:
|
||
allowed = {cid}
|
||
|
||
role_uuids = list(
|
||
UserRole.objects.filter(UserUUID=user.UserUUID).values_list('RoleUUID', flat=True)
|
||
)
|
||
if not allowed or not role_uuids:
|
||
return set()
|
||
|
||
switchable = set()
|
||
for cid in allowed:
|
||
if filter_role_uuids_for_club_scope(
|
||
role_uuids, cid, DATA_SCOPE_SINGLE, list(allowed),
|
||
):
|
||
switchable.add(cid)
|
||
return switchable
|
||
|
||
|
||
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,
|
||
}
|