#!/usr/bin/env python # -*- coding: utf-8 -*- # 回答评论 from __future__ import unicode_literals, absolute_import, print_function from itertools import chain, groupby from operator import itemgetter from django.db.models import Q, Count from django.utils.html import escape from gm_types.gaia import ( USER_TYPE, ) from qa.models.answer import ( AnswerReply, AnswerVoteReply, AnswerReplyImages, ) from qa.utils.time import get_timestamp_or_none from talos.services import ( UserConvertService, ) from utils.base_manager import ( BaseManager, ) class AnswerReplyManager(BaseManager): model = AnswerReply @staticmethod def reply_is_voted_by_ids(reply_ids, viewer_user_id=None): """ 获取回答评论的点赞状态 :param reply_ids: :param viewer_user_id: :return: """ result = {} if not all([reply_ids, viewer_user_id]): return result avrs = AnswerVoteReply.objects.filter( user=viewer_user_id, answerreply_id__in=reply_ids ).values_list("answerreply_id", flat=True) if avrs: result = {av: True for av in avrs} return result @staticmethod def get_reply_images_by_reply_ids(reply_ids): """ 通过评论id获取图片 :param reply_ids: :return: """ reply_images = AnswerReplyImages.objects.filter( reply_id__in=reply_ids ).values("reply_id", "url", "width", "height") result = {} for reply_id, items in groupby( sorted(reply_images, key=itemgetter("reply_id")), key=itemgetter("reply_id") ): result.update({ reply_id: [{ "image_url": item.get("url", ""), "width": item.get("width", 0), "height": item.get("height", 0), } for item in items] }) return result def get_reply_comment_nums_by_ids(self, reply_ids): """ 获取评论的子评论数 :param reply_ids: :return: """ if not reply_ids: return {} _nums = self.model.objects.filter( is_online=True, first_reply_id__in=reply_ids ).values("first_reply_id").annotate(cnt=Count("first_reply_id")).values("first_reply_id", "cnt") return {num["first_reply_id"]: num["cnt"] for num in _nums} def get_comments_data(self, reply_map_ids): """ 获取评论数据 :param reply_map_ids:评论映射id列表[(reply_id, commented_id)] :return: """ result = {} if not list(filter(None, reply_map_ids)): return result comments_info = self.model.objects.filter( pk__in=set(filter(None, chain.from_iterable(reply_map_ids))) ).values("id", "content", "user") valid_user_ids, comments_info_pre_dic = set(), dict() for item in comments_info: _data = dict(item) _user_id = _data.pop("user", 0) _data.update({ "user_id": _user_id }) valid_user_ids.add(_user_id) comments_info_pre_dic.update({ item["id"]: _data }) # 获取用户信息,这里不需要拿是否关注的状态 users_info = UserConvertService.get_user_info_by_user_ids( user_ids=valid_user_ids, ) # 获取评论图片数据 comment_images_data = self.get_reply_images_by_reply_ids( reply_ids=set(filter(None, map(itemgetter(0), reply_map_ids))) ) for r_id, comment_id in reply_map_ids: reply_info = comments_info_pre_dic.get(r_id, {}) comment_info = comments_info_pre_dic.get(comment_id, {}) reply_user_info = users_info.get(reply_info.get("user_id", 0), {}) comment_user_info = users_info.get(comment_info.get("user_id", 0), {}) if comment_info and reply_user_info.get("user_id", 0) != comment_user_info.get("user_id", 0): at_nickname = comment_user_info.get("user_name", "") else: at_nickname = "" if reply_info: _data = { 'content': escape(reply_info.get("content", "")), "comment_id": r_id, "comment_user_id": reply_user_info.get("user_id", 0), 'comment_user_type': USER_TYPE.NORMAL, "comment_user_gm_url": "", # 占位,在backend处理 "nickname": reply_user_info.get("user_name", ""), "at_user_id": comment_user_info.get("user_id", 0), "at_user_gm_url": "", # 占位,在backend处理 "at_user_type": USER_TYPE.NORMAL, "at_nickname": at_nickname, # 补充用户信息 "comment_user_info": reply_user_info, "at_user_info": comment_user_info, "images": comment_images_data.get(r_id, []), } result.update({ (r_id, comment_id): _data }) return result def get_reply_sub_comments(self, reply_id, need_newest=False, top_comment_id=None, start_num=0, count=2): """ 获取评论的子评论,引用时注意传参,默认获取全部的评论(老逻辑),考虑性能问题不要这么整…… :param reply_id: 评论id :param need_newest: 是否需要最新的 :param top_comment_id: 回答的二级评论置顶 :param start_num: :param count: :return: [(id, commented_id)]注意这里只返回映射关系 """ if need_newest: _order_by = "-id" else: _order_by = "id" comment_map_ids = [] if top_comment_id and not start_num: second_level_comment = self.model.objects.filter( id=top_comment_id, is_online=True ).only("id", "commented_reply_id").first() if second_level_comment: comment_map_ids.append((second_level_comment.id, second_level_comment.commented_reply_id)) count -= 1 comment_map_ids.extend(list(self.model.objects.filter( first_reply_id=reply_id, is_online=True ).order_by(_order_by).values_list( "id", "commented_reply_id")[start_num: start_num + count]) ) return comment_map_ids def get_reply_base_data(self, reply, need_sub_comments=False, need_newest=False, top_comment_id=None): """ 获取评论信息 :param reply: :param need_sub_comments: 是否取子评论 :param need_newest: 子评论排序(是否需要最新) :param top_comment_id: 回答的二级评论置顶 :return: """ _data = { "reply_id": reply.id, "content": escape(reply.content), "user_id": reply.user_id, # 用于获取用户信息 "reply_date": get_timestamp_or_none(reply.create_time), 'comments': [], "favor_amount": reply.like_num, 'reply_count': 0, # 子评论数 'is_liked': False, # 是否点赞,这个后期再拼接 "comments_map_ids": [] # 子评论映射列表 } if need_sub_comments: _data["comments_map_ids"] = self.get_reply_sub_comments( reply_id=reply.id, need_newest=need_newest, top_comment_id=top_comment_id ) return _data def get_reply_data_by_replies_obj(self, replies, viewer_user_id=None, need_sub_comments=False, need_newest=False, top_comment_id=None): """ 通过评论obj对象获取评论数据 :param replies: :param viewer_user_id: 当前作者用户id :param need_sub_comments: 是否取子评论 :param need_newest: 子评论排序(是否需要最新) :param top_comment_id: 回答的二级评论置顶 :return: """ result = {} if not replies: return result valid_user_ids = set() sub_comment_map_ids = [] for reply in replies: valid_user_ids.add(reply.user_id) _data = self.get_reply_base_data( reply, need_sub_comments=need_sub_comments, need_newest=need_newest, top_comment_id=top_comment_id ) sub_comment_map_ids.extend(_data.get("comments_map_ids", [])) result[reply.id] = _data _reply_ids = list(result.keys()) # 评论的用户信息 reply_user_infos = UserConvertService.get_user_info_by_user_ids(user_ids=valid_user_ids) # 评论的子评论信息 sub_comments_data = self.get_comments_data(sub_comment_map_ids) # 评论的子评论数 reply_comment_nums = self.get_reply_comment_nums_by_ids(_reply_ids) # 评论的点赞状态 reply_liked_status = self.reply_is_voted_by_ids(_reply_ids, viewer_user_id=viewer_user_id) # 评论的图片 reply_images_data = self.get_reply_images_by_reply_ids(_reply_ids) for reply_id, reply_data in result.items(): reply_data.update({ "reply_user_info": reply_user_infos.get(reply_data.get("user_id", 0), {}), "reply_count": reply_comment_nums.get(reply_id, 0), "comments": list(filter(None, ( sub_comments_data.get(map_id) for map_id in reply_data.pop("comments_map_ids", [])))), "is_liked": reply_liked_status.get(reply_id, False), "images": reply_images_data.get(reply_id, []), }) return result answer_reply_manager = AnswerReplyManager()