from rest_framework import viewsets, generics, permissions, status from rest_framework.response import Response from rest_framework.decorators import action from .models import Announcement, Notification, SystemConfig, AiModel, ModelToken, Skill from .serializers import AnnouncementSerializer, NotificationSerializer, SystemConfigSerializer, AiModelSerializer, ModelTokenSerializer, SkillSerializer class AnnouncementViewSet(viewsets.ModelViewSet): """ @author: xujl Api说明: 系统公告接口视图。管理员发布全局或定向通知公告,前端获取并展示给对应受众。 """ queryset = Announcement.objects.filter(is_deleted=False) serializer_class = AnnouncementSerializer def get_queryset(self): qs = Announcement.objects.filter(is_deleted=False).order_by('-is_recommended', '-recommend_priority', '-created_at') audience = self.request.query_params.get('audience') if audience: qs = qs.filter(target_audience__in=[audience, 'ALL']) if self.action == 'list' and not self.request.user.is_staff: qs = qs.filter(is_published=True) return qs def get_permissions(self): if self.action in ['create', 'update', 'partial_update', 'destroy', 'toggle_recommend']: return [permissions.IsAdminUser()] return [permissions.AllowAny()] @action(detail=True, methods=['post']) def toggle_recommend(self, request, pk=None): ann = self.get_object() ann.is_recommended = request.data.get('is_recommended', not ann.is_recommended) ann.recommend_priority = request.data.get('recommend_priority', ann.recommend_priority) ann.save(update_fields=['is_recommended', 'recommend_priority']) return Response({'is_recommended': ann.is_recommended, 'recommend_priority': ann.recommend_priority}) class NotificationViewSet(viewsets.ModelViewSet): """ @author: xujl Api说明: 站内消息通知接口视图。用于系统内部产生的各类站内信、状态变更通知的拉取和未读/已读状态更新。 """ serializer_class = NotificationSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return Notification.objects.filter(user=self.request.user, is_deleted=False) @action(detail=True, methods=['post']) def read(self, request, pk=None): notification = self.get_object() notification.is_read = True notification.save() return Response({'status': '已标记为已读'}) @action(detail=False, methods=['post']) def read_all(self, request): self.get_queryset().update(is_read=True) return Response({'status': '全部标记为已读'}) class SystemConfigViewSet(viewsets.ModelViewSet): """ @author: xujl Api说明: 系统全局动态配置接口视图。用于保存字典数据、开关配置等,避免硬编码参数。 """ queryset = SystemConfig.objects.all() serializer_class = SystemConfigSerializer lookup_field = 'config_key' def get_permissions(self): if self.action in ['create', 'update', 'partial_update', 'destroy']: return [permissions.IsAdminUser()] return [permissions.AllowAny()] class AiModelViewSet(viewsets.ModelViewSet): """ @author: xujl Api说明: AI模型配置接口视图。统一管理平台对接的各大AI平台(如OpenAI、文心一言等)的模型参数及端点。 """ queryset = AiModel.objects.all() serializer_class = AiModelSerializer def get_permissions(self): if self.action in ['create', 'update', 'partial_update', 'destroy']: return [permissions.IsAdminUser()] return [permissions.IsAuthenticatedOrReadOnly()] def get_queryset(self): qs = super().get_queryset() if self.action == 'list' and not self.request.user.is_staff: return qs.filter(is_active=True) return qs class ModelTokenViewSet(viewsets.ModelViewSet): """ @author: xujl Api说明: 大模型Token/密钥接口视图。安全管理模型请求时使用的Token或API Key认证信息。 """ serializer_class = ModelTokenSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return ModelToken.objects.filter(user=self.request.user) def perform_create(self, serializer): # In a real app, this would call MoRouter or another provider # For now, we generate a mock token import uuid mock_token = f"sk-opc-{uuid.uuid4().hex}" serializer.save(user=self.request.user, token_value=mock_token) class SkillViewSet(viewsets.ModelViewSet): """ @author: xujl Api说明: 技能字典/用户标签接口视图。用于系统内的技能标签池管理,以供任务画像和用户画像进行匹配。 """ """Admin-managed skill/expertise tags. Read-only for non-admins.""" queryset = Skill.objects.all() serializer_class = SkillSerializer def get_permissions(self): if self.action in ['create', 'update', 'partial_update', 'destroy']: return [permissions.IsAdminUser()] return [permissions.AllowAny()] def get_queryset(self): qs = super().get_queryset() if self.action == 'list' and not (self.request.user.is_authenticated and self.request.user.is_staff): return qs.filter(is_active=True) return qs from .minio_utils import minio_client class FileUploadView(generics.GenericAPIView): """ @author: xujl Api说明: 系统通用文件上传接口。对接MinIO或S3等对象存储,处理前端传入的文件并返回URL外链。 """ permission_classes = [permissions.AllowAny] def post(self, request): file_obj = request.FILES.get('file') if not file_obj: return Response({'detail': '没有上传文件'}, status=status.HTTP_400_BAD_REQUEST) folder = request.data.get('folder', 'general') try: file_url = minio_client.upload_file(file_obj, folder=folder) return Response({'url': file_url}, status=status.HTTP_201_CREATED) except Exception as e: return Response({'detail': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)