Introduce content/cms/accounts modules with DRF routes, and point default DB settings at the official starintldb instance. Co-authored-by: Cursor <cursoragent@cursor.com>
212 lines
6.9 KiB
Python
212 lines
6.9 KiB
Python
import uuid
|
|
from pathlib import Path
|
|
|
|
from django.conf import settings
|
|
from django.db.models import Q
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from rest_framework import permissions, status, viewsets
|
|
from rest_framework.decorators import action
|
|
from rest_framework.parsers import FormParser, MultiPartParser
|
|
from rest_framework.response import Response
|
|
|
|
from apps.content.models import Article, Organize, Professor
|
|
from apps.content.serializers import (
|
|
ArticleListSerializer,
|
|
OrganizeListSerializer,
|
|
ProfessorSerializer,
|
|
)
|
|
from apps.content.views import IsAdminOrReadOnly, PublicMixin
|
|
|
|
ALLOWED_IMAGE_EXT = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"}
|
|
MAX_UPLOAD_BYTES = 8 * 1024 * 1024
|
|
|
|
from .models import (
|
|
AboutInfo,
|
|
ContactUs,
|
|
CopyrightInfo,
|
|
ExtendLink,
|
|
HomeBanner,
|
|
Keyword,
|
|
News,
|
|
Publication,
|
|
Report,
|
|
)
|
|
from .serializers import (
|
|
AboutInfoSerializer,
|
|
ContactUsSerializer,
|
|
CopyrightInfoSerializer,
|
|
ExtendLinkSerializer,
|
|
HomeBannerSerializer,
|
|
KeywordSerializer,
|
|
NewsListSerializer,
|
|
NewsSerializer,
|
|
PublicationListSerializer,
|
|
PublicationSerializer,
|
|
ReportSerializer,
|
|
)
|
|
|
|
|
|
class NewsViewSet(PublicMixin, viewsets.ModelViewSet):
|
|
queryset = News.objects.all()
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["is_disabled"]
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == "list":
|
|
return NewsListSerializer
|
|
return NewsSerializer
|
|
|
|
def retrieve(self, request, *args, **kwargs):
|
|
instance = self.get_object()
|
|
News.objects.filter(pk=instance.pk).update(read_num=instance.read_num + 1)
|
|
instance.refresh_from_db()
|
|
return Response(self.get_serializer(instance).data)
|
|
|
|
|
|
class PublicationViewSet(PublicMixin, viewsets.ModelViewSet):
|
|
queryset = Publication.objects.all()
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["is_disabled"]
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == "list":
|
|
return PublicationListSerializer
|
|
return PublicationSerializer
|
|
|
|
|
|
class ReportViewSet(PublicMixin, viewsets.ModelViewSet):
|
|
queryset = Report.objects.all()
|
|
serializer_class = ReportSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ["year", "is_disabled"]
|
|
|
|
|
|
class AboutInfoViewSet(viewsets.ModelViewSet):
|
|
queryset = AboutInfo.objects.all()
|
|
serializer_class = AboutInfoSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
|
|
@action(detail=False, methods=["get"])
|
|
def current(self, request):
|
|
obj = self.get_queryset().first()
|
|
if not obj:
|
|
return Response({})
|
|
return Response(self.get_serializer(obj).data)
|
|
|
|
|
|
class CopyrightInfoViewSet(viewsets.ModelViewSet):
|
|
queryset = CopyrightInfo.objects.all()
|
|
serializer_class = CopyrightInfoSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
|
|
@action(detail=False, methods=["get"])
|
|
def current(self, request):
|
|
obj = self.get_queryset().first()
|
|
if not obj:
|
|
return Response({})
|
|
return Response(self.get_serializer(obj).data)
|
|
|
|
|
|
class ContactUsViewSet(viewsets.ModelViewSet):
|
|
queryset = ContactUs.objects.all()
|
|
serializer_class = ContactUsSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
|
|
@action(detail=False, methods=["get"])
|
|
def current(self, request):
|
|
obj = self.get_queryset().first()
|
|
if not obj:
|
|
return Response({})
|
|
return Response(self.get_serializer(obj).data)
|
|
|
|
|
|
class ExtendLinkViewSet(PublicMixin, viewsets.ModelViewSet):
|
|
queryset = ExtendLink.objects.all()
|
|
serializer_class = ExtendLinkSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
|
|
|
|
class HomeBannerViewSet(PublicMixin, viewsets.ModelViewSet):
|
|
queryset = HomeBanner.objects.all()
|
|
serializer_class = HomeBannerSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
|
|
@action(
|
|
detail=False,
|
|
methods=["post"],
|
|
url_path="upload",
|
|
parser_classes=[MultiPartParser, FormParser],
|
|
permission_classes=[permissions.IsAdminUser],
|
|
)
|
|
def upload(self, request):
|
|
"""上传轮播图片,返回可直接写入 image_url 的路径。"""
|
|
file = request.FILES.get("file")
|
|
if not file:
|
|
return Response({"detail": "请选择图片文件"}, status=status.HTTP_400_BAD_REQUEST)
|
|
if file.size > MAX_UPLOAD_BYTES:
|
|
return Response({"detail": "图片不能超过 8MB"}, status=status.HTTP_400_BAD_REQUEST)
|
|
ext = Path(file.name).suffix.lower()
|
|
if ext not in ALLOWED_IMAGE_EXT:
|
|
return Response(
|
|
{"detail": f"不支持的格式,请上传 {', '.join(sorted(ALLOWED_IMAGE_EXT))}"},
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
filename = f"{uuid.uuid4().hex}{ext}"
|
|
rel_dir = Path("banners")
|
|
abs_dir = Path(settings.MEDIA_ROOT) / rel_dir
|
|
abs_dir.mkdir(parents=True, exist_ok=True)
|
|
abs_path = abs_dir / filename
|
|
with open(abs_path, "wb") as out:
|
|
for chunk in file.chunks():
|
|
out.write(chunk)
|
|
url = f"/{settings.MEDIA_URL.strip('/')}/{rel_dir.as_posix()}/{filename}"
|
|
return Response({"url": url, "name": file.name})
|
|
|
|
|
|
class KeywordViewSet(viewsets.ModelViewSet):
|
|
queryset = Keyword.objects.all()
|
|
serializer_class = KeywordSerializer
|
|
permission_classes = [IsAdminOrReadOnly]
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
if self.action in ("list", "retrieve"):
|
|
user = self.request.user
|
|
if not (user and user.is_staff):
|
|
qs = qs.filter(is_show=True)
|
|
return qs
|
|
|
|
|
|
class SearchViewSet(viewsets.ViewSet):
|
|
"""全站搜索(文献 / 机构 / 专家 / 新闻)。"""
|
|
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
def list(self, request):
|
|
q = (request.query_params.get("q") or "").strip()
|
|
if not q:
|
|
return Response({"articles": [], "organizes": [], "professors": [], "news": []})
|
|
|
|
articles = Article.objects.filter(
|
|
is_disabled=False, is_deleted=False
|
|
).filter(Q(title__icontains=q) | Q(introduction__icontains=q))[:20]
|
|
organizes = Organize.objects.filter(
|
|
is_disabled=False, name__icontains=q
|
|
)[:10]
|
|
professors = Professor.objects.filter(
|
|
is_disabled=False, name__icontains=q
|
|
)[:10]
|
|
news = News.objects.filter(is_disabled=False, title__icontains=q)[:10]
|
|
|
|
return Response(
|
|
{
|
|
"articles": ArticleListSerializer(articles, many=True).data,
|
|
"organizes": OrganizeListSerializer(organizes, many=True).data,
|
|
"professors": ProfessorSerializer(professors, many=True).data,
|
|
"news": NewsListSerializer(news, many=True).data,
|
|
}
|
|
)
|