aifeilong 38ec52abf5 Add STAR International API apps and wire production Postgres defaults.
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>
2026-10-14 17:08:01 +08:00

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,
}
)