285 lines
12 KiB
Python
285 lines
12 KiB
Python
from rest_framework import status
|
|
from rest_framework.views import APIView
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from rest_framework.parsers import JSONParser
|
|
from rest_framework.pagination import PageNumberPagination
|
|
from rest_framework.response import Response
|
|
from django.db.models import Count, Q
|
|
from drf_yasg.utils import swagger_auto_schema
|
|
from drf_yasg import openapi
|
|
from chunyu_project.common_schemas import success_response, error_response, unauthorized_response, not_found_response
|
|
|
|
from utils.response_codes import ResponseCode, create_standardized_response, create_standardized_error_response
|
|
from .models import Message, SystemMessage, SystemMessageRead
|
|
from .serializers import MessageListSerializer, SystemMessageSerializer, SystemMessageDetailSerializer
|
|
|
|
|
|
class MessagePagination(PageNumberPagination):
|
|
page_size = 20
|
|
page_size_query_param = 'page_size'
|
|
max_page_size = 100
|
|
|
|
|
|
class MessageListView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='获取消息列表',
|
|
operation_description='获取当前用户的消息列表,支持按类型筛选: reply/at_me/like/system',
|
|
manual_parameters=[
|
|
openapi.Parameter('type', openapi.IN_QUERY, description='消息类型: reply/at_me/like/system', type=openapi.TYPE_STRING),
|
|
openapi.Parameter('page', openapi.IN_QUERY, description='页码', type=openapi.TYPE_INTEGER),
|
|
openapi.Parameter('page_size', openapi.IN_QUERY, description='每页数量', type=openapi.TYPE_INTEGER),
|
|
],
|
|
responses={200: success_response, 401: unauthorized_response}
|
|
)
|
|
def get(self, request):
|
|
msg_type = request.query_params.get('type', '')
|
|
|
|
if msg_type == 'system':
|
|
queryset = SystemMessage.objects.all()
|
|
paginator = MessagePagination()
|
|
page = paginator.paginate_queryset(queryset, request)
|
|
read_ids = set(
|
|
SystemMessageRead.objects.filter(
|
|
user=request.user,
|
|
system_message__in=page
|
|
).values_list('system_message_id', flat=True)
|
|
)
|
|
serializer = SystemMessageSerializer(page, many=True, context={'request': request, 'read_ids': read_ids})
|
|
return create_standardized_response(data=paginator.get_paginated_response(serializer.data).data, code=ResponseCode.SUCCESS)
|
|
|
|
valid_types = ['reply', 'at_me', 'like']
|
|
if msg_type and msg_type in valid_types:
|
|
queryset = Message.objects.filter(recipient=request.user, msg_type=msg_type)
|
|
else:
|
|
queryset = Message.objects.filter(recipient=request.user)
|
|
|
|
paginator = MessagePagination()
|
|
page = paginator.paginate_queryset(queryset, request)
|
|
serializer = MessageListSerializer(page, many=True, context={'request': request})
|
|
return create_standardized_response(data=paginator.get_paginated_response(serializer.data).data, code=ResponseCode.SUCCESS)
|
|
|
|
|
|
class UnreadCountView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='获取未读消息数量',
|
|
operation_description='获取当前用户各类型未读消息的数量统计',
|
|
responses={200: success_response, 401: unauthorized_response}
|
|
)
|
|
def get(self, request):
|
|
user = request.user
|
|
message_counts = Message.objects.filter(
|
|
recipient=user, is_read=False
|
|
).values('msg_type').annotate(count=Count('id'))
|
|
|
|
counts = {'reply': 0, 'at_me': 0, 'like': 0}
|
|
for item in message_counts:
|
|
if item['msg_type'] in counts:
|
|
counts[item['msg_type']] = item['count']
|
|
|
|
read_system_ids = SystemMessageRead.objects.filter(user=user).values_list('system_message_id', flat=True)
|
|
system_unread = SystemMessage.objects.filter(is_global=True).exclude(id__in=read_system_ids).count()
|
|
|
|
counts['system'] = system_unread
|
|
counts['total'] = counts['reply'] + counts['at_me'] + counts['like'] + counts['system']
|
|
|
|
return create_standardized_response(data=counts, code=ResponseCode.SUCCESS)
|
|
|
|
|
|
class MessageReadView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='标记消息为已读',
|
|
operation_description='将指定消息标记为已读,支持普通消息和系统消息',
|
|
manual_parameters=[
|
|
openapi.Parameter('pk', openapi.IN_PATH, description='消息ID', type=openapi.TYPE_INTEGER, required=True),
|
|
],
|
|
responses={200: success_response, 401: unauthorized_response, 404: not_found_response}
|
|
)
|
|
def post(self, request, pk):
|
|
try:
|
|
message = Message.objects.get(pk=pk, recipient=request.user)
|
|
message.is_read = True
|
|
message.save(update_fields=['is_read'])
|
|
return create_standardized_response(data={'is_read': True}, code=ResponseCode.SUCCESS)
|
|
except Message.DoesNotExist:
|
|
pass
|
|
|
|
try:
|
|
system_message = SystemMessage.objects.get(pk=pk)
|
|
SystemMessageRead.objects.get_or_create(
|
|
user=request.user,
|
|
system_message=system_message
|
|
)
|
|
return create_standardized_response(data={'is_read': True}, code=ResponseCode.SUCCESS)
|
|
except SystemMessage.DoesNotExist:
|
|
return create_standardized_error_response(
|
|
code=ResponseCode.NOT_FOUND,
|
|
message='消息不存在',
|
|
status_code=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
|
|
class MessageReadAllView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='全部标记为已读',
|
|
operation_description='将指定类型或所有消息标记为已读',
|
|
request_body=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
'type': openapi.Schema(type=openapi.TYPE_STRING, description='消息类型: reply/at_me/like/system,不传则标记全部'),
|
|
}
|
|
),
|
|
responses={200: success_response, 401: unauthorized_response}
|
|
)
|
|
def post(self, request):
|
|
msg_type = request.data.get('type', '')
|
|
user = request.user
|
|
affected = 0
|
|
|
|
if msg_type == 'system':
|
|
system_messages = SystemMessage.objects.filter(is_global=True)
|
|
for sm in system_messages:
|
|
_, created = SystemMessageRead.objects.get_or_create(
|
|
user=user,
|
|
system_message=sm
|
|
)
|
|
if created:
|
|
affected += 1
|
|
elif msg_type and msg_type in ['reply', 'at_me', 'like']:
|
|
affected = Message.objects.filter(
|
|
recipient=user, msg_type=msg_type, is_read=False
|
|
).update(is_read=True)
|
|
else:
|
|
affected = Message.objects.filter(
|
|
recipient=user, is_read=False
|
|
).update(is_read=True)
|
|
|
|
system_messages = SystemMessage.objects.filter(is_global=True)
|
|
for sm in system_messages:
|
|
_, created = SystemMessageRead.objects.get_or_create(
|
|
user=user,
|
|
system_message=sm
|
|
)
|
|
if created:
|
|
affected += 1
|
|
|
|
return create_standardized_response(data={'affected': affected}, code=ResponseCode.SUCCESS)
|
|
|
|
|
|
class MessageDeleteView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='删除消息',
|
|
operation_description='删除指定消息,支持普通消息和系统消息',
|
|
manual_parameters=[
|
|
openapi.Parameter('pk', openapi.IN_PATH, description='消息ID', type=openapi.TYPE_INTEGER, required=True),
|
|
],
|
|
responses={204: '删除成功', 401: unauthorized_response, 404: not_found_response}
|
|
)
|
|
def delete(self, request, pk):
|
|
try:
|
|
message = Message.objects.get(pk=pk, recipient=request.user)
|
|
message.delete()
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
except Message.DoesNotExist:
|
|
pass
|
|
|
|
try:
|
|
system_message = SystemMessage.objects.get(pk=pk)
|
|
SystemMessageRead.objects.get_or_create(
|
|
user=request.user,
|
|
system_message=system_message
|
|
)
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
except SystemMessage.DoesNotExist:
|
|
return create_standardized_error_response(
|
|
code=ResponseCode.NOT_FOUND,
|
|
message='消息不存在',
|
|
status_code=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
|
|
class MessageClearAllView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='清空所有消息',
|
|
operation_description='删除指定类型或全部消息,可选类型: reply/at_me/like/system',
|
|
request_body=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
'type': openapi.Schema(type=openapi.TYPE_STRING, description='消息类型: reply/at_me/like/system,不传则清空全部'),
|
|
}
|
|
),
|
|
responses={200: success_response, 401: unauthorized_response}
|
|
)
|
|
def delete(self, request):
|
|
msg_type = request.data.get('type', '')
|
|
user = request.user
|
|
deleted_count = 0
|
|
|
|
if msg_type == 'system':
|
|
# 删除系统消息的已读记录(相当于清空系统消息)
|
|
deleted_count = SystemMessageRead.objects.filter(user=user).delete()[0]
|
|
elif msg_type and msg_type in ['reply', 'at_me', 'like']:
|
|
deleted_count = Message.objects.filter(recipient=user, msg_type=msg_type).delete()[0]
|
|
else:
|
|
# 删除所有普通消息
|
|
deleted_count = Message.objects.filter(recipient=user).delete()[0]
|
|
# 同时删除所有系统消息已读记录
|
|
deleted_count += SystemMessageRead.objects.filter(user=user).delete()[0]
|
|
|
|
return create_standardized_response(data={'deleted': deleted_count}, code=ResponseCode.SUCCESS)
|
|
|
|
|
|
class SystemMessageDetailView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [JSONParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['消息'],
|
|
operation_summary='获取系统消息详情',
|
|
operation_description='获取指定系统消息的详细内容,并自动标记为已读',
|
|
manual_parameters=[
|
|
openapi.Parameter('pk', openapi.IN_PATH, description='系统消息ID', type=openapi.TYPE_INTEGER, required=True),
|
|
],
|
|
responses={200: success_response, 401: unauthorized_response, 404: not_found_response}
|
|
)
|
|
def get(self, request, pk):
|
|
try:
|
|
system_message = SystemMessage.objects.get(pk=pk)
|
|
except SystemMessage.DoesNotExist:
|
|
return create_standardized_error_response(
|
|
code=ResponseCode.NOT_FOUND,
|
|
message='系统消息不存在',
|
|
status_code=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
SystemMessageRead.objects.get_or_create(
|
|
user=request.user,
|
|
system_message=system_message
|
|
)
|
|
|
|
serializer = SystemMessageDetailSerializer(system_message, context={'request': request})
|
|
return create_standardized_response(data=serializer.data, code=ResponseCode.SUCCESS)
|