125 lines
4.8 KiB
Python
125 lines
4.8 KiB
Python
from rest_framework import viewsets, status, mixins, parsers, filters
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
from rest_framework import serializers
|
|
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
|
|
from drf_spectacular.utils import extend_schema
|
|
|
|
from shop.models import WeChatUser
|
|
from .models import Activity, ActivitySignup, Topic, Reply, TopicMedia
|
|
from .serializers import ActivitySerializer, ActivitySignupSerializer, TopicSerializer, ReplySerializer, TopicMediaSerializer
|
|
|
|
def get_current_wechat_user(request):
|
|
"""
|
|
根据 Authorization 头获取当前微信用户 (复用 shop app 的逻辑)
|
|
"""
|
|
auth_header = request.headers.get('Authorization')
|
|
if not auth_header or not auth_header.startswith('Bearer '):
|
|
return None
|
|
token = auth_header.split(' ')[1]
|
|
signer = TimestampSigner()
|
|
try:
|
|
# 签名包含 openid
|
|
openid = signer.unsign(token, max_age=86400 * 30) # 30天有效
|
|
return WeChatUser.objects.filter(openid=openid).first()
|
|
except (BadSignature, SignatureExpired):
|
|
return None
|
|
|
|
class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
|
|
"""
|
|
社区活动接口
|
|
"""
|
|
queryset = Activity.objects.filter(is_active=True).order_by('-created_at')
|
|
serializer_class = ActivitySerializer
|
|
|
|
@extend_schema(summary="报名活动")
|
|
@action(detail=True, methods=['post'])
|
|
def signup(self, request, pk=None):
|
|
user = get_current_wechat_user(request)
|
|
if not user:
|
|
return Response({'error': '请先登录'}, status=401)
|
|
|
|
activity = self.get_object()
|
|
|
|
# Check if already signed up
|
|
if ActivitySignup.objects.filter(activity=activity, user=user).exists():
|
|
return Response({'error': '您已报名该活动'}, status=400)
|
|
|
|
if activity.signups.count() >= activity.max_participants:
|
|
return Response({'error': '活动名额已满'}, status=400)
|
|
|
|
signup = ActivitySignup.objects.create(activity=activity, user=user)
|
|
serializer = ActivitySignupSerializer(signup)
|
|
return Response(serializer.data, status=201)
|
|
|
|
@extend_schema(summary="我的报名记录")
|
|
@action(detail=False, methods=['get'])
|
|
def my_signups(self, request):
|
|
user = get_current_wechat_user(request)
|
|
if not user:
|
|
return Response({'error': '请先登录'}, status=401)
|
|
signups = ActivitySignup.objects.filter(user=user).order_by('-signup_time')
|
|
serializer = ActivitySignupSerializer(signups, many=True)
|
|
return Response(serializer.data)
|
|
|
|
class TopicViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
技术论坛帖子接口
|
|
"""
|
|
queryset = Topic.objects.all()
|
|
serializer_class = TopicSerializer
|
|
filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend]
|
|
search_fields = ['title', 'content']
|
|
filterset_fields = ['category', 'is_pinned']
|
|
ordering_fields = ['created_at', 'view_count']
|
|
ordering = ['-is_pinned', '-created_at']
|
|
|
|
def perform_create(self, serializer):
|
|
user = get_current_wechat_user(self.request)
|
|
# Auth check is done in create or permission, but here we need user for save
|
|
if user:
|
|
serializer.save(author=user)
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
user = get_current_wechat_user(request)
|
|
if not user:
|
|
return Response({'error': '请先登录'}, status=401)
|
|
return super().create(request, *args, **kwargs)
|
|
|
|
class ReplyViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
帖子回复接口
|
|
"""
|
|
queryset = Reply.objects.all()
|
|
serializer_class = ReplySerializer
|
|
|
|
def perform_create(self, serializer):
|
|
user = get_current_wechat_user(self.request)
|
|
if user:
|
|
serializer.save(author=user)
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
user = get_current_wechat_user(request)
|
|
if not user:
|
|
return Response({'error': '请先登录'}, status=401)
|
|
return super().create(request, *args, **kwargs)
|
|
|
|
class TopicMediaViewSet(viewsets.GenericViewSet, mixins.CreateModelMixin):
|
|
"""
|
|
论坛多媒体资源上传接口
|
|
"""
|
|
queryset = TopicMedia.objects.all()
|
|
serializer_class = TopicMediaSerializer
|
|
parser_classes = [parsers.MultiPartParser, parsers.FormParser]
|
|
|
|
@extend_schema(summary="上传媒体文件 (返回URL用于Markdown)")
|
|
def create(self, request, *args, **kwargs):
|
|
user = get_current_wechat_user(request)
|
|
if not user:
|
|
return Response({'error': '请先登录'}, status=401)
|
|
|
|
# 允许上传时不关联 Topic (发帖前上传),或后续关联
|
|
# 主要是返回 url
|
|
return super().create(request, *args, **kwargs)
|