1834 lines
71 KiB
Python
1834 lines
71 KiB
Python
![]() |
from rest_framework import viewsets, status
|
|||
|
from rest_framework.decorators import action, api_view, permission_classes
|
|||
|
from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser
|
|||
|
from rest_framework.response import Response
|
|||
|
from rest_framework.exceptions import APIException, PermissionDenied, ValidationError, NotFound
|
|||
|
from rest_framework.authentication import TokenAuthentication
|
|||
|
from django.utils import timezone
|
|||
|
from django.db import connection
|
|||
|
from django.db.models import Q, Max, Count, F
|
|||
|
from datetime import timedelta, datetime
|
|||
|
import mysql.connector
|
|||
|
from django.contrib.auth import get_user_model, authenticate, login, logout
|
|||
|
from channels.layers import get_channel_layer
|
|||
|
from asgiref.sync import async_to_sync
|
|||
|
from rest_framework.authtoken.models import Token
|
|||
|
import requests
|
|||
|
import json
|
|||
|
from django.db import transaction
|
|||
|
from django.core.exceptions import ObjectDoesNotExist
|
|||
|
import sys
|
|||
|
import random
|
|||
|
import string
|
|||
|
import time
|
|||
|
import logging
|
|||
|
import os
|
|||
|
from rest_framework.test import APIRequestFactory
|
|||
|
from django.contrib.contenttypes.models import ContentType
|
|||
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|||
|
from django.http import Http404
|
|||
|
from django.db import IntegrityError
|
|||
|
from channels.exceptions import ChannelFull
|
|||
|
from django.conf import settings
|
|||
|
from django.shortcuts import get_object_or_404
|
|||
|
from django.db import models
|
|||
|
from rest_framework.views import APIView
|
|||
|
from django.core.validators import validate_email
|
|||
|
# from django.core.exceptions import ValidationError
|
|||
|
from django.views.decorators.csrf import csrf_exempt
|
|||
|
from django.utils.decorators import method_decorator
|
|||
|
import uuid
|
|||
|
from rest_framework import serializers
|
|||
|
import traceback
|
|||
|
|
|||
|
|
|||
|
# 添加模型导入
|
|||
|
from .models import (
|
|||
|
User,
|
|||
|
Data, # 替换原来的 AdminData, LeaderData, MemberData
|
|||
|
Permission, # 替换原来的 DataPermission, TablePermission
|
|||
|
ChatHistory,
|
|||
|
KnowledgeBase,
|
|||
|
Notification,
|
|||
|
KnowledgeBasePermission as KBPermissionModel
|
|||
|
)
|
|||
|
from .serializers import (
|
|||
|
UserSerializer,
|
|||
|
DataSerializer, # 需要更新
|
|||
|
PermissionSerializer, # 需要更新
|
|||
|
ChatHistorySerializer,
|
|||
|
KnowledgeBaseSerializer,
|
|||
|
KnowledgePermissionSerializer, # 添加这个导入
|
|||
|
NotificationSerializer
|
|||
|
)
|
|||
|
# 导入自定义权限类
|
|||
|
from .permissions import ResourceCRUDPermission, PermissionRequestPermission, DataPermission, KnowledgeBasePermission as KBPermissionClass
|
|||
|
from .exceptions import ExternalAPIError
|
|||
|
|
|||
|
# 获取正确的用户模型
|
|||
|
User = get_user_model()
|
|||
|
|
|||
|
logger = logging.getLogger(__name__)
|
|||
|
logging.basicConfig(
|
|||
|
level=logging.INFO,
|
|||
|
format='%(asctime)s [%(levelname)s] %(message)s',
|
|||
|
handlers=[
|
|||
|
logging.StreamHandler() # 输出到控制台
|
|||
|
]
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
class ChatHistoryViewSet(viewsets.ModelViewSet):
|
|||
|
permission_classes = [IsAuthenticated]
|
|||
|
queryset = ChatHistory.objects.all()
|
|||
|
|
|||
|
def get_queryset(self):
|
|||
|
"""确保用户只能看到自己的聊天记录"""
|
|||
|
return ChatHistory.objects.filter(user=self.request.user)
|
|||
|
|
|||
|
def list(self, request):
|
|||
|
"""获取聊天记录列表,按dataset_id分组"""
|
|||
|
try:
|
|||
|
# 获取查询参数
|
|||
|
dataset_id = request.query_params.get('dataset_id')
|
|||
|
page = int(request.query_params.get('page', 1))
|
|||
|
page_size = int(request.query_params.get('page_size', 10))
|
|||
|
|
|||
|
# 基础查询
|
|||
|
query = self.get_queryset()
|
|||
|
|
|||
|
if dataset_id:
|
|||
|
# 如果指定了dataset_id,获取该数据集的完整对话历史
|
|||
|
records = query.filter(
|
|||
|
dataset_id=dataset_id
|
|||
|
).order_by('created_at') # 按时间正序排列
|
|||
|
|
|||
|
# 序列化对话数据
|
|||
|
conversation = {
|
|||
|
'dataset_id': dataset_id,
|
|||
|
'dataset_name': records.first().dataset_name if records.exists() else None,
|
|||
|
'messages': [{
|
|||
|
'id': record.id,
|
|||
|
'role': 'user' if idx % 2 == 0 else 'assistant',
|
|||
|
'content': record.question if idx % 2 == 0 else record.answer,
|
|||
|
'created_at': record.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
} for idx, record in enumerate(records)]
|
|||
|
}
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '获取成功',
|
|||
|
'data': conversation
|
|||
|
})
|
|||
|
else:
|
|||
|
# 如果没有指定dataset_id,获取所有对话的概览
|
|||
|
# 按dataset_id分组,获取最新一条记录
|
|||
|
latest_chats = query.values(
|
|||
|
'dataset_id'
|
|||
|
).annotate(
|
|||
|
latest_id=Max('id'),
|
|||
|
dataset_name=F('dataset_name'),
|
|||
|
message_count=Count('id'),
|
|||
|
last_message=Max('created_at')
|
|||
|
).order_by('-last_message')
|
|||
|
|
|||
|
# 计算分页
|
|||
|
total = latest_chats.count()
|
|||
|
start = (page - 1) * page_size
|
|||
|
end = start + page_size
|
|||
|
|
|||
|
# 获取分页数据
|
|||
|
chats = latest_chats[start:end]
|
|||
|
|
|||
|
results = []
|
|||
|
for chat in chats:
|
|||
|
latest_record = ChatHistory.objects.get(id=chat['latest_id'])
|
|||
|
results.append({
|
|||
|
'dataset_id': chat['dataset_id'],
|
|||
|
'dataset_name': chat['dataset_name'],
|
|||
|
'message_count': chat['message_count'],
|
|||
|
'last_message': latest_record.answer,
|
|||
|
'last_time': chat['last_message'].strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
})
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '获取成功',
|
|||
|
'data': {
|
|||
|
'total': total,
|
|||
|
'page': page,
|
|||
|
'page_size': page_size,
|
|||
|
'results': results
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
'error': f'获取聊天记录失败: {str(e)}'
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
def create(self, request):
|
|||
|
"""创建新的聊天记录"""
|
|||
|
try:
|
|||
|
data = request.data
|
|||
|
required_fields = ['dataset_id', 'dataset_name', 'question', 'answer']
|
|||
|
|
|||
|
# 检查必填字段
|
|||
|
for field in required_fields:
|
|||
|
if field not in data:
|
|||
|
return Response({
|
|||
|
'error': f'缺少必填字段: {field}'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 创建记录
|
|||
|
record = ChatHistory.objects.create(
|
|||
|
user=request.user,
|
|||
|
dataset_id=data['dataset_id'],
|
|||
|
dataset_name=data['dataset_name'],
|
|||
|
question=data['question'],
|
|||
|
answer=data['answer'],
|
|||
|
model_name=data.get('model_name', 'default')
|
|||
|
)
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '创建成功',
|
|||
|
'data': {
|
|||
|
'id': record.id,
|
|||
|
'dataset_id': record.dataset_id,
|
|||
|
'role': 'assistant',
|
|||
|
'content': record.answer,
|
|||
|
'created_at': record.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
}
|
|||
|
}, status=status.HTTP_201_CREATED)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
'error': f'创建聊天记录失败: {str(e)}'
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
def update(self, request, pk=None):
|
|||
|
"""更新聊天记录"""
|
|||
|
try:
|
|||
|
# 获取记录
|
|||
|
record = self.get_queryset().filter(id=pk).first()
|
|||
|
|
|||
|
if not record:
|
|||
|
return Response({
|
|||
|
'error': '记录不存在或无权限'
|
|||
|
}, status=status.HTTP_404_NOT_FOUND)
|
|||
|
|
|||
|
# 更新字段
|
|||
|
data = request.data
|
|||
|
updateable_fields = ['question', 'answer', 'model_name']
|
|||
|
|
|||
|
for field in updateable_fields:
|
|||
|
if field in data:
|
|||
|
setattr(record, field, data[field])
|
|||
|
|
|||
|
record.save()
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '更新成功',
|
|||
|
'data': {
|
|||
|
'id': record.id,
|
|||
|
'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
'error': f'更新聊天记录失败: {str(e)}'
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
def destroy(self, request, pk=None):
|
|||
|
"""删除聊天记录"""
|
|||
|
try:
|
|||
|
# 获取记录
|
|||
|
record = self.get_queryset().filter(id=pk).first()
|
|||
|
|
|||
|
if not record:
|
|||
|
return Response({
|
|||
|
'error': '记录不存在或无权限'
|
|||
|
}, status=status.HTTP_404_NOT_FOUND)
|
|||
|
|
|||
|
# 删除记录
|
|||
|
record.delete()
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '删除成功'
|
|||
|
})
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
'error': f'删除聊天记录失败: {str(e)}'
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
@action(detail=False, methods=['get'])
|
|||
|
def search(self, request):
|
|||
|
"""搜索聊天记录"""
|
|||
|
try:
|
|||
|
# 获取查询参数
|
|||
|
keyword = request.query_params.get('keyword', '').strip()
|
|||
|
dataset_id = request.query_params.get('dataset_id')
|
|||
|
start_date = request.query_params.get('start_date')
|
|||
|
end_date = request.query_params.get('end_date')
|
|||
|
page = int(request.query_params.get('page', 1))
|
|||
|
page_size = int(request.query_params.get('page_size', 10))
|
|||
|
|
|||
|
# 基础查询:当前用户的记录
|
|||
|
query = self.get_queryset()
|
|||
|
|
|||
|
# 添加关键词搜索
|
|||
|
if keyword:
|
|||
|
query = query.filter(
|
|||
|
Q(question__icontains=keyword) | # 问题包含关键词
|
|||
|
Q(answer__icontains=keyword) | # 回答包含关键词
|
|||
|
Q(dataset_name__icontains=keyword) # 知识库名称包含关键词
|
|||
|
)
|
|||
|
|
|||
|
# 添加其他过滤条件
|
|||
|
if dataset_id:
|
|||
|
query = query.filter(dataset_id=dataset_id)
|
|||
|
if start_date:
|
|||
|
query = query.filter(created_at__gte=start_date)
|
|||
|
if end_date:
|
|||
|
query = query.filter(created_at__lte=end_date)
|
|||
|
|
|||
|
# 计算分页
|
|||
|
total = query.count()
|
|||
|
start = (page - 1) * page_size
|
|||
|
end = start + page_size
|
|||
|
|
|||
|
# 获取分页数据
|
|||
|
records = query.order_by('-created_at')[start:end]
|
|||
|
|
|||
|
# 序列化数据
|
|||
|
results = []
|
|||
|
for record in records:
|
|||
|
result = {
|
|||
|
'id': record.id,
|
|||
|
'dataset_id': record.dataset_id,
|
|||
|
'dataset_name': record.dataset_name,
|
|||
|
'question': record.question,
|
|||
|
'answer': record.answer,
|
|||
|
'model_name': record.model_name,
|
|||
|
'created_at': record.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
}
|
|||
|
|
|||
|
# 如果有关键词,添加高亮信息
|
|||
|
if keyword:
|
|||
|
result['highlights'] = {
|
|||
|
'question': self._highlight_keyword(record.question, keyword),
|
|||
|
'answer': self._highlight_keyword(record.answer, keyword)
|
|||
|
}
|
|||
|
|
|||
|
results.append(result)
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '搜索成功',
|
|||
|
'data': {
|
|||
|
'total': total,
|
|||
|
'page': page,
|
|||
|
'page_size': page_size,
|
|||
|
'results': results
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"搜索失败: {str(e)}")
|
|||
|
print(f"错误类型: {type(e)}")
|
|||
|
print(f"错误堆栈: {traceback.format_exc()}")
|
|||
|
return Response({
|
|||
|
'error': f'搜索失败: {str(e)}'
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
def _highlight_keyword(self, text, keyword):
|
|||
|
"""高亮关键词"""
|
|||
|
if not keyword or not text:
|
|||
|
return text
|
|||
|
return text.replace(
|
|||
|
keyword,
|
|||
|
f'<em class="highlight">{keyword}</em>'
|
|||
|
)
|
|||
|
|
|||
|
class KnowledgeBaseViewSet(viewsets.ModelViewSet):
|
|||
|
serializer_class = KnowledgeBaseSerializer
|
|||
|
permission_classes = [IsAuthenticated]
|
|||
|
|
|||
|
def list(self, request, *args, **kwargs):
|
|||
|
try:
|
|||
|
queryset = self.get_queryset()
|
|||
|
serializer = self.get_serializer(queryset, many=True)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "获取知识库列表成功",
|
|||
|
"data": serializer.data
|
|||
|
})
|
|||
|
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)
|
|||
|
|
|||
|
def get_queryset(self):
|
|||
|
"""获取用户有权限查看的知识库列表"""
|
|||
|
user = self.request.user
|
|||
|
|
|||
|
# 返回用户创建的或有读权限的知识库
|
|||
|
return KnowledgeBase.objects.filter(
|
|||
|
Q(user_id=user.id) | # 是创建者
|
|||
|
Q( # 或在权限表中有读权限
|
|||
|
id__in=KBPermissionModel.objects.filter(
|
|||
|
user=user,
|
|||
|
can_read=True,
|
|||
|
status='active'
|
|||
|
).values_list('knowledge_base_id', flat=True)
|
|||
|
)
|
|||
|
).distinct()
|
|||
|
|
|||
|
def create(self, request, *args, **kwargs):
|
|||
|
try:
|
|||
|
# 1. 验证知识库名称
|
|||
|
name = request.data.get('name')
|
|||
|
if not name:
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '知识库名称不能为空',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
if KnowledgeBase.objects.filter(name=name).exists():
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': f'知识库名称 "{name}" 已存在',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 2. 验证用户权限和必填字段
|
|||
|
user = request.user
|
|||
|
type = request.data.get('type', 'private')
|
|||
|
department = request.data.get('department')
|
|||
|
group = request.data.get('group')
|
|||
|
|
|||
|
# 权限验证
|
|||
|
if type == 'admin':
|
|||
|
if user.role != 'admin':
|
|||
|
return Response({
|
|||
|
'code': 403,
|
|||
|
'message': '只有管理员可以创建管理员级知识库',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
department = None
|
|||
|
group = None
|
|||
|
|
|||
|
elif type == 'secret':
|
|||
|
if user.role != 'admin':
|
|||
|
return Response({
|
|||
|
'code': 403,
|
|||
|
'message': '只有管理员可以创建保密级知识库',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
elif type == 'leader':
|
|||
|
if user.role != 'admin':
|
|||
|
return Response({
|
|||
|
'code': 403,
|
|||
|
'message': '只有管理员可以创建组长级知识库',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
if not department:
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '创建组长级知识库时必须指定部门',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
elif type == 'member':
|
|||
|
if user.role not in ['admin', 'leader']:
|
|||
|
return Response({
|
|||
|
'code': 403,
|
|||
|
'message': '只有管理员和组长可以创建成员级知识库',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
if user.role == 'admin' and not department:
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '管理员创建成员知识库时必须指定部门',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
elif user.role == 'leader':
|
|||
|
department = user.department
|
|||
|
|
|||
|
if not group:
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '创建成员知识库时必须指定组',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 3. 验证请求数据
|
|||
|
data = request.data.copy()
|
|||
|
data['department'] = department
|
|||
|
data['group'] = group
|
|||
|
|
|||
|
# 不需要手动设置 user_id,由序列化器自动处理
|
|||
|
serializer = self.get_serializer(data=data)
|
|||
|
if not serializer.is_valid():
|
|||
|
logger.error(f"数据验证失败: {serializer.errors}")
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '数据验证失败',
|
|||
|
'data': serializer.errors
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
with transaction.atomic():
|
|||
|
# 4. 创建知识库
|
|||
|
try:
|
|||
|
knowledge_base = serializer.save()
|
|||
|
logger.info(f"知识库创建成功: id={knowledge_base.id}, name={knowledge_base.name}, user_id={knowledge_base.user_id}")
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"知识库创建失败: {str(e)}")
|
|||
|
raise
|
|||
|
|
|||
|
# 5. 调用外部API创建知识库
|
|||
|
try:
|
|||
|
external_response = self._create_external_dataset(knowledge_base)
|
|||
|
logger.info(f"外部知识库创建响应: {external_response}")
|
|||
|
|
|||
|
# 处理外部API响应
|
|||
|
if isinstance(external_response, str):
|
|||
|
knowledge_base.external_id = external_response
|
|||
|
knowledge_base.save()
|
|||
|
logger.info(f"更新knowledge_base的external_id为: {external_response}")
|
|||
|
else:
|
|||
|
if external_response.get('code') == 200:
|
|||
|
external_id = external_response.get('data', {}).get('id')
|
|||
|
if external_id:
|
|||
|
knowledge_base.external_id = external_id
|
|||
|
knowledge_base.save()
|
|||
|
logger.info(f"更新knowledge_base的external_id为: {external_id}")
|
|||
|
else:
|
|||
|
raise ValueError("外部API响应中未找到知识库ID")
|
|||
|
else:
|
|||
|
raise ValueError(f"外部API调用失败: {external_response.get('message', '未知错误')}")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"外部知识库创建失败: {str(e)}")
|
|||
|
logger.error(f"外部API响应内容: {external_response if locals().get('external_response') else 'No response'}")
|
|||
|
raise ExternalAPIError(f"外部知识库创建失败: {str(e)}")
|
|||
|
|
|||
|
# 6. 创建权限记录
|
|||
|
try:
|
|||
|
# 创建者权限
|
|||
|
KBPermissionModel.objects.create(
|
|||
|
knowledge_base=knowledge_base,
|
|||
|
user=request.user,
|
|||
|
can_read=True,
|
|||
|
can_edit=True,
|
|||
|
can_delete=True,
|
|||
|
granted_by=request.user,
|
|||
|
status='active'
|
|||
|
)
|
|||
|
logger.info(f"创建者权限创建成功")
|
|||
|
|
|||
|
# 根据类型批量创建其他用户权限
|
|||
|
if type == 'admin':
|
|||
|
users_query = User.objects.exclude(id=request.user.id)
|
|||
|
elif type == 'secret':
|
|||
|
users_query = User.objects.filter(role='admin').exclude(id=request.user.id)
|
|||
|
elif type == 'leader':
|
|||
|
users_query = User.objects.filter(
|
|||
|
Q(role='admin') |
|
|||
|
Q(role='leader', department=department)
|
|||
|
).exclude(id=request.user.id)
|
|||
|
elif type == 'member':
|
|||
|
users_query = User.objects.filter(
|
|||
|
Q(role='admin') |
|
|||
|
Q(department=department, role='leader') |
|
|||
|
Q(department=department, group=group, role='member')
|
|||
|
).exclude(id=request.user.id)
|
|||
|
else: # private
|
|||
|
users_query = User.objects.none()
|
|||
|
|
|||
|
if users_query.exists():
|
|||
|
permissions = [
|
|||
|
KBPermissionModel(
|
|||
|
knowledge_base=knowledge_base,
|
|||
|
user=user,
|
|||
|
can_read=True,
|
|||
|
can_edit=self._can_edit(type, user),
|
|||
|
can_delete=self._can_delete(type, user),
|
|||
|
granted_by=request.user,
|
|||
|
status='active'
|
|||
|
) for user in users_query
|
|||
|
]
|
|||
|
KBPermissionModel.objects.bulk_create(permissions)
|
|||
|
logger.info(f"{type}类型权限创建完成: {len(permissions)}条记录")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"权限创建失败: {str(e)}")
|
|||
|
logger.error(traceback.format_exc())
|
|||
|
raise
|
|||
|
|
|||
|
return Response({
|
|||
|
'code': 200,
|
|||
|
'message': '知识库创建成功',
|
|||
|
'data': {
|
|||
|
'knowledge_base': serializer.data,
|
|||
|
'external_id': knowledge_base.external_id
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
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)
|
|||
|
|
|||
|
def _can_edit(self, type, user):
|
|||
|
"""判断用户是否有编辑权限"""
|
|||
|
if type == 'admin':
|
|||
|
return True
|
|||
|
elif type == 'secret':
|
|||
|
return user.role == 'admin'
|
|||
|
elif type in ['leader', 'member']:
|
|||
|
return user.role in ['admin', 'leader']
|
|||
|
return False
|
|||
|
|
|||
|
def _can_delete(self, type, user):
|
|||
|
"""判断用户是否有删除权限"""
|
|||
|
if type in ['admin', 'secret']:
|
|||
|
return user.role == 'admin'
|
|||
|
elif type in ['leader', 'member']:
|
|||
|
return user.role in ['admin', 'leader']
|
|||
|
return False
|
|||
|
|
|||
|
def _create_external_dataset(self, instance):
|
|||
|
"""创建外部知识库"""
|
|||
|
try:
|
|||
|
api_data = {
|
|||
|
"name": instance.name,
|
|||
|
"desc": instance.desc,
|
|||
|
"type": "0", # 添加必要的type字段
|
|||
|
"meta": {}, # 添加必要的meta字段
|
|||
|
"documents": [] # 初始化为空列表
|
|||
|
}
|
|||
|
|
|||
|
response = requests.post(
|
|||
|
f'{settings.API_BASE_URL}/api/dataset',
|
|||
|
json=api_data,
|
|||
|
headers={'Content-Type': 'application/json'},
|
|||
|
timeout=30
|
|||
|
)
|
|||
|
|
|||
|
if response.status_code != 200:
|
|||
|
raise ExternalAPIError(f"创建失败,状态码: {response.status_code}, 响应: {response.text}")
|
|||
|
|
|||
|
api_response = response.json()
|
|||
|
if not api_response.get('code') == 200:
|
|||
|
raise ExternalAPIError(f"业务处理失败: {api_response.get('message', '未知错误')}")
|
|||
|
|
|||
|
dataset_id = api_response.get('data', {}).get('id')
|
|||
|
if not dataset_id:
|
|||
|
raise ExternalAPIError("响应数据中缺少dataset id")
|
|||
|
|
|||
|
return dataset_id
|
|||
|
|
|||
|
except requests.exceptions.Timeout:
|
|||
|
raise ExternalAPIError("请求超时,请稍后重试")
|
|||
|
except requests.exceptions.RequestException as e:
|
|||
|
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
|||
|
except Exception as e:
|
|||
|
raise ExternalAPIError(f"创建外部知识库失败: {str(e)}")
|
|||
|
|
|||
|
def _delete_external_dataset(self, external_id):
|
|||
|
"""删除外部知识库"""
|
|||
|
try:
|
|||
|
if not external_id:
|
|||
|
raise ExternalAPIError("外部知识库ID不能为空")
|
|||
|
|
|||
|
response = requests.delete(
|
|||
|
f'{settings.API_BASE_URL}/api/dataset/{external_id}',
|
|||
|
headers={'Content-Type': 'application/json'},
|
|||
|
timeout=30
|
|||
|
)
|
|||
|
|
|||
|
logger.info(f"删除外部知识库响应: status_code={response.status_code}, response={response.text}")
|
|||
|
|
|||
|
# 检查响应状态码
|
|||
|
if response.status_code == 404:
|
|||
|
logger.warning(f"外部知识库不存在: {external_id}")
|
|||
|
return True # 如果知识库不存在,也视为删除成功
|
|||
|
elif response.status_code not in [200, 204]:
|
|||
|
raise ExternalAPIError(f"删除失败,状态码: {response.status_code}, 响应: {response.text}")
|
|||
|
|
|||
|
# 如果是 204 状态码,说明删除成功但无返回内容
|
|||
|
if response.status_code == 204:
|
|||
|
logger.info(f"外部知识库删除成功: {external_id}")
|
|||
|
return True
|
|||
|
|
|||
|
# 如果是 200 状态码,检查响应内容
|
|||
|
try:
|
|||
|
api_response = response.json()
|
|||
|
if api_response.get('code') != 200:
|
|||
|
raise ExternalAPIError(f"业务处理失败: {api_response.get('message', '未知错误')}")
|
|||
|
logger.info(f"外部知识库删除成功: {external_id}")
|
|||
|
return True
|
|||
|
except ValueError:
|
|||
|
# 如果无法解析 JSON,但状态码是 200,也认为成功
|
|||
|
logger.warning(f"外部知识库删除响应无法解析JSON,但状态码为200,视为成功: {external_id}")
|
|||
|
return True
|
|||
|
|
|||
|
except requests.exceptions.Timeout:
|
|||
|
logger.error(f"删除外部知识库超时: {external_id}")
|
|||
|
raise ExternalAPIError("请求超时,请稍后重试")
|
|||
|
except requests.exceptions.RequestException as e:
|
|||
|
logger.error(f"删除外部知识库请求异常: {external_id}, error={str(e)}")
|
|||
|
raise ExternalAPIError(f"API请求失败: {str(e)}")
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"删除外部知识库其他错误: {external_id}, error={str(e)}")
|
|||
|
raise ExternalAPIError(f"删除外部知识库失败: {str(e)}")
|
|||
|
|
|||
|
def update(self, request, *args, **kwargs):
|
|||
|
"""更新知识库"""
|
|||
|
try:
|
|||
|
instance = self.get_object()
|
|||
|
|
|||
|
# 检查编辑权限
|
|||
|
permission = KBPermissionModel.objects.filter(
|
|||
|
knowledge_base=instance,
|
|||
|
user=request.user,
|
|||
|
can_edit=True,
|
|||
|
status='active'
|
|||
|
).first()
|
|||
|
|
|||
|
if not permission:
|
|||
|
return Response({
|
|||
|
"code": 403,
|
|||
|
"message": "没有编辑权限",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
# 执行更新
|
|||
|
serializer = self.get_serializer(instance, data=request.data, partial=True)
|
|||
|
serializer.is_valid(raise_exception=True)
|
|||
|
self.perform_update(serializer)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "知识库更新成功",
|
|||
|
"data": serializer.data
|
|||
|
})
|
|||
|
|
|||
|
except Http404:
|
|||
|
return Response({
|
|||
|
"code": 404,
|
|||
|
"message": "知识库不存在",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_404_NOT_FOUND)
|
|||
|
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)
|
|||
|
|
|||
|
def destroy(self, request, *args, **kwargs):
|
|||
|
"""删除知识库"""
|
|||
|
try:
|
|||
|
instance = self.get_object()
|
|||
|
|
|||
|
# 检查删除权限
|
|||
|
permission = KBPermissionModel.objects.filter(
|
|||
|
knowledge_base=instance,
|
|||
|
user=request.user,
|
|||
|
can_delete=True,
|
|||
|
status='active'
|
|||
|
).first()
|
|||
|
|
|||
|
if not permission:
|
|||
|
return Response({
|
|||
|
"code": 403,
|
|||
|
"message": "没有删除权限",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
with transaction.atomic():
|
|||
|
# 删除外部知识库
|
|||
|
if instance.external_id:
|
|||
|
try:
|
|||
|
self._delete_external_dataset(instance.external_id)
|
|||
|
logger.info(f"外部知识库删除成功: {instance.external_id}")
|
|||
|
except ExternalAPIError as e:
|
|||
|
logger.error(f"删除外部知识库失败: {str(e)}")
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"删除外部知识库失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
# 删除本地知识库
|
|||
|
self.perform_destroy(instance)
|
|||
|
logger.info(f"本地知识库删除成功: id={instance.id}, name={instance.name}")
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "知识库删除成功",
|
|||
|
"data": None
|
|||
|
})
|
|||
|
|
|||
|
except Http404:
|
|||
|
return Response({
|
|||
|
"code": 404,
|
|||
|
"message": "知识库不存在",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_404_NOT_FOUND)
|
|||
|
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)
|
|||
|
|
|||
|
@action(detail=True, methods=['get'])
|
|||
|
def permissions(self, request, pk=None):
|
|||
|
"""获取用户对特定知识库的权限"""
|
|||
|
try:
|
|||
|
instance = self.get_object()
|
|||
|
|
|||
|
# 从权限表获取权限信息
|
|||
|
permission = KBPermissionModel.objects.filter(
|
|||
|
knowledge_base=instance,
|
|||
|
user=request.user,
|
|||
|
status='active'
|
|||
|
).first()
|
|||
|
|
|||
|
# 构建权限数据
|
|||
|
permissions_data = {
|
|||
|
"can_read": False,
|
|||
|
"can_edit": False,
|
|||
|
"can_delete": False
|
|||
|
}
|
|||
|
|
|||
|
if permission:
|
|||
|
permissions_data.update({
|
|||
|
"can_read": permission.can_read,
|
|||
|
"can_edit": permission.can_edit,
|
|||
|
"can_delete": permission.can_delete
|
|||
|
})
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "获取权限信息成功",
|
|||
|
"data": {
|
|||
|
"knowledge_base_id": instance.id,
|
|||
|
"knowledge_base_name": instance.name,
|
|||
|
"permissions": permissions_data
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
except Http404:
|
|||
|
return Response({
|
|||
|
"code": 404,
|
|||
|
"message": "知识库不存在",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_404_NOT_FOUND)
|
|||
|
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)
|
|||
|
|
|||
|
@action(detail=False, methods=['get'])
|
|||
|
def summary(self, request):
|
|||
|
"""获取所有可见知识库的概要信息(除了secret类型)"""
|
|||
|
try:
|
|||
|
user = request.user
|
|||
|
|
|||
|
# 基础查询:根据用户角色过滤
|
|||
|
if user.role == 'admin':
|
|||
|
# 管理员可以看到所有知识库
|
|||
|
queryset = KnowledgeBase.objects.all()
|
|||
|
else:
|
|||
|
# 其他用户看不到secret类型的知识库
|
|||
|
queryset = KnowledgeBase.objects.exclude(type='secret')
|
|||
|
|
|||
|
# 获取每个知识库的权限信息
|
|||
|
summaries = []
|
|||
|
for kb in queryset:
|
|||
|
# 默认权限
|
|||
|
permissions = {
|
|||
|
"can_read": False,
|
|||
|
"can_edit": False,
|
|||
|
"can_delete": False
|
|||
|
}
|
|||
|
|
|||
|
# 根据角色和知识库类型设置权限
|
|||
|
if kb.type == 'admin':
|
|||
|
permissions.update({
|
|||
|
"can_read": user.role == 'admin', # 只有管理员可以读
|
|||
|
"can_edit": user.role == 'admin', # 只有管理员可以编辑
|
|||
|
"can_delete": user.role == 'admin' # 只有管理员可以删除
|
|||
|
})
|
|||
|
elif kb.type == 'leader':
|
|||
|
if user.role == 'admin':
|
|||
|
permissions.update({
|
|||
|
"can_read": True,
|
|||
|
"can_edit": True,
|
|||
|
"can_delete": True
|
|||
|
})
|
|||
|
elif user.role == 'leader' and kb.department == user.department:
|
|||
|
permissions.update({
|
|||
|
"can_read": True,
|
|||
|
"can_edit": False,
|
|||
|
"can_delete": False
|
|||
|
})
|
|||
|
elif kb.type == 'member':
|
|||
|
if user.role == 'admin':
|
|||
|
permissions.update({
|
|||
|
"can_read": True,
|
|||
|
"can_edit": True,
|
|||
|
"can_delete": True
|
|||
|
})
|
|||
|
elif user.role == 'leader' and kb.department == user.department:
|
|||
|
permissions.update({
|
|||
|
"can_read": True,
|
|||
|
"can_edit": True,
|
|||
|
"can_delete": True
|
|||
|
})
|
|||
|
elif user.role == 'member' and kb.department == user.department:
|
|||
|
permissions.update({
|
|||
|
"can_read": True,
|
|||
|
"can_edit": False,
|
|||
|
"can_delete": False
|
|||
|
})
|
|||
|
elif kb.type == 'private':
|
|||
|
if str(kb.user_id) == str(user.id):
|
|||
|
permissions.update({
|
|||
|
"can_read": True,
|
|||
|
"can_edit": True,
|
|||
|
"can_delete": True
|
|||
|
})
|
|||
|
|
|||
|
summary = {
|
|||
|
"id": kb.id,
|
|||
|
"name": kb.name,
|
|||
|
"desc": kb.desc,
|
|||
|
"type": kb.type,
|
|||
|
"permissions": permissions
|
|||
|
}
|
|||
|
summaries.append(summary)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "获取知识库概要信息成功",
|
|||
|
"data": summaries
|
|||
|
})
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"获取知识库概要信息失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
class PermissionViewSet(viewsets.ModelViewSet):
|
|||
|
serializer_class = PermissionSerializer
|
|||
|
permission_classes = [IsAuthenticated]
|
|||
|
|
|||
|
def can_manage_knowledge_base(self, user, knowledge_base):
|
|||
|
"""检查用户是否是知识库的创建者"""
|
|||
|
return str(knowledge_base.user_id) == str(user.id)
|
|||
|
|
|||
|
def get_queryset(self):
|
|||
|
"""
|
|||
|
获取权限申请列表:
|
|||
|
1. 自己发出的申请
|
|||
|
2. 对自己有管理权限的知识库收到的申请
|
|||
|
"""
|
|||
|
user = self.request.user
|
|||
|
|
|||
|
# 获取用户有管理权限的知识库ID列表
|
|||
|
managed_kb_ids = KBPermissionModel.objects.filter(
|
|||
|
user=user,
|
|||
|
can_edit=True, # 假设有编辑权限就可以管理权限申请
|
|||
|
status='active'
|
|||
|
).values_list('knowledge_base_id', flat=True)
|
|||
|
|
|||
|
# 构建查询条件
|
|||
|
query = Q(applicant=user) # 自己发出的申请
|
|||
|
query |= Q(knowledge_base_id__in=managed_kb_ids) # 有管理权限的知识库的申请
|
|||
|
|
|||
|
return Permission.objects.filter(query).distinct().select_related(
|
|||
|
'knowledge_base', 'applicant', 'approver'
|
|||
|
)
|
|||
|
|
|||
|
def perform_create(self, serializer):
|
|||
|
"""创建权限申请并发送通知给知识库创建者"""
|
|||
|
# 获取知识库
|
|||
|
knowledge_base = serializer.validated_data['knowledge_base']
|
|||
|
|
|||
|
# 验证权限请求
|
|||
|
requested_permissions = serializer.validated_data.get('permissions', {})
|
|||
|
expires_at = serializer.validated_data.get('expires_at')
|
|||
|
|
|||
|
if not any([requested_permissions.get('can_read'),
|
|||
|
requested_permissions.get('can_edit'),
|
|||
|
requested_permissions.get('can_delete')]):
|
|||
|
raise ValidationError("至少需要申请一种权限(读/改/删)")
|
|||
|
|
|||
|
if not expires_at:
|
|||
|
raise ValidationError("请指定权限到期时间")
|
|||
|
|
|||
|
# 检查是否已有未过期的权限申请
|
|||
|
existing_request = Permission.objects.filter(
|
|||
|
knowledge_base=knowledge_base,
|
|||
|
applicant=self.request.user,
|
|||
|
status='pending'
|
|||
|
).first()
|
|||
|
|
|||
|
if existing_request:
|
|||
|
raise ValidationError("您已有一个待处理的权限申请")
|
|||
|
|
|||
|
# 检查是否已有有效的权限
|
|||
|
existing_permission = Permission.objects.filter(
|
|||
|
knowledge_base=knowledge_base,
|
|||
|
applicant=self.request.user,
|
|||
|
status='approved',
|
|||
|
expires_at__gt=timezone.now()
|
|||
|
).first()
|
|||
|
|
|||
|
if existing_permission:
|
|||
|
raise ValidationError("您已有此知识库的访问权限")
|
|||
|
|
|||
|
# 保存权限申请
|
|||
|
permission = serializer.save(
|
|||
|
applicant=self.request.user,
|
|||
|
status='pending'
|
|||
|
)
|
|||
|
|
|||
|
# 获取权限类型字符串
|
|||
|
permission_types = []
|
|||
|
if requested_permissions.get('can_read'):
|
|||
|
permission_types.append('读取')
|
|||
|
if requested_permissions.get('can_edit'):
|
|||
|
permission_types.append('编辑')
|
|||
|
if requested_permissions.get('can_delete'):
|
|||
|
permission_types.append('删除')
|
|||
|
permission_str = '、'.join(permission_types)
|
|||
|
|
|||
|
# 发送通知给知识库创建者
|
|||
|
owner = User.objects.get(id=knowledge_base.user_id)
|
|||
|
self.send_notification(
|
|||
|
user=owner,
|
|||
|
title="新的权限申请",
|
|||
|
content=f"用户 {self.request.user.name} 申请了知识库 '{knowledge_base.name}' 的{permission_str}权限",
|
|||
|
notification_type="permission_request",
|
|||
|
related_object_id=permission.id
|
|||
|
)
|
|||
|
|
|||
|
def send_notification(self, user, title, content, notification_type, related_object_id):
|
|||
|
"""发送通知"""
|
|||
|
try:
|
|||
|
notification = Notification.objects.create(
|
|||
|
sender=self.request.user,
|
|||
|
receiver=user,
|
|||
|
title=title,
|
|||
|
content=content,
|
|||
|
type=notification_type,
|
|||
|
related_resource=related_object_id,
|
|||
|
)
|
|||
|
|
|||
|
# 通过WebSocket发送实时通知
|
|||
|
channel_layer = get_channel_layer()
|
|||
|
async_to_sync(channel_layer.group_send)(
|
|||
|
f"notification_user_{user.id}",
|
|||
|
{
|
|||
|
"type": "notification",
|
|||
|
"data": {
|
|||
|
"id": str(notification.id),
|
|||
|
"title": notification.title,
|
|||
|
"content": notification.content,
|
|||
|
"type": notification.type,
|
|||
|
"created_at": notification.created_at.isoformat(),
|
|||
|
"sender": {
|
|||
|
"id": str(notification.sender.id),
|
|||
|
"name": notification.sender.name
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
)
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"发送通知时发生错误: {str(e)}")
|
|||
|
|
|||
|
@action(detail=True, methods=['post'])
|
|||
|
def approve(self, request, pk=None):
|
|||
|
try:
|
|||
|
# 获取权限申请记录
|
|||
|
permission = self.get_object()
|
|||
|
|
|||
|
# 只检查是否是知识库创建者
|
|||
|
if not self.can_manage_knowledge_base(request.user, permission.knowledge_base):
|
|||
|
logger.warning(f"用户 {request.user.username} 尝试审批知识库 {permission.knowledge_base.name} 的权限申请,但不是创建者")
|
|||
|
return Response({
|
|||
|
'code': 403,
|
|||
|
'message': '只有知识库创建者可以审批此申请',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
# 获取审批意见
|
|||
|
response_message = request.data.get('response_message', '')
|
|||
|
|
|||
|
with transaction.atomic():
|
|||
|
# 更新权限申请状态
|
|||
|
permission.status = 'approved'
|
|||
|
permission.approver = request.user
|
|||
|
permission.response_message = response_message
|
|||
|
permission.save()
|
|||
|
|
|||
|
# 检查是否已存在权限记录
|
|||
|
kb_permission = KBPermissionModel.objects.filter(
|
|||
|
knowledge_base=permission.knowledge_base,
|
|||
|
user=permission.applicant
|
|||
|
).first()
|
|||
|
|
|||
|
if kb_permission:
|
|||
|
# 更新现有权限
|
|||
|
kb_permission.can_read = permission.permissions.get('can_read', False)
|
|||
|
kb_permission.can_edit = permission.permissions.get('can_edit', False)
|
|||
|
kb_permission.can_delete = permission.permissions.get('can_delete', False)
|
|||
|
kb_permission.granted_by = request.user
|
|||
|
kb_permission.status = 'active'
|
|||
|
kb_permission.expires_at = permission.expires_at
|
|||
|
kb_permission.save()
|
|||
|
logger.info(f"更新知识库权限记录: {kb_permission.id}")
|
|||
|
else:
|
|||
|
# 创建新的权限记录
|
|||
|
kb_permission = KBPermissionModel.objects.create(
|
|||
|
knowledge_base=permission.knowledge_base,
|
|||
|
user=permission.applicant,
|
|||
|
can_read=permission.permissions.get('can_read', False),
|
|||
|
can_edit=permission.permissions.get('can_edit', False),
|
|||
|
can_delete=permission.permissions.get('can_delete', False),
|
|||
|
granted_by=request.user,
|
|||
|
status='active',
|
|||
|
expires_at=permission.expires_at
|
|||
|
)
|
|||
|
logger.info(f"创建新的知识库权限记录: {kb_permission.id}")
|
|||
|
|
|||
|
# 发送通知给申请人
|
|||
|
self.send_notification(
|
|||
|
user=permission.applicant,
|
|||
|
title="权限申请已通过",
|
|||
|
content=f"您对知识库 '{permission.knowledge_base.name}' 的权限申请已通过",
|
|||
|
notification_type="permission_approved",
|
|||
|
related_object_id=permission.id
|
|||
|
)
|
|||
|
|
|||
|
return Response({
|
|||
|
'code': 200,
|
|||
|
'message': '权限申请已批准',
|
|||
|
'data': None
|
|||
|
})
|
|||
|
|
|||
|
except Permission.DoesNotExist:
|
|||
|
return Response({
|
|||
|
'code': 404,
|
|||
|
'message': '权限申请不存在',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_404_NOT_FOUND)
|
|||
|
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)
|
|||
|
|
|||
|
@action(detail=True, methods=['post'])
|
|||
|
def reject(self, request, pk=None):
|
|||
|
"""拒绝权限申请"""
|
|||
|
permission = self.get_object()
|
|||
|
|
|||
|
# 检查是否是知识库创建者
|
|||
|
if str(permission.knowledge_base.user_id) != str(request.user.id):
|
|||
|
return Response({
|
|||
|
'code': 403,
|
|||
|
'message': '只有知识库创建者可以审批此申请',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
# 检查申请是否已被处理
|
|||
|
if permission.status != 'pending':
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '该申请已被处理',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证拒绝原因
|
|||
|
response_message = request.data.get('response_message')
|
|||
|
if not response_message:
|
|||
|
return Response({
|
|||
|
'code': 400,
|
|||
|
'message': '请填写拒绝原因',
|
|||
|
'data': None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 更新权限状态
|
|||
|
permission.status = 'rejected'
|
|||
|
permission.approver = request.user
|
|||
|
permission.response_message = response_message
|
|||
|
permission.save()
|
|||
|
|
|||
|
# 发送通知给申请人
|
|||
|
self.send_notification(
|
|||
|
user=permission.applicant,
|
|||
|
title="权限申请已拒绝",
|
|||
|
content=f"您对知识库 '{permission.knowledge_base.name}' 的权限申请已被拒绝\n"
|
|||
|
f"拒绝原因:{response_message}",
|
|||
|
notification_type="permission_rejected",
|
|||
|
related_object_id=permission.id
|
|||
|
)
|
|||
|
|
|||
|
return Response({
|
|||
|
'code': 200,
|
|||
|
'message': '权限申请已拒绝',
|
|||
|
'data': PermissionSerializer(permission).data
|
|||
|
})
|
|||
|
|
|||
|
@action(detail=True, methods=['post'])
|
|||
|
def extend(self, request, pk=None):
|
|||
|
"""延长权限有效期"""
|
|||
|
instance = self.get_object()
|
|||
|
user = request.user
|
|||
|
|
|||
|
# 检查是否有权限延长
|
|||
|
if not self.check_extend_permission(instance, user):
|
|||
|
return Response({
|
|||
|
"code": 403,
|
|||
|
"message": "您没有权限延长此权限",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_403_FORBIDDEN)
|
|||
|
|
|||
|
new_expires_at = request.data.get('expires_at')
|
|||
|
if not new_expires_at:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "请设置新的过期时间",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
try:
|
|||
|
with transaction.atomic():
|
|||
|
# 更新权限申请表的过期时间
|
|||
|
instance.expires_at = new_expires_at
|
|||
|
instance.save()
|
|||
|
|
|||
|
# 同步更新知识库权限表的过期时间
|
|||
|
kb_permission = KBPermissionModel.objects.get(
|
|||
|
knowledge_base=instance.knowledge_base,
|
|||
|
user=instance.applicant
|
|||
|
)
|
|||
|
kb_permission.expires_at = new_expires_at
|
|||
|
kb_permission.save()
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "权限有效期延长成功",
|
|||
|
"data": PermissionSerializer(instance).data
|
|||
|
})
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"延长权限有效期失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
def check_extend_permission(self, permission, user):
|
|||
|
"""检查是否有权限延长权限有效期"""
|
|||
|
knowledge_base = permission.knowledge_base
|
|||
|
|
|||
|
# 私人知识库只有拥有者能延长
|
|||
|
if knowledge_base.type == 'private':
|
|||
|
return knowledge_base.owner == user
|
|||
|
|
|||
|
# 组长知识库只有管理员能延长
|
|||
|
if knowledge_base.type == 'leader':
|
|||
|
return user.role == 'admin'
|
|||
|
|
|||
|
# 组员知识库可以由管理员或本部门组长延长
|
|||
|
if knowledge_base.type == 'member':
|
|||
|
return (
|
|||
|
user.role == 'admin' or
|
|||
|
(user.role == 'leader' and user.department == knowledge_base.department)
|
|||
|
)
|
|||
|
|
|||
|
return False
|
|||
|
|
|||
|
class NotificationViewSet(viewsets.ModelViewSet):
|
|||
|
"""通知视图集"""
|
|||
|
queryset = Notification.objects.all()
|
|||
|
serializer_class = NotificationSerializer
|
|||
|
permission_classes = [IsAuthenticated]
|
|||
|
|
|||
|
def get_queryset(self):
|
|||
|
"""只返回用户自己的通知"""
|
|||
|
return Notification.objects.filter(receiver=self.request.user)
|
|||
|
|
|||
|
@action(detail=True, methods=['post'])
|
|||
|
def mark_as_read(self, request, pk=None):
|
|||
|
"""标记通知为已读"""
|
|||
|
notification = self.get_object()
|
|||
|
notification.is_read = True
|
|||
|
notification.save()
|
|||
|
return Response({'status': 'marked as read'})
|
|||
|
|
|||
|
@action(detail=False, methods=['post'])
|
|||
|
def mark_all_as_read(self, request):
|
|||
|
"""标记所有通知为已读"""
|
|||
|
self.get_queryset().update(is_read=True)
|
|||
|
return Response({'status': 'all marked as read'})
|
|||
|
|
|||
|
@action(detail=False, methods=['get'])
|
|||
|
def unread_count(self, request):
|
|||
|
"""获取未读通知数量"""
|
|||
|
count = self.get_queryset().filter(is_read=False).count()
|
|||
|
return Response({'unread_count': count})
|
|||
|
|
|||
|
@action(detail=False, methods=['get'])
|
|||
|
def latest(self, request):
|
|||
|
"""获取最新通知"""
|
|||
|
notifications = self.get_queryset().filter(
|
|||
|
is_read=False
|
|||
|
).order_by('-created_at')[:5]
|
|||
|
serializer = self.get_serializer(notifications, many=True)
|
|||
|
return Response(serializer.data)
|
|||
|
|
|||
|
def perform_create(self, serializer):
|
|||
|
"""创建通知时自动设置发送者"""
|
|||
|
serializer.save(sender=self.request.user)
|
|||
|
|
|||
|
@method_decorator(csrf_exempt, name='dispatch')
|
|||
|
class LoginView(APIView):
|
|||
|
"""用户登录视图"""
|
|||
|
authentication_classes = [] # 清空认证类
|
|||
|
permission_classes = [AllowAny]
|
|||
|
|
|||
|
def post(self, request):
|
|||
|
try:
|
|||
|
username = request.data.get('username')
|
|||
|
password = request.data.get('password')
|
|||
|
|
|||
|
# 参数验证
|
|||
|
if not username or not password:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "请提供用户名和密码",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证用户
|
|||
|
user = authenticate(request, username=username, password=password)
|
|||
|
|
|||
|
if user is not None:
|
|||
|
# 获取或创建token
|
|||
|
token, _ = Token.objects.get_or_create(user=user)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "登录成功",
|
|||
|
"data": {
|
|||
|
"id": str(user.id),
|
|||
|
"username": user.username,
|
|||
|
"email": user.email,
|
|||
|
"role": user.role,
|
|||
|
"department": user.department,
|
|||
|
"name": user.name,
|
|||
|
"group": user.group,
|
|||
|
"token": token.key
|
|||
|
}
|
|||
|
})
|
|||
|
else:
|
|||
|
return Response({
|
|||
|
"code": 401,
|
|||
|
"message": "用户名或密码错误",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_401_UNAUTHORIZED)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
import traceback
|
|||
|
logger.error(f"登录失败: {str(e)}")
|
|||
|
logger.error(f"错误类型: {type(e)}")
|
|||
|
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": "登录失败,请稍后重试",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
@method_decorator(csrf_exempt, name='dispatch')
|
|||
|
class RegisterView(APIView):
|
|||
|
"""用户注册视图"""
|
|||
|
permission_classes = [AllowAny]
|
|||
|
|
|||
|
def post(self, request):
|
|||
|
try:
|
|||
|
data = request.data
|
|||
|
|
|||
|
# 检查必填字段
|
|||
|
required_fields = ['username', 'password', 'email', 'role', 'department', 'name']
|
|||
|
for field in required_fields:
|
|||
|
if not data.get(field):
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": f"缺少必填字段: {field}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证角色
|
|||
|
valid_roles = ['admin', 'leader', 'member']
|
|||
|
roles_str = ', '.join(valid_roles) # 先构造角色字符串
|
|||
|
if data['role'] not in valid_roles:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": f"无效的角色,必须是: {roles_str}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证部门是否存在
|
|||
|
if data['department'] not in settings.DEPARTMENT_GROUPS:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": f"无效的部门,可选部门: {', '.join(settings.DEPARTMENT_GROUPS.keys())}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 如果是组员,验证小组
|
|||
|
if data['role'] == 'member':
|
|||
|
if not data.get('group'):
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "组员必须指定所属小组",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证小组是否存在且属于指定部门
|
|||
|
valid_groups = settings.DEPARTMENT_GROUPS.get(data['department'], [])
|
|||
|
if data['group'] not in valid_groups:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": f"无效的小组,{data['department']}的可选小组: {', '.join(valid_groups)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 检查用户名是否已存在
|
|||
|
if User.objects.filter(username=data['username']).exists():
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "用户名已存在",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 检查邮箱是否已存在
|
|||
|
if User.objects.filter(email=data['email']).exists():
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "邮箱已被注册",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证密码强度
|
|||
|
if len(data['password']) < 8:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "密码长度必须至少为8位",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证邮箱格式
|
|||
|
try:
|
|||
|
validate_email(data['email'])
|
|||
|
except ValidationError:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "邮箱格式不正确",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 创建用户
|
|||
|
user = User.objects.create_user(
|
|||
|
username=data['username'],
|
|||
|
email=data['email'],
|
|||
|
password=data['password'],
|
|||
|
role=data['role'],
|
|||
|
department=data['department'],
|
|||
|
name=data['name'],
|
|||
|
group=data.get('group') if data['role'] == 'member' else None,
|
|||
|
is_staff=False,
|
|||
|
is_superuser=False
|
|||
|
)
|
|||
|
|
|||
|
# 生成认证令牌
|
|||
|
token, _ = Token.objects.get_or_create(user=user)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "注册成功",
|
|||
|
"data": {
|
|||
|
"id": user.id,
|
|||
|
"username": user.username,
|
|||
|
"email": user.email,
|
|||
|
"role": user.role,
|
|||
|
"department": user.department,
|
|||
|
"name": user.name,
|
|||
|
"group": user.group,
|
|||
|
"token": token.key,
|
|||
|
"created_at": user.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
}
|
|||
|
}, status=status.HTTP_201_CREATED)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"注册失败: {str(e)}")
|
|||
|
print(f"错误类型: {type(e)}")
|
|||
|
print(f"错误堆栈: {traceback.format_exc()}")
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"注册失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
@method_decorator(csrf_exempt, name='dispatch')
|
|||
|
class LogoutView(APIView):
|
|||
|
"""用户登出视图"""
|
|||
|
permission_classes = [IsAuthenticated]
|
|||
|
|
|||
|
def post(self, request):
|
|||
|
try:
|
|||
|
# 删除用户的token
|
|||
|
request.user.auth_token.delete()
|
|||
|
# 执行django的登出
|
|||
|
logout(request)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "登出成功",
|
|||
|
"data": None
|
|||
|
})
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"登出失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
@api_view(['GET', 'PUT'])
|
|||
|
@permission_classes([IsAuthenticated])
|
|||
|
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 = request.user
|
|||
|
# 只允许更新特定字段
|
|||
|
allowed_fields = ['email', 'phone', 'department']
|
|||
|
for field in allowed_fields:
|
|||
|
if field in request.data:
|
|||
|
setattr(user, field, request.data[field])
|
|||
|
user.save()
|
|||
|
return Response({'message': '用户信息更新成功'})
|
|||
|
|
|||
|
@csrf_exempt
|
|||
|
@api_view(['POST'])
|
|||
|
@permission_classes([IsAuthenticated])
|
|||
|
def change_password(request):
|
|||
|
"""修改密码"""
|
|||
|
try:
|
|||
|
old_password = request.data.get('old_password')
|
|||
|
new_password = request.data.get('new_password')
|
|||
|
|
|||
|
# 验证参数
|
|||
|
if not old_password or not new_password:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "请提供旧密码和新密码",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证旧密码
|
|||
|
user = request.user
|
|||
|
if not user.check_password(old_password):
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "旧密码错误",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证新密码长度
|
|||
|
if len(new_password) < 8:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "新密码长度必须至少为8位",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 修改密码
|
|||
|
user.set_password(new_password)
|
|||
|
user.save()
|
|||
|
|
|||
|
# 更新token
|
|||
|
user.auth_token.delete()
|
|||
|
token, _ = Token.objects.get_or_create(user=user)
|
|||
|
|
|||
|
return Response({
|
|||
|
"code": 200,
|
|||
|
"message": "密码修改成功",
|
|||
|
"data": {
|
|||
|
"token": token.key
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"密码修改失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
@api_view(['POST'])
|
|||
|
@permission_classes([AllowAny])
|
|||
|
def user_register(request):
|
|||
|
"""用户注册"""
|
|||
|
try:
|
|||
|
data = request.data
|
|||
|
|
|||
|
# 检查必填字段
|
|||
|
required_fields = ['username', 'password', 'email', 'role', 'department', 'name']
|
|||
|
for field in required_fields:
|
|||
|
if not data.get(field):
|
|||
|
return Response({
|
|||
|
'error': f'缺少必填字段: {field}'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证角色
|
|||
|
valid_roles = ['admin', 'leader', 'member']
|
|||
|
if data['role'] not in valid_roles:
|
|||
|
return Response({
|
|||
|
'error': f'无效的角色,必须是: {", ".join(valid_roles)}'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 如果是组员,必须指定小组
|
|||
|
if data['role'] == 'member' and not data.get('group'):
|
|||
|
return Response({
|
|||
|
'error': '组员必须指定所属小组'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 检查用户名是否已存在
|
|||
|
if User.objects.filter(username=data['username']).exists():
|
|||
|
return Response({
|
|||
|
'error': '用户名已存在'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 检查邮箱是否已存在
|
|||
|
if User.objects.filter(email=data['email']).exists():
|
|||
|
return Response({
|
|||
|
'error': '邮箱已被注册'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证密码强度
|
|||
|
if len(data['password']) < 8:
|
|||
|
return Response({
|
|||
|
'error': '密码长度必须至少为8位'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 验证邮箱格式
|
|||
|
try:
|
|||
|
validate_email(data['email'])
|
|||
|
except ValidationError:
|
|||
|
return Response({
|
|||
|
'error': '邮箱格式不正确'
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
# 创建用户
|
|||
|
user = User.objects.create_user(
|
|||
|
username=data['username'],
|
|||
|
email=data['email'],
|
|||
|
password=data['password'],
|
|||
|
role=data['role'],
|
|||
|
department=data['department'],
|
|||
|
name=data['name'],
|
|||
|
group=data.get('group') if data['role'] == 'member' else None,
|
|||
|
is_staff=False,
|
|||
|
is_superuser=False
|
|||
|
)
|
|||
|
|
|||
|
# 生成认证令牌
|
|||
|
token, _ = Token.objects.get_or_create(user=user)
|
|||
|
|
|||
|
return Response({
|
|||
|
'message': '注册成功',
|
|||
|
'data': {
|
|||
|
'id': user.id,
|
|||
|
'username': user.username,
|
|||
|
'email': user.email,
|
|||
|
'role': user.role,
|
|||
|
'department': user.department,
|
|||
|
'name': user.name,
|
|||
|
'group': user.group,
|
|||
|
'token': token.key,
|
|||
|
'created_at': user.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
|||
|
}
|
|||
|
}, status=status.HTTP_201_CREATED)
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"注册失败: {str(e)}")
|
|||
|
print(f"错误类型: {type(e)}")
|
|||
|
print(f"错误堆栈: {traceback.format_exc()}")
|
|||
|
return Response({
|
|||
|
'error': 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": 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'])
|
|||
|
@permission_classes([IsAuthenticated])
|
|||
|
def user_detail(request, pk):
|
|||
|
"""获取用户详情"""
|
|||
|
try:
|
|||
|
# 尝试转换为 UUID
|
|||
|
if not isinstance(pk, uuid.UUID):
|
|||
|
try:
|
|||
|
pk = uuid.UUID(pk)
|
|||
|
except ValueError:
|
|||
|
return Response({
|
|||
|
"code": 400,
|
|||
|
"message": "无效的用户ID格式",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|||
|
|
|||
|
user = get_object_or_404(User, pk=pk)
|
|||
|
|
|||
|
return Response({
|
|||
|
"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
|
|||
|
}
|
|||
|
})
|
|||
|
except Exception as e:
|
|||
|
return Response({
|
|||
|
"code": 500,
|
|||
|
"message": f"获取用户信息失败: {str(e)}",
|
|||
|
"data": None
|
|||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|||
|
|
|||
|
@api_view(['PUT'])
|
|||
|
@permission_classes([IsAdminUser])
|
|||
|
def user_update(request, pk):
|
|||
|
"""更新用户信息"""
|
|||
|
try:
|
|||
|
user = User.objects.get(pk=pk)
|
|||
|
# 只允许更新特定字段
|
|||
|
allowed_fields = ['email', 'role', 'department', 'is_active', 'phone']
|
|||
|
for field in allowed_fields:
|
|||
|
if field in request.data:
|
|||
|
setattr(user, field, request.data[field])
|
|||
|
user.save()
|
|||
|
return Response({'message': '用户信息更新成功'})
|
|||
|
except User.DoesNotExist:
|
|||
|
return Response({'message': '用户不存在'}, status=404)
|
|||
|
|
|||
|
@api_view(['DELETE'])
|
|||
|
@permission_classes([IsAdminUser])
|
|||
|
def user_delete(request, pk):
|
|||
|
"""删除用户"""
|
|||
|
try:
|
|||
|
user = User.objects.get(pk=pk)
|
|||
|
user.delete()
|
|||
|
return Response({'message': '用户删除成功'})
|
|||
|
except User.DoesNotExist:
|
|||
|
return Response({'message': '用户不存在'}, status=404)
|