#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import time
import ffmpy
import hashlib
import json
import requests
import random
import datetime

from django.conf import settings
from django.db.models import F
from celery import shared_task
# from celery_once import QueueOnce
from urllib.parse import urljoin

from gm_types.push import PUSH_INFO_TYPE, AUTOMATED_PUSH
from gm_types.mimas import TRACTATE_PLATFORM, TRACTATE_DATA_TYPE, TRACTATE_CONTENT_LEVEL, APPLET_PAGE_FROM, \
    APPLET_SUBSCRIBE_MSG_TYPE, TRACATE_VIDEO_URL_SOURCE, PGC_TYPE, TRACTATE_STATUS
from gm_types.gaia import (
    VIDEO_CODE_STATUS,
    QINIU_VIDEO_HANDLE_STATUS_CODE,
    LIST_TRACTATE_FROM,
)

from gm_upload import (
    set_video_watermark,
    fetch_picture_and_save_to_qiniu,
    video_clipping,
    upload_file,
    video_delete,
)
from gm_upload.utils.qiniu_tool import QiniuTool
from gm_upload.utils.image_utils import Picture

from talos.libs.image_utils import fetch_picture_and_save_to_qiniu_v2
from talos.logger import info_logger
from talos.models.soft_article.soft_article import SoftArticleVideo
from talos.models.soft_article.reply import SoftArticleReply
from talos.models.tractate.vote import TractateReplyVote
from talos.models.tractate.tractate import TractateTag
from talos.models.tractate import TractateVideo, TractateExtra, TractateVote, Tractate, TractateReply, \
    TractateReplyImages
from talos.rpc import logging_exception
from utils.common import get_new_video_name, replace_video_url_for_rich_text
from utils.push import push_task_to_user_multi, vote_push, send_applet_subscribe_msg
from utils.protocol import gm_protocol

from talos.cache.base import (
    tractate_pv_cache,
    tractate_vote_count_cache,
    vote_cache,
    pgc_tractate_cache
)
from talos.services import UserService
from talos.tasks import get_sleep_user
from talos.tools.vote_tool import VoteTool
from talos.cache.base import fake_vote_cache
from communal.normal_manager import (
    tag_manager,
)
from talos.rpc import get_current_rpc_invoker
from talos.models.tractate import TractateScore
from communal.tasks import intelligent_push_task


def fake_view_num(start, end):
    """
    帖子的浏览量灌水
    :param start:
    :param end:
    :return:
    """
    return random.randint(start, end)


def upload_new_image_and_get_base_info(video_url):

    result = {
        "video_cover_url": "",
        "width": 0,
        "height": 0,
    }

    _video_cover_url = "{url}{params}".format(
        url=urljoin(settings.VIDEO_HOST, video_url), params=settings.VIDEO_PIC_URL)

    try:
        new_video_caver_url = fetch_picture_and_save_to_qiniu_v2(_video_cover_url)
        _base_data = Picture.get_image_base_info(new_video_caver_url)
        if not all(_base_data.values()):
            _base_data = {
                "width": 0,
                "height": 0,
            }

        result["video_cover_url"] = new_video_caver_url
        result.update(_base_data)

        return result
    except:  # 上传失败则返回默认值
        return result


@shared_task
def set_tractate_rich_text_water_video_url(tractate_id, raw_video_url, water_video_url):
    try:
        tractate_obj = Tractate.objects.get(id=tractate_id)
    except:
        tractate_obj = None

    if not tractate_obj:
        return

    rich_text, _ = replace_video_url_for_rich_text(tractate_obj.content, {raw_video_url: water_video_url})
    tractate_obj.content = rich_text
    tractate_obj.save(update_fields=["content"])


@shared_task
def set_tractate_video_water_mark_url(video_id):
    video = TractateVideo.objects.get(id=video_id)

    raw_video_url = video.raw_video_url
    pid = set_video_watermark(
        raw_video_url,
        get_new_video_name(raw_video_url),
        water_mark_url=settings.WATER_MARK_URL_FOR_VIDEO
    )

    video.persistent_id = pid
    video.persistent_status = VIDEO_CODE_STATUS.WAITING
    video.save(update_fields=["persistent_id", "persistent_status"])


@shared_task
def check_tractate_video_water_mark_url_is_finish():
    videos = TractateVideo.objects.filter(persistent_status=VIDEO_CODE_STATUS.WAITING)
    for video in videos:
        _pid = video.persistent_id
        try:
            if _pid:
                result = json.loads(requests.get('http://api.qiniu.com/status/get/prefop?id=' + _pid).text)
                # 状态码0成功,1等待处理,2正在处理,3处理失败,4通知提交失败。
                # 如果请求失败,返回包含如下内容的JSON字符串{"error":   "<ErrMsg    string>"}
                code = result.get('code')
                error = result.get('error', '')
                if error:
                    continue
                if code != QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                    continue
                else:
                    if result['items'][0]['code'] == QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                        water_video_url = result['items'][0]['key']
                        # 刷新cdn缓存
                        QiniuTool.refresh_qiniu_resource_cache([urljoin(settings.VIDEO_HOST, water_video_url)])
                        # 上传一下有水印的视频第一帧图片
                        others = upload_new_image_and_get_base_info(water_video_url)
                        if others.get("video_cover_url", ""):
                            for k, v in others.items():
                                setattr(video, k, v)

                        video.water_video_url = water_video_url
                        video.persistent_status = VIDEO_CODE_STATUS.SUCCESS
                        video.save()
                        # 如果视频来源于帖子富文本中,则更新帖子富文本中的视频地址
                        if video.video_url_source == TRACATE_VIDEO_URL_SOURCE.RICH_TEXT:
                            set_tractate_rich_text_water_video_url.delay(video.tractate_id, video.raw_video_url, water_video_url)
                    else:
                        continue
        except:
            logging_exception()


@shared_task
def tractate_fake_vote(
        repeat_times, tractate_id, need_view_increase=True, incr_range=[1, 1], alert='', force_push=False
):
    """
    异步增加虚拟点赞量、浏览量
    :param repeat_times:
    :param tractate_id:
    :param incr_range:
    :param need_view_increase:
    :param alert:
    :return:
    """
    now_date = datetime.datetime.now()
    if not 9 <= now_date.hour < 22:  # 超过晚十点灌水任务不执行
        return
    tractate_fake_vote_key = "tractate_fake_vote_{id}".format(id=tractate_id)
    is_set = fake_vote_cache.set(tractate_fake_vote_key, 1, nx=True, ex=30)
    if not is_set:
        return

    tractate = Tractate.objects.get(id=tractate_id)
    if tractate.user_del or not tractate.is_online or tractate.content_level == TRACTATE_CONTENT_LEVEL.BAD:
        info_logger.info('帖子:{}已下线, 不执行点赞灌水.'.format(tractate.id))
        return

    users = get_sleep_user(repeat_times)
    cnt = 0
    for u in users:
        u_nick_name = ''
        tractate_vote, created = TractateVote.objects.get_or_create(user_id=u, tractate_id=tractate.id, is_fake=True)
        if created:
            # 对作者增加点赞数
            author = UserService.get_user_by_user_id(tractate.user_id)
            author.incr_vote_count()
            cnt += 1

        if not u_nick_name:
            vote_user = UserService.get_user_by_user_id(u)
            u_nick_name = vote_user.nickname or u'更美用户'

        vote_tool = VoteTool(redis_c=vote_cache, user_id=tractate.user_id, new_version=True)
        vote_tool.receive_tractate_vote(tractate_vote.id)

        # 点赞推送
        push_url = gm_protocol.get_tractate_detail(
            tractate_id=tractate.id,
            tractate_detail_from=LIST_TRACTATE_FROM.NOTICE_VOTE
        )

        if not alert:
            alert = u'{user_name}赞了你的帖子{content}'.format(
                user_name=str(u_nick_name), content=tractate.content[:10])

        if force_push:
            vote_push(user_id=tractate.user_id, alert=alert, push_type=AUTOMATED_PUSH.TRACTATE_GET_VOTED, push_url=push_url)

    if not need_view_increase:
        return

    # 增加可视点赞数
    te, _ = TractateExtra.objects.get_or_create(tractate_id=tractate.id)
    te.favor_count = F('vote_count') + cnt
    te.save(update_fields=['vote_count'])
    tractate_vote_count_cache.incrby(str(tractate.id), cnt)

    # 每个点赞增加随机浏览量
    view_num = 0
    for i in range(cnt):
        view_num += random.randint(*incr_range)

    # 增加可视浏览数
    tractate_pv_cache.incrby(str(tractate.id), view_num)


@shared_task
def reply_push(user_id, tractate_id, reply_id, content=None):
    """
    :param user_id: 回复人的user_id
    :param tractate_id:
    :param reply_id: 发出的那条回复ID
    :param content: 评论的内容
    :return:
    """

    if not tractate_id and not reply_id:
        return

    try:
        reply_info = TractateReply.objects.get(id=reply_id)
    except TractateReply.DoesNotExist:
        return
    if reply_info.replied_id:
        try:
            replied_info = TractateReply.objects.get(id=reply_info.replied_id)
        except TractateReply.DoesNotExist:
            return
        push_user_id = replied_info.user_id
        push_type = AUTOMATED_PUSH.TRACTATE_REPLY_GET_REPLY
        content_id = replied_info.id        # 二级评论传给demeter的id是二级评论id
        push_image = ''                     # 帖子评论暂时没有图片
    else:
        try:
            tractate = Tractate.objects.get(id=tractate_id)
        except Tractate.DoesNotExist:
            return
        push_user_id = tractate.user_id
        images = list(TractateReplyImages.objects.using(settings.SLAVE_DB_NAME).filter(
            reply_id=reply_id,
        ).values_list('image_url', flat=True))
        push_image = images and Picture(images[0]).watermarked or ''
        content_id = tractate.id
        push_type = AUTOMATED_PUSH.TRACTATE_GET_REPLY

    push_url = gm_protocol.get_user_answer_list()
    user = UserService.get_user_by_user_id(user_id=user_id)
    alert = u'@{}:{}...'.format(user.nickname if user else '更美用户', content[:25])

    intelligent_push_task.apply_async(
        args=(
            content_id, [push_user_id], push_type,
            {
                'type': PUSH_INFO_TYPE.GM_PROTOCOL,
                'pushUrl': push_url,
                'push_url': push_url,
                'image': push_image,
                'push_image': push_image,
            }
        ),
        kwargs={
            "platform": None,
            "alert": alert,
            "others": {
                "title": "你收到了一条新评论",
                "alert": alert,
            },
            "labels": {
                'event_type': 'push',
                'event': 'tractate_received_reply'
            },
        },
    )


@shared_task
def set_softarticle_video_water_mark_url(video_id):
    # 医生后台帖子封面图修改
    video = SoftArticleVideo.objects.get(id=video_id)

    raw_video_url = video.raw_video_url
    pid = set_video_watermark(
        raw_video_url,
        get_new_video_name(raw_video_url),
        water_mark_url=settings.WATER_MARK_URL_FOR_VIDEO
    )

    video.persistent_id = pid
    video.persistent_status = VIDEO_CODE_STATUS.WAITING
    video.save(update_fields=["persistent_id", "persistent_status"])


@shared_task
def check_soft_article_video_water_mark_url_is_finish():
    videos = SoftArticleVideo.objects.filter(persistent_status=VIDEO_CODE_STATUS.WAITING)
    for video in videos:
        _pid = video.persistent_id
        try:
            if _pid:
                result = json.loads(requests.get('http://api.qiniu.com/status/get/prefop?id=' + _pid).text)
                # 状态码0成功,1等待处理,2正在处理,3处理失败,4通知提交失败。
                # 如果请求失败,返回包含如下内容的JSON字符串{"error":   "<ErrMsg    string>"}
                code = result.get('code')
                error = result.get('error', '')
                if error:
                    continue
                if code != QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                    continue
                else:
                    if result['items'][0]['code'] == QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                        water_video_url = result['items'][0]['key']
                        # 刷新cdn缓存
                        QiniuTool.refresh_qiniu_resource_cache([urljoin(settings.VIDEO_HOST, water_video_url)])
                        # 上传一下有水印的视频第一帧图片
                        others = upload_new_image_and_get_base_info(water_video_url)
                        if others.get("video_cover_url", ""):
                            for k, v in others.items():
                                setattr(video, k, v)

                        video.water_video_url = water_video_url
                        video.persistent_status = VIDEO_CODE_STATUS.SUCCESS
                        video.save()
                    else:
                        continue
        except:
            logging_exception()


@shared_task
def doctor_reply_push(soft_article_id, reply_id):
    """

    :param soft_article_id:
    :param reply_id: 发出的那条回复ID
    :return:
    """

    if not soft_article_id and not reply_id:
        return

    try:
        reply_info = SoftArticleReply.objects.get(id=reply_id)
    except SoftArticleReply.DoesNotExist:
        return
    if reply_info.replied_id:
        try:
            replied_info = SoftArticleReply.objects.get(id=reply_info.replied_id, source_id=TRACTATE_PLATFORM.GM)
        except SoftArticleReply.DoesNotExist:
            return
        user = UserService.get_user_by_user_id(user_id=reply_info.user_id)
        if not user:
            return
        push_user_id = replied_info.user_id
        content = reply_info.content if len(reply_info.content) >= 10 else reply_info.content[:10]
        push_msg = "{user_name}回复了你的回复{content}".format(user_name=user.nickname, content=content)
        push_type = AUTOMATED_PUSH.TRACTATE_REPLY_GET_REPLY
    else:
        return

    push_url = gm_protocol.get_tractate_detail(
        comment_id=reply_id,
        tractate_id=replied_info.softarticle_id,
        data_type=TRACTATE_DATA_TYPE.DOCTOR,
        tractate_detail_from=LIST_TRACTATE_FROM.NOTICE_REPLY
    ) if reply_id else gm_protocol.get_comment_detail(tractate_id=replied_info.softarticle_id)

    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'pushUrl': push_url,
        'push_url': push_url,
    }

    push_task_to_user_multi(
        user_ids=[push_user_id], push_type=push_type,
        extra=extra,
        labels={'event_type': 'push', 'event': 'question_received_answer'},
        alert=push_msg,
    )


@shared_task
def tractate_video_fsm_runner():
    """
    状态机
    考虑封装成类,根据状态给对应worker添加任务
    :return:
    """

    # 截取视频前两秒
    clipping_id_list = list(TractateVideo.objects.filter(
        persistent_clip_status=VIDEO_CODE_STATUS.NOSTART
    ).values_list("id", flat=True))
    for video_id in clipping_id_list:
        set_tractate_video_clipping_to_video.delay(video_id)

    # 检查七牛云截取状态
    check_id_list = list(TractateVideo.objects.filter(
        persistent_clip_status=VIDEO_CODE_STATUS.WAITING
    ).values_list("id", flat=True))
    for video_id in check_id_list:
        check_tractate_video_clipping_is_finish.delay(video_id)

    # 将七牛云截取成功的 mp4 转换为 webP 动图并上传
    set_id_list = list(TractateVideo.objects.filter(
        persistent_clip_status=VIDEO_CODE_STATUS.OPERATING_LOCAL
    ).values_list("id", flat=True))
    for video_id in set_id_list:
        set_tractate_video_webp_pic.delay(video_id)

@shared_task
def set_tractate_video_clipping_to_video(video_id):
    video = TractateVideo.objects.get(id=video_id)
    if video.persistent_clip_status != VIDEO_CODE_STATUS.NOSTART:
        return

    hash_key = hashlib.md5(str(time.time()).encode("utf8")).hexdigest()
    pid = video_clipping(
        video.raw_video_url,
        new_filename='{}_clip.mp4'.format(hash_key),
        video_type='mp4',
        water_mark_url=None,
        start_time=3,
        # water_mark_url=settings.WATER_MARK_URL_FOR_VIDEO
    )

    video.persistent_clip_status = VIDEO_CODE_STATUS.WAITING
    video.persistent_clip_id = pid
    video.save(update_fields=['persistent_clip_id', 'persistent_clip_status'])


@shared_task
def check_tractate_video_clipping_is_finish(video_id):
    try:
        video = TractateVideo.objects.get(id=video_id)
        if video.persistent_clip_status != VIDEO_CODE_STATUS.WAITING:
            return

        if video.persistent_clip_id:
            result = json.loads(requests.get('{}{}'.format(
                settings.QINIU_VIDEO_INQUIRE_HOST,
                video.persistent_clip_id
            )).text)
            # 状态码0成功,1等待处理,2正在处理,3处理失败,4通知提交失败。
            # 如果请求失败,返回包含如下内容的JSON字符串{"error":   "<ErrMsg    string>"}
            code = result.get('code')
            error = result.get('error', '')
            if error:
                return
            if code not in (
                    QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS,
                    QINIU_VIDEO_HANDLE_STATUS_CODE.PROCESSING_FAIL
            ):
                return
            elif code == QINIU_VIDEO_HANDLE_STATUS_CODE.PROCESSING_FAIL:
                video.persistent_clip_status = VIDEO_CODE_STATUS.FAIL
                video.save(update_fields=['persistent_clip_status'])
            else:
                if result['items'][0]['code'] == QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                    mp4_key = result['items'][0]['key']

                    # 刷新cdn缓存
                    QiniuTool.refresh_qiniu_resource_cache([urljoin(settings.VIDEO_HOST, mp4_key)])

                    video.intercept_video_url = mp4_key
                    video.persistent_clip_status = VIDEO_CODE_STATUS.OPERATING_LOCAL
                    video.save(update_fields=['persistent_clip_status', 'intercept_video_url'])
                    # set_tractate_video_webp_pic(video.id)
                else:
                    video.persistent_status = result['items'][0]['code']
                    video.save(update_fields=['persistent_clip_status'])

    except:
        logging_exception()


@shared_task
def set_tractate_video_webp_pic(video_id):
    video = TractateVideo.objects.get(id=video_id)
    if video.persistent_clip_status != VIDEO_CODE_STATUS.OPERATING_LOCAL:
        return
    mp4_key = video.intercept_video_url

    hash_key = hashlib.md5(str(time.time()).encode("utf8")).hexdigest()
    input_path = os.path.join(settings.VIDEO_CONVERT_PATH, "{}.mp4".format(hash_key))
    output_path = os.path.join(settings.VIDEO_CONVERT_PATH, '{}.webp'.format(hash_key))

    try:
        clipping_video_url = settings.VIDEO_HOST + mp4_key
        res = requests.get(clipping_video_url)

        # 文件存储到本地
        with open(input_path, 'wb') as f:
            f.write(res.content)

        # 使用底层 FFmpeg 库进行转码
        ff = ffmpy.FFmpeg(
            inputs={input_path: None},
            outputs={output_path: settings.VIDEO_FF_ARGS}
        )
        ff.run()

        # 上传webP图片
        video.webp_url = upload_file(output_path)
        video.persistent_clip_status = VIDEO_CODE_STATUS.SUCCESS
        video.save(update_fields=['webp_url', 'persistent_clip_status'])

        # 删除中间数据 
        video_delete(mp4_key)

        # 刷新缓存
        QiniuTool.refresh_qiniu_resource_cache(
            [urljoin(settings.VIDEO_HOST, mp4_key)]
        )

    except:
        logging_exception()
        video.persistent_clip_status = VIDEO_CODE_STATUS.FAIL
        video.save(update_fields=['persistent_clip_status'])

    finally:
        if os.path.exists(input_path):
            os.remove(input_path)
        if os.path.exists(output_path):
            os.remove(output_path)


@shared_task
def applet_replied_push(reply_id):
    """
    帖子的评论被评论,给帖子评论的用户发送小程序推送  自己给自己评论无效
    :param reply_id: 发出的那条回复ID
    :return:
    """

    if not reply_id:
        return

    try:
        reply_info = TractateReply.objects.get(id=reply_id)
    except TractateReply.DoesNotExist:
        return

    if not reply_info.replied_id:
        return

    try:
        replied_info = TractateReply.objects.get(id=reply_info.replied_id)
    except TractateReply.DoesNotExist:
        return

    reply_content = reply_info.content[:20]
    nickname = UserService.get_user_by_user_id(reply_info.user_id).nickname[:10]

    data = {
        "name1": {
            "value": nickname
        },
        "thing2": {
            "value": reply_content
        },
        "date3": {
            "value": "{date}".format(date=datetime.datetime.now().strftime('%Y年%m月%d日 %H:%M'))
        },
        "thing4": {
            "value": "点击快速查看>>"
        }
    }

    # 模板id
    template_id = settings.APPLET_SUBSCRIBE_MSG.get("comment", "")

    # 跳转页面
    page = '/packageBBS/pages/posts/main?tractate_id={tractate_id}&top_id={top_id}&reply_id=' \
           '{reply_id}&from={from_page}&from_action={from_action}'.format(
        tractate_id=reply_info.tractate_id,
        top_id=reply_info.top_id,
        reply_id=reply_info.id,
        from_page=APPLET_PAGE_FROM.CARD,
        from_action=APPLET_SUBSCRIBE_MSG_TYPE.COMMENT
    )

    # 给一级评论用户发
    if reply_info.replied_id != reply_info.top_id:
        try:
            level_1_reply = TractateReply.objects.get(id=reply_info.top_id)
        except:
            level_1_reply = None

        if level_1_reply:
            # 自己给自己评论无效
            if reply_info.user_id != level_1_reply.user_id:

                # 发送小程序推送
                send_applet_subscribe_msg(level_1_reply.user_id, template_id, data=data, page=page)

    # 给被评论用户发,自己给自己评论无效
    if reply_info.user_id == replied_info.user_id:
        return

    # 发送小程序推送
    send_applet_subscribe_msg(replied_info.user_id, template_id, data=data, page=page)


@shared_task
def applet_reply_summary_push(reply_id):
    """
    用户评论完24小时后,
    如果没有收到评论或点赞,则给用户推送帖子新增评论总数,或帖子的相关内容
    如果被赞,且被赞数大于1,则推送新增被赞数
    :param reply_id: 当前评论id
    :return:
    """
    if not reply_id:
        return

    try:
        reply_info = TractateReply.objects.get(id=reply_id)
    except TractateReply.DoesNotExist:
        return

    user_id = reply_info.user_id

    # 查询当前用户有没有新的对帖子的评论
    new_tractate_reply = TractateReply.objects.using(settings.SLAVE_DB_NAME).filter(id__gt=reply_id, user_id=user_id,
                                                                                    is_online=True).exists()
    if new_tractate_reply:
        return

    replied_count = TractateReply.objects.using(settings.SLAVE_DB_NAME).\
        filter(replied_id=reply_id, is_online=True).exclude(user_id=user_id).count()
    voted_count = TractateReplyVote.objects.using(settings.SLAVE_DB_NAME).\
        filter(tractate_id=reply_info.tractate_id, reply_id=reply_id, is_online=True).exclude(user_id=user_id).count()

    tractate_id = reply_info.tractate_id
    tractate_content = Tractate.objects.filter(id=tractate_id).first().content[:10]

    # 当前评论有被评论或被赞
    if replied_count or voted_count:
        return

   # 帖子新增的一级评论数(不包含自己的)
    additional_reply_count = TractateReply.objects.using(settings.SLAVE_DB_NAME).\
        filter(id__gt=reply_id, tractate_id=tractate_id, top_id=0, is_online=True).exclude(user_id=user_id).count()

    # 有新增评论,发送24小内新增评论总数
    if additional_reply_count:
        data = {
            "thing1": {
                "value": tractate_content
            },
            "thing2": {
                "value": "你评论的帖子,新增{reply_count}条吐槽,立即查看".format(reply_count=additional_reply_count)[:20]
            }
        }

        # 模板id
        template_id = settings.APPLET_SUBSCRIBE_MSG.get("vote", "")

        # 跳转页面
        page = '/packageBBS/pages/posts/main?tractate_id={tractate_id}&data_type=user_post&from={from_page}' \
               '&from_action={from_action}'\
            .format(
            tractate_id=reply_info.tractate_id,
            from_page=APPLET_PAGE_FROM.CARD,
            from_action=APPLET_SUBSCRIBE_MSG_TYPE.NEW_COMMENT
        )

    # 无新增评论,推送评论的帖子的相同标签下的其它内容详情页
    else:
        data = {
            "thing2": {
                "value": tractate_content
            },
            "thing4": {
                "value": "亲!你关注过的话题又有新内容啦>>"
            }
        }

        # 模板id
        template_id = settings.APPLET_SUBSCRIBE_MSG.get("recommend", "")

        # 获取帖子标签
        tag_id_list = TractateTag.objects.filter(tractate_id=tractate_id).values_list('tag_id', flat=True)
        tag_info = tag_manager.get_tags_info_by_ids(tag_id_list)
        tag_info_list = list(tag_info.values())
        tag_name = tag_info_list and tag_info_list[0] and tag_info_list[0].get("tag_name")

        if not tag_name:
            return

        # 调策略 获取相关内容
        def get_new_tractate_id(rpc_client, offset):
            res = rpc_client['doris/search/query_filter_tractate'](query=tag_name, size=1, offset=offset).unwrap()
            new_tractate_id = res and res.get("tractate_id_list") and res.get("tractate_id_list")[0]
            return new_tractate_id

        rpc_client = get_current_rpc_invoker()
        offset = random.randint(0, 200)
        origin_offset = offset
        new_tractate_id = None
        num = 6
        while not new_tractate_id and num :
            try:
                new_tractate_id = get_new_tractate_id(rpc_client, offset)
            except:
                new_tractate_id = None

            offset = origin_offset % num
            if num > 1:
                offset += random.randint(0, 10)
            num -= 1

        if not new_tractate_id:
            return

        # 跳转页面
        page = '/packageBBS/pages/posts/main?tractate_id={tractate_id}&data_type=user_post&from={from_page}' \
               '&from_action={from_action}' \
            .format(
            tractate_id=new_tractate_id,
            from_page=APPLET_PAGE_FROM.CARD,
            from_action=APPLET_SUBSCRIBE_MSG_TYPE.RELATED_CONTENT
        )

    # 发送小程序推送
    send_applet_subscribe_msg(user_id, template_id, data=data, page=page)


@shared_task
def refresh_pgc_info():
    """
    定时更新有pgc属性的帖子状态:1. 更新新加入的帖子;2. 更新 smart_rank 值
    直接做好排序,写入redis
    :return:
    """
    tractate_list = list(Tractate.objects.using(settings.SLAVE_DB_NAME).filter(
        pgc_type=PGC_TYPE.COMMUNITY,
        is_online=True,
        status=TRACTATE_STATUS.AUDIT_SUCCESS
    ).values_list(
        'id', 'create_time', 'pgc_type'
    ))
    tractate_info = {
        i[0]: {
            'tractate_id': i[0],
            'tractate_create_time': i[1],
            'pgc_type': i[2],
            'tractate_score': 0,
            'is_online': 1
        } for i in tractate_list
    }
    tractate_ids = [i[0] for i in tractate_list]

    start_pos, end_pos = 0, 0
    offset = 100
    smart_rank_info = []
    tractate_num = len(tractate_ids)
    while end_pos < tractate_num:
        end_pos = start_pos + offset
        if end_pos > tractate_num:
            end_pos = tractate_num
        smart_rank_info.extend(list(TractateScore.objects.filter(
            tractate_id__in=tractate_ids[start_pos:end_pos]
        ).values_list('tractate_id', 'tractate_score')))
        start_pos = end_pos

    for item in smart_rank_info:
        tractate_info[item[0]]['tractate_score'] = item[1]

    # 先按发布时间倒叙排列,发布时间相同再按smr分数从高到低排列
    scored_list = list(tractate_info.values())
    scored_list.sort(key=lambda x: (x['tractate_create_time'], x['tractate_score']), reverse=True)
    sorted_ids = json.dumps([i['tractate_id'] for i in scored_list])

    info_logger.info("更新pgc帖子缓存:{}".format(sorted_ids))
    pgc_tractate_cache.set('pgc_tractate_sorted_ids', sorted_ids, 86400)
    pgc_tractate_cache.set('pgc_tractate_update_time', int(time.time()), 86400)