feat: 多俱乐部后台权限与管理员用户俱乐部分配

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-06 17:59:59 +08:00
parent f1c8633345
commit 1dfb46c38c
7 changed files with 253 additions and 23 deletions

View File

@@ -166,12 +166,32 @@ def verify_kefu_permission(request, username_from_frontend=None):
# ---------- 4. 获取用户的所有权限 ----------
# 使用 gvsdsdk RBAC 表UserRole → RolePermission → Permission
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_SINGLE
from jituan.services.admin_context import resolve_admin_effective_club, get_allowed_club_ids, is_system_super_admin
from jituan.services.club_rbac import filter_role_uuids_for_club_scope
eff_club = getattr(request, 'club_id', None) or CLUB_ID_DEFAULT
eff_scope = getattr(request, 'club_scope', None) or DATA_SCOPE_SINGLE
allowed_club_ids = None
if not is_system_super_admin(current_user):
eff_club, eff_scope, club_err = resolve_admin_effective_club(current_user, request)
if club_err:
return None, Response({'code': 403, 'msg': club_err}, status=403)
request.club_id = eff_club
request.club_scope = eff_scope
allowed_club_ids = list(get_allowed_club_ids(current_user))
try:
user_uuid = current_user.UserUUID
role_uuids = list(SdkUserRole.objects.filter(
UserUUID=user_uuid
).values_list('RoleUUID', flat=True))
if role_uuids and not is_system_super_admin(current_user):
role_uuids = filter_role_uuids_for_club_scope(
role_uuids, eff_club, eff_scope, allowed_club_ids,
)
if role_uuids:
perm_uuids = list(SdkRolePermission.objects.filter(
RoleUUID__in=role_uuids

View File

@@ -420,8 +420,10 @@ class GetAdminUserListView(APIView):
KefuProfile__isnull=False
).select_related('KefuProfile')
from jituan.services.club_rbac import filter_kefu_users_qs
users = filter_kefu_users_qs(users, request)
from jituan.models import AdminAssignment, Club
from jituan.constants import ADMIN_ROLE_LABELS
from jituan.services.club_rbac import filter_kefu_users_by_admin_scope
users = filter_kefu_users_by_admin_scope(users, request)
if phone:
users = users.filter(Phone__icontains=phone)
@@ -452,17 +454,37 @@ class GetAdminUserListView(APIView):
.values_list('UserUUID', 'RoleUUID')
) if _page_user_uuids else []
_role_uuids_in_page = list({r for _, r in _ur_pairs})
_role_name_by_uuid = {
r.RoleUUID: r.RoleName
_role_info_by_uuid = {
r.RoleUUID: {'role_name': r.RoleName, 'club_id': getattr(r, 'ClubID', '') or ''}
for r in Role.objects.filter(RoleUUID__in=_role_uuids_in_page)
} if _role_uuids_in_page else {}
_roles_by_user = {}
for user_uuid, role_uuid in _ur_pairs:
role_name = _role_name_by_uuid.get(role_uuid)
if role_name is not None:
_roles_by_user.setdefault(user_uuid, []).append(
{'role_code': role_name, 'role_name': role_name}
)
info = _role_info_by_uuid.get(role_uuid)
if info is not None:
_roles_by_user.setdefault(user_uuid, []).append({
'role_code': info['role_name'],
'role_name': info['role_name'],
'club_id': info['club_id'],
})
_page_yonghuids = [u.UserUID for u in _page_users]
_club_name_map = {c.club_id: c.name for c in Club.query.filter(status=1)}
_assignments_by_user = {}
if _page_yonghuids:
for a in AdminAssignment.query.filter(yonghuid__in=_page_yonghuids, status=1):
if a.club_id:
club_label = _club_name_map.get(a.club_id, a.club_id)
else:
club_label = '集团(全部子公司)'
_assignments_by_user.setdefault(a.yonghuid, []).append({
'id': a.id,
'club_id': a.club_id,
'club_label': club_label,
'role_code': a.role_code,
'role_name': ADMIN_ROLE_LABELS.get(a.role_code, a.role_code),
'is_primary': a.is_primary,
})
data_list = []
for user in _page_users:
@@ -473,6 +495,7 @@ class GetAdminUserListView(APIView):
'nicheng': user.KefuProfile.nicheng,
'club_id': getattr(user, 'ClubID', '') or '',
'roles': roles_data,
'assignments': _assignments_by_user.get(user.UserUID, []),
'status': user.KefuProfile.zhuangtai,
'create_time': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user, 'UserCreateTime', None) else '',
'update_time': user.KefuProfile.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user.KefuProfile, 'UpdateTime', None) else '',
@@ -512,7 +535,11 @@ class GetAdminRolesView(APIView):
return Response({'code': 403, 'msg': '您无权访问此页面'})
from jituan.services.club_rbac import filter_roles_for_request
roles = filter_roles_for_request(request).values('RoleName', 'ClubID')
all_clubs = request.data.get('all_clubs') in (True, 1, '1', 'true', 'yes')
if all_clubs:
roles = Role.objects.filter(RoleStatus=1).order_by('ClubID', 'RoleName').values('RoleName', 'ClubID')
else:
roles = filter_roles_for_request(request).values('RoleName', 'ClubID')
return Response({
'code': 0,
'data': {
@@ -660,10 +687,13 @@ class ModifyAdminUserView(APIView):
if not role_codes:
return Response({'code': 400, 'msg': '请选择角色'})
from jituan.services.club_rbac import resolve_role_by_name
club_id = getattr(target_user, 'ClubID', '') or ''
from jituan.services.club_context import resolve_club_id_from_request
bind_club_id = (request.data.get('target_club_id') or '').strip()
if not bind_club_id:
bind_club_id = resolve_club_id_from_request(request)
added, missing = [], []
for rc in role_codes:
role = resolve_role_by_name(rc, club_id)
role = resolve_role_by_name(rc, bind_club_id)
if not role:
missing.append(rc)
continue
@@ -696,13 +726,52 @@ class ModifyAdminUserView(APIView):
if not role_code:
return Response({'code': 400, 'msg': '缺少角色编码'})
from jituan.services.club_rbac import resolve_role_by_name
club_id = getattr(target_user, 'ClubID', '') or ''
role = resolve_role_by_name(role_code, club_id)
from jituan.services.club_context import resolve_club_id_from_request
bind_club_id = (request.data.get('target_club_id') or '').strip()
if not bind_club_id:
bind_club_id = resolve_club_id_from_request(request)
role = resolve_role_by_name(role_code, bind_club_id)
if not role:
return Response({'code': 404, 'msg': '角色不存在或与用户俱乐部不匹配'})
return Response({'code': 404, 'msg': '角色不存在或与所选俱乐部不匹配'})
UserRole.objects.filter(UserUUID=target_user.UserUUID, RoleUUID=role.RoleUUID).delete()
return Response({'code': 0, 'msg': '角色移除成功'})
elif action == 'add_club_assignment':
club_id = (request.data.get('club_id') or '').strip()
if not club_id:
return Response({'code': 400, 'msg': '请选择俱乐部'})
from jituan.services.admin_assignments import create_or_reactivate_assignment
from jituan.constants import DATA_SCOPE_SINGLE
role_code = (request.data.get('role_code') or 'CLUB_ADMIN').strip()
is_primary = bool(request.data.get('is_primary', False))
_, err = create_or_reactivate_assignment(
yonghuid=target_user.UserUID,
club_id=club_id,
role_code=role_code,
data_scope=DATA_SCOPE_SINGLE,
is_primary=is_primary,
granted_by=request.user.UserUID,
)
if err:
return Response({'code': 400, 'msg': err})
return Response({'code': 0, 'msg': '俱乐部任职已添加'})
elif action == 'remove_club_assignment':
assignment_id = request.data.get('assignment_id')
if not assignment_id:
return Response({'code': 400, 'msg': '缺少任职 id'})
from jituan.services.admin_assignments import deactivate_assignment
from jituan.models import AdminAssignment
row = AdminAssignment.query.filter(
id=assignment_id, yonghuid=target_user.UserUID, status=1,
).first()
if not row:
return Response({'code': 404, 'msg': '任职记录不存在'})
ok, err = deactivate_assignment(assignment_id)
if not ok:
return Response({'code': 404, 'msg': err or '停用失败'})
return Response({'code': 0, 'msg': '俱乐部任职已移除'})
else:
return Response({'code': 400, 'msg': '无效的action'})
@@ -816,6 +885,17 @@ class AddAdminUserView(APIView):
},
)
from jituan.services.admin_assignments import create_or_reactivate_assignment
from jituan.constants import DATA_SCOPE_SINGLE
create_or_reactivate_assignment(
yonghuid=yonghuid,
club_id=target_club_id,
role_code='CLUB_ADMIN',
data_scope=DATA_SCOPE_SINGLE,
is_primary=True,
granted_by=request.user.UserUID,
)
return Response({'code': 0, 'msg': '添加成功'})