处理超时和获取个人资料相关
This commit is contained in:
parent
58d0c021b3
commit
d769f814b4
@ -13,7 +13,8 @@ from .views import (
|
|||||||
RegisterView,
|
RegisterView,
|
||||||
LoginView,
|
LoginView,
|
||||||
LogoutView,
|
LogoutView,
|
||||||
ChatHistoryViewSet
|
ChatHistoryViewSet,
|
||||||
|
user_profile
|
||||||
)
|
)
|
||||||
|
|
||||||
# 创建路由器
|
# 创建路由器
|
||||||
@ -39,6 +40,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
# 用户管理相关
|
# 用户管理相关
|
||||||
path('users/', user_list, name='user-list'),
|
path('users/', user_list, name='user-list'),
|
||||||
|
path('users/profile/', user_profile, name='user-profile'),
|
||||||
path('users/<str:pk>/', user_detail, name='user-detail'),
|
path('users/<str:pk>/', user_detail, name='user-detail'),
|
||||||
path('users/<str:pk>/update/', user_update, name='user-update'),
|
path('users/<str:pk>/update/', user_update, name='user-update'),
|
||||||
path('users/<str:pk>/delete/', user_delete, name='user-delete'),
|
path('users/<str:pk>/delete/', user_delete, name='user-delete'),
|
||||||
|
@ -803,8 +803,7 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
},
|
},
|
||||||
"problem_optimization": False
|
"problem_optimization": False
|
||||||
},
|
},
|
||||||
headers={"Content-Type": "application/json"},
|
headers={"Content-Type": "application/json"}
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if chat_response.status_code != 200:
|
if chat_response.status_code != 200:
|
||||||
@ -832,8 +831,7 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
url=message_url,
|
url=message_url,
|
||||||
json={"message": question, "re_chat": False, "stream": True},
|
json={"message": question, "re_chat": False, "stream": True},
|
||||||
headers={"Content-Type": "application/json"},
|
headers={"Content-Type": "application/json"},
|
||||||
stream=True, # 启用流式传输
|
stream=True # 启用流式传输
|
||||||
timeout=60
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if message_request.status_code != 200:
|
if message_request.status_code != 200:
|
||||||
@ -1010,8 +1008,7 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
chat_response = requests.post(
|
chat_response = requests.post(
|
||||||
url=f"{settings.API_BASE_URL}/api/application/chat/open",
|
url=f"{settings.API_BASE_URL}/api/application/chat/open",
|
||||||
json=chat_request_data,
|
json=chat_request_data,
|
||||||
headers={"Content-Type": "application/json"},
|
headers={"Content-Type": "application/json"}
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"API响应状态码: {chat_response.status_code}")
|
logger.info(f"API响应状态码: {chat_response.status_code}")
|
||||||
@ -1041,8 +1038,7 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
message_response = requests.post(
|
message_response = requests.post(
|
||||||
url=f"{settings.API_BASE_URL}/api/application/chat_message/{chat_id}",
|
url=f"{settings.API_BASE_URL}/api/application/chat_message/{chat_id}",
|
||||||
json=message_request_data,
|
json=message_request_data,
|
||||||
headers={"Content-Type": "application/json"},
|
headers={"Content-Type": "application/json"}
|
||||||
timeout=60
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if message_response.status_code != 200:
|
if message_response.status_code != 200:
|
||||||
@ -1299,7 +1295,6 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
|
|
||||||
export_response = requests.get(
|
export_response = requests.get(
|
||||||
url=export_url,
|
url=export_url,
|
||||||
timeout=60,
|
|
||||||
stream=True # 使用流式传输处理大文件
|
stream=True # 使用流式传输处理大文件
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1356,8 +1351,7 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
# 调用外部API
|
# 调用外部API
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
url=api_url,
|
url=api_url,
|
||||||
params=params,
|
params=params
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
@ -1543,8 +1537,7 @@ class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
|
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
url=url,
|
url=url,
|
||||||
params=params,
|
params=params
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
@ -2067,8 +2060,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
response = requests.put(
|
response = requests.put(
|
||||||
f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}',
|
f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}',
|
||||||
json=api_data,
|
json=api_data,
|
||||||
headers={'Content-Type': 'application/json'},
|
headers={'Content-Type': 'application/json'}
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
@ -2080,8 +2072,6 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
|
|
||||||
logger.info(f"外部知识库更新成功: {instance.external_id}")
|
logger.info(f"外部知识库更新成功: {instance.external_id}")
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
raise ExternalAPIError("请求超时,请稍后重试")
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -2622,11 +2612,10 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
logger.info(f"调用分割API URL: {url}")
|
logger.info(f"调用分割API URL: {url}")
|
||||||
logger.info(f"请求字段: {list(files_data.keys())}")
|
logger.info(f"请求字段: {list(files_data.keys())}")
|
||||||
|
|
||||||
# 发送请求
|
# 发送请求 - 移除timeout参数
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url,
|
url,
|
||||||
files=files_data,
|
files=files_data
|
||||||
timeout=60
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 记录请求头和响应信息,方便排查问题
|
# 记录请求头和响应信息,方便排查问题
|
||||||
@ -2758,7 +2747,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
try:
|
try:
|
||||||
# 简单的验证请求
|
# 简单的验证请求
|
||||||
verify_url = f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}'
|
verify_url = f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}'
|
||||||
verify_response = requests.get(verify_url, timeout=10)
|
verify_response = requests.get(verify_url)
|
||||||
if verify_response.status_code != 200:
|
if verify_response.status_code != 200:
|
||||||
logger.error(f"外部知识库不存在或无法访问: {instance.external_id}, 状态码: {verify_response.status_code}")
|
logger.error(f"外部知识库不存在或无法访问: {instance.external_id}, 状态码: {verify_response.status_code}")
|
||||||
return Response({
|
return Response({
|
||||||
@ -2919,7 +2908,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
# 记录请求数据,方便调试
|
# 记录请求数据,方便调试
|
||||||
logger.info(f"上传文档数据: 文档名={doc_data.get('name')}, 段落数={len(doc_data.get('paragraphs', []))}")
|
logger.info(f"上传文档数据: 文档名={doc_data.get('name')}, 段落数={len(doc_data.get('paragraphs', []))}")
|
||||||
|
|
||||||
# 发送请求
|
# 发送请求,不设置超时限制
|
||||||
response = requests.post(url, json=doc_data)
|
response = requests.post(url, json=doc_data)
|
||||||
|
|
||||||
# 记录响应结果
|
# 记录响应结果
|
||||||
@ -2996,8 +2985,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
response = requests.post(
|
response = requests.post(
|
||||||
f'{settings.API_BASE_URL}/api/dataset',
|
f'{settings.API_BASE_URL}/api/dataset',
|
||||||
json=api_data,
|
json=api_data,
|
||||||
headers={'Content-Type': 'application/json'},
|
headers={'Content-Type': 'application/json'}
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
@ -3013,8 +3001,6 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
|
|
||||||
return dataset_id
|
return dataset_id
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
raise ExternalAPIError("请求超时,请稍后重试")
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -3028,8 +3014,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
|
|
||||||
response = requests.delete(
|
response = requests.delete(
|
||||||
f'{settings.API_BASE_URL}/api/dataset/{external_id}',
|
f'{settings.API_BASE_URL}/api/dataset/{external_id}',
|
||||||
headers={'Content-Type': 'application/json'},
|
headers={'Content-Type': 'application/json'}
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"删除外部知识库响应: status_code={response.status_code}, response={response.text}")
|
logger.info(f"删除外部知识库响应: status_code={response.status_code}, response={response.text}")
|
||||||
@ -3058,9 +3043,6 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
logger.warning(f"外部知识库删除响应无法解析JSON,但状态码为200,视为成功: {external_id}")
|
logger.warning(f"外部知识库删除响应无法解析JSON,但状态码为200,视为成功: {external_id}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
logger.error(f"删除外部知识库超时: {external_id}")
|
|
||||||
raise ExternalAPIError("请求超时,请稍后重试")
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logger.error(f"删除外部知识库请求异常: {external_id}, error={str(e)}")
|
logger.error(f"删除外部知识库请求异常: {external_id}, error={str(e)}")
|
||||||
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
||||||
@ -3096,8 +3078,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
url = f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}/document'
|
url = f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}/document'
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
url,
|
url,
|
||||||
headers={'Content-Type': 'application/json'},
|
headers={'Content-Type': 'application/json'}
|
||||||
timeout=30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
@ -3224,7 +3205,7 @@ class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet):
|
|||||||
# 调用正确的外部API获取文档段落内容
|
# 调用正确的外部API获取文档段落内容
|
||||||
try:
|
try:
|
||||||
url = f'{settings.API_BASE_URL}/api/dataset/{knowledge_base.external_id}/document/{document.external_id}/paragraph'
|
url = f'{settings.API_BASE_URL}/api/dataset/{knowledge_base.external_id}/document/{document.external_id}/paragraph'
|
||||||
response = requests.get(url, timeout=30)
|
response = requests.get(url)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
logger.error(f"获取文档段落内容失败: {response.status_code}, {response.text}")
|
logger.error(f"获取文档段落内容失败: {response.status_code}, {response.text}")
|
||||||
@ -4239,6 +4220,9 @@ class LoginView(APIView):
|
|||||||
# 获取或创建token
|
# 获取或创建token
|
||||||
token, _ = Token.objects.get_or_create(user=user)
|
token, _ = Token.objects.get_or_create(user=user)
|
||||||
|
|
||||||
|
# 登录用户(可选)
|
||||||
|
login(request, user)
|
||||||
|
|
||||||
return Response({
|
return Response({
|
||||||
"code": 200,
|
"code": 200,
|
||||||
"message": "登录成功",
|
"message": "登录成功",
|
||||||
@ -4246,9 +4230,9 @@ class LoginView(APIView):
|
|||||||
"id": str(user.id),
|
"id": str(user.id),
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"email": user.email,
|
"email": user.email,
|
||||||
|
"name": user.name,
|
||||||
"role": user.role,
|
"role": user.role,
|
||||||
"department": user.department,
|
"department": user.department,
|
||||||
"name": user.name,
|
|
||||||
"group": user.group,
|
"group": user.group,
|
||||||
"token": token.key
|
"token": token.key
|
||||||
}
|
}
|
||||||
@ -4381,7 +4365,7 @@ class RegisterView(APIView):
|
|||||||
"code": 200,
|
"code": 200,
|
||||||
"message": "注册成功",
|
"message": "注册成功",
|
||||||
"data": {
|
"data": {
|
||||||
"id": user.id,
|
"id": str(user.id),
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"email": user.email,
|
"email": user.email,
|
||||||
"role": user.role,
|
"role": user.role,
|
||||||
@ -4430,28 +4414,111 @@ class LogoutView(APIView):
|
|||||||
@api_view(['GET', 'PUT'])
|
@api_view(['GET', 'PUT'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def user_profile(request):
|
def user_profile(request):
|
||||||
"""获取或更新用户信息"""
|
"""
|
||||||
if request.method == 'GET':
|
获取或更新当前登录用户信息
|
||||||
data = {
|
|
||||||
'id': request.user.id,
|
|
||||||
'username': request.user.username,
|
|
||||||
'email': request.user.email,
|
|
||||||
'role': request.user.role,
|
|
||||||
'department': request.user.department,
|
|
||||||
'phone': request.user.phone,
|
|
||||||
'date_joined': request.user.date_joined
|
|
||||||
}
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
elif request.method == 'PUT':
|
此接口与user_update的区别:
|
||||||
user = request.user
|
1. 任何已认证用户可访问
|
||||||
# 只允许更新特定字段
|
2. 仅能更新当前登录用户自己的信息
|
||||||
allowed_fields = ['email', 'phone', 'department']
|
3. 不能修改角色等重要字段
|
||||||
for field in allowed_fields:
|
4. 不需要指定用户ID,自动使用当前用户
|
||||||
if field in request.data:
|
"""
|
||||||
setattr(user, field, request.data[field])
|
try:
|
||||||
user.save()
|
if request.method == 'GET':
|
||||||
return Response({'message': '用户信息更新成功'})
|
# 检查用户是否已认证
|
||||||
|
user = request.user
|
||||||
|
if not user.is_authenticated:
|
||||||
|
return Response({
|
||||||
|
'code': 401,
|
||||||
|
'message': '用户未认证',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'id': str(user.id),
|
||||||
|
'username': user.username,
|
||||||
|
'email': user.email,
|
||||||
|
'name': user.name,
|
||||||
|
'role': user.role,
|
||||||
|
'department': user.department,
|
||||||
|
'group': user.group,
|
||||||
|
'date_joined': user.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
}
|
||||||
|
return Response({
|
||||||
|
'code': 200,
|
||||||
|
'message': '获取用户信息成功',
|
||||||
|
'data': data
|
||||||
|
})
|
||||||
|
|
||||||
|
elif request.method == 'PUT':
|
||||||
|
# 检查请求数据格式
|
||||||
|
try:
|
||||||
|
if not request.data:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '请求数据为空或格式错误',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as data_error:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': f'请求数据格式错误: {str(data_error)}。请确保提交的是有效的JSON格式数据,属性名必须使用双引号。',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
user = request.user
|
||||||
|
# 只允许更新特定字段
|
||||||
|
allowed_fields = ['email', 'name', 'phone', 'department', 'group']
|
||||||
|
updated_fields = []
|
||||||
|
|
||||||
|
for field in allowed_fields:
|
||||||
|
if field in request.data:
|
||||||
|
setattr(user, field, request.data[field])
|
||||||
|
updated_fields.append(field)
|
||||||
|
|
||||||
|
if updated_fields:
|
||||||
|
try:
|
||||||
|
user.save()
|
||||||
|
return Response({
|
||||||
|
'code': 200,
|
||||||
|
'message': f'用户信息更新成功,已更新字段: {", ".join(updated_fields)}',
|
||||||
|
'data': {
|
||||||
|
'id': str(user.id),
|
||||||
|
'username': user.username,
|
||||||
|
'email': user.email,
|
||||||
|
'name': user.name,
|
||||||
|
'role': user.role,
|
||||||
|
'department': user.department,
|
||||||
|
'group': user.group,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except Exception as save_error:
|
||||||
|
logger.error(f"保存用户数据失败: {str(save_error)}")
|
||||||
|
return Response({
|
||||||
|
'code': 500,
|
||||||
|
'message': f'更新用户信息失败: {str(save_error)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
else:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '没有提供任何可更新的字段',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
return Response({
|
||||||
|
'code': 405,
|
||||||
|
'message': f'不支持的请求方法: {request.method}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理用户信息请求失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return Response({
|
||||||
|
'code': 500,
|
||||||
|
'message': f'处理请求失败: {str(e)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@api_view(['POST'])
|
@api_view(['POST'])
|
||||||
@ -4513,7 +4580,14 @@ def change_password(request):
|
|||||||
@api_view(['POST'])
|
@api_view(['POST'])
|
||||||
@permission_classes([AllowAny])
|
@permission_classes([AllowAny])
|
||||||
def user_register(request):
|
def user_register(request):
|
||||||
"""用户注册"""
|
"""
|
||||||
|
[已弃用] 用户注册 - 请使用 RegisterView 类代替
|
||||||
|
|
||||||
|
此函数仅保留用于兼容性目的,新代码应该使用 /api/auth/register/ 接口
|
||||||
|
"""
|
||||||
|
# 打印弃用警告
|
||||||
|
logger.warning("使用已弃用的user_register函数,请改用RegisterView类")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.data
|
data = request.data
|
||||||
|
|
||||||
@ -4522,38 +4596,50 @@ def user_register(request):
|
|||||||
for field in required_fields:
|
for field in required_fields:
|
||||||
if not data.get(field):
|
if not data.get(field):
|
||||||
return Response({
|
return Response({
|
||||||
'error': f'缺少必填字段: {field}'
|
'code': 400,
|
||||||
|
'message': f'缺少必填字段: {field}',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 验证角色
|
# 验证角色
|
||||||
valid_roles = ['admin', 'leader', 'member']
|
valid_roles = ['admin', 'leader', 'member']
|
||||||
if data['role'] not in valid_roles:
|
if data['role'] not in valid_roles:
|
||||||
return Response({
|
return Response({
|
||||||
'error': f'无效的角色,必须是: {", ".join(valid_roles)}'
|
'code': 400,
|
||||||
|
'message': f'无效的角色,必须是: {", ".join(valid_roles)}',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 如果是组员,必须指定小组
|
# 如果是组员,必须指定小组
|
||||||
if data['role'] == 'member' and not data.get('group'):
|
if data['role'] == 'member' and not data.get('group'):
|
||||||
return Response({
|
return Response({
|
||||||
'error': '组员必须指定所属小组'
|
'code': 400,
|
||||||
|
'message': '组员必须指定所属小组',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 检查用户名是否已存在
|
# 检查用户名是否已存在
|
||||||
if User.objects.filter(username=data['username']).exists():
|
if User.objects.filter(username=data['username']).exists():
|
||||||
return Response({
|
return Response({
|
||||||
'error': '用户名已存在'
|
'code': 400,
|
||||||
|
'message': '用户名已存在',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 检查邮箱是否已存在
|
# 检查邮箱是否已存在
|
||||||
if User.objects.filter(email=data['email']).exists():
|
if User.objects.filter(email=data['email']).exists():
|
||||||
return Response({
|
return Response({
|
||||||
'error': '邮箱已被注册'
|
'code': 400,
|
||||||
|
'message': '邮箱已被注册',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 验证密码强度
|
# 验证密码强度
|
||||||
if len(data['password']) < 8:
|
if len(data['password']) < 8:
|
||||||
return Response({
|
return Response({
|
||||||
'error': '密码长度必须至少为8位'
|
'code': 400,
|
||||||
|
'message': '密码长度必须至少为8位',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 验证邮箱格式
|
# 验证邮箱格式
|
||||||
@ -4561,7 +4647,9 @@ def user_register(request):
|
|||||||
validate_email(data['email'])
|
validate_email(data['email'])
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
return Response({
|
return Response({
|
||||||
'error': '邮箱格式不正确'
|
'code': 400,
|
||||||
|
'message': '邮箱格式不正确',
|
||||||
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 创建用户
|
# 创建用户
|
||||||
@ -4581,9 +4669,10 @@ def user_register(request):
|
|||||||
token, _ = Token.objects.get_or_create(user=user)
|
token, _ = Token.objects.get_or_create(user=user)
|
||||||
|
|
||||||
return Response({
|
return Response({
|
||||||
|
'code': 200,
|
||||||
'message': '注册成功',
|
'message': '注册成功',
|
||||||
'data': {
|
'data': {
|
||||||
'id': user.id,
|
'id': str(user.id),
|
||||||
'username': user.username,
|
'username': user.username,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'role': user.role,
|
'role': user.role,
|
||||||
@ -4596,82 +4685,41 @@ def user_register(request):
|
|||||||
}, status=status.HTTP_201_CREATED)
|
}, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"注册失败: {str(e)}")
|
logger.error(f"注册失败: {str(e)}")
|
||||||
print(f"错误类型: {type(e)}")
|
logger.error(f"错误类型: {type(e)}")
|
||||||
print(f"错误堆栈: {traceback.format_exc()}")
|
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
||||||
return Response({
|
return Response({
|
||||||
'error': f'注册失败: {str(e)}',
|
'code': 500,
|
||||||
|
'message': f'注册失败: {str(e)}',
|
||||||
'data': None
|
'data': None
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@api_view(['POST'])
|
|
||||||
@permission_classes([IsAuthenticated])
|
|
||||||
def verify_token(request):
|
|
||||||
"""验证令牌有效性"""
|
|
||||||
try:
|
|
||||||
return Response({
|
|
||||||
"code": 200,
|
|
||||||
"message": "令牌有效",
|
|
||||||
"data": {
|
|
||||||
"is_valid": True,
|
|
||||||
"user": {
|
|
||||||
"id": request.user.id,
|
|
||||||
"username": request.user.username,
|
|
||||||
"email": request.user.email,
|
|
||||||
"role": request.user.role,
|
|
||||||
"department": request.user.department,
|
|
||||||
"name": request.user.name,
|
|
||||||
"group": request.user.group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return Response({
|
|
||||||
"code": 500,
|
|
||||||
"message": f"验证失败: {str(e)}",
|
|
||||||
"data": None
|
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
||||||
|
|
||||||
@api_view(['GET'])
|
|
||||||
@permission_classes([IsAuthenticated])
|
|
||||||
def user_list(request):
|
|
||||||
"""获取用户列表"""
|
|
||||||
user = request.user
|
|
||||||
if user.role == 'admin':
|
|
||||||
users = User.objects.all()
|
|
||||||
elif user.role == 'leader':
|
|
||||||
users = User.objects.filter(department=user.department)
|
|
||||||
else:
|
|
||||||
users = User.objects.filter(id=user.id)
|
|
||||||
|
|
||||||
data = [{
|
|
||||||
'id': u.id,
|
|
||||||
'username': u.username,
|
|
||||||
'email': u.email,
|
|
||||||
'role': u.role,
|
|
||||||
'department': u.department,
|
|
||||||
'is_active': u.is_active,
|
|
||||||
'date_joined': u.date_joined
|
|
||||||
} for u in users]
|
|
||||||
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def user_detail(request, pk):
|
def user_detail(request, pk):
|
||||||
"""获取用户详情"""
|
"""获取用户详情"""
|
||||||
try:
|
try:
|
||||||
# 尝试转换为 UUID
|
# 尝试转换为 UUID,处理多种可能的格式
|
||||||
if not isinstance(pk, uuid.UUID):
|
try:
|
||||||
try:
|
if not isinstance(pk, uuid.UUID):
|
||||||
pk = uuid.UUID(pk)
|
# 移除所有空格,以防万一
|
||||||
except ValueError:
|
pk = pk.strip()
|
||||||
return Response({
|
|
||||||
"code": 400,
|
# 处理带连字符和不带连字符的格式
|
||||||
"message": "无效的用户ID格式",
|
if '-' not in pk and len(pk) == 32:
|
||||||
"data": None
|
# 转换没有连字符的UUID格式
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
pk_with_hyphens = f"{pk[0:8]}-{pk[8:12]}-{pk[12:16]}-{pk[16:20]}-{pk[20:]}"
|
||||||
|
pk = uuid.UUID(pk_with_hyphens)
|
||||||
|
else:
|
||||||
|
# 尝试直接转换
|
||||||
|
pk = uuid.UUID(pk)
|
||||||
|
except ValueError:
|
||||||
|
# 提供更详细的错误信息
|
||||||
|
return Response({
|
||||||
|
"code": 400,
|
||||||
|
"message": f"无效的用户ID格式: {pk}。用户ID应为有效的UUID格式。",
|
||||||
|
"data": None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
user = get_object_or_404(User, pk=pk)
|
user = get_object_or_404(User, pk=pk)
|
||||||
|
|
||||||
@ -4689,6 +4737,8 @@ def user_detail(request, pk):
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(f"获取用户信息失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
return Response({
|
return Response({
|
||||||
"code": 500,
|
"code": 500,
|
||||||
"message": f"获取用户信息失败: {str(e)}",
|
"message": f"获取用户信息失败: {str(e)}",
|
||||||
@ -4698,27 +4748,270 @@ def user_detail(request, pk):
|
|||||||
@api_view(['PUT'])
|
@api_view(['PUT'])
|
||||||
@permission_classes([IsAdminUser])
|
@permission_classes([IsAdminUser])
|
||||||
def user_update(request, pk):
|
def user_update(request, pk):
|
||||||
"""更新用户信息"""
|
"""
|
||||||
|
管理员更新用户信息
|
||||||
|
|
||||||
|
此接口与user_profile的区别:
|
||||||
|
1. 仅管理员可访问
|
||||||
|
2. 可以更新任何用户的信息
|
||||||
|
3. 可以修改角色等重要字段
|
||||||
|
4. 需要在URL中指定用户ID
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(pk=pk)
|
# 检查请求数据格式
|
||||||
|
try:
|
||||||
|
if not request.data:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '请求数据为空或格式错误',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as data_error:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': f'请求数据格式错误: {str(data_error)}。请确保提交的是有效的JSON格式数据,属性名必须使用双引号。',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 尝试转换为 UUID
|
||||||
|
try:
|
||||||
|
if not isinstance(pk, uuid.UUID):
|
||||||
|
pk = pk.strip()
|
||||||
|
|
||||||
|
# 处理带连字符和不带连字符的格式
|
||||||
|
if '-' not in pk and len(pk) == 32:
|
||||||
|
# 转换没有连字符的UUID格式
|
||||||
|
pk_with_hyphens = f"{pk[0:8]}-{pk[8:12]}-{pk[12:16]}-{pk[16:20]}-{pk[20:]}"
|
||||||
|
pk = uuid.UUID(pk_with_hyphens)
|
||||||
|
else:
|
||||||
|
# 尝试直接转换
|
||||||
|
pk = uuid.UUID(pk)
|
||||||
|
except ValueError:
|
||||||
|
return Response({
|
||||||
|
"code": 400,
|
||||||
|
"message": f"无效的用户ID格式: {pk}。用户ID应为有效的UUID格式。",
|
||||||
|
"data": None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=pk)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return Response({
|
||||||
|
'code': 404,
|
||||||
|
'message': '用户不存在',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
# 只允许更新特定字段
|
# 只允许更新特定字段
|
||||||
allowed_fields = ['email', 'role', 'department', 'is_active', 'phone']
|
allowed_fields = ['email', 'role', 'department', 'group', 'is_active', 'phone', 'name']
|
||||||
|
updated_fields = []
|
||||||
|
|
||||||
for field in allowed_fields:
|
for field in allowed_fields:
|
||||||
if field in request.data:
|
if field in request.data:
|
||||||
setattr(user, field, request.data[field])
|
setattr(user, field, request.data[field])
|
||||||
|
updated_fields.append(field)
|
||||||
|
|
||||||
|
if not updated_fields:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '没有提供任何可更新的字段',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
user.save()
|
user.save()
|
||||||
return Response({'message': '用户信息更新成功'})
|
|
||||||
except User.DoesNotExist:
|
return Response({
|
||||||
return Response({'message': '用户不存在'}, status=404)
|
'code': 200,
|
||||||
|
'message': '用户信息更新成功',
|
||||||
|
'data': {
|
||||||
|
'id': str(user.id),
|
||||||
|
'username': user.username,
|
||||||
|
'email': user.email,
|
||||||
|
'name': user.name,
|
||||||
|
'role': user.role,
|
||||||
|
'department': user.department,
|
||||||
|
'group': user.group,
|
||||||
|
'is_active': user.is_active
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新用户信息失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return Response({
|
||||||
|
'code': 500,
|
||||||
|
'message': f'更新用户信息失败: {str(e)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
@api_view(['DELETE'])
|
@api_view(['DELETE'])
|
||||||
@permission_classes([IsAdminUser])
|
@permission_classes([IsAdminUser])
|
||||||
def user_delete(request, pk):
|
def user_delete(request, pk):
|
||||||
"""删除用户"""
|
"""删除用户"""
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(pk=pk)
|
# 尝试转换为 UUID
|
||||||
user.delete()
|
try:
|
||||||
return Response({'message': '用户删除成功'})
|
if not isinstance(pk, uuid.UUID):
|
||||||
except User.DoesNotExist:
|
pk = pk.strip()
|
||||||
return Response({'message': '用户不存在'}, status=404)
|
|
||||||
|
# 处理带连字符和不带连字符的格式
|
||||||
|
if '-' not in pk and len(pk) == 32:
|
||||||
|
# 转换没有连字符的UUID格式
|
||||||
|
pk_with_hyphens = f"{pk[0:8]}-{pk[8:12]}-{pk[12:16]}-{pk[16:20]}-{pk[20:]}"
|
||||||
|
pk = uuid.UUID(pk_with_hyphens)
|
||||||
|
else:
|
||||||
|
# 尝试直接转换
|
||||||
|
pk = uuid.UUID(pk)
|
||||||
|
except ValueError:
|
||||||
|
return Response({
|
||||||
|
"code": 400,
|
||||||
|
"message": f"无效的用户ID格式: {pk}。用户ID应为有效的UUID格式。",
|
||||||
|
"data": None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=pk)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return Response({
|
||||||
|
'code': 404,
|
||||||
|
'message': '用户不存在',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
# 检查是否试图删除管理员账户
|
||||||
|
if user.is_superuser or user.role == 'admin':
|
||||||
|
return Response({
|
||||||
|
'code': 403,
|
||||||
|
'message': '不允许删除管理员账户',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
# 删除用户
|
||||||
|
username = user.username
|
||||||
|
user.delete()
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'code': 200,
|
||||||
|
'message': f'用户 {username} 删除成功',
|
||||||
|
'data': None
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除用户失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return Response({
|
||||||
|
'code': 500,
|
||||||
|
'message': f'删除用户失败: {str(e)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@api_view(['POST'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def verify_token(request):
|
||||||
|
"""验证令牌有效性"""
|
||||||
|
try:
|
||||||
|
return Response({
|
||||||
|
"code": 200,
|
||||||
|
"message": "令牌有效",
|
||||||
|
"data": {
|
||||||
|
"is_valid": True,
|
||||||
|
"user": {
|
||||||
|
"id": str(request.user.id),
|
||||||
|
"username": request.user.username,
|
||||||
|
"email": request.user.email,
|
||||||
|
"name": request.user.name,
|
||||||
|
"role": request.user.role,
|
||||||
|
"department": request.user.department,
|
||||||
|
"group": request.user.group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"验证令牌失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return Response({
|
||||||
|
"code": 500,
|
||||||
|
"message": f"验证失败: {str(e)}",
|
||||||
|
"data": None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def user_list(request):
|
||||||
|
"""获取用户列表"""
|
||||||
|
try:
|
||||||
|
# 获取查询参数
|
||||||
|
page = int(request.query_params.get('page', 1))
|
||||||
|
page_size = int(request.query_params.get('page_size', 20))
|
||||||
|
keyword = request.query_params.get('keyword', '')
|
||||||
|
|
||||||
|
# 根据用户角色获取不同范围的用户列表
|
||||||
|
user = request.user
|
||||||
|
base_query = User.objects.all()
|
||||||
|
|
||||||
|
if user.role == 'admin':
|
||||||
|
users_query = base_query
|
||||||
|
elif user.role == 'leader':
|
||||||
|
users_query = base_query.filter(department=user.department)
|
||||||
|
else:
|
||||||
|
users_query = base_query.filter(id=user.id)
|
||||||
|
|
||||||
|
# 添加关键字搜索
|
||||||
|
if keyword:
|
||||||
|
users_query = users_query.filter(
|
||||||
|
Q(username__icontains=keyword) |
|
||||||
|
Q(email__icontains=keyword) |
|
||||||
|
Q(name__icontains=keyword) |
|
||||||
|
Q(department__icontains=keyword)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 计算总数
|
||||||
|
total = users_query.count()
|
||||||
|
|
||||||
|
# 分页
|
||||||
|
start = (page - 1) * page_size
|
||||||
|
end = start + page_size
|
||||||
|
users = users_query[start:end]
|
||||||
|
|
||||||
|
# 构造数据
|
||||||
|
user_data = []
|
||||||
|
for u in users:
|
||||||
|
user_data.append({
|
||||||
|
'id': str(u.id),
|
||||||
|
'username': u.username,
|
||||||
|
'email': u.email,
|
||||||
|
'name': u.name,
|
||||||
|
'role': u.role,
|
||||||
|
'department': u.department,
|
||||||
|
'group': u.group,
|
||||||
|
'is_active': u.is_active,
|
||||||
|
'date_joined': u.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
})
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'code': 200,
|
||||||
|
'message': '获取用户列表成功',
|
||||||
|
'data': {
|
||||||
|
'total': total,
|
||||||
|
'page': page,
|
||||||
|
'page_size': page_size,
|
||||||
|
'users': user_data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
# 处理参数转换错误
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': f'参数错误: {str(e)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取用户列表失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return Response({
|
||||||
|
'code': 500,
|
||||||
|
'message': f'获取用户列表失败: {str(e)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user