尝试将 backend 做了视图拆分,来保证可维护性,若此版本存在生产环境问题应该回退
This commit is contained in:
806
backend/views/roles.py
Normal file
806
backend/views/roles.py
Normal file
@@ -0,0 +1,806 @@
|
||||
import hmac
|
||||
import threading
|
||||
import traceback
|
||||
import uuid
|
||||
import hashlib
|
||||
import xmltodict
|
||||
import time
|
||||
import logging
|
||||
import requests
|
||||
import os
|
||||
import secrets
|
||||
import random
|
||||
import string
|
||||
from calendar import monthrange
|
||||
from decimal import Decimal
|
||||
from datetime import date, datetime, timedelta
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.paginator import Paginator
|
||||
from django.db import models, transaction, IntegrityError
|
||||
from django.db.models import Q, Count, Sum, F, Exists, OuterRef
|
||||
from gvsdsdk.fluent import db, func, FQ
|
||||
from django.db.models.functions import TruncDate, TruncMonth, TruncYear
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
|
||||
|
||||
# 工具类导入
|
||||
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
|
||||
from backend.utils import (
|
||||
update_dashou_daily_by_action,
|
||||
update_shangjia_daily
|
||||
)
|
||||
from jituan.services.club_context import filter_club_char_field, filter_queryset_by_club, filter_user_related_by_club, filter_user_qs_by_club, orders_for_request
|
||||
from jituan.services.catalog_scope import block_non_group_catalog_scope
|
||||
from jituan.services.club_penalty import (
|
||||
filter_penalty_qs, resolve_penalty_club_id, filter_gsfenhong_qs,
|
||||
forbid_penalty_out_of_scope, resolve_penalty_record_club_id,
|
||||
)
|
||||
from jituan.services.club_user_access import forbid_if_user_out_of_scope, list_response_meta
|
||||
from shop.utils import update_dianpu_daily_stat
|
||||
from products.utils import update_shangpin_daily_stat
|
||||
from orders.notice_tasks import dingdan_guangbo
|
||||
from ..utils import (
|
||||
verify_kefu_permission,
|
||||
PERMISSION_TO_SHENFEN,
|
||||
SHENFEN_PROFILE_MAP,
|
||||
has_fadan_view_permission,
|
||||
has_merchant_order_permission,
|
||||
check_fadan_permission,
|
||||
pick_order_id,
|
||||
pick_penalty_create_params,
|
||||
write_xiugai_log,
|
||||
XIUGAI_LEIXING_DASHOU,
|
||||
XIUGAI_LEIXING_GUANSHI,
|
||||
XIUGAI_LEIXING_SHANGJIA,
|
||||
XIUGAI_LEIXING_ZUZHANG,
|
||||
XIUGAI_LEIXING_LABEL,
|
||||
)
|
||||
|
||||
# models 集中导入
|
||||
## gvsdsdk RBAC 模型(使用 PascalCase 字段名,匹配实际数据库表结构)
|
||||
from gvsdsdk.models import Role, Permission, RolePermission, UserRole
|
||||
## backend
|
||||
from backend.models import WithdrawalDailyStats
|
||||
|
||||
## users
|
||||
from users.models import (
|
||||
UserGuanshi, UserBoss, UserZuzhang,
|
||||
UserKefu, UserDashou, Xiugaijilu, UserShangjia, UserShenheguan
|
||||
)
|
||||
from users.business_models import User
|
||||
|
||||
## orders
|
||||
from orders.models import (
|
||||
CommissionRate, PlayerDeliveryImage, PenaltyRecord,
|
||||
OrderPlayerHistory, Order, RefundRecord,
|
||||
MerchantOrderExt, Penalty, PenaltyAppealImage, PenaltyBonus
|
||||
)
|
||||
|
||||
## shop
|
||||
from shop.models import Dianpu, DianpuMorenPeizhi, YonghuPingzheng, ShangpinLeixingDianpu, DianpuShangpinShenheShezhi
|
||||
|
||||
## products
|
||||
from products.models import (
|
||||
Shangpin, ShangpinLeixing, ShangpinZhuanqu, Huiyuan,
|
||||
DuociFenhong, Czjilu, Huiyuangoumai, Gsfenhong
|
||||
)
|
||||
|
||||
## config
|
||||
from config.models import (
|
||||
WithdrawConfig, ShangjiaLianjie, AccountPermission,
|
||||
TixianQuotaDefault,
|
||||
DailyIncomeStat, DailyPayoutStat, PopupPage, PopupConfig, PopupImage
|
||||
)
|
||||
|
||||
## rank
|
||||
from rank.models import (
|
||||
KaoheguanBankuai, Bankuai, Chenghao, KaoheCishuFeiyong,
|
||||
YonghuChenghao, ShenheJilu, KaoheJiluFeiyong, KaoheJujueJilu
|
||||
)
|
||||
|
||||
# 序列化器
|
||||
from ..serializers import PopupPageSerializer
|
||||
|
||||
# 全局常量、日志对象
|
||||
logger = logging.getLogger('houtai')
|
||||
SHOP_PRODUCT_PERMS = ['1199ab', '1199abc', '1199abd']
|
||||
|
||||
def _serialize_permission_row(perm_row):
|
||||
"""gvsdsdk Permission → 后台 Vue 约定的小写字段"""
|
||||
if isinstance(perm_row, dict):
|
||||
code = perm_row.get('PermCode') or perm_row.get('perm_code') or ''
|
||||
name = perm_row.get('PermName') or perm_row.get('perm_name') or code
|
||||
desc = perm_row.get('PermDesc') or perm_row.get('perm_desc') or ''
|
||||
else:
|
||||
code = perm_row.PermCode or ''
|
||||
name = perm_row.PermName or code
|
||||
desc = perm_row.PermDesc or ''
|
||||
return {'perm_code': code, 'perm_name': name, 'perm_desc': desc}
|
||||
|
||||
|
||||
class GetRolePermissionView(APIView):
|
||||
"""
|
||||
获取所有角色、权限以及角色关联的用户数
|
||||
权限要求:用户必须拥有 000001 权限
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
if not username:
|
||||
return Response({'code': 400, 'msg': '缺少username'})
|
||||
|
||||
# 1. 权限校验(验证客服身份,并获取权限列表)
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions # 直接返回错误响应
|
||||
|
||||
# 2. 检查是否有 000001 权限
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权访问此页面'})
|
||||
|
||||
# 3. 获取所有权限
|
||||
all_perms = Permission.objects.filter(PermStatus=1).values('PermCode', 'PermName', 'PermDesc')
|
||||
permissions_list = [_serialize_permission_row(p) for p in all_perms]
|
||||
|
||||
from jituan.services.club_rbac import count_role_users_in_scope, filter_roles_for_request
|
||||
|
||||
kefu_user_uuids = list(
|
||||
User.objects.filter(KefuProfile__isnull=False).values_list('UserUUID', flat=True)
|
||||
)
|
||||
roles = filter_roles_for_request(request)
|
||||
roles_data = []
|
||||
for role in roles:
|
||||
user_count = count_role_users_in_scope(role, request, kefu_user_uuids)
|
||||
role_perm_uuids = RolePermission.objects.filter(
|
||||
RoleUUID=role.RoleUUID
|
||||
).values_list('PermUUID', flat=True)
|
||||
perms_of_role = [
|
||||
_serialize_permission_row(p)
|
||||
for p in Permission.objects.filter(
|
||||
PermUUID__in=list(role_perm_uuids)
|
||||
).values('PermCode', 'PermName', 'PermDesc')
|
||||
]
|
||||
roles_data.append({
|
||||
'role_code': role.RoleName,
|
||||
'role_name': role.RoleName,
|
||||
'description': role.RoleDesc or '',
|
||||
'club_id': getattr(role, 'ClubID', '') or '',
|
||||
'user_count': user_count,
|
||||
'permissions': perms_of_role
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'roles': roles_data,
|
||||
'permissions': permissions_list
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class ModifyRolePermissionView(APIView):
|
||||
"""
|
||||
通用接口:支持删除角色、为角色添加权限、移除角色权限
|
||||
操作类型通过 action 字段区分
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
action = request.data.get('action') # delete_role, add_permission, remove_permission
|
||||
if not username or not action:
|
||||
return Response({'code': 400, 'msg': '缺少必要参数'})
|
||||
|
||||
# 权限校验
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权进行此操作'})
|
||||
|
||||
# 获取角色(需要角色名称)
|
||||
role_code = request.data.get('role_code')
|
||||
if not role_code:
|
||||
return Response({'code': 400, 'msg': '缺少角色编码'})
|
||||
|
||||
try:
|
||||
role = Role.objects.get(RoleName=role_code)
|
||||
except Role.DoesNotExist:
|
||||
return Response({'code': 404, 'msg': '角色不存在'})
|
||||
|
||||
# 超级管理员角色的特殊限制
|
||||
if role.RoleName == '管理员':
|
||||
if action in ['delete_role']:
|
||||
return Response({'code': 403, 'msg': '超级管理员角色不能删除'})
|
||||
|
||||
# 根据 action 处理
|
||||
if action == 'delete_role':
|
||||
# 删除角色:同时删除角色权限关联、用户角色关联
|
||||
with transaction.atomic():
|
||||
RolePermission.objects.filter(RoleUUID=role.RoleUUID).delete()
|
||||
UserRole.objects.filter(RoleUUID=role.RoleUUID).delete()
|
||||
role.delete()
|
||||
return Response({'code': 0, 'msg': '角色删除成功'})
|
||||
|
||||
elif action == 'add_permission':
|
||||
perm_code = request.data.get('perm_code')
|
||||
if not perm_code:
|
||||
return Response({'code': 400, 'msg': '缺少权限编码'})
|
||||
if role.RoleName == '管理员' and perm_code == '000001':
|
||||
return Response({'code': 403, 'msg': '此权限已存在,不能重复添加'})
|
||||
try:
|
||||
perm = Permission.objects.get(PermCode=perm_code)
|
||||
except Permission.DoesNotExist:
|
||||
return Response({'code': 404, 'msg': '权限不存在'})
|
||||
# 检查是否已存在
|
||||
if RolePermission.objects.filter(RoleUUID=role.RoleUUID, PermUUID=perm.PermUUID).exists():
|
||||
return Response({'code': 400, 'msg': '该角色已拥有此权限'})
|
||||
RolePermission.objects.create(
|
||||
RolePermUUID=uuid.uuid4().bytes,
|
||||
RoleUUID=role.RoleUUID,
|
||||
PermUUID=perm.PermUUID,
|
||||
CreateTime=timezone.now(),
|
||||
)
|
||||
return Response({'code': 0, 'msg': '权限添加成功'})
|
||||
|
||||
elif action == 'remove_permission':
|
||||
perm_code = request.data.get('perm_code')
|
||||
if not perm_code:
|
||||
return Response({'code': 400, 'msg': '缺少权限编码'})
|
||||
if role.RoleName == '管理员' and perm_code == '000001':
|
||||
return Response({'code': 403, 'msg': '不能移除超级管理员的此权限'})
|
||||
try:
|
||||
perm = Permission.objects.get(PermCode=perm_code)
|
||||
except Permission.DoesNotExist:
|
||||
return Response({'code': 404, 'msg': '权限不存在'})
|
||||
deleted, _ = RolePermission.objects.filter(RoleUUID=role.RoleUUID, PermUUID=perm.PermUUID).delete()
|
||||
if deleted == 0:
|
||||
return Response({'code': 400, 'msg': '该角色未拥有此权限'})
|
||||
return Response({'code': 0, 'msg': '权限移除成功'})
|
||||
|
||||
elif action == 'update_role':
|
||||
role_name = (request.data.get('role_name') or '').strip()
|
||||
description = (request.data.get('description') or '').strip()
|
||||
perm_codes = request.data.get('perm_codes') or []
|
||||
if role.RoleName == '管理员':
|
||||
role_name = role.RoleName
|
||||
elif role_name and role_name != role.RoleName:
|
||||
if Role.objects.filter(RoleName=role_name).exclude(RoleUUID=role.RoleUUID).exists():
|
||||
return Response({'code': 400, 'msg': '角色名称已存在'})
|
||||
with transaction.atomic():
|
||||
if role.RoleName != '管理员':
|
||||
if role_name:
|
||||
role.RoleName = role_name
|
||||
role.RoleDesc = description
|
||||
role.save(update_fields=['RoleName', 'RoleDesc'])
|
||||
RolePermission.objects.filter(RoleUUID=role.RoleUUID).delete()
|
||||
for pc in perm_codes:
|
||||
if pc == '000001' and role.RoleName != '管理员':
|
||||
continue
|
||||
try:
|
||||
perm = Permission.objects.get(PermCode=pc, PermStatus=1)
|
||||
except Permission.DoesNotExist:
|
||||
continue
|
||||
RolePermission.objects.create(
|
||||
RolePermUUID=uuid.uuid4().bytes,
|
||||
RoleUUID=role.RoleUUID,
|
||||
PermUUID=perm.PermUUID,
|
||||
CreateTime=timezone.now(),
|
||||
)
|
||||
if role.RoleName == '管理员':
|
||||
try:
|
||||
super_perm = Permission.objects.get(PermCode='000001', PermStatus=1)
|
||||
if not RolePermission.objects.filter(
|
||||
RoleUUID=role.RoleUUID, PermUUID=super_perm.PermUUID,
|
||||
).exists():
|
||||
RolePermission.objects.create(
|
||||
RolePermUUID=uuid.uuid4().bytes,
|
||||
RoleUUID=role.RoleUUID,
|
||||
PermUUID=super_perm.PermUUID,
|
||||
CreateTime=timezone.now(),
|
||||
)
|
||||
except Permission.DoesNotExist:
|
||||
pass
|
||||
return Response({'code': 0, 'msg': '角色保存成功'})
|
||||
|
||||
else:
|
||||
return Response({'code': 400, 'msg': '无效的 action'})
|
||||
|
||||
|
||||
class AddRoleView(APIView):
|
||||
"""
|
||||
添加角色:自动生成角色编码,前端提供角色名称、描述、选择的权限列表
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
role_name = request.data.get('role_name', '').strip()
|
||||
description = request.data.get('description', '').strip()
|
||||
perm_codes = request.data.get('perm_codes', []) # 列表
|
||||
|
||||
if not username or not role_name:
|
||||
return Response({'code': 400, 'msg': '缺少必要参数'})
|
||||
|
||||
# 权限校验
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权进行此操作'})
|
||||
|
||||
# 检查角色名称是否已存在(同俱乐部内唯一)
|
||||
from jituan.services.club_rbac import resolve_role_club_id, role_name_exists
|
||||
target_club_id = resolve_role_club_id(
|
||||
request, (request.data.get('target_club_id') or '').strip() or None,
|
||||
)
|
||||
if role_name_exists(role_name, target_club_id):
|
||||
return Response({'code': 400, 'msg': '该俱乐部下角色名称已存在'})
|
||||
|
||||
# 生成唯一角色编码(格式:ROLE_ + 8位随机字母数字)
|
||||
role_code = f"ROLE_{''.join(random.choices(string.ascii_uppercase + string.digits, k=8))}"
|
||||
|
||||
# 创建角色
|
||||
with transaction.atomic():
|
||||
role = Role.objects.create(
|
||||
RoleUUID=uuid.uuid4().bytes,
|
||||
TenantUUID=b'\x00' * 16,
|
||||
RoleName=role_name,
|
||||
RoleType=2,
|
||||
RoleStatus=1,
|
||||
AssignScope=0,
|
||||
RoleDesc=description,
|
||||
ClubID=target_club_id,
|
||||
CreateTime=timezone.now(),
|
||||
)
|
||||
# 绑定权限(过滤掉不存在的权限编码)
|
||||
for pc in perm_codes:
|
||||
# 不允许添加 000001 权限(此权限仅供超级管理员)
|
||||
if pc == '000001':
|
||||
continue
|
||||
try:
|
||||
perm = Permission.objects.get(PermCode=pc)
|
||||
RolePermission.objects.create(
|
||||
RolePermUUID=uuid.uuid4().bytes,
|
||||
RoleUUID=role.RoleUUID,
|
||||
PermUUID=perm.PermUUID,
|
||||
CreateTime=timezone.now(),
|
||||
)
|
||||
except Permission.DoesNotExist:
|
||||
logger.warning(f"添加角色时忽略不存在的权限编码: {pc}")
|
||||
|
||||
return Response({'code': 0, 'msg': '角色添加成功', 'data': {'role_code': role_code}})
|
||||
|
||||
|
||||
|
||||
class GetAdminUserListView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
username = request.data.get('username', '').strip()
|
||||
if not username:
|
||||
return Response({'code': 400, 'msg': '缺少username'})
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权访问此页面'})
|
||||
|
||||
phone = request.data.get('phone', '').strip()
|
||||
nicheng = request.data.get('nicheng', '').strip()
|
||||
role_code = request.data.get('roleCode', '').strip()
|
||||
page = int(request.data.get('page', 1))
|
||||
page_size = int(request.data.get('pageSize', 20))
|
||||
|
||||
# UserType 是 @property 非数据库字段,不能用 filter;
|
||||
# KefuProfile__isnull=False 已确保只查到客服用户
|
||||
users = User.query.filter(
|
||||
KefuProfile__isnull=False
|
||||
).select_related('KefuProfile')
|
||||
|
||||
from jituan.services.club_rbac import filter_kefu_users_qs
|
||||
users = filter_kefu_users_qs(users, request)
|
||||
|
||||
if phone:
|
||||
users = users.filter(Phone__icontains=phone)
|
||||
if nicheng:
|
||||
users = users.filter(KefuProfile__nicheng__icontains=nicheng)
|
||||
|
||||
# 关键修正:先求值为列表,再传入 phone__in
|
||||
if role_code:
|
||||
target_role = Role.objects.filter(RoleName=role_code).first()
|
||||
if target_role:
|
||||
user_uuids = list(
|
||||
UserRole.objects.filter(RoleUUID=target_role.RoleUUID)
|
||||
.values_list('UserUUID', flat=True)
|
||||
)
|
||||
users = users.filter(UserUUID__in=user_uuids)
|
||||
else:
|
||||
users = users.none()
|
||||
|
||||
users = users.order_by('-UserCreateTime')
|
||||
paginator = Paginator(users, page_size)
|
||||
page_obj = paginator.get_page(page)
|
||||
|
||||
data_list = []
|
||||
for user in page_obj:
|
||||
# 通过 gvsdsdk UserRole 获取用户角色
|
||||
user_role_uuids = list(
|
||||
UserRole.objects.filter(UserUUID=user.UserUUID)
|
||||
.values_list('RoleUUID', flat=True)
|
||||
)
|
||||
user_roles = Role.objects.filter(RoleUUID__in=user_role_uuids)
|
||||
roles_data = [
|
||||
{'role_code': r.RoleName, 'role_name': r.RoleName}
|
||||
for r in user_roles
|
||||
]
|
||||
data_list.append({
|
||||
'phone': user.Phone or '',
|
||||
'yonghuid': user.UserUID or '',
|
||||
'nicheng': user.KefuProfile.nicheng,
|
||||
'club_id': getattr(user, 'ClubID', '') or '',
|
||||
'roles': roles_data,
|
||||
'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 '',
|
||||
'CreateTime': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user, 'UserCreateTime', None) else '',
|
||||
'UpdateTime': user.KefuProfile.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user.KefuProfile, 'UpdateTime', None) else '',
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'list': data_list,
|
||||
'total': paginator.count,
|
||||
'page': page,
|
||||
'pageSize': page_size
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"GetAdminUserListView 异常: {e}\n{traceback.format_exc()}")
|
||||
return Response({'code': 500, 'msg': f'服务器内部错误: {str(e)}'})
|
||||
|
||||
|
||||
|
||||
class GetAdminRolesView(APIView):
|
||||
"""获取所有角色(用于筛选和分配)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
if not username:
|
||||
return Response({'code': 400, 'msg': '缺少username'})
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权访问此页面'})
|
||||
|
||||
from jituan.services.club_rbac import filter_roles_for_request
|
||||
roles = filter_roles_for_request(request).values('RoleName', 'ClubID')
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'roles': [
|
||||
{
|
||||
'role_code': r['RoleName'],
|
||||
'role_name': r['RoleName'],
|
||||
'club_id': r.get('ClubID') or '',
|
||||
}
|
||||
for r in roles
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
'''class GetAdminUserListView(APIView):
|
||||
"""获取后台用户列表(支持分页、筛选)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
if not username:
|
||||
return Response({'code': 400, 'msg': '缺少username'})
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权访问此页面'})
|
||||
|
||||
# 筛选条件
|
||||
phone = request.data.get('phone', '').strip()
|
||||
nicheng = request.data.get('nicheng', '').strip()
|
||||
role_code = request.data.get('roleCode', '').strip()
|
||||
page = int(request.data.get('page', 1))
|
||||
page_size = int(request.data.get('pageSize', 20))
|
||||
|
||||
# 基础查询:存在客服扩展表
|
||||
users = User.query.filter(KefuProfile__isnull=False).select_related('KefuProfile')
|
||||
if phone:
|
||||
users = users.filter(Phone__icontains=phone)
|
||||
if nicheng:
|
||||
users = users.filter(KefuProfile__nicheng__icontains=nicheng)
|
||||
if role_code:
|
||||
# 筛选拥有该角色的用户
|
||||
user_ids_with_role = UserRole.query.filter(role__role_code=role_code).values_list('account_id', flat=True)
|
||||
users = users.filter(phone__in=user_ids_with_role)
|
||||
|
||||
# 排序
|
||||
users = users.order_by('-CreateTime')
|
||||
|
||||
# 分页
|
||||
paginator = Paginator(users, page_size)
|
||||
page_obj = paginator.get_page(page)
|
||||
|
||||
# 构建返回数据
|
||||
data_list = []
|
||||
for user in page_obj:
|
||||
# 获取用户的所有角色
|
||||
user_roles = UserRole.query.filter(account_id=user.Phone).select_related('role')
|
||||
roles_data = [{'role_code': ur.role.role_code, 'role_name': ur.role.role_name} for ur in user_roles]
|
||||
data_list.append({
|
||||
'phone': user.Phone,
|
||||
'nicheng': user.KefuProfile.nicheng,
|
||||
'roles': roles_data,
|
||||
'status': user.KefuProfile.zhuangtai,
|
||||
'CreateTime': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'UpdateTime': user.UpdateTime.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'list': data_list,
|
||||
'total': paginator.count,
|
||||
'page': page,
|
||||
'pageSize': page_size
|
||||
}
|
||||
})'''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ModifyAdminUserView(APIView):
|
||||
"""修改后台用户信息(昵称、密码、二级密码、状态、角色)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
action = request.data.get('action')
|
||||
if not username or not action:
|
||||
return Response({'code': 400, 'msg': '缺少参数'})
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权进行此操作'})
|
||||
|
||||
target_yonghuid = request.data.get('yonghuid', '').strip()
|
||||
target_phone = request.data.get('phone', '').strip()
|
||||
if not target_yonghuid and not target_phone:
|
||||
return Response({'code': 400, 'msg': '缺少目标用户(yonghuid 或 phone)'})
|
||||
|
||||
from jituan.services.club_rbac import resolve_kefu_admin_user
|
||||
target_user = resolve_kefu_admin_user(yonghuid=target_yonghuid, phone=target_phone)
|
||||
if not target_user:
|
||||
return Response({'code': 404, 'msg': '用户不存在或非后台账号'})
|
||||
try:
|
||||
target_kefu = target_user.KefuProfile
|
||||
except UserKefu.DoesNotExist:
|
||||
return Response({'code': 404, 'msg': '用户扩展信息缺失'})
|
||||
|
||||
with transaction.atomic():
|
||||
if action == 'update_user_info':
|
||||
# 修改昵称、状态、密码、二级密码
|
||||
nicheng = request.data.get('nicheng')
|
||||
status = request.data.get('status')
|
||||
password = request.data.get('password')
|
||||
erjimima = request.data.get('erjimima')
|
||||
if nicheng is not None:
|
||||
target_kefu.nicheng = nicheng
|
||||
if status is not None:
|
||||
target_kefu.zhuangtai = int(status)
|
||||
if password:
|
||||
if len(password) < 6:
|
||||
return Response({'code': 400, 'msg': '密码至少6位'})
|
||||
target_user.SetPassword(password)
|
||||
if erjimima:
|
||||
if len(erjimima) < 6:
|
||||
return Response({'code': 400, 'msg': '二级密码至少6位'})
|
||||
import bcrypt as _bcrypt
|
||||
target_kefu.erjimima = _bcrypt.hashpw(erjimima.encode('utf-8'), _bcrypt.gensalt(rounds=12)).decode('utf-8')
|
||||
target_user.save()
|
||||
target_kefu.save()
|
||||
return Response({'code': 0, 'msg': '修改成功'})
|
||||
|
||||
elif action == 'add_user_roles':
|
||||
role_codes = request.data.get('role_codes', [])
|
||||
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 ''
|
||||
added, missing = [], []
|
||||
for rc in role_codes:
|
||||
role = resolve_role_by_name(rc, club_id)
|
||||
if not role:
|
||||
missing.append(rc)
|
||||
continue
|
||||
_, created = UserRole.objects.get_or_create(
|
||||
UserUUID=target_user.UserUUID,
|
||||
RoleUUID=role.RoleUUID,
|
||||
defaults={
|
||||
'UserRoleUUID': uuid.uuid4().bytes,
|
||||
'CompanyUUID': uuid.UUID('b0000000-0000-0000-0000-000000000001').bytes,
|
||||
},
|
||||
)
|
||||
if created:
|
||||
added.append(role.RoleName)
|
||||
elif role.RoleName not in added:
|
||||
added.append(role.RoleName)
|
||||
if not added:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'msg': f'未找到可绑定的角色:{", ".join(missing or role_codes)}。请确认顶栏俱乐部与角色所属俱乐部一致',
|
||||
})
|
||||
if missing:
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': f'已绑定:{", ".join(added)};未找到:{", ".join(missing)}',
|
||||
})
|
||||
return Response({'code': 0, 'msg': '角色添加成功'})
|
||||
|
||||
elif action == 'remove_user_role':
|
||||
role_code = request.data.get('role_code')
|
||||
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)
|
||||
if not role:
|
||||
return Response({'code': 404, 'msg': '角色不存在或与用户俱乐部不匹配'})
|
||||
UserRole.objects.filter(UserUUID=target_user.UserUUID, RoleUUID=role.RoleUUID).delete()
|
||||
return Response({'code': 0, 'msg': '角色移除成功'})
|
||||
|
||||
else:
|
||||
return Response({'code': 400, 'msg': '无效的action'})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AddAdminUserView(APIView):
|
||||
"""添加后台用户(客服账号)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '').strip()
|
||||
if not username:
|
||||
return Response({'code': 400, 'msg': '缺少username'})
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, username)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if '000001' not in permissions:
|
||||
return Response({'code': 403, 'msg': '您无权进行此操作'})
|
||||
|
||||
phone = request.data.get('phone', '').strip()
|
||||
password = request.data.get('password', '').strip()
|
||||
erjimima = request.data.get('erjimima', '').strip()
|
||||
nicheng = request.data.get('nicheng', '').strip()
|
||||
role_codes = request.data.get('role_codes', [])
|
||||
|
||||
if not all([phone, password, erjimima, nicheng]):
|
||||
return Response({'code': 400, 'msg': '请填写完整信息'})
|
||||
if len(password) < 6 or len(erjimima) < 6:
|
||||
return Response({'code': 400, 'msg': '密码和二级密码至少6位'})
|
||||
|
||||
from jituan.services.club_context import resolve_club_id_from_request
|
||||
from jituan.services.club_rbac import resolve_roles_for_binding
|
||||
|
||||
target_club_id = resolve_club_id_from_request(request)
|
||||
|
||||
# 校验手机号是否已被后台客服占用(同俱乐部优先)
|
||||
existing_kefu = User.query.filter(
|
||||
Phone=phone, KefuProfile__isnull=False
|
||||
).order_by('-UserCreateTime').first()
|
||||
if existing_kefu:
|
||||
return Response({'code': 400, 'msg': '该账号已存在'})
|
||||
|
||||
if not isinstance(role_codes, list):
|
||||
return Response({'code': 400, 'msg': 'role_codes 格式错误'})
|
||||
|
||||
# 分配角色(默认客服 + 所选角色,去重;任一角色找不到则整单拒绝)
|
||||
role_names_to_bind = []
|
||||
seen_names = set()
|
||||
for name in (['客服'] if '客服' not in role_codes else []) + list(role_codes):
|
||||
n = (name or '').strip()
|
||||
if n and n not in seen_names:
|
||||
seen_names.add(n)
|
||||
role_names_to_bind.append(n)
|
||||
|
||||
roles_to_bind, missing_roles = resolve_roles_for_binding(role_names_to_bind, target_club_id)
|
||||
if missing_roles:
|
||||
return Response({
|
||||
'code': 400,
|
||||
'msg': f'角色不存在或与当前俱乐部不匹配:{", ".join(missing_roles)}',
|
||||
})
|
||||
if not roles_to_bind:
|
||||
return Response({'code': 400, 'msg': '请至少选择一个有效角色'})
|
||||
|
||||
with transaction.atomic():
|
||||
# 事务内二次校验,防止并发重复创建
|
||||
if User.query.filter(Phone=phone, KefuProfile__isnull=False).exists():
|
||||
return Response({'code': 400, 'msg': '该账号已存在'})
|
||||
|
||||
# 生成唯一的 yonghuid(7位数字)
|
||||
while True:
|
||||
yonghuid = str(random.randint(1000000, 9999999))
|
||||
if not User.query.filter(UserUID=yonghuid).exists():
|
||||
break
|
||||
|
||||
# 生成唯一的 openid(格式:admin_{时间戳}_{随机6位})
|
||||
timestamp = str(int(time.time() * 1000))
|
||||
random_suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
||||
openid = f"admin_{timestamp}_{random_suffix}"
|
||||
while User.query.filter(OpenID=openid).exists():
|
||||
random_suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
||||
openid = f"admin_{timestamp}_{random_suffix}"
|
||||
|
||||
user_main = User.query.create(
|
||||
UserUID=yonghuid,
|
||||
UserName=openid,
|
||||
OpenID=openid,
|
||||
Phone=phone,
|
||||
ClubID=target_club_id,
|
||||
)
|
||||
user_main.SetPassword(password)
|
||||
user_main.save(update_fields=['UserPassword', 'ClubID'])
|
||||
# 创建客服扩展表(后台用户扩展)
|
||||
import bcrypt as _bcrypt
|
||||
erjimima_hashed = _bcrypt.hashpw(erjimima.encode('utf-8'), _bcrypt.gensalt(rounds=12)).decode('utf-8')
|
||||
UserKefu.query.create(
|
||||
user=user_main,
|
||||
nicheng=nicheng,
|
||||
erjimima=erjimima_hashed,
|
||||
zhuangtai=1
|
||||
)
|
||||
for role in roles_to_bind:
|
||||
UserRole.objects.get_or_create(
|
||||
UserUUID=user_main.UserUUID,
|
||||
RoleUUID=role.RoleUUID,
|
||||
defaults={
|
||||
'UserRoleUUID': uuid.uuid4().bytes,
|
||||
'CompanyUUID': uuid.UUID('b0000000-0000-0000-0000-000000000001').bytes,
|
||||
},
|
||||
)
|
||||
|
||||
return Response({'code': 0, 'msg': '添加成功'})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user