from adrf.serializers import ( ModelSerializer, Serializer, IntegerField, CharField, SerializerMethodField, ) from .models import FriendRequest, Friendship, Conversation, ConversationParticipant, ChatMessage from django.conf import settings class UserBriefSerializer(Serializer): id = IntegerField() username = CharField() nickname = SerializerMethodField() avatar = SerializerMethodField() async def get_nickname(self, obj): return getattr(obj, 'nickname', '') or obj.username async def get_avatar(self, obj): avatar = getattr(obj, 'avatar', None) if avatar and hasattr(avatar, 'url'): request = self.context.get('request') if request: return request.build_absolute_uri(avatar.url) return avatar.url return '' class FriendRequestSerializer(ModelSerializer): from_user = UserBriefSerializer(read_only=True) to_user = UserBriefSerializer(read_only=True) from_user_id = IntegerField(write_only=True, required=False) to_user_id = IntegerField(write_only=True, required=False) class Meta: model = FriendRequest fields = ['id', 'from_user', 'to_user', 'from_user_id', 'to_user_id', 'status', 'message', 'created_at', 'updated_at'] class FriendshipSerializer(ModelSerializer): friend = SerializerMethodField() class Meta: model = Friendship fields = ['id', 'friend', 'created_at'] async def get_friend(self, obj): request = self.context.get('request') if not request: return None friend_user = obj.user2 if obj.user1_id == request.user.id else obj.user1 return await UserBriefSerializer(friend_user, context=self.context).adata class ConversationSerializer(ModelSerializer): other_user = SerializerMethodField() last_message = SerializerMethodField() unread_count = SerializerMethodField() class Meta: model = Conversation fields = ['id', 'type', 'other_user', 'last_message', 'unread_count', 'created_at'] async def get_other_user(self, obj): request = self.context.get('request') if not request: return None participant = await obj.participants.exclude(user=request.user).afirst() if participant: return await UserBriefSerializer(participant.user, context=self.context).adata return None async def get_last_message(self, obj): last_msg = await obj.messages.order_by('-created_at').select_related('sender', 'reply_to__sender').afirst() if last_msg: return await ChatMessageSerializer(last_msg, context=self.context).adata return None async def get_unread_count(self, obj): request = self.context.get('request') if not request: return 0 try: participant = await ConversationParticipant.objects.aget(conversation=obj, user=request.user) if participant.last_read_at: return await obj.messages.filter(created_at__gt=participant.last_read_at).exclude(sender=request.user).acount() return await obj.messages.exclude(sender=request.user).acount() except ConversationParticipant.DoesNotExist: return 0 class ChatMessageSerializer(ModelSerializer): sender_info = UserBriefSerializer(source='sender', read_only=True) is_own = SerializerMethodField() reply_to_message = SerializerMethodField() class Meta: model = ChatMessage fields = ['id', 'conversation', 'sender', 'sender_info', 'content', 'msg_type', 'file_url', 'reply_to', 'reply_to_message', 'is_recalled', 'is_own', 'created_at'] read_only_fields = ['sender', 'conversation'] async def get_is_own(self, obj): request = self.context.get('request') if request and hasattr(request, 'user'): # 用 id 比较,避免 sender 外键懒加载触发同步查询 return obj.sender_id == request.user.id return False async def get_reply_to_message(self, obj): if not obj.reply_to_id: return None # 异步加载被回复消息及其发送者,避免外键懒加载触发同步查询 reply = await ChatMessage.objects.select_related('sender').aget(pk=obj.reply_to_id) if reply.is_recalled: return None return { 'id': reply.id, 'content': reply.content[:50], 'sender_name': getattr(reply.sender, 'nickname', '') or reply.sender.username, 'msg_type': reply.msg_type, }