# coding=utf8 from __future__ import unicode_literals, absolute_import, print_function import datetime import json from collections import defaultdict from operator import itemgetter from django.conf import settings from django.db import transaction from django.db.models import Q, Count from django.db.models.query import QuerySet from django.utils.html import escape from gm_types.gaia import ( AUTHOR_TYPE, DIARY_OPERATE, TAG_TYPE, PROBLEM_FLAG_CHOICES, ) from gm_types.gaia import CONST_STRINGS from ..models.diary import PreOperationImage from ..models.topic import Problem, TopicImage, Video # TODO: CR from social.models import SocialInfo from talos.cache.service import diary_index_data_cache from talos.libs.datetime_utils import ( get_timestamp_epoch, get_timestamp_or_none, get_humanize_datetime, ) from talos.cache.viewrecord import ViewRecord from talos.libs.image_utils import get_full_path from ..models.diary import DiaryVote, Diary, DiaryFavor, DiaryTagV3, DiaryTag from talos.rpc import get_objects_from_queryset_and_pk_list from talos.services.doctor import DoctorService from talos.services.hospital import HospitalService from talos.services.user import UserService from talos.services.goods import GoodsService from talos.services import UserConvertService, TagV3Service, TagService from utils.gevent_jobs import Tasks def get_diary_voted_info_by_user_ids(diary, user_ids): user_ids = list(set(user_ids)) if not user_ids: return {} dvs = DiaryVote.objects.filter(diary=diary, user_id__in=user_ids) result = {} for dv in dvs: result[dv.user_id] = True return result def get_diary_voted_info_for_user(diaries, user_id): if not diaries: return {} dvs = DiaryVote.objects.filter(diary__in=diaries, user_id=user_id) if not dvs: return {} result = {} for dv in dvs: result[dv.diary_id] = True return result def get_diary_favored_info_for_user(diaries, user_id): """ 获取日记本的收藏信息 :param diaries: :param user_id: :return: """ result = {} if not diaries: return result dfs = DiaryFavor.objects.filter( diary__in=diaries, user_id=user_id, is_deleted=False ).values_list("diary_id", flat=True) for diary_id in dfs: result[diary_id] = True return result def get_diary_favor_nums_by_diaries(diaries): """ 获取日记本的收藏数 :param diaries: :return: """ result = {} if not diaries: return result diary_favor_nums = DiaryFavor.objects.filter( diary__in=diaries, is_deleted=False ).values("diary_id").annotate(favor_count=Count("diary_id")).values("diary_id", "favor_count") for item in diary_favor_nums: result.setdefault(item["diary_id"], item["favor_count"]) return result def get_diary_tags_info_by_ids(diary_tags): tag_infos = TagService.get_tags_dict_by_tag_ids(set(item.tag_id for item in diary_tags)) tags_info_dict = defaultdict(list) for item in diary_tags: tag_obj = tag_infos.get(item.tag_id, None) if not tag_obj: continue tags_info_dict[item.diary_id].append(TagService.format_tag(tag_obj)) return tags_info_dict def get_diary_tags_v3_info_by_ids(diary_tags_v3): tags_v3 = TagV3Service.get_tags_by_tag_v3_ids(set(item.tag_v3_id for item in diary_tags_v3)) tag_v3_dict = defaultdict(list) for item in diary_tags_v3: tag_v3_info = tags_v3.get(item.tag_v3_id, None) if not tag_v3_info: continue tag_v3_dict[item.diary_id].append(TagV3Service.format_tag_v3(tag_v3_info)) return tag_v3_dict def get_diary_tagv3_info_by_diary_ids(diary_ids): """ 获取日记本关联的tag_v3的信息 :param diary_ids: :return: """ result = {} if not diary_ids: return result diary_tag_v3_ids = DiaryTagV3.objects.filter( diary_id__in=diary_ids ).values_list("diary_id", "tag_v3_id") tag_v3_infos = TagV3Service.get_tags_by_tag_v3_ids(set(item[1] for item in diary_tag_v3_ids)) for diary_id, tag_v3_id in diary_tag_v3_ids: if diary_id not in result: result[diary_id] = [] tag_v3_info = tag_v3_infos.get(tag_v3_id, None) if tag_v3_info: result[diary_id].append(TagV3Service.format_tag_v3(tag_v3_info)) return result def get_diary_tag_info_by_diary_ids(diary_ids): """ 获取日记本关联的tag的信息 :param diary_ids: :return: """ result = {} if not diary_ids: return result diary_tag_ids = DiaryTag.objects.filter( diary_id__in=diary_ids ).values_list("diary_id", "tag_id") tag_infos = TagService.get_tags_dict_by_tag_ids(set(item[1] for item in diary_tag_ids)) for diary_id, tag_id in diary_tag_ids: if diary_id not in result: result[diary_id] = [] tag_obj = tag_infos.get(tag_id, None) if not tag_obj: continue result[diary_id].append(TagService.format_tag(tag_obj)) return result def tags_sort_for_special(tags): result = get_tags_from_tags_by_types( tags, [ TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI ] ) result += get_tags_from_tags_by_types( tags, [ TAG_TYPE.CITY, TAG_TYPE.PROVINCE, TAG_TYPE.COUNTRY ] ) result += get_tags_from_tags_by_types(tags, [TAG_TYPE.FREE]) return result def get_tags_from_tags_by_types(tags, types): result = [] for tag in tags: if tag["name"] in settings.UNSHOW_TAGS: continue if tag["tag_type"] in types: result.append(tag) return result def filter_diary_tags(tags): result = [] for tag in tags: # 添加热门tag后 跟日记本相关的显示部分 热门tag作为3级tag来显示 d = {'name': tag["name"], 'id': tag["id"], 'type': tag["tag_type"], 'tag_id': tag["id"]} result.append(d) return result[0:10] class DiaryListManager(object): def __init__(self, redis_client): self._redis = redis_client def _get_cache_key(self, diary_id): return 'talos:diary-v2:lm:%s' % diary_id def _get_simple_cache_key(self, diary_id): return 'talos:diary-v5:lm:%s' % diary_id def get_city_by_tag_objs(self, tags): for tag in tags: if tag.tag_type in [TAG_TYPE.CITY, TAG_TYPE.PROVINCE, TAG_TYPE.PROVINCE]: return tag.name def get_city_by_tags(self, tags): for tag in tags: if tag["tag_type"] in [TAG_TYPE.CITY, TAG_TYPE.PROVINCE, TAG_TYPE.PROVINCE]: return tag["name"] def list_cached_diary_data_by_ids(self, ids): keys = [self._get_simple_cache_key(_id) for _id in ids] cache_result_list = self._redis.mget(keys) result, missing_ids = {}, [] for index, _id in enumerate(ids): v = cache_result_list[index] if v is not None: if isinstance(v, bytes): v = v.decode() result[_id] = json.loads(v) else: missing_ids.append(_id) return result, missing_ids def set_cache(self, diary_id, data, timeout=60*10): key = self._get_simple_cache_key(diary_id) self._redis.set(key, json.dumps(data), timeout) def _diary_base_info(self, diary): latest_topic = diary.latest_topic title = latest_topic and latest_topic.get_title() or diary.title content = latest_topic and escape(latest_topic.answer) or '' diary_data = { 'id': diary.id, 'diary_id': diary.id, 'title': title, 'content': content, 'tags': [], 'tags_v3': [], 'images': diary.enduring_cover, 'user_id': diary.user_id, 'latest_topic_id': latest_topic and latest_topic.id or 0, 'lasest_topic_created_time': (latest_topic and get_timestamp_or_none(latest_topic.created_time)) or None, 'date': get_humanize_datetime(diary.last_modified), 'last_modified': get_timestamp_or_none(diary.last_modified), 'created_time': get_timestamp_or_none(diary.created_time), 'city': '中国', 'title_style_type': diary.title_style_type, 'author_type': AUTHOR_TYPE.USER, 'is_identification': diary.is_identification, # 兼容 'content_level': diary.content_level, 'service_id': diary.service_id, 'diary_title': diary.title or None, 'import_type': diary.is_import, 'sticky_post': diary.sticky_post, 'doctor_id': diary.doctor_id, 'hospital_id': diary.hospital_id, 'last_topic_time': get_timestamp_or_none(diary.last_topic_add_time), 'sell_point': diary.sell_point, 'is_online': diary.is_online, 'is_headline': diary.is_headline, "operation_time": get_timestamp_or_none(diary.operation_time) - 8 * 3600 if diary.operation_time else None, # 治疗时间 'stick_priority': diary.stick_priority, 'is_sink': diary.is_sink, 'reply_num': diary.reply_num, 'topic_num': diary.topic_num, } return diary_data def _get_or_init_cached_data(self, diary): k = self._get_cache_key(diary.id) _cached = self._redis.get(k) if _cached: return json.loads(_cached) latest_topic = diary.latest_topic title = latest_topic and latest_topic.get_title() or diary.title content = latest_topic and escape(latest_topic.answer) or '' diary_data = { 'id': diary.id, 'diary_id': diary.id, 'title': title, 'content': content, 'tags': diary.tags_new_era, 'images': diary.cover, 'diary_num': diary.topic_num, 'user_id': diary.user_id, 'latest_topic_id': latest_topic and latest_topic.id or 0, 'lasest_topic_created_time': (latest_topic and get_timestamp_or_none(latest_topic.created_time)) or None, 'date': get_humanize_datetime(diary.last_modified), 'last_modified': get_timestamp_or_none(diary.last_modified), 'created_time': get_timestamp_or_none(diary.created_time), 'city': self.get_city_by_tag_objs(diary.tags) or u'中国', 'title_style_type': diary.title_style_type, 'author_type': AUTHOR_TYPE.USER, 'view_num': diary.view_num, 'reply_num': diary.reply_num, 'vote_num': diary.vote_num, 'is_identification': diary.is_identification, # 兼容 } diary_data.update(diary.video_cover()) self._redis.setex(k, 3 * 60, json.dumps(diary_data)) return diary_data @staticmethod def _get_users_info(user_ids): users = UserService.get_users_by_user_ids(user_ids) return users @staticmethod def get_users_base_info(user_ids): users = UserService.get_users_base_info_by_user_ids(user_ids) return users @staticmethod def _get_users_info_v2(user_ids, simple=True): result = {} users = UserConvertService.get_user_info_by_user_ids(user_ids, simple=simple) for user_id, user_info in users.items(): if not simple: user_info.update({ "city": user_info.pop("city_name", ""), 'topic_num_posted': user_info.pop("topic_count", 0), 'vote_num_gained': user_info.pop("vote_count", 0), }) result[user_id] = user_info return result @staticmethod def _get_follow_info(viewer_user_id, diary_author_ids): follow_rels = {} if viewer_user_id: social_info = SocialInfo(uid=viewer_user_id) follow_rels = social_info.is_following_users(uids=diary_author_ids) return follow_rels def _get_hospitals_info(self, hospital_ids): if not hospital_ids: return {} hospital_ids = list(set(hospital_ids)) hospitals = HospitalService.get_hospital_from_hospital_ids(hospital_ids) hospitals = {h.id: h for h in hospitals} return hospitals def _get_doctors_info(self, doctor_ids): if not doctor_ids: return {} doctor_ids = list(set(doctor_ids)) doctors = DoctorService.get_doctor_from_doctor_ids(doctor_ids) doctors = {doctor.id: doctor for doctor in doctors} return doctors def _get_doctors_info_by_user_ids(self, user_ids): if not user_ids: return {} doctors = DoctorService.list_doctors_by_user_ids(user_ids) return doctors def _get_diary_show_info_by_service_ids(self, service_ids): """ v 7.6.95新增,通过美购id列表 返回基本的展示信息 :param service_ids: :return: """ if not service_ids: return {} service_ids = list(set(service_ids)) return GoodsService.get_diary_show_info_by_service_ids(service_ids) def _get_service_item_info_by_order_ids(self, order_ids): """ v 7.7.30 新增,通过订单id获取sku信息 :param order_ids: :return: """ if not order_ids: return {} order_ids = list(set(order_ids)) return GoodsService.get_service_item_info_by_order_ids(order_ids) @staticmethod def _filter_pre_and_post_topic_list(topic_list, reference_time): """ 通过 参照时间,把帖子分成术前帖和术后帖 :param topic_list: :param reference_time: :return: """ pre_topic_list, post_topic_list = [], [] for topic_info in topic_list: operation_date, created_time = topic_info.pop("operation_date"), topic_info.pop("created_time") topic_operation_time = operation_date or created_time topic_info["topic_operation_time"] = get_timestamp_or_none(topic_operation_time) - 8 * 3600 topic_info["topic_id"] = topic_info.pop("id") _topic_image = topic_info.get("images", []) _topic_video = topic_info.get("video_url", '') if _topic_image and topic_operation_time <= reference_time: pre_topic_list.append(topic_info) elif topic_operation_time > reference_time and (_topic_image or _topic_video): post_topic_list.append(topic_info) else: continue return pre_topic_list, post_topic_list @staticmethod def get_new_images_and_content_by_diary_info(diary_info_list): """ 日记本卡片展示新的逻辑,目前应用于美购列表页卡片展示 取第一篇有图术前帖及最新一篇有图 or 有视频的术后帖,before 展示术前帖图片 after展示术后帖图片,若术后帖有视频则展示视频 :param diary_info_list:[{"id": 1, "operation_time": '2017-12-10(datetime)', "created_time": '2015-12-10(datetime)'}] :return: """ diary_info_dic = {diary["id"]: diary for diary in diary_info_list} diary_ids = list(diary_info_dic.keys()) # 拿到所有的帖子数据 _query = Q(diary_id__in=diary_ids, is_online=True, flag=PROBLEM_FLAG_CHOICES.NORMAL) topic_info_list = Problem.objects.filter(_query).values( "id", "diary_id", "answer", "operation_date", "created_time") # 查图,查视频 _need_filter_images_or_video_topic_ids = [topic["id"] for topic in topic_info_list] # 图片 topic_images_dic = defaultdict(list) topic_image_list = TopicImage.objects.filter( topic_id__in=_need_filter_images_or_video_topic_ids).values_list("topic_id", "image_url") for topic_id, img_url in topic_image_list: topic_images_dic[topic_id].append(img_url) # 视频 topic_videos_dic = defaultdict(dict) topic_videos = Video.objects.filter(topic_id__in=_need_filter_images_or_video_topic_ids) for video_item in topic_videos: topic_id = video_item.topic_id _data = video_item.get_video_info() _data["topic_id"] = topic_id topic_videos_dic[topic_id] = _data # 通过日记本id分类 diary_topic_info_dic = defaultdict(list) for topic_info in topic_info_list: _topic_id = topic_info.get("id", 0) _images_list = topic_images_dic.get(_topic_id, []) _video_dic = topic_videos_dic.get(_topic_id, {}) if not _images_list and not _video_dic: # 去除无图无视频的帖子 continue # 将图片、视频数据归入即将筛选的帖子数据,可能存在 有视频无图的情况 topic_info = dict(topic_info) topic_info["images"] = _images_list topic_info.update(_video_dic) diary_topic_info_dic[topic_info["diary_id"]].append(topic_info) # 查下日记本的术前图库 diary_pre_images_dic = defaultdict(list) diary_pre_images_list = PreOperationImage.objects.filter( diary_id__in=diary_ids).values_list("diary_id", "image_url") for diary_id, diary_pre_image_url in diary_pre_images_list: diary_pre_images_dic[diary_id].append(diary_pre_image_url) # 数据重组,取出满足要求的术前术后帖 for diary_id in diary_ids: diary_info = diary_info_dic.pop(diary_id, {}) if not diary_info: continue topic_list = diary_topic_info_dic.get(diary_id, []) diary_operation_time = diary_info.get("operation_time") or diary_info.get("created_time") pre_topic_list, post_topic_list = DiaryListManager._filter_pre_and_post_topic_list( topic_list, diary_operation_time) # 术前帖,术后帖数据 pre_topic_info = pre_topic_list and min(pre_topic_list, key=itemgetter("topic_operation_time", "topic_id")) or {} post_topic_info = post_topic_list and max(post_topic_list, key=itemgetter("topic_operation_time", "topic_id")) or {} diary_pre_images_list = diary_pre_images_dic.get(diary_id, []) if pre_topic_info: _ = pre_topic_info.pop("answer", "") if post_topic_info: post_topic_info["content"] = escape(post_topic_info.pop('answer', "")) diary_info_dic[diary_id] = { "pre": pre_topic_info, "post": post_topic_info, "diary_pre_images": diary_pre_images_list, } return diary_info_dic @staticmethod def _force_queryset_to_list(queryset): if isinstance(queryset, QuerySet): queryset = list(queryset) return queryset def get_diary_list_by_diary_ids_with_doctor_hospital_name( self, ids, viewer_user_id=None, is_online=None, need_service=True ): diary_objs = self._get_diaries_by_ids(ids, is_online=is_online) return self.get_diary_list_by_diary_objs_with_doctor_hospital_name( diary_objs, viewer_user_id, need_service=need_service ) def get_diary_list_by_diary_objs_with_doctor_hospital_name( self, diary_objs, viewer_user_id=None, need_service=True): result = self.get_diary_list_by_diary_objs( diary_objs, viewer_user_id=viewer_user_id, need_service=need_service ) if not result: return result doctor_ids = [d.doctor_id for d in diary_objs] doctors = self._get_doctors_info(doctor_ids=doctor_ids) hospital_ids = [d.hospital_id for d in diary_objs] hospitals = self._get_hospitals_info(hospital_ids=hospital_ids) for diary in result: doctor = doctors.get(diary['doctor_id']) doctor_name = '' if doctor: doctor_name = doctor.name hospital = hospitals.get(diary['hospital_id']) hospital_name = '' if hospital: hospital_name = hospital.name diary.update({ 'doctor_name': doctor_name, 'hospital_name': hospital_name, }) return result def get_service_case_by_diary_objs(self, diary_objs): if not diary_objs: return [] user_ids = [d.user_id for d in diary_objs] users = self._get_users_info(user_ids=user_ids) result = [] for diary in diary_objs: user = users.get(diary.user_id) if not user: continue diary_data = { "user_id": user.id, "user_portrait": user.portrait, "diary_id": diary.id, "vote_num": diary.vote_num, "user": user.nickname, "membership_level": user.membership_level } image = diary.post_operation_image if image: diary_data['image'] = get_full_path(image, '-half') else: continue result.append(diary_data) return result def get_diary_image_info_by_diary_objs(self, diary_objs): result = [] diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return [] user_ids = [d.user_id for d in diary_objs] users = self._get_users_info(user_ids=user_ids) for diary in diary_objs: user = users.get(diary.user_id) if not user: continue post_operation_image = diary.post_operation_image result.append({ 'pic': get_full_path(post_operation_image, '-half'), 'pic_w': get_full_path(post_operation_image, '-w'), 'name': user.nickname }) return result def get_diary_list_by_diary_objs(self, diary_objs, viewer_user_id=None, get_last_video=False, need_service=True, need_service_item_info=False): """更具日记本对象列表获取全部的日记本信息。 :param diary_objs: 日记本对象列表 :param viewer_user_id: 请求用户 :param get_last_video: 是否获取日记本的最后一个视频信息 :param need_service: 是否需要美购 :param need_service_item_info: 是否展示日记本所属美购的sku :return: """ diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return [] diary_ids, user_ids, service_ids, order_ids = [], [], [], [] for d in diary_objs: user_ids.append(d.user_id) diary_ids.append(d.id) if d.service_id: service_ids.append(d.service_id) if d.order_id: order_ids.append(d.order_id) follow_rels = self._get_follow_info(viewer_user_id=viewer_user_id, diary_author_ids=user_ids) vote_info = get_diary_voted_info_for_user(diaries=diary_objs, user_id=viewer_user_id) users = self._get_users_info(user_ids=user_ids) services = self._get_diary_show_info_by_service_ids(service_ids) if need_service else {} service_items_dic = self._get_service_item_info_by_order_ids(order_ids) if need_service_item_info else {} tag_v3_infos = get_diary_tagv3_info_by_diary_ids(diary_ids) result = [] for diary in diary_objs: user = users.get(diary.user_id) if not user: continue diary_data = self._get_or_init_cached_data(diary) latest_topic_video = {} latest_topic = diary.latest_topic service_info = services.get(diary.service_id, {}) relation_service_sku = service_items_dic.get(diary.order_id, {}) _ = relation_service_sku.pop("order_id", "") # 将订单id 从数据中剔除 if latest_topic: latest_topic_video = latest_topic.get_video_info() bj_lasest_topic_operation_date = None if latest_topic and get_timestamp_or_none(latest_topic.operation_date): bj_lasest_topic_operation_date = get_timestamp_or_none(latest_topic.operation_date)-8*3600 diary_data.update({ 'diary_amount': diary_data['diary_num'], # for doctor backend, 20171009 'content_level': diary.content_level, 'service_id': diary.service_id, 'diary_title': diary.title or None, 'service_info': service_info, 'import_type': diary.is_import, # 区分是导入数据还是非导入数据 'relation_service_sku': relation_service_sku, 'tags_new_era': diary_data['tags'], "tags_v3": tag_v3_infos.get(diary.id, []), 'sticky_post': diary.sticky_post, 'doctor_id': diary.doctor_id, 'hospital_id': diary.hospital_id, 'last_topic_time': get_timestamp_or_none(diary.last_topic_add_time), 'is_following': follow_rels.get(diary.user_id, False), 'is_liked': vote_info.get(diary.id, False), 'is_voted': vote_info.get(diary.id, False), 'sell_point': diary.sell_point, 'user': { 'user_id': user.id, 'user_name': user.nickname, 'city': user.city_name, 'portrait': user.portrait, 'membership_level': user.membership_level, 'topic_num_posted': user.topic_count, 'vote_num_gained': user.vote_count, }, 'user_level': { 'level_icon': user.level_icon, 'membership_icon': user.membership_icon, 'constellation_icon': user.constellation_icon }, # FOR COMPATIBLE start 'topic_num_posted': user.topic_count, 'vote_num_gained': user.vote_count, 'user_portrait': user.portrait, 'user_nickname': user.nickname, 'membership_level': user.membership_level, 'author': { 'vote_num_gained': user.vote_count, 'topic_num_posted': user.topic_count, }, # FOR COMPATIBLE end # ONLY FOR PCWEB 'is_online': diary.is_online, 'is_headline': diary.is_headline, 'video_url': latest_topic_video['video_url'] if latest_topic_video else "", 'video_pic': latest_topic_video['video_pic'] if latest_topic_video else "", 'short_video_url': latest_topic_video.get("short_video_url", ""), "operation_time": get_timestamp_or_none(diary.operation_time) - 8 * 3600 if diary.operation_time else None, # 治疗时间 'lasest_topic_operation_date': bj_lasest_topic_operation_date, "service_city_name": service_info.get("city_name", ""), # 美购对应的城市名字 'stick_priority': diary.stick_priority, 'is_sink': diary.is_sink, 'vote_num': diary.vote_num, }) # 需要获取最后一个视频、并且最后一个日记贴中没有视频 if get_last_video and (not latest_topic_video or not latest_topic_video["video_url"]): topic = Problem.objects.filter(Q(diary=diary) & Q(is_online=True) & Q(video__isnull=False)).prefetch_related("video"). \ order_by('-created_time').first() if not topic: continue video_info = topic.get_video_info() diary_data['video_url'] = video_info['video_url'] diary_data['video_pic'] = video_info['video_pic'] diary_data['short_video_url'] = video_info.get('short_video_url', "") result.append(diary_data) return result def list_diary_vote_num_by_ids(self, diary_ids): topics = Problem.objects.using(settings.SLAVE_DB_NAME).filter(diary_id__in=diary_ids).only("id", "diary_id") topic_ids = [i.id for i in topics] topic_diary = {i.id: i.diary_id for i in topics} topic_vote_num_list = ViewRecord(CONST_STRINGS.TOPIC_VOTE_V1).view_hmget(topic_ids) topic_belong_diary = {} for topic in topics: topic_belong_diary[topic.id] = topic.diary_id diary_vote_num_dict = defaultdict(int) for index, topic_id in enumerate(topic_ids): vote_num = topic_vote_num_list[index] if vote_num is None: continue diary_id = topic_diary[topic_id] diary_vote_num_dict[diary_id] += int(vote_num) return diary_vote_num_dict def list_diary_video_cover_by_objs(self, diary_objs): if not settings.SHOW_VIDEO_COVER: return {} last_topics = {diary.latest_topic.id: diary.id for diary in diary_objs if diary.latest_topic} topic_ids = [topic.id for topic, _ in last_topics.items() if topic] if not topic_ids: return {} diary_videos = {} for video in Video.objects.filter(topic_id__in=topic_ids): info = video.get_video_info() info["video_cover"] = info['video_pic'] # 为客户端添加:`video_cover` 字段 diary_videos[last_topics[video.topic_id]] = info return diary_videos def list_simple_diaries_info_by_diary_objs(self, diary_objs, viewer_user_id, index=False): diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return [] tasks = Tasks() user_ids = [d.user_id for d in diary_objs] diary_ids = [diary.id for diary in diary_objs] diary_v3_tags = list(DiaryTagV3.objects.filter(diary_id__in=diary_ids)) diary_tags = list(DiaryTag.objects.filter(diary_id__in=diary_ids)) tasks.add("tag_v3_infos", get_diary_tags_v3_info_by_ids, diary_v3_tags) tasks.add("tags_info", get_diary_tags_info_by_ids, diary_tags) tasks.add("user_dic", self.get_users_base_info, user_ids) vote_info = get_diary_voted_info_for_user(diaries=diary_objs, user_id=viewer_user_id) vote_num_dict = self.list_diary_vote_num_by_ids(diary_ids) diaries_dict, missing_ids = self.list_cached_diary_data_by_ids(diary_ids) missing_objs = [obj for obj in diary_objs if obj in missing_ids] missing_video_cover_dict = self.list_diary_video_cover_by_objs(missing_objs) # missing_cover_dict = self.list_diary_cover_by_objs(missing_objs) tasks.joinall() _data = tasks.jobs users = _data['user_dic'] or {} tags_info = _data['tags_info'] or {} tag_v3_infos = _data['tag_v3_infos'] or {} result = [] for diary in diary_objs: user = users.get(diary.user_id) if not user: continue tags = tags_sort_for_special(tags_info.get(diary.id, [])) tags_new_era = filter_diary_tags(tags) if diary.id in diaries_dict: diary_data = diaries_dict[diary.id] else: diary_data = self._diary_base_info(diary) diary_data.update(missing_video_cover_dict.get(diary.id, {})) self.set_cache(diary.id, diary_data) diary_data.update({ 'is_voted': vote_info.get(diary.id, False), 'user': { 'doctor': { "user_id": user.id, "doctor_id": user.doctor_id, "nickname": user.doctor_name, "portrait": user.doctor_portrait, }, 'user_id': user.id, 'user_name': user.nickname, 'city': '', 'portrait': user.portrait, 'membership_level': user.membership_level, }, 'user_level': { 'level_icon': user.level_icon, 'membership_icon': user.membership_icon, 'constellation_icon': user.constellation_icon }, "tags_v3": tag_v3_infos.get(diary.id, []), 'tags': tags_new_era, 'city': self.get_city_by_tags(tags), 'vote_num': vote_num_dict.get(diary.id, 0) + diary.like_num, }) result.append(diary_data) return result def get_simple_diary_list_by_diary_objs(self, diary_objs): """ 精简接口,通过日记本对象,获取简单信息 :param diary_objs: :return: """ result = [] diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return result tag_v3_infos = get_diary_tagv3_info_by_diary_ids(set(diary.id for diary in diary_objs)) for diary in diary_objs: _data = { 'id': diary.id, 'diary_id': diary.id, 'title': diary.title or "", 'view_num': diary.view_num, "topics_num": diary.topic_num, 'order_id': diary.order_id, "hospital_id": diary.hospital_id, 'hospital_name': diary.raw_hospital, 'doctor_id': diary.doctor_id, 'doctor_name': diary.raw_doctor, 'operation_time': get_timestamp_or_none(diary.operation_time), 'tags': diary.tags_new_era, "tags_v3": tag_v3_infos.get(diary.id, []), 'cover': diary.cover, "is_online": diary.is_online, } result.append(_data) return result def get_diary_image_list_by_diary(self, ids): if not ids: return [] diaries = self._get_diaries_by_ids(ids) return self.get_diary_image_info_by_diary_objs(diaries) def get_diary_list_by_diary_ids(self, ids, viewer_user_id=None, get_last_video=False, need_service_item_info=False): """ :param ids: :param viewer_user_id: :param get_last_video: :param need_service_item_info: :return: """ if not ids: return [] diaries = self._get_diaries_by_ids(ids, is_online=True) return self.get_diary_list_by_diary_objs( diaries, viewer_user_id=viewer_user_id, get_last_video=get_last_video, need_service_item_info=need_service_item_info ) def list_diary_by_ids(self, ids, viewer_user_id=None, get_last_video=False, index=False): """获取简单的数据""" diary_ids = map(int, list(filter(lambda x: x is not None, ids))) if not diary_ids: return [] diaries = get_objects_from_queryset_and_pk_list( Diary.objects.filter(is_online=True), list(diary_ids), ) return self.list_simple_diaries_info_by_diary_objs( diaries, viewer_user_id=viewer_user_id, index=index ) def list_diary_by_objs(self, diary_objs): """获取简单的日记本数据""" if not diary_objs: return [] tag_v3_infos = get_diary_tagv3_info_by_diary_ids(set(diary.id for diary in diary_objs)) result = [] for diary in diary_objs: latest_topic = diary.latest_topic title = latest_topic and latest_topic.get_title() or diary.title content = latest_topic and escape(latest_topic.answer) or '' diary_data = { 'id': diary.id, 'diary_id': diary.id, 'title': title, 'content': content, 'images': diary.cover, 'tags': diary.tags_new_era, "tags_v3": tag_v3_infos.get(diary.id, []), 'user_id': diary.user_id, 'latest_topic_id': latest_topic and latest_topic.id or 0, 'lasest_topic_created_time': (latest_topic and get_timestamp_or_none(latest_topic.created_time)) or None, 'date': get_humanize_datetime(diary.last_modified), 'last_modified': get_timestamp_or_none(diary.last_modified), 'created_time': get_timestamp_or_none(diary.created_time), 'city': self.get_city_by_tag_objs(diary.tags) or u'中国', 'title_style_type': diary.title_style_type, 'author_type': AUTHOR_TYPE.USER, 'is_identification': diary.is_identification, # 兼容 'content_level': diary.content_level, 'service_id': diary.service_id, 'diary_title': diary.title or None, 'import_type': diary.is_import, 'sticky_post': diary.sticky_post, 'doctor_id': diary.doctor_id, 'hospital_id': diary.hospital_id, 'last_topic_time': get_timestamp_or_none(diary.last_topic_add_time), 'sell_point': diary.sell_point, 'is_online': diary.is_online, 'is_headline': diary.is_headline, "operation_time": get_timestamp_or_none(diary.operation_time) - 8 * 3600 if diary.operation_time else None, # 治疗时间 'stick_priority': diary.stick_priority, 'is_sink': diary.is_sink, 'images': diary.cover, 'reply_num': diary.reply_num, } diary_data.update(diary.video_cover()) result.append(diary_data) return result def get_diary_list_by_diary_ids_for_journey(self, ids): if not ids: return [] diaries = self._get_diaries_by_ids(ids) return self.get_diary_list_by_diary_objs_for_journey(diaries) def get_diary_list_by_diary_objs_for_journey(self, diary_objs): diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return [] user_ids = [d.user_id for d in diary_objs] users = self._get_users_info(user_ids=user_ids) result = [] for diary in diary_objs: user = users.get(diary.user_id) if not user: continue cover = diary.cover if len(diary.cover) > 1: img_url = cover[1]["image"] elif len(diary.cover) == 1: img_url = cover[0]["image"] else: img_url = '' diary_data = { "city": user.city_name, "pic": get_full_path(img_url, '-half'), "id": diary.id, "num": diary.vote_num, "user_portrait": user.portrait, "nickname": user.nickname, "service_id": diary.service_id } result.append(diary_data) return result def get_diary_list_for_service_case(self, ids): if not ids: return [] diaries = self._get_diaries_by_ids(ids) return self.get_service_case_by_diary_objs(diaries) def list_diaries_by_ids(self, diary_ids): return { item.id: item for item in self._get_diaries_by_ids(diary_ids, is_online=True) } @staticmethod def _get_diaries_by_ids(diary_ids, is_online=None): filtered_ids = filter(lambda x: x is not None, diary_ids) diary_ids = map(int, list(filtered_ids)) if is_online is None: q = Q() else: q = Q(is_online=is_online) diaries = get_objects_from_queryset_and_pk_list( Diary.objects.filter(q).prefetch_related("topics"), list(diary_ids), ) return diaries def get_diary_list_for_esquery_by_diary_ids(self, ids, viewer_user_id=None): if not ids: return [] diaries = self._get_diaries_by_ids(ids) diary_dict = {d.id: d for d in diaries} result = self.get_diary_list_by_diary_objs_with_doctor_hospital_name(diaries, viewer_user_id) for r in result: # 兼容字段 r['tags_info'] = [] r['is_identification'] = True r['membership_level'] = r['user']['membership_level'] r['user_id'] = r['user']['user_id'] r['city'] = r['user']['city'] diary = diary_dict[r['id']] r['pre_operation_image'] = diary.pre_operation_image r['post_operation_image'] = diary.post_operation_image return result def get_diary_list_for_index_by_diary_ids(self, ids, viewer_user_id=None): if not ids: return [] result = [] diaries = self._get_diaries_by_ids(ids, is_online=True) user_ids = [d.user_id for d in diaries] # follow_rels = self._get_follow_info(viewer_user_id=viewer_user_id, diary_author_ids=user_ids) vote_info = get_diary_voted_info_for_user(diaries=diaries, user_id=viewer_user_id) users = self._get_users_info(user_ids=user_ids) for diary in diaries: user = users.get(diary.user_id) if not user: continue diary_data = self._get_or_init_cached_data(diary) diary_data.update({ 'is_voted': vote_info.get(diary.id, False), # 'is_following': follow_rels.get(diary.user_id, False), 'is_following': True, 'user_portrait': user.portrait, 'user_nickname': user.nickname, 'membership_level': user.membership_level, 'author': { 'vote_num_gained': user.vote_count, 'topic_num_posted': user.topic_count, }, "user_level": { 'membership_icon': user.membership_icon, 'level_icon': user.level_icon, 'constellation_icon': user.constellation_icon, }, }) result.append(diary_data) return result def get_diary_list_for_pc_index_by_diary_ids(self, ids): if not ids: return [] result = [] diaries = self._get_diaries_by_ids(ids, is_online=True) for diary in diaries: diary_data = self._get_or_init_cached_data(diary) diary_data.update({ 'operation_time': diary.operation_time, 'service_name': diary.related_service_name, }) result.append(diary_data) return result @staticmethod def get_new_diary_card_display_content_by_diary_id(diary_id): content_list = list(Problem.objects.filter( diary_id=diary_id, is_online=True, flag=PROBLEM_FLAG_CHOICES.NORMAL, answer__isnull=False ).order_by("-operation_date", "-id").exclude(answer='').values_list("answer", flat=True)) # 过滤掉内容为换行、空格的情况 content_list = list(filter(lambda item: item.strip(), content_list)) return content_list and content_list[0] or '' def get_new_service_case_by_diary_objs(self, diary_objs, diary_info_list, viewer_user_id=None): """ 获取新版的美购案例卡片展示逻辑 :param diary_objs: :param diary_info_list: :param viewer_user_id: :return: """ diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return [] result, user_ids, order_ids = [], [], [] for d in diary_objs: user_ids.append(d.user_id) if d.order_id: order_ids.append(d.order_id) vote_info = get_diary_voted_info_for_user(diaries=diary_objs, user_id=viewer_user_id) users = self._get_users_info(user_ids=user_ids) service_items_dic = self._get_service_item_info_by_order_ids(order_ids) new_images_display_logic_dic = self.get_new_images_and_content_by_diary_info(diary_info_list) for diary in diary_objs: new_image_display_logic_dic = new_images_display_logic_dic.get(diary.id, {}) user = users.get(diary.user_id) if not user: continue diary_data = self._get_or_init_cached_data(diary) relation_service_sku = service_items_dic.get(diary.order_id, {}) _ = relation_service_sku.pop("order_id", "") # 将订单id 从数据中剔除 lastest_post_topic = new_image_display_logic_dic.get("post", {}) diary_data.update({ 'content_level': diary.content_level, 'service_id': diary.service_id, 'diary_title': diary.title or None, 'relation_service_sku': relation_service_sku, 'is_liked': vote_info.get(diary.id, False), 'is_voted': vote_info.get(diary.id, False), 'user': { 'user_id': user.id, 'user_name': user.nickname, 'city': user.city_name, 'portrait': user.portrait, 'membership_level': user.membership_level, 'topic_num_posted': user.topic_count, 'vote_num_gained': user.vote_count, }, 'user_level': { 'level_icon': user.level_icon, 'membership_icon': user.membership_icon, 'constellation_icon': user.constellation_icon }, 'membership_level': user.membership_level, 'is_online': diary.is_online, "operation_time": get_timestamp_or_none(diary.operation_time or diary.created_time) - 8 * 3600, # 治疗时间 'lasest_topic_operation_date': lastest_post_topic.get("topic_operation_time", None) or None, 'video_pic': lastest_post_topic.get("video_pic", ""), 'video_url': lastest_post_topic.get("video_url", ""), 'short_video_url': lastest_post_topic.get("short_video_url", ""), }) diary_data.update(new_image_display_logic_dic) if not diary_data.get("content", ""): # 如果日记本最新一篇日记帖没内容,则找最新有内容的一篇的日记帖 diary_data["content"] = escape(self.get_new_diary_card_display_content_by_diary_id(diary.id) or '') result.append(diary_data) return result def get_diary_list_by_diary_objs_v2(self, diary_objs, viewer_user_id=None): """ 日记本列表整合,后期需要啥再往上加 :param diary_objs: :param viewer_user_id: :return: """ result = { "diary_list": [], "valid_diary_ids": [], } diary_objs = self._force_queryset_to_list(diary_objs) if not diary_objs: return result diary_ids, user_ids, service_ids = [], [], [] for d in diary_objs: user_ids.append(d.user_id) service_ids.append(d.service_id) diary_ids.append(d.id) # 点赞 voted_dic = get_diary_voted_info_for_user(diaries=diary_objs, user_id=viewer_user_id) # 收藏 favor_nums_dic = get_diary_favor_nums_by_diaries(diaries=diary_objs) favored_dic = get_diary_favored_info_for_user(diaries=diary_objs, user_id=viewer_user_id) # 关注状态 follow_rels = self._get_follow_info(viewer_user_id=viewer_user_id, diary_author_ids=user_ids) # 用户信息 users_info = self._get_users_info_v2(user_ids=user_ids, simple=False) # 美购信息 service_dic = GoodsService.get_diary_show_info_by_service_ids(list(filter(None, set(service_ids)))) # 标签3.0 tag_v3_infos = get_diary_tagv3_info_by_diary_ids(diary_ids=diary_ids) for diary_obj in diary_objs: _user_id = diary_obj.user_id _id = diary_obj.id user_info = users_info.get(_user_id, {}) service_info = service_dic.get(diary_obj.service_id, {}) if not user_info: continue latest_topic = diary_obj.latest_topic video_info = latest_topic and latest_topic.get_video_info() or {} _data = { "diary_id": diary_obj.id, 'diary_title': diary_obj.title or "", "user": user_info, 'tags': diary_obj.tags_new_era, 'tags_v3': tag_v3_infos.get(diary_obj.id, []), 'cover': diary_obj.cover, 'city': self.get_city_by_tag_objs(diary_obj.tags) or u'中国', 'service_city_name': service_info.get("city_name", "") or users_info.get("city", "") or "", 'content_level': diary_obj.content_level, 'create_timestamp': get_timestamp_epoch(diary_obj.created_time), "operation_timestamp": get_timestamp_epoch(diary_obj.operation_time), # 治疗时间 'topic_num': diary_obj.topic_num, "view_num": diary_obj.view_num, 'vote_num': diary_obj.vote_num, 'favor_num': favor_nums_dic.get(_id, 0), 'is_following': follow_rels.get(_user_id, False), 'is_voted': voted_dic.get(_id, False), 'is_favored': favored_dic.get(_id, False), # 以下是最新一个日记帖的数据 'content': latest_topic and escape(latest_topic.answer) or '', 'video_url': video_info.get("video_url", ""), 'video_pic': video_info.get("video_pic", ""), "short_video_url": video_info.get("short_video_url", ""), 'lastest_topic_id': latest_topic and latest_topic.id or 0, 'lastest_topic_reply_num': latest_topic and latest_topic.reply_num or 0, 'lastest_topic_create_timestamp': latest_topic and get_timestamp_epoch(latest_topic.created_time) or 0, # 发帖选择的治疗时间 'lastest_topic_operation_timestamp': latest_topic and get_timestamp_epoch(latest_topic.operation_date) or 0, } result["diary_list"].append(_data) result["valid_diary_ids"].append(_id) return result def get_diary_list_by_ids_v2(self, diary_ids, viewer_user_id=None): """ 获取日记本信息列表 :param diary_ids: :param viewer_user_id: :return: """ diaries = self._get_diaries_by_ids(diary_ids, is_online=True) return self.get_diary_list_by_diary_objs_v2( diary_objs=diaries, viewer_user_id=viewer_user_id ) def clean_video_url(video_url): return video_url.replace(settings.VIDEO_HOST, '') def set_diary_cover(cover, diary): if cover: with transaction.atomic(): cover = get_full_path(cover) pre_images = diary.pre_operation_images.all() for image in pre_images: if image.image_url == cover or image.cover_image_url == cover: image.is_cover = True else: image.is_cover = False image.save() def set_diary_postoperation_cover(cover, diary): if cover: topic_ids = Problem.objects.filter(diary_id=diary.id, is_online=True).values_list('id', flat=True) topic_ids = list(topic_ids) images = TopicImage.objects.filter(topic_id__in=topic_ids) with transaction.atomic(): images.update(is_cover=False) image = images.filter(Q(image_url=cover) | Q(cover_image_url=cover)) image.update(is_cover=True) def set_pre_operation_images(pre_operation_images, cover, diary): if pre_operation_images: taken_time = datetime.datetime.now() has_cover = False for image in pre_operation_images: if not image.get('image', ''): break if has_cover: is_cover = False else: is_cover = False if image['image'] != cover else True if is_cover: has_cover = True diary.pre_operation_images.create( image_url=image['image'], taken_time=taken_time, is_cover=is_cover, cover_image_url=image.get('modified_image_url')) # 记录操作 UPDATE_INFO diary.set_diary_operate(DIARY_OPERATE.UPDATE_INFO) def set_pre_operation_images_v1(pre_operation_images, cover, diary): if pre_operation_images: diary.pre_operation_images.clear() set_pre_operation_images(pre_operation_images, cover, diary) diary_list_manager = DiaryListManager(diary_index_data_cache)