You've already forked opc-backend
153 lines
6.2 KiB
Python
153 lines
6.2 KiB
Python
|
|
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)
|