diff --git a/deploy.ps1 b/deploy.ps1 deleted file mode 100644 index 356af42a..00000000 --- a/deploy.ps1 +++ /dev/null @@ -1,59 +0,0 @@ -# 确保在项目根目录下执行此脚本 - -# 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/feishu/feishu.py b/feishu/feishu.py index 7063d35d..ecfc8de2 100644 --- a/feishu/feishu.py +++ b/feishu/feishu.py @@ -2,6 +2,8 @@ import json import django import os import sys +import pandas as pd +from django.db import transaction # 设置 Django 环境 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -30,7 +32,7 @@ def extract_field_value(field_value): return field_value def save_to_database(record): - """保存记录到数据库""" + """从飞书多维表格保存记录到数据库""" fields = record.fields record_id = record.record_id @@ -57,7 +59,7 @@ def save_to_database(record): 'system_categories': extract_field_value(fields.get('系统展示的带货品类', [])), 'actual_categories': extract_field_value(fields.get('实际高播放量带货品类', [])), 'human_categories': fields.get('达人标想要货品类', ''), - 'creator_base': '', # 如果有这个字段,添加相应的处理 + 'creator_base': '', 'notes': extract_field_value(fields.get('父记录', '')), } @@ -131,7 +133,92 @@ def fetch_all_records(client, app_token, table_id, user_access_token): print(f"最终获取到 {len(total_records)} 条记录") return total_records -def main(): +def update_from_excel(excel_file_path): + """从Excel文件更新数据库记录""" + try: + print(f"开始读取Excel文件: {excel_file_path}") + df = pd.read_excel(excel_file_path) + + if 'Handle' not in df.columns: + print("错误: Excel文件中缺少'Handle'列") + return + + update_count = 0 + skip_count = 0 + error_count = 0 + + # 获取可更新的字段列表 + excluded_fields = {'id', 'record_id', 'created_at', 'updated_at'} + model_fields = {f.name for f in FeishuCreator._meta.get_fields()} - excluded_fields + valid_columns = set(df.columns) & model_fields + + print(f"可更新的列: {valid_columns}") + + with transaction.atomic(): + for index, row in df.iterrows(): + try: + handle = str(row['Handle']).strip() + if not handle: + print(f"跳过第{index+2}行: Handle为空") + skip_count += 1 + continue + + # 查找现有记录 + creator = FeishuCreator.objects.filter(handle=handle).first() + if not creator: + print(f"跳过Handle为'{handle}'的记录: 数据库中不存在") + skip_count += 1 + continue + + # 准备更新数据 + update_data = {} + for column in valid_columns: + if column == 'Handle': + continue + + value = row[column] + if pd.isna(value): + continue + + # 处理特殊类型 + if isinstance(value, (list, dict)): + value = json.dumps(value) + elif isinstance(value, (int, float)): + if column in ['fans_count']: + value = int(value) + else: + value = str(value) + else: + value = str(value).strip() + + if value: + update_data[column] = value + + # 更新记录 + if update_data: + for field, value in update_data.items(): + setattr(creator, field, value) + creator.save() + update_count += 1 + print(f"已更新Handle为'{handle}'的记录") + else: + skip_count += 1 + print(f"跳过Handle为'{handle}'的记录: 无需更新") + + except Exception as e: + error_count += 1 + print(f"处理Handle'{handle}'时出错: {str(e)}") + + print("\nExcel更新完成!统计信息:") + print(f"更新记录数:{update_count}") + print(f"跳过记录数:{skip_count}") + print(f"错误记录数:{error_count}") + + except Exception as e: + print(f"处理Excel文件时出错: {str(e)}") + +def sync_from_feishu(): + """从飞书多维表格同步数据""" # 创建client client = lark.Client.builder() \ .enable_set_token(True) \ @@ -143,15 +230,13 @@ def main(): TABLE_ID = "tbl3oikG3F8YYtVA" USER_ACCESS_TOKEN = "u-ecM5BmzKx4uHz3sG0FouQSk1l9kxgl_3Xa00l5Ma24Jy" - # 获取所有记录 - print("开始获取所有记录...") + print("开始从飞书同步数据...") all_records = fetch_all_records(client, APP_TOKEN, TABLE_ID, USER_ACCESS_TOKEN) if not all_records: print("未获取到任何记录") return - # 更新数据库 print("\n开始更新数据库...") created_count = 0 updated_count = 0 @@ -162,7 +247,7 @@ def main(): if creator: if created: created_count += 1 - if created_count % 10 == 0: # 每10条才打印一次,避免输出过多 + if created_count % 10 == 0: print(f"已创建 {created_count} 条记录...") else: updated_count += 1 @@ -172,13 +257,34 @@ def main(): error_count += 1 print(f"处理记录失败") - # 打印统计信息 - print("\n更新完成!统计信息:") + print("\n飞书同步完成!统计信息:") print(f"新建记录:{created_count}") print(f"更新记录:{updated_count}") print(f"错误记录:{error_count}") print(f"总记录数:{len(all_records)}") +def main(): + """主函数""" + if len(sys.argv) < 2: + print("使用方法:") + print("1. 从飞书同步: python feishu.py sync") + print("2. 从Excel更新: python feishu.py excel ") + return + + command = sys.argv[1] + + if command == 'sync': + sync_from_feishu() + elif command == 'excel': + if len(sys.argv) != 3: + print("使用方法: python feishu.py excel ") + return + excel_file_path = sys.argv[2] + update_from_excel(excel_file_path) + else: + print("无效的命令。使用方法:") + print("1. 从飞书同步: python feishu.py sync") + print("2. 从Excel更新: python feishu.py excel ") if __name__ == "__main__": main() \ No newline at end of file diff --git a/user_management/migrations/0004_knowledgebasedocument.py b/user_management/migrations/0004_knowledgebasedocument.py new file mode 100644 index 00000000..de7e51af --- /dev/null +++ b/user_management/migrations/0004_knowledgebasedocument.py @@ -0,0 +1,35 @@ +# Generated by Django 5.1.5 on 2025-03-31 14:03 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('user_management', '0003_alter_feishucreator_handle'), + ] + + operations = [ + migrations.CreateModel( + name='KnowledgeBaseDocument', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('document_id', models.CharField(max_length=100, verbose_name='文档ID')), + ('document_name', models.CharField(max_length=255, verbose_name='文档名称')), + ('external_id', models.CharField(max_length=100, verbose_name='外部文档ID')), + ('status', models.CharField(choices=[('active', '有效'), ('deleted', '已删除')], default='active', max_length=20, verbose_name='状态')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('knowledge_base', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='kb_documents', to='user_management.knowledgebase', verbose_name='知识库')), + ], + options={ + 'verbose_name': '知识库文档', + 'verbose_name_plural': '知识库文档', + 'db_table': 'knowledge_base_documents', + 'indexes': [models.Index(fields=['knowledge_base', 'status'], name='knowledge_b_knowled_a4db1b_idx'), models.Index(fields=['document_id'], name='knowledge_b_documen_dab90f_idx'), models.Index(fields=['external_id'], name='knowledge_b_externa_b0060c_idx')], + 'unique_together': {('knowledge_base', 'document_id')}, + }, + ), + ] diff --git a/user_management/models.py b/user_management/models.py index 1139e57d..04e387dd 100644 --- a/user_management/models.py +++ b/user_management/models.py @@ -669,3 +669,41 @@ class FeishuCreator(models.Model): db_table = 'feishu_creators' verbose_name = '创作者数据' verbose_name_plural = '创作者数据' + +class KnowledgeBaseDocument(models.Model): + """知识库文档关联模型""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + knowledge_base = models.ForeignKey( + 'KnowledgeBase', + on_delete=models.CASCADE, + related_name='kb_documents', + verbose_name='知识库' + ) + document_id = models.CharField(max_length=100, verbose_name='文档ID') + document_name = models.CharField(max_length=255, verbose_name='文档名称') + external_id = models.CharField(max_length=100, verbose_name='外部文档ID') + status = models.CharField( + max_length=20, + default='active', + choices=[ + ('active', '有效'), + ('deleted', '已删除') + ], + verbose_name='状态' + ) + create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间') + + class Meta: + db_table = 'knowledge_base_documents' + unique_together = ['knowledge_base', 'document_id'] + indexes = [ + models.Index(fields=['knowledge_base', 'status']), + models.Index(fields=['document_id']), + models.Index(fields=['external_id']) + ] + verbose_name = '知识库文档' + verbose_name_plural = '知识库文档' + + def __str__(self): + return f"{self.knowledge_base.name} - {self.document_name}" diff --git a/user_management/views.py b/user_management/views.py index f411efbb..50661a38 100644 --- a/user_management/views.py +++ b/user_management/views.py @@ -53,7 +53,8 @@ from .models import ( ChatHistory, KnowledgeBase, Notification, - KnowledgeBasePermission as KBPermissionModel + KnowledgeBasePermission as KBPermissionModel, + KnowledgeBaseDocument ) from .serializers import ( UserSerializer, @@ -81,7 +82,151 @@ logging.basicConfig( ) -class ChatHistoryViewSet(viewsets.ModelViewSet): +class KnowledgeBasePermissionMixin: + """知识库权限管理混入类""" + + def _can_read(self, type, user, department=None, group=None, creator_id=None, knowledge_base_id=None): + """检查读取权限""" + try: + # 1. 检查显式权限表 + if knowledge_base_id: + permission = KBPermissionModel.objects.filter( + knowledge_base_id=knowledge_base_id, + user=user, + can_read=True, + status='active' + ).first() + if permission: + return True + + # 2. 检查角色权限 + # 私有知识库 + if type == 'private': + return str(user.id) == str(creator_id) + + # 成员级知识库 + if type == 'member': + return user.department == department + + # 部门级知识库 + if type == 'leader': + return (user.department == department and + user.role in ['leader', 'admin']) + + # 管理级知识库 + if type == 'admin': + return user.role == 'admin' + + return False + + except Exception as e: + logger.error(f"检查读取权限时出错: {str(e)}") + return False + + def _can_edit(self, type, user, department=None, group=None, creator_id=None, knowledge_base_id=None): + """检查编辑权限""" + try: + # 1. 检查显式权限表 + if knowledge_base_id: + permission = KBPermissionModel.objects.filter( + knowledge_base_id=knowledge_base_id, + user=user, + can_edit=True, + status='active' + ).first() + if permission: + return True + + # 2. 检查角色权限 + # 私有知识库 + if type == 'private': + return str(user.id) == str(creator_id) + + # 成员级知识库 + if type == 'member': + return (user.department == department and + user.role in ['leader', 'admin']) + + # 部门级知识库 + if type == 'leader': + return (user.department == department and + user.role in ['leader', 'admin']) + + # 管理级知识库 + if type == 'admin': + return user.role == 'admin' + + return False + + except Exception as e: + logger.error(f"检查编辑权限时出错: {str(e)}") + return False + + def _can_delete(self, type, user, department=None, group=None, creator_id=None, knowledge_base_id=None): + """检查删除权限""" + try: + # 1. 检查显式权限表 + if knowledge_base_id: + permission = KBPermissionModel.objects.filter( + knowledge_base_id=knowledge_base_id, + user=user, + can_delete=True, + status='active' + ).first() + if permission: + return True + + # 2. 检查角色权限 + # 私有知识库 + if type == 'private': + return str(user.id) == str(creator_id) + + # 成员级知识库 + if type == 'member': + return (user.department == department and + user.role == 'admin') + + # 部门级知识库 + if type == 'leader': + return (user.department == department and + user.role == 'admin') + + # 管理级知识库 + if type == 'admin': + return user.role == 'admin' + + return False + + except Exception as e: + logger.error(f"检查删除权限时出错: {str(e)}") + return False + + def check_knowledge_base_permission(self, knowledge_base, user, required_permission='read'): + """统一的知识库权限检查方法""" + if not knowledge_base: + return False + + permission_method = { + 'read': self._can_read, + 'edit': self._can_edit, + 'delete': self._can_delete + }.get(required_permission) + + if not permission_method: + return False + + return permission_method( + type=knowledge_base.type, + user=user, + department=knowledge_base.department, + group=knowledge_base.group, + creator_id=knowledge_base.user_id, + knowledge_base_id=knowledge_base.id + ) + + + +class ChatHistoryViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet): permission_classes = [IsAuthenticated] queryset = ChatHistory.objects.all() @@ -202,11 +347,16 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): datasets = KnowledgeBase.objects.filter( id__in=first_message.metadata['dataset_id_list'] ) + # 过滤出用户有权限访问的知识库 + accessible_datasets = [ + ds for ds in datasets + if self.check_knowledge_base_permission(ds, request.user, 'read') + ] dataset_info = [{ 'id': str(ds.id), 'name': ds.name, 'type': ds.type - } for ds in datasets] + } for ds in accessible_datasets] return Response({ 'code': 200, @@ -237,30 +387,13 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): """获取用户可访问的知识库列表""" try: user = request.user + all_datasets = KnowledgeBase.objects.all() - # 获取用户有权限访问的知识库 - accessible_datasets = [] - - # 1. 获取用户通过权限表直接授权的知识库 - permission_datasets = KnowledgeBase.objects.filter( - id__in=KBPermissionModel.objects.filter( - user=user, - can_read=True, - status='active' - ).values_list('knowledge_base_id', flat=True) - ) - - # 2. 获取用户根据角色可以访问的知识库 - role_datasets = KnowledgeBase.objects.filter( - Q(type='member', department=user.department) | # 成员级知识库 - Q(type='leader', department=user.department) | # 部门级知识库,组长可访问 - Q(type='admin') # 管理级知识库,管理员可访问 - ).exclude( - Q(type='private') & ~Q(user_id=str(user.id)) # 排除不属于自己的私人知识库 - ) - - # 3. 合并并去重 - accessible_datasets = list(set(list(permission_datasets) + list(role_datasets))) + # 使用统一的权限检查方法 + accessible_datasets = [ + dataset for dataset in all_datasets + if self.check_knowledge_base_permission(dataset, user, 'read') + ] return Response({ 'code': 200, @@ -276,7 +409,6 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): except Exception as e: logger.error(f"获取可用知识库列表失败: {str(e)}") - logger.error(traceback.format_exc()) return Response({ 'code': 500, 'message': f'获取可用知识库列表失败: {str(e)}', @@ -347,29 +479,8 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): knowledge_bases.append(knowledge_base) - # 检查知识库权限 - can_read = False - - # 检查权限表 - permission = KBPermissionModel.objects.filter( - knowledge_base=knowledge_base, - user=user, - can_read=True, - status='active' - ).first() - - if permission: - can_read = True - else: - can_read = self._can_read( - type=knowledge_base.type, - user=user, - department=knowledge_base.department, - group=knowledge_base.group, - creator_id=knowledge_base.user_id - ) - - if not can_read: + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(knowledge_base, user, 'read'): return Response({ 'code': 403, 'message': f'无权访问知识库: {knowledge_base.name}', @@ -697,6 +808,14 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): ) if dataset_id: + # 检查知识库权限 + knowledge_base = KnowledgeBase.objects.filter(id=dataset_id).first() + if knowledge_base and not self.check_knowledge_base_permission(knowledge_base, request.user, 'read'): + return Response({ + 'code': 403, + 'message': '无权访问该知识库', + 'data': None + }, status=status.HTTP_403_FORBIDDEN) query = query.filter(knowledge_base__id=dataset_id) if start_date: query = query.filter(created_at__gte=start_date) @@ -780,27 +899,8 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_404_NOT_FOUND) - # 检查是否有读取权限 - can_read = False - permission = KBPermissionModel.objects.filter( - knowledge_base=knowledge_base, - user=user, - can_read=True, - status='active' - ).first() - - if permission: - can_read = True - else: - can_read = self._can_read( - type=knowledge_base.type, - user=user, - department=knowledge_base.department, - group=knowledge_base.group, - creator_id=knowledge_base.user_id - ) - - if not can_read: + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(knowledge_base, user, 'read'): return Response({ 'code': 403, 'message': '无权访问该知识库', @@ -965,7 +1065,8 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): f'{keyword}' ) -class KnowledgeBaseViewSet(viewsets.ModelViewSet): + +class KnowledgeBaseViewSet(KnowledgeBasePermissionMixin, viewsets.ModelViewSet): serializer_class = KnowledgeBaseSerializer permission_classes = [IsAuthenticated] @@ -992,24 +1093,6 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): 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() @@ -1023,50 +1106,35 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): data = serializer.data # 为每个知识库添加权限信息 + user = request.user for item in data: + # 获取必要的知识库属性 + kb_type = item['type'] + department = item.get('department') + group = item.get('group') + creator_id = item.get('user_id') kb_id = item['id'] - permissions = { - 'can_read': False, - 'can_edit': False, - 'can_delete': False + + # 使用统一的权限判断方法 + item['permissions'] = { + 'can_read': self._can_read(kb_type, user, department, group, creator_id, kb_id), + 'can_edit': self._can_edit(kb_type, user, department, group, creator_id, kb_id), + 'can_delete': self._can_delete(kb_type, user, department, group, creator_id, kb_id) } - # 检查知识库特定权限 - 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( + item['highlighted_name'] = 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( + item['highlighted_desc'] = desc_text.replace( keyword, f'{keyword}' ) - item['highlighted_desc'] = highlighted return Response({ "code": 200, @@ -1103,14 +1171,16 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): permission_conditions |= Q(user_id=user.id) # 4. 添加显式权限条件 - permission_conditions |= Q( - id__in=KBPermissionModel.objects.filter( - user=user, - can_read=True, - status='active', - expires_at__gt=timezone.now() - ).values_list('knowledge_base_id', flat=True) - ) + # 获取所有活跃的权限记录 + active_permissions = KBPermissionModel.objects.filter( + user=user, + can_read=True, + status='active', + expires_at__gt=timezone.now() + ).values_list('knowledge_base_id', flat=True) + + if active_permissions: + permission_conditions |= Q(id__in=active_permissions) # 5. 根据用户角色添加隐式权限 if user.role == 'admin': @@ -1341,212 +1411,14 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - def _can_edit(self, type, user, department=None, group=None, creator_id=None): - """判断用户是否有编辑权限""" - # admin 类型知识库所有用户都有编辑权限 - if type == 'admin': - return True - - if user.role == 'admin': - # 管理员对其他用户的 private 类型没有编辑权限 - if type == 'private' and str(user.id) != str(creator_id): - return False - 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, department=None, group=None, creator_id=None): - """判断用户是否有删除权限""" - # admin 类型知识库所有用户都有删除权限 - if type == 'admin': - return True - - if user.role == 'admin': - # 管理员对其他用户的 private 类型没有删除权限 - if type == 'private' and str(user.id) != str(creator_id): - return False - return True - - if type == 'secret': - 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): - """判断用户是否有读取权限""" - # admin 类型知识库所有用户都有读取权限 - if type == 'admin': - return True - - if user.role == 'admin': - # 管理员对其他用户的 private 类型没有读取权限 - if type == 'private' and str(user.id) != str(creator_id): - return False - 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): - """创建外部知识库""" - 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() user = request.user - # 首先检查权限表中是否有显式权限 - permission = KBPermissionModel.objects.filter( - knowledge_base=instance, - user=user, - can_edit=True, - status='active' - ).first() - - # 如果权限表中没有记录,则使用_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: + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(instance, user, 'edit'): return Response({ "code": 403, "message": "没有编辑权限", @@ -1628,27 +1500,8 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): instance = self.get_object() user = request.user - # 检查删除权限 - permission = KBPermissionModel.objects.filter( - knowledge_base=instance, - user=user, - can_delete=True, - status='active' - ).first() - - # 如果权限表中没有记录,则使用_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: + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(instance, user, 'delete'): return Response({ "code": 403, "message": "没有删除权限", @@ -1701,52 +1554,12 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): instance = self.get_object() user = request.user - # 构建默认权限数据 + # 使用统一的权限检查方法 permissions_data = { - "can_read": False, - "can_edit": False, - "can_delete": False + "can_read": self.check_knowledge_base_permission(instance, user, 'read'), + "can_edit": self.check_knowledge_base_permission(instance, user, 'edit'), + "can_delete": self.check_knowledge_base_permission(instance, user, 'delete') } - - # 从权限表获取权限信息 - 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, @@ -1781,76 +1594,14 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): # 基础查询:排除secret类型的知识库 queryset = KnowledgeBase.objects.exclude(type='secret') - # 获取用户所有有效的知识库权限 - 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 - } - summaries = [] for kb in queryset: - # 获取基础权限 + # 使用统一的权限判断方法 permissions = { - 'can_read': False, - 'can_edit': False, - 'can_delete': False + 'can_read': self.check_knowledge_base_permission(kb, user, 'read'), + 'can_edit': self.check_knowledge_base_permission(kb, user, 'edit'), + 'can_delete': self.check_knowledge_base_permission(kb, user, 'delete') } - - # 检查知识库特定权限 - kb_id = str(kb.id) - if kb_id in permission_map: - permissions.update(permission_map[kb_id]) - # 如果没有特定权限,根据角色和部门设置默认权限 - else: - if user.role == 'admin': - permissions.update({ - 'can_read': True, - 'can_edit': True, - 'can_delete': True - }) - elif kb.type == 'leader': - if 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, - 'can_edit': False, - 'can_delete': False - }) - elif kb.type == 'member': - if 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, - 'can_edit': False, - 'can_delete': False - }) - elif kb.type == 'private' and str(kb.user_id) == str(user.id): - permissions.update({ - 'can_read': True, - 'can_edit': True, - 'can_delete': True - }) # 只返回概要信息 summary = { @@ -1886,60 +1637,13 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): # 获取用户 user = request.user - # 默认权限设置 - permissions = { - 'can_read': False, - 'can_edit': False, - 'can_delete': False + # 使用统一的权限判断方法 + data['permissions'] = { + 'can_read': self.check_knowledge_base_permission(instance, user, 'read'), + 'can_edit': self.check_knowledge_base_permission(instance, user, 'edit'), + 'can_delete': self.check_knowledge_base_permission(instance, user, 'delete') } - # 尝试获取特定权限记录 - 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': '获取知识库详情成功', @@ -2019,35 +1723,39 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): # 处理每个知识库项的权限和返回内容 result_items = [] for item in data: - kb_id = item['id'] + # 使用统一的权限判断方法 kb_permissions = { - 'can_read': False, - 'can_edit': False, - 'can_delete': False + 'can_read': self.check_knowledge_base_permission( + type=item['type'], + user=user, + department=item.get('department'), + group=item.get('group'), + creator_id=item.get('user_id'), + knowledge_base_id=item['id'] + ), + 'can_edit': self.check_knowledge_base_permission( + type=item['type'], + user=user, + department=item.get('department'), + group=item.get('group'), + creator_id=item.get('user_id'), + knowledge_base_id=item['id'] + ), + 'can_delete': self.check_knowledge_base_permission( + type=item['type'], + user=user, + department=item.get('department'), + group=item.get('group'), + creator_id=item.get('user_id'), + knowledge_base_id=item['id'] + ) } - # 检查知识库特定权限 - 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: # 无读取权限,只返回概要信息 @@ -2104,29 +1812,16 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): instance = self.get_object() user = request.user - # 判断角色和权限 - is_creator = str(user.id) == str(instance.user_id) - is_admin = user.role == 'admin' - is_leader = user.role == 'leader' - is_member = user.role == 'member' or user.role == 'user' # 组员或普通用户 - - # 组员无权修改知识库类型 - if is_member and not (is_admin or is_leader): + # 使用统一的权限检查方法检查编辑权限 + if not self.check_knowledge_base_permission(instance, user, 'edit'): return Response({ "code": 403, - "message": "组员无权修改知识库类型,只能使用private类型", + "message": "没有修改权限", "data": None }, status=status.HTTP_403_FORBIDDEN) - - # 权限检查 - if not is_creator: - # 非创建者无法修改知识库类型 - return Response({ - "code": 403, - "message": "只有知识库创建者可以修改知识库类型", - "data": None - }, status=status.HTTP_403_FORBIDDEN) - + + # 其余代码保持不变... + # 获取新类型 new_type = request.data.get('type') if not new_type: @@ -2146,7 +1841,7 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): }, status=status.HTTP_400_BAD_REQUEST) # 角色特定的类型限制 - if is_leader and not is_admin: # 组长且不是管理员 + if new_type == 'leader' and not user.role == 'admin': # 组长且不是管理员 # 组长只能在private和member类型之间切换 if new_type not in ['private', 'member']: return Response({ @@ -2160,7 +1855,7 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): group = request.data.get('group') # 组长只能设置自己部门 - if is_leader and not is_admin and new_type == 'member': + if new_type == 'leader' and not user.role == 'admin': if department and department != user.department: return Response({ "code": 403, @@ -2238,6 +1933,332 @@ class KnowledgeBaseViewSet(viewsets.ModelViewSet): "data": None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @action(detail=True, methods=['post']) + def upload_document(self, request, pk=None): + """上传文档到知识库""" + try: + instance = self.get_object() + user = request.user + + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(instance, user, 'edit'): + return Response({ + "code": 403, + "message": "没有编辑权限", + "data": None + }, status=status.HTTP_403_FORBIDDEN) + + # 获取文件 + file = request.FILES.get('file') + if not file: + return Response({ + "code": 400, + "message": "未找到上传文件", + "data": None + }, status=status.HTTP_400_BAD_REQUEST) + + # 调用文档分割API + split_response = self._call_split_api(file) + if not split_response or split_response.get('code') != 200: + return Response({ + "code": 500, + "message": "文档分割失败", + "data": split_response + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + # 处理分割后的文档 + documents_data = split_response.get('data', []) + saved_documents = [] + + for doc in documents_data: + # 准备文档数据 + doc_data = { + "name": doc.get('name'), + "paragraphs": [{ + "content": para.get('content', ''), + "title": para.get('title', ''), + "is_active": True, + "problem_list": [] + } for para in doc.get('content', [])] + } + + # 调用文档上传API + upload_response = self._call_upload_api(instance.external_id, doc_data) + if upload_response and upload_response.get('code') == 200: + # 保存文档记录到数据库 + doc_record = KnowledgeBaseDocument.objects.create( + knowledge_base=instance, + document_id=upload_response['data']['id'], + document_name=doc.get('name'), + external_id=upload_response['data']['id'] + ) + saved_documents.append(doc_record) + + return Response({ + "code": 200, + "message": "文档上传成功", + "data": { + "uploaded_count": len(saved_documents), + "documents": [{ + "id": str(doc.id), + "name": doc.document_name, + "external_id": doc.external_id + } for doc in saved_documents] + } + }) + + 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=['delete']) + def delete_document(self, request, pk=None): + """从知识库中删除文档""" + try: + instance = self.get_object() + user = request.user + + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(instance, user, 'delete'): + return Response({ + "code": 403, + "message": "没有删除权限", + "data": None + }, status=status.HTTP_403_FORBIDDEN) + + # 查找文档记录 + try: + doc_record = KnowledgeBaseDocument.objects.get( + knowledge_base=instance, + document_id=document_id, + status='active' + ) + except KnowledgeBaseDocument.DoesNotExist: + return Response({ + "code": 404, + "message": "文档不存在", + "data": None + }, status=status.HTTP_404_NOT_FOUND) + + # 调用外部API删除文档 + delete_response = self._call_delete_document_api( + instance.external_id, + doc_record.external_id + ) + + if delete_response and delete_response.get('code') == 200: + # 更新文档状态为已删除 + doc_record.status = 'deleted' + doc_record.save() + + return Response({ + "code": 200, + "message": "文档删除成功", + "data": { + "document_id": document_id, + "name": doc_record.document_name + } + }) + else: + raise Exception("外部API删除文档失败") + + 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 documents(self, request, pk=None): + """获取知识库下的所有文档信息""" + try: + instance = self.get_object() + user = request.user + + # 使用统一的权限检查方法 + if not self.check_knowledge_base_permission(instance, user, 'read'): + return Response({ + "code": 403, + "message": "没有查看权限", + "data": None + }, status=status.HTTP_403_FORBIDDEN) + + # 2. 获取分页参数 + 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 + + # 3. 查询文档记录 + documents = KnowledgeBaseDocument.objects.filter( + knowledge_base=instance, + status='active' + ).order_by('-create_time') + + # 4. 计算总数 + total = documents.count() + + # 5. 分页 + start = (page - 1) * page_size + end = start + page_size + documents = documents[start:end] + + # 6. 构建响应数据 + documents_data = [{ + "id": str(doc.id), + "document_id": doc.document_id, + "document_name": doc.document_name, + "external_id": doc.external_id, + "create_time": doc.create_time.strftime("%Y-%m-%d %H:%M:%S"), + "update_time": doc.update_time.strftime("%Y-%m-%d %H:%M:%S") + } for doc in documents] + + return Response({ + "code": 200, + "message": "获取文档列表成功", + "data": { + "total": total, + "page": page, + "page_size": page_size, + "items": documents_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=True, methods=['get']) + def document_content(self, request, pk=None): + """获取文档内容""" + try: + instance = self.get_object() + user = request.user + document_id = request.query_params.get('document_id') + + if not document_id: + return Response({ + "code": 400, + "message": "缺少document_id参数", + "data": None + }, status=status.HTTP_400_BAD_REQUEST) + + # 使用统一的权限判断方法 + if not self.check_knowledge_base_permission( + type=instance.type, + user=user, + department=instance.department, + group=instance.group, + creator_id=instance.user_id, + knowledge_base_id=instance.id + ): + return Response({ + "code": 403, + "message": "没有查看权限", + "data": None + }, status=status.HTTP_403_FORBIDDEN) + + # 2. 查找文档记录 + try: + doc_record = KnowledgeBaseDocument.objects.get( + knowledge_base=instance, + document_id=document_id, + status='active' + ) + except KnowledgeBaseDocument.DoesNotExist: + return Response({ + "code": 404, + "message": "文档不存在", + "data": None + }, status=status.HTTP_404_NOT_FOUND) + + # 3. 调用外部API获取文档内容 + try: + response = requests.get( + f'{settings.API_BASE_URL}/api/dataset/{instance.external_id}/document/{doc_record.external_id}/paragraph', + headers={'Content-Type': 'application/json'}, + timeout=30 + ) + + if response.status_code != 200: + raise Exception(f"获取文档内容失败,状态码: {response.status_code}") + + content_data = response.json() + + # 4. 构建响应数据 + return Response({ + "code": 200, + "message": "获取文档内容成功", + "data": { + "document_info": { + "id": str(doc_record.id), + "name": doc_record.document_name, + "create_time": doc_record.create_time.strftime("%Y-%m-%d %H:%M:%S"), + "update_time": doc_record.update_time.strftime("%Y-%m-%d %H:%M:%S") + }, + "content": content_data.get('data', []) + } + }) + + except requests.exceptions.RequestException as e: + raise Exception(f"调用外部API失败: {str(e)}") + + 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 _call_split_api(self, file): + """调用文档分割API""" + try: + url = f'{settings.API_BASE_URL}/api/dataset/document/split' + files = {'file': file} + response = requests.post(url, files=files) + return response.json() + except Exception as e: + logger.error(f"调用分割API失败: {str(e)}") + return None + + def _call_upload_api(self, external_id, doc_data): + """调用文档上传API""" + try: + url = f'{settings.API_BASE_URL}/api/dataset/{external_id}/document' + response = requests.post(url, json=doc_data) + return response.json() + except Exception as e: + logger.error(f"调用上传API失败: {str(e)}") + return None + + def _call_delete_document_api(self, external_id, document_id): + """调用文档删除API""" + try: + url = f'{settings.API_BASE_URL}/api/dataset/{external_id}/document/{document_id}' + response = requests.delete(url) + return response.json() + except Exception as e: + logger.error(f"调用删除API失败: {str(e)}") + return None + + class PermissionViewSet(viewsets.ModelViewSet): serializer_class = PermissionSerializer permission_classes = [IsAuthenticated] @@ -3050,6 +3071,7 @@ class PermissionViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + class NotificationViewSet(viewsets.ModelViewSet): """通知视图集""" queryset = Notification.objects.all() @@ -3093,6 +3115,7 @@ class NotificationViewSet(viewsets.ModelViewSet): """创建通知时自动设置发送者""" serializer.save(sender=self.request.user) + @method_decorator(csrf_exempt, name='dispatch') class LoginView(APIView): """用户登录视图""" @@ -3601,3 +3624,4 @@ def user_delete(request, pk): return Response({'message': '用户删除成功'}) except User.DoesNotExist: return Response({'message': '用户不存在'}, status=404) +