From af5aed7c0f931b973c8783909ebbcc2fe2872b97 Mon Sep 17 00:00:00 2001 From: wanjia Date: Mon, 10 Mar 2025 17:03:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.ps1 | 59 ++++ role_based_system/settings.py | 15 +- user_management/views.py | 522 +++++++++++++++++++++++++++++++--- 3 files changed, 558 insertions(+), 38 deletions(-) create mode 100644 deploy.ps1 diff --git a/deploy.ps1 b/deploy.ps1 new file mode 100644 index 00000000..356af42a --- /dev/null +++ b/deploy.ps1 @@ -0,0 +1,59 @@ +# 确保在项目根目录下执行此脚本 + +# 7-Zip路径 - 请根据实际安装位置修改 +$7zipPath = "D:\7zip\7-Zip\7z.exe" # 原始路径 +# $7zipPath = "C:\Program Files\7-Zip\7z.exe" # 修改后的路径 + +# 创建临时部署目录 +New-Item -Path "deploy_temp" -ItemType Directory -Force + +# 复制整个项目目录到临时目录 +Copy-Item -Path "role_based_system" -Destination "deploy_temp\" -Recurse -Force +Copy-Item -Path "user_management" -Destination "deploy_temp\" -Recurse -Force +Copy-Item -Path "manage.py" -Destination "deploy_temp\" -Force +Copy-Item -Path "requirements.txt" -Destination "deploy_temp\" -Force +Copy-Item -Path "*.md" -Destination "deploy_temp\" -Force -ErrorAction SilentlyContinue + +# 移除不需要的文件和目录 +Get-ChildItem -Path "deploy_temp" -Recurse -Filter "__pycache__" -Directory | Remove-Item -Recurse -Force +Get-ChildItem -Path "deploy_temp" -Recurse -Filter "*.pyc" | Remove-Item -Force + +# 特别排除不需要的目录 +$excludeDirs = @( + ".git", ".idea", ".vscode", + "venv", ".venv", "env", "__pycache__", "migrations" +) + +foreach ($dir in $excludeDirs) { + Get-ChildItem -Path "deploy_temp" -Recurse -Directory -Filter $dir | + Where-Object { $_.FullName -notmatch "\\migrations\\__pycache__" } | + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue +} + +# 保留migrations目录但删除其中的pyc文件 +if (Test-Path "deploy_temp\user_management\migrations") { + Get-ChildItem -Path "deploy_temp\user_management\migrations" -Filter "*.pyc" | Remove-Item -Force + Get-ChildItem -Path "deploy_temp\user_management\migrations" -Directory -Filter "__pycache__" | Remove-Item -Recurse -Force +} + +# 排除不需要的文件 +$excludeFiles = @( + "*.pyc", "*.pyo", "*.pyd", "*.so", "*.dll", + "*.db", "*.sqlite3", "*.log", "*.zip", "*.tar.gz", + "local_settings.py", "*.bak" +) + +foreach ($pattern in $excludeFiles) { + Get-ChildItem -Path "deploy_temp" -Recurse -Filter $pattern | Remove-Item -Force -ErrorAction SilentlyContinue +} + + +# 使用7-Zip打包 +& $7zipPath a -ttar knowledge_system.tar ".\deploy_temp\*" +& $7zipPath a -tgzip knowledge_system.tar.gz knowledge_system.tar + +# 清理临时文件 +Remove-Item -Path "knowledge_system.tar" -Force +Remove-Item -Path "deploy_temp" -Recurse -Force + +Write-Host "部署包已创建: knowledge_system.tar.gz" -ForegroundColor Green \ No newline at end of file diff --git a/role_based_system/settings.py b/role_based_system/settings.py index dfcc1d58..1fe78058 100644 --- a/role_based_system/settings.py +++ b/role_based_system/settings.py @@ -65,7 +65,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - # 'django.middleware.csrf.CsrfViewMiddleware', # WebSocket不需要CSRF + 'django.middleware.csrf.CsrfViewMiddleware', # 确保这行没有被注释 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', @@ -176,8 +176,10 @@ CORS_ALLOW_CREDENTIALS = True CORS_ALLOWED_ORIGINS = [ "http://localhost:8000", "http://127.0.0.1:8000", + "http://124.222.236.141:8000", "ws://localhost:8000", # 添加 WebSocket - "ws://127.0.0.1:8000" # 添加 WebSocket + "ws://127.0.0.1:8000", # 添加 WebSocket + "ws://124.222.236.141:8000", # 添加 WebSocket ] # 允许的请求头 CORS_ALLOWED_HEADERS = [ @@ -208,8 +210,10 @@ CORS_ALLOWED_METHODS = [ CSRF_TRUSTED_ORIGINS = [ 'http://localhost:8000', 'http://127.0.0.1:8000', + 'http://124.222.236.141:8000', 'ws://localhost:8000', # 添加 WebSocket 'ws://127.0.0.1:8000', # 添加 WebSocket + 'ws://124.222.236.141:8000', # 添加 WebSocket ] # 服务器配置 # 静态文件配置 @@ -241,6 +245,13 @@ LOGGING = { 'handlers': ['console', 'file'], 'level': 'DEBUG', }, + 'loggers': { + 'django.security.csrf': { + 'handlers': ['file'], + 'level': 'WARNING', + 'propagate': True, + }, + }, } # CSRF 配置 diff --git a/user_management/views.py b/user_management/views.py index 565fbc4f..a7a33cd1 100644 --- a/user_management/views.py +++ b/user_management/views.py @@ -420,12 +420,112 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): def list(self, request, *args, **kwargs): try: queryset = self.get_queryset() - serializer = self.get_serializer(queryset, many=True) + + # 获取搜索关键字 + keyword = request.query_params.get('keyword', '') + + # 如果有关键字,构建搜索条件 + if keyword: + query = Q(name__icontains=keyword) | \ + Q(desc__icontains=keyword) | \ + Q(department__icontains=keyword) | \ + Q(group__icontains=keyword) + queryset = queryset.filter(query) + + # 获取分页参数 + try: + page = int(request.query_params.get('page', 1)) + page_size = int(request.query_params.get('page_size', 10)) + except ValueError: + page = 1 + page_size = 10 + + # 获取用户所有有效的知识库权限 + user = request.user + active_permissions = KBPermissionModel.objects.filter( + user=user, + status='active', + expires_at__gt=timezone.now() + ).select_related('knowledge_base') + + # 创建权限映射字典 + permission_map = { + str(perm.knowledge_base.id): { + 'can_read': perm.can_read, + 'can_edit': perm.can_edit, + 'can_delete': perm.can_delete + } + for perm in active_permissions + } + + # 计算总数量 + total = queryset.count() + + # 分页处理 + start = (page - 1) * page_size + end = start + page_size + paginated_queryset = queryset[start:end] + + # 序列化知识库数据 + serializer = self.get_serializer(paginated_queryset, many=True) + data = serializer.data + + # 为每个知识库添加权限信息 + for item in data: + kb_id = item['id'] + permissions = { + 'can_read': False, + 'can_edit': False, + 'can_delete': False + } + + # 检查知识库特定权限 + if kb_id in permission_map: + permissions.update(permission_map[kb_id]) + # 如果没有特定权限,使用_can_read/_can_edit/_can_delete方法设置默认权限 + else: + # 获取必要的知识库属性 + kb_type = item['type'] + department = item.get('department') + group = item.get('group') + creator_id = item.get('user_id') + + # 使用方法判断权限 + permissions['can_read'] = self._can_read(kb_type, user, department, group, creator_id) + permissions['can_edit'] = self._can_edit(kb_type, user, department, group, creator_id) + permissions['can_delete'] = self._can_delete(kb_type, user, department, group, creator_id) + + # 添加权限信息到知识库数据 + item['permissions'] = permissions + + # 如果有关键字,添加高亮信息 + if keyword: + # 处理name高亮 + if 'name' in item and keyword.lower() in item['name'].lower(): + highlighted = item['name'].replace( + keyword, f'{keyword}' + ) + item['highlighted_name'] = highlighted + + # 处理desc高亮,注意处理None值 + if 'desc' in item and item.get('desc') is not None: + desc_text = str(item['desc']) + if keyword.lower() in desc_text.lower(): + highlighted = desc_text.replace( + keyword, f'{keyword}' + ) + item['highlighted_desc'] = highlighted return Response({ "code": 200, "message": "获取知识库列表成功", - "data": serializer.data + "data": { + "total": total, + "page": page, + "page_size": page_size, + "keyword": keyword if keyword else None, + "items": data + } }) except Exception as e: logger.error(f"获取知识库列表失败: {str(e)}") @@ -439,9 +539,10 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): def get_queryset(self): """获取用户有权限查看的知识库列表""" user = self.request.user + all_knowledge_bases = KnowledgeBase.objects.all() - # 返回用户创建的或有读权限的知识库 - return KnowledgeBase.objects.filter( + # 首先过滤出用户创建的或有显式读权限的知识库 + explicit_permission_kbs = KnowledgeBase.objects.filter( Q(user_id=user.id) | # 是创建者 Q( # 或在权限表中有读权限 id__in=KBPermissionModel.objects.filter( @@ -450,7 +551,34 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): status='active' ).values_list('knowledge_base_id', flat=True) ) - ).distinct() + ) + + # 获取显式权限知识库的ID集合 + explicit_kb_ids = set(explicit_permission_kbs.values_list('id', flat=True)) + + # 对于没有显式权限的知识库,使用_can_read方法判断 + result_kbs = list(explicit_permission_kbs) + + # 如果用户是管理员,可以查看所有知识库 + if user.role == 'admin': + return all_knowledge_bases + + # 否则,根据角色和部门/组筛选 + for kb in all_knowledge_bases: + if kb.id not in explicit_kb_ids: + has_read_permission = self._can_read( + type=kb.type, + user=user, + department=kb.department, + group=kb.group, + creator_id=kb.user_id + ) + if has_read_permission: + result_kbs.append(kb) + + # 转换为queryset并去重 + kb_ids = [kb.id for kb in result_kbs] + return KnowledgeBase.objects.filter(id__in=kb_ids).distinct() def create(self, request, *args, **kwargs): try: @@ -655,22 +783,76 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - def _can_edit(self, type, user): + def _can_edit(self, type, user, department=None, group=None, creator_id=None): """判断用户是否有编辑权限""" - if type == 'admin': - return True - elif type == 'secret': - return user.role == 'admin' - elif type in ['leader', 'member']: - return user.role in ['admin', 'leader'] + if user.role == 'admin': + return True # 管理员对所有知识库都有编辑权限 + + if type == 'secret': + return False # 除管理员外,其他人无权编辑secret类型知识库 + + if type == 'leader': + # 同部门组长可编辑leader知识库 + return user.role == 'leader' and user.department == department + + if type == 'member': + # 同部门组长可编辑所有member知识库 + if user.role == 'leader' and user.department == department: + return True + # 成员只能编辑同部门同组的member知识库 + return user.role == 'member' and user.department == department and user.group == group + + if type == 'private': + # 私有知识库只有创建者可编辑 + return str(user.id) == str(creator_id) + return False - def _can_delete(self, type, user): + def _can_delete(self, type, user, department=None, group=None, creator_id=None): """判断用户是否有删除权限""" + if user.role == 'admin': + return True # 管理员对所有知识库都有删除权限 + if type in ['admin', 'secret']: - return user.role == 'admin' - elif type in ['leader', 'member']: - return user.role in ['admin', 'leader'] + return False # 除管理员外,其他人无权删除admin和secret类型知识库 + + if type == 'leader': + # 同部门组长可删除leader知识库 + return user.role == 'leader' and user.department == department + + if type == 'member': + # 只有组长可以删除成员知识库(组员不能删除) + return user.role == 'leader' and user.department == department + + if type == 'private': + # 私有知识库只有创建者可删除 + return str(user.id) == str(creator_id) + + return False + + def _can_read(self, type, user, department=None, group=None, creator_id=None): + """判断用户是否有读取权限""" + if user.role == 'admin': + return True # 管理员可以读取所有知识库 + + if type == 'secret': + return False # 除管理员外,secret类型知识库对其他人不可读 + + if type == 'leader': + # 同部门的leader和member都可以读取 + return user.department == department + + if type == 'member': + # 同部门组长可以读取所有member知识库 + if user.role == 'leader' and user.department == department: + return True + # 成员可以读取同部门同组的member知识库 + return user.role == 'member' and user.department == department and user.group == group + + if type == 'private': + # 私有知识库只有创建者可读 + return str(user.id) == str(creator_id) + return False def _create_external_dataset(self, instance): @@ -763,16 +945,29 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): """更新知识库""" try: instance = self.get_object() + user = request.user - # 检查编辑权限 + # 首先检查权限表中是否有显式权限 permission = KBPermissionModel.objects.filter( knowledge_base=instance, - user=request.user, + user=user, can_edit=True, status='active' ).first() - if not permission: + # 如果权限表中没有记录,则使用_can_edit方法判断 + has_permission = bool(permission) + if not has_permission: + # 根据知识库类型和用户角色判断权限 + has_permission = self._can_edit( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ) + + if not has_permission: return Response({ "code": 403, "message": "没有编辑权限", @@ -852,16 +1047,29 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): """删除知识库""" try: instance = self.get_object() + user = request.user # 检查删除权限 permission = KBPermissionModel.objects.filter( knowledge_base=instance, - user=request.user, + user=user, can_delete=True, status='active' ).first() - if not permission: + # 如果权限表中没有记录,则使用_can_delete方法判断 + has_permission = bool(permission) + if not has_permission: + # 根据知识库类型和用户角色判断权限 + has_permission = self._can_delete( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ) + + if not has_permission: return Response({ "code": 403, "message": "没有删除权限", @@ -912,27 +1120,54 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): """获取用户对特定知识库的权限""" try: instance = self.get_object() + user = request.user - # 从权限表获取权限信息 - permission = KBPermissionModel.objects.filter( - knowledge_base=instance, - user=request.user, - status='active' - ).first() - - # 构建权限数据 + # 构建默认权限数据 permissions_data = { "can_read": False, "can_edit": False, "can_delete": False } + + # 从权限表获取权限信息 + permission = KBPermissionModel.objects.filter( + knowledge_base=instance, + user=user, + status='active' + ).first() + # 如果在权限表中有记录,则使用表中的权限 if permission: permissions_data.update({ "can_read": permission.can_read, "can_edit": permission.can_edit, "can_delete": permission.can_delete }) + # 否则,使用_can_read/_can_edit/_can_delete方法判断权限 + else: + permissions_data.update({ + "can_read": self._can_read( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ), + "can_edit": self._can_edit( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ), + "can_delete": self._can_delete( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ) + }) return Response({ "code": 200, @@ -958,7 +1193,6 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): "message": f"获取权限信息失败: {str(e)}", "data": None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - @action(detail=False, methods=['get']) def summary(self, request): """获取所有可见知识库的概要信息(除了secret类型)""" @@ -1013,12 +1247,6 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): 'can_edit': True, 'can_delete': True }) - elif user.role == 'leader' and user.department == kb.department: - permissions.update({ - 'can_read': True, - 'can_edit': True, - 'can_delete': True - }) elif user.role == 'member' and user.department == kb.department: permissions.update({ 'can_read': True, @@ -1069,6 +1297,228 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + def retrieve(self, request, *args, **kwargs): + try: + # 获取知识库对象 + instance = self.get_object() + serializer = self.get_serializer(instance) + data = serializer.data + + # 获取用户 + user = request.user + + # 默认权限设置 + permissions = { + 'can_read': False, + 'can_edit': False, + 'can_delete': False + } + + # 尝试获取特定权限记录 + try: + permission_record = KBPermissionModel.objects.filter( + knowledge_base=instance, + user=user, + status='active', + expires_at__gt=timezone.now() + ).first() + + if permission_record: + permissions.update({ + 'can_read': permission_record.can_read, + 'can_edit': permission_record.can_edit, + 'can_delete': permission_record.can_delete + }) + else: + # 使用_can_read/_can_edit/_can_delete方法判断权限 + permissions.update({ + 'can_read': self._can_read( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ), + 'can_edit': self._can_edit( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ), + 'can_delete': self._can_delete( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id + ) + }) + except Exception as e: + logger.error(f"获取权限信息失败: {str(e)}") + # 权限获取失败时使用默认权限 + + # 添加权限信息到返回数据 + data['permissions'] = permissions + + return Response({ + 'code': 200, + 'message': '获取知识库详情成功', + 'data': 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) + + @action(detail=False, methods=['get']) + def search(self, request): + """搜索知识库功能""" + try: + # 获取搜索关键字 + keyword = request.query_params.get('keyword', '') + if not keyword: + return Response({ + "code": 400, + "message": "搜索关键字不能为空", + "data": None + }, status=status.HTTP_400_BAD_REQUEST) + + # 获取分页参数 + try: + page = int(request.query_params.get('page', 1)) + page_size = int(request.query_params.get('page_size', 10)) + except ValueError: + page = 1 + page_size = 10 + + # 构建搜索条件 + query = Q(name__icontains=keyword) | \ + Q(desc__icontains=keyword) | \ + Q(department__icontains=keyword) | \ + Q(group__icontains=keyword) + + # 排除 secret 类型的知识库 + queryset = KnowledgeBase.objects.filter(query).exclude(type='secret') + + # 获取用户 + user = request.user + + # 获取用户所有有效的知识库权限 + active_permissions = KBPermissionModel.objects.filter( + user=user, + status='active', + expires_at__gt=timezone.now() + ).select_related('knowledge_base') + + # 创建权限映射字典 + permission_map = { + str(perm.knowledge_base.id): { + 'can_read': perm.can_read, + 'can_edit': perm.can_edit, + 'can_delete': perm.can_delete + } + for perm in active_permissions + } + + # 计算总数量 + total = queryset.count() + + # 分页处理 + start = (page - 1) * page_size + end = start + page_size + paginated_queryset = queryset[start:end] + + # 序列化知识库数据 + serializer = self.get_serializer(paginated_queryset, many=True) + data = serializer.data + + # 处理每个知识库项的权限和返回内容 + result_items = [] + for item in data: + kb_id = item['id'] + kb_permissions = { + 'can_read': False, + 'can_edit': False, + 'can_delete': False + } + + # 检查知识库特定权限 + if kb_id in permission_map: + kb_permissions.update(permission_map[kb_id]) + # 如果没有特定权限,使用_can方法判断默认权限 + else: + kb_type = item['type'] + department = item.get('department') + group = item.get('group') + creator_id = item.get('user_id') + + kb_permissions.update({ + 'can_read': self._can_read(kb_type, user, department, group, creator_id), + 'can_edit': self._can_edit(kb_type, user, department, group, creator_id), + 'can_delete': self._can_delete(kb_type, user, department, group, creator_id) + }) + + # 添加权限信息 + item['permissions'] = kb_permissions + + # 根据权限返回不同级别的信息 + if kb_permissions['can_read']: + # 有读取权限,返回完整信息 + result_items.append(item) + else: + # 无读取权限,只返回概要信息 + summary_info = { + 'id': item['id'], + 'name': item['name'], + 'type': item['type'], + 'department': item.get('department'), + 'permissions': kb_permissions + } + result_items.append(summary_info) + + # 高亮搜索关键字 + for item in result_items: + if 'name' in item and keyword.lower() in item['name'].lower(): + highlighted = item['name'].replace( + keyword, f'{keyword}' + ) + item['highlighted_name'] = highlighted + + # 确保desc不为None并且是字符串 + if 'desc' in item and item.get('desc') is not None: + desc_text = str(item['desc']) # 转换为字符串以确保安全 + if keyword.lower() in desc_text.lower(): + highlighted = desc_text.replace( + keyword, f'{keyword}' + ) + item['highlighted_desc'] = highlighted + + return Response({ + "code": 200, + "message": "搜索知识库成功", + "data": { + "total": total, + "page": page, + "page_size": page_size, + "keyword": keyword, + "items": result_items + } + }) + 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) + + class PermissionViewSet(viewsets.ModelViewSet): serializer_class = PermissionSerializer permission_classes = [IsAuthenticated]