400 lines
16 KiB
Python
400 lines
16 KiB
Python
"""用户详情 API — 查询用户关联的角色、权限、订单等信息
|
||
支持主服务器和子服务器(通过 MSYNC form proxy 查询)。"""
|
||
|
||
from rest_framework.response import Response
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from . import ELTAuthenticatedAPIView
|
||
from gvsdsdk.auth.permissions import PermissionVerifier
|
||
from gvsdsdk.model_utils import UUIDObj
|
||
import datetime
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class UserDetailView(ELTAuthenticatedAPIView):
|
||
"""查询指定用户的详细信息:角色、权限、订单等
|
||
|
||
GET /api/gerp/users/detail/?server=<name>&UserUUID=<uuid>
|
||
|
||
参数:
|
||
server: 服务器名称(默认 gvsds-master 查主库)
|
||
UserUUID: 用户 UUID(十六进制字符串)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def get(self, request):
|
||
if not PermissionVerifier(True, ['admin:saas_server'], user=request.user, request=request):
|
||
return Response({'code': 403, 'msg': '权限不足'}, status=403)
|
||
|
||
server_name = request.query_params.get('server', 'gvsds-master')
|
||
user_uuid_str = request.query_params.get('UserUUID', '')
|
||
if not user_uuid_str:
|
||
return Response({'code': 1, 'msg': '缺少 UserUUID 参数'})
|
||
|
||
# 主服务器直接 ORM 查询
|
||
if server_name == 'gvsds-master':
|
||
return self._query_master(user_uuid_str)
|
||
|
||
# 子服务器通过 form proxy 查询
|
||
return self._query_sub(server_name, user_uuid_str)
|
||
|
||
def _query_master(self, user_uuid_str):
|
||
"""主服务器:直接 ORM 查询"""
|
||
from gvsds.apps.core.models import User
|
||
from gvsds.apps.gerp.models import UserRole, Role, RolePermission, Permission
|
||
|
||
try:
|
||
user_uuid = UUIDObj(user_uuid_str).bytes
|
||
except Exception:
|
||
return Response({'code': 1, 'msg': '无效的 UserUUID'})
|
||
|
||
# 用户基本信息
|
||
try:
|
||
user = User.objects.get(UserUUID=user_uuid)
|
||
except User.DoesNotExist:
|
||
return Response({'code': 1, 'msg': '用户不存在'})
|
||
|
||
user_info = {
|
||
'UserUUID': user_uuid_str,
|
||
'UserName': user.UserName,
|
||
'UserEmail': user.UserEmail or '',
|
||
'UserTele': user.UserTele or '',
|
||
'UserGender': user.UserGender,
|
||
'UserAccountLicense': user.UserAccountLicense,
|
||
'UserPositionStatus': user.UserPositionStatus,
|
||
'UserCreateTime': str(user.UserCreateTime) if user.UserCreateTime else '',
|
||
'UserLastLoginDate': str(user.UserLastLoginDate) if user.UserLastLoginDate else '',
|
||
'IsStaff': user.IsStaff,
|
||
'IsSuperuser': user.IsSuperuser,
|
||
}
|
||
|
||
# 角色列表(通过 UserRole.CompanyUUID 获取关联公司)
|
||
roles = []
|
||
user_roles = UserRole.objects.filter(UserUUID=user_uuid)
|
||
for ur in user_roles:
|
||
role = Role.objects.filter(RoleUUID=ur.RoleUUID).first()
|
||
if role:
|
||
company_name = ''
|
||
company_short = ''
|
||
if ur.CompanyUUID:
|
||
from gvsds.apps.gerp.models import Company
|
||
company = Company.objects.filter(CompanyUUID=ur.CompanyUUID).first()
|
||
if company:
|
||
company_name = company.CompanyName
|
||
company_short = company.CompanyShort or ''
|
||
roles.append({
|
||
'RoleUUID': str(UUIDObj(role.RoleUUID)) if role.RoleUUID else '',
|
||
'RoleName': role.RoleName,
|
||
'RoleType': role.RoleType,
|
||
'RoleStatus': role.RoleStatus,
|
||
'AssignScope': role.AssignScope,
|
||
'RoleDesc': role.RoleDesc or '',
|
||
'CompanyName': company_name,
|
||
'CompanyShort': company_short,
|
||
})
|
||
|
||
# 权限列表(通过 UserRole → RolePermission → Permission 链路查询,按公司过滤)
|
||
permissions = []
|
||
role_uuids = [ur.RoleUUID for ur in user_roles if ur.RoleUUID]
|
||
company_uuids = [ur.CompanyUUID for ur in user_roles if ur.CompanyUUID]
|
||
if role_uuids:
|
||
rp_qs = RolePermission.objects.filter(
|
||
RoleUUID__in=role_uuids
|
||
)
|
||
if company_uuids:
|
||
rp_qs = rp_qs.filter(CompanyUUID__in=company_uuids)
|
||
perm_uuids = rp_qs.values_list('PermUUID', flat=True)
|
||
perms = Permission.objects.filter(PermUUID__in=perm_uuids, PermStatus=1)
|
||
seen = set()
|
||
for p in perms:
|
||
key = p.PermCode
|
||
if key not in seen:
|
||
seen.add(key)
|
||
permissions.append({
|
||
'PermName': p.PermName,
|
||
'PermCode': p.PermCode,
|
||
'PermCategory': p.PermCategory or '',
|
||
'PermDesc': p.PermDesc or '',
|
||
})
|
||
|
||
# 订单列表(最近 20 条)
|
||
orders = []
|
||
try:
|
||
from gvsdsdk.order.models import Order
|
||
user_orders = Order.objects.filter(
|
||
BuyerUserUUID=user_uuid
|
||
).order_by('-CreateTime')[:20]
|
||
for o in user_orders:
|
||
orders.append({
|
||
'OrderNO': o.OrderNO,
|
||
'OrderTitle': o.OrderTitle,
|
||
'OrderType': o.OrderType,
|
||
'TotalAmount': str(o.TotalAmount),
|
||
'PayAmount': str(o.PayAmount),
|
||
'Currency': o.Currency,
|
||
'OrderStatus': o.OrderStatus,
|
||
'CreateTime': str(o.CreateTime) if o.CreateTime else '',
|
||
})
|
||
except Exception:
|
||
pass # 订单表可能不存在
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'User': user_info,
|
||
'Roles': roles,
|
||
'Permissions': permissions,
|
||
'Orders': orders,
|
||
}
|
||
})
|
||
|
||
def _query_sub(self, server_name, user_uuid_str):
|
||
"""子服务器:通过 form proxy 查询 User 表"""
|
||
from gvsds.apps.msync.models import ServiceNode
|
||
from gvsds.apps.gerp.views.msync_admin import _get_sub_db_connection
|
||
|
||
node = ServiceNode.objects.filter(ServiceName=server_name).first()
|
||
if not node:
|
||
return Response({'code': 1, 'msg': f'服务不存在: {server_name}'})
|
||
|
||
conn, err = _get_sub_db_connection(node)
|
||
if err:
|
||
return Response({'code': 1, 'msg': err})
|
||
|
||
try:
|
||
conn.ensure_connection()
|
||
except Exception as e:
|
||
return Response({'code': 1, 'msg': f'数据库连接失败: {e}'})
|
||
|
||
try:
|
||
import uuid as _uuid
|
||
user_uuid_bytes = _uuid.UUID(user_uuid_str).bytes
|
||
except Exception:
|
||
return Response({'code': 1, 'msg': '无效的 UserUUID'})
|
||
|
||
try:
|
||
with conn.cursor() as cursor:
|
||
# 查用户基本信息
|
||
cursor.execute(
|
||
"SELECT UserUUID, UserName, UserEmail, UserTele, UserGender, "
|
||
"UserAccountLicense, UserPositionStatus, UserCreateTime, UserLastLoginDate, "
|
||
"IsStaff, IsSuperuser FROM `User` WHERE UserUUID = %s",
|
||
[user_uuid_bytes]
|
||
)
|
||
row = cursor.fetchone()
|
||
if not row:
|
||
return Response({'code': 1, 'msg': '用户不存在'})
|
||
|
||
cols = [desc[0] for desc in cursor.description]
|
||
user_info = {}
|
||
for i, col in enumerate(cols):
|
||
val = row[i]
|
||
if isinstance(val, bytes) and len(val) == 16:
|
||
try:
|
||
val = str(_uuid.UUID(bytes=val))
|
||
except Exception:
|
||
val = val.hex()
|
||
elif isinstance(val, datetime.datetime):
|
||
val = str(val)
|
||
user_info[col] = val
|
||
|
||
# 查角色(通过 UserRole.CompanyUUID 获取关联公司)
|
||
cursor.execute(
|
||
"SELECT r.RoleUUID, r.RoleName, r.RoleType, r.RoleStatus, "
|
||
"r.AssignScope, r.RoleDesc, c.CompanyName, c.CompanyShort "
|
||
"FROM UserRole ur "
|
||
"JOIN Role r ON ur.RoleUUID = r.RoleUUID "
|
||
"LEFT JOIN Company c ON ur.CompanyUUID = c.CompanyUUID "
|
||
"WHERE ur.UserUUID = %s",
|
||
[user_uuid_bytes]
|
||
)
|
||
roles = []
|
||
for r in cursor.fetchall():
|
||
roles.append({
|
||
'RoleUUID': str(_uuid.UUID(bytes=r[0])) if isinstance(r[0], bytes) and len(r[0]) == 16 else str(r[0]),
|
||
'RoleName': r[1],
|
||
'RoleType': r[2],
|
||
'RoleStatus': r[3],
|
||
'AssignScope': r[4],
|
||
'RoleDesc': r[5] or '',
|
||
'CompanyName': r[6] or '',
|
||
'CompanyShort': r[7] or '',
|
||
})
|
||
|
||
# 查权限(通过 UserRole → RolePermission → Permission 链路)
|
||
cursor.execute(
|
||
"SELECT DISTINCT p.PermName, p.PermCode, p.PermCategory, p.PermDesc "
|
||
"FROM Permission p "
|
||
"JOIN RolePermission rp ON p.PermUUID = rp.PermUUID "
|
||
"JOIN UserRole ur ON rp.RoleUUID = ur.RoleUUID "
|
||
"WHERE ur.UserUUID = %s AND p.PermStatus = 1",
|
||
[user_uuid_bytes]
|
||
)
|
||
permissions = []
|
||
for p in cursor.fetchall():
|
||
permissions.append({
|
||
'PermName': p[0],
|
||
'PermCode': p[1],
|
||
'PermCategory': p[2] or '',
|
||
'PermDesc': p[3] or '',
|
||
})
|
||
|
||
# 查订单(如果 Order 表存在)
|
||
orders = []
|
||
try:
|
||
cursor.execute(
|
||
"SELECT OrderNO, OrderTitle, OrderType, TotalAmount, PayAmount, "
|
||
"Currency, OrderStatus, CreateTime FROM `Order` "
|
||
"WHERE BuyerUserUUID = %s ORDER BY CreateTime DESC LIMIT 20",
|
||
[user_uuid_bytes]
|
||
)
|
||
for o in cursor.fetchall():
|
||
orders.append({
|
||
'OrderNO': o[0],
|
||
'OrderTitle': o[1],
|
||
'OrderType': o[2],
|
||
'TotalAmount': str(o[3]),
|
||
'PayAmount': str(o[4]),
|
||
'Currency': o[5],
|
||
'OrderStatus': o[6],
|
||
'CreateTime': str(o[7]) if o[7] else '',
|
||
})
|
||
except Exception:
|
||
pass
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'User': user_info,
|
||
'Roles': roles,
|
||
'Permissions': permissions,
|
||
'Orders': orders,
|
||
}
|
||
})
|
||
except Exception as e:
|
||
logger.error(f'查询子服务器 {server_name} 用户详情失败: {e}')
|
||
return Response({'code': 1, 'msg': f'查询失败: {e}'})
|
||
|
||
|
||
class UserListAPIView(ELTAuthenticatedAPIView):
|
||
"""查询用户列表(支持主服务器和子服务器)
|
||
|
||
GET /api/gerp/users/list/?server=<name>&PageIndex=1&PageSize=20&Keyword=xxx
|
||
|
||
参数:
|
||
server: 服务器名称(默认 gvsds-master)
|
||
PageIndex: 页码
|
||
PageSize: 每页条数
|
||
Keyword: 搜索关键词(可选,匹配用户名/邮箱)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def get(self, request):
|
||
if not PermissionVerifier(True, ['admin:saas_server'], user=request.user, request=request):
|
||
return Response({'code': 403, 'msg': '权限不足'}, status=403)
|
||
|
||
server_name = request.query_params.get('server', 'gvsds-master')
|
||
page_index = int(request.query_params.get('PageIndex', 1))
|
||
page_size = int(request.query_params.get('PageSize', 20))
|
||
keyword = request.query_params.get('Keyword', '').strip()
|
||
|
||
if server_name == 'gvsds-master':
|
||
return self._query_master(page_index, page_size, keyword)
|
||
return self._query_sub(server_name, page_index, page_size, keyword)
|
||
|
||
def _query_master(self, page_index, page_size, keyword):
|
||
"""主服务器:直接 ORM 查询"""
|
||
from gvsds.apps.core.models import User
|
||
|
||
qs = User.objects.all()
|
||
if keyword:
|
||
qs = qs.filter(UserName__icontains=keyword) | qs.filter(UserEmail__icontains=keyword)
|
||
|
||
total = qs.count()
|
||
offset = (page_index - 1) * page_size
|
||
users = qs[offset:offset + page_size]
|
||
|
||
records = []
|
||
for u in users:
|
||
records.append({
|
||
'UserUUID': UUIDObj(u.UserUUID).str if u.UserUUID else '',
|
||
'UserName': u.UserName or '',
|
||
'UserEmail': u.UserEmail or '',
|
||
'UserTele': u.UserTele or '',
|
||
'UserGender': u.UserGender,
|
||
'UserAccountLicense': u.UserAccountLicense,
|
||
'UserPositionStatus': u.UserPositionStatus,
|
||
'UserCreateTime': str(u.UserCreateTime) if u.UserCreateTime else '',
|
||
'UserLastLoginDate': str(u.UserLastLoginDate) if u.UserLastLoginDate else '',
|
||
'IsStaff': u.IsStaff,
|
||
'IsSuperuser': u.IsSuperuser,
|
||
})
|
||
|
||
return Response({'code': 0, 'data': records, 'total': total})
|
||
|
||
def _query_sub(self, server_name, page_index, page_size, keyword):
|
||
"""子服务器:通过 form proxy 查询"""
|
||
from gvsds.apps.msync.models import ServiceNode
|
||
from gvsds.apps.gerp.views.msync_admin import _get_sub_db_connection
|
||
|
||
node = ServiceNode.objects.filter(ServiceName=server_name).first()
|
||
if not node:
|
||
return Response({'code': 1, 'msg': f'服务不存在: {server_name}'})
|
||
|
||
conn, err = _get_sub_db_connection(node)
|
||
if err:
|
||
return Response({'code': 1, 'msg': err})
|
||
|
||
try:
|
||
conn.ensure_connection()
|
||
except Exception as e:
|
||
return Response({'code': 1, 'msg': f'数据库连接失败: {e}'})
|
||
|
||
import uuid as _uuid
|
||
|
||
try:
|
||
with conn.cursor() as cursor:
|
||
where = ''
|
||
params = []
|
||
if keyword:
|
||
where = "WHERE UserName LIKE %s OR UserEmail LIKE %s"
|
||
params = [f'%{keyword}%', f'%{keyword}%']
|
||
|
||
# 总数
|
||
cursor.execute(f"SELECT COUNT(*) FROM `User` {where}", params)
|
||
total = cursor.fetchone()[0]
|
||
|
||
# 分页
|
||
offset = (page_index - 1) * page_size
|
||
cursor.execute(
|
||
f"SELECT UserUUID, UserName, UserEmail, UserTele, UserGender, "
|
||
f"UserAccountLicense, UserPositionStatus, UserCreateTime, UserLastLoginDate, "
|
||
f"IsStaff, IsSuperuser FROM `User` {where} "
|
||
f"ORDER BY UserCreateTime DESC LIMIT %s OFFSET %s",
|
||
params + [page_size, offset]
|
||
)
|
||
|
||
records = []
|
||
for row in cursor.fetchall():
|
||
record = {}
|
||
cols = ['UserUUID', 'UserName', 'UserEmail', 'UserTele', 'UserGender',
|
||
'UserAccountLicense', 'UserPositionStatus', 'UserCreateTime',
|
||
'UserLastLoginDate', 'IsStaff', 'IsSuperuser']
|
||
for i, col in enumerate(cols):
|
||
val = row[i]
|
||
if isinstance(val, bytes) and len(val) == 16:
|
||
try:
|
||
val = str(_uuid.UUID(bytes=val))
|
||
except Exception:
|
||
val = val.hex()
|
||
elif isinstance(val, datetime.datetime):
|
||
val = str(val)
|
||
record[col] = val
|
||
records.append(record)
|
||
|
||
return Response({'code': 0, 'data': records, 'total': total})
|
||
except Exception as e:
|
||
logger.error(f'查询子服务器 {server_name} 用户列表失败: {e}')
|
||
return Response({'code': 1, 'msg': f'查询失败: {e}'})
|