# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function
import datetime
import json
import random
from operator import itemgetter

from django.conf import settings
from django.db.models import Count
from django.db.models import Q
from django.utils import timezone
from gm_types.gaia import DOCTOR_TYPE, COUPON_TYPES
from gm_types.gaia import TAG_TYPE, BENEFIT_TYPE, ORDER_STATUS_FILTER
from gm_types.doris import RANK_MODE
from gm_protocol import GmProtocol
from api.models import (
    Service,
    ServiceTag,
    ServiceAttr,
    ServiceAttrOption,
    ServiceItem,
    ServiceReserve,
    ServiceFavor,
    ServiceActivity,
    Special,
    City,
    SERVICE_ORDER_TYPE,
    RecommendDoctor,
    StockAlert,
    Order,
    AdvertiseManagement,
    Tag,
    CategoryGadget,
    ExtendTips,
    ServiceComment,
    CouponInfo,
)

from api.manager import coupon_manager
from api.manager import service_info_manager
from api.manager.service_info_manager import get_seckill_info_for_service_home_list, \
    get_toc_spu_info_list_mapping_by_spu_ids, add_double_eleven_image

from api.tool.datetime_tool import get_timestamp_or_none, get_strftime
from api.tool.doctor_tool import get_doctors_detail
from api.tool.geo_tool import get_location_tag_id_by_city_id, get_geo_related_tags
from api.tool.image_utils import get_full_path
from api.tool.service_extra_filters import ExtraFilterSet
from api.tool.service_tool import get_serviceitem_by_option_id
from api.tool.servicehome_tool import ServiceHomeLayout, SeckillDeploy, SceneButton, \
    SideButton
from api.tool.user_tool import get_user_city_tagid, is_new_service_user
from api.tool.user_tool import get_user_from_context
from api.tool.log_tool import logging_exception, info_logger

import rpc.db_opt
from hippo.models import Doctor, Hospital
from rpc.all import get_rpc_remote_invoker
from rpc.cache import tag_child_tags_cache, tag_top_sale_cache
from rpc.decorators import bind, cache_page, bind_context
from rpc.decorators import list_interface
from rpc.tool.dict_mixin import to_dict
from rpc.tool.error_code import gen, CODES
from rpc.tool.es_helpers import get_objects_from_queryset_and_pk_list, get_obj_map_from_queryset_and_pk_list
from rpc.tool.param_check import assert_uint
from rpc.context import get_rpc_remote_invoker
from hippo.tool.merchant_tool import doctor_merchant_map
from search.utils.service import filter_service, filter_service_v2
from search.utils.service import service_scatter_by_city, filter_special_promtions_new
from api.manager.service_info_manager import get_sku_id_to_sku_name_info

# from services.talos_service import get_service_related_diary_amount


__author__ = 'leaf'
__doc__ = '''福利相关内容，包括福利列表、福利详情；收藏和取消收藏福利；我收藏的福利列表.'''

gm = GmProtocol()


@bind('api/perf_test_only/sleep')
def perf_test_only_sleep(seconds=30):
    import gevent
    if seconds < 60:
        gevent.sleep(seconds)


@bind("api/services")
@bind("api/service/list")
@list_interface(offset_name='start_num', limit_name='count', element_model=Service,
                element_func_list=[Service.get_service_info])
def get_services(count=10, start_num=0, province_id=u'nationwide', channel_id=None, body_part_id=None,
                 sub_item_id=None, payment_type=u'', service_channel=u'', query=u'', doctor_id=None,
                 hospital_id=None):
    """福利列表
       count: 每次请求记录条数, 默认为10
       start_num: 起始条数, 默认为0
       channel_id: 频道筛选, 默认为所有频道
       body_part_id: 一级分类筛选, 默认为全部二级分类
       sub_item_id: 二级分类筛选, 默认为全部二级分类
       province_id: 省份筛选, 默认为全国
       payment_type: 付款模式筛选, 默认为所有  0 全款 1 预付款 2 免费 3 礼品换购
       service_channel: 福利频道筛选, 默认为所有  0 名医 1 特惠 2 免费
       doctor_id: 医生筛选
       hospital_id: 机构筛选
       query: 关键词搜索
    """
    result = []
    services = Service.objects.all()

    services = services.filter(is_online=True, doctor__is_online=True)

    gengmei_filter = Q(doctor__hospital__id=u'wanmeizhensuo')
    if province_id:
        if province_id == u'nationwide':
            pass
        else:
            province_filter = gengmei_filter | Q(doctor__hospital__city__province_id=province_id)
            services = services.filter(province_filter)

    payment_type = str(payment_type)
    if payment_type:
        services = services.filter(payment_type=payment_type)

    if channel_id:
        services = services.filter(bodypart_subitem__channel__id=channel_id)
    elif body_part_id:
        services = services.filter(bodypart_subitem__body__id=body_part_id)
    elif sub_item_id:
        services = services.filter(bodypart_subitem__id=sub_item_id)

    service_channel = str(service_channel)
    if service_channel:
        services = services.filter(channel=service_channel)
    if query:
        services = services.filter(Q(name__contains=query) | Q(short_description__contains=query))

    if doctor_id:
        services = services.filter(doctor__id=doctor_id)
    elif hospital_id:
        services = services.filter(doctor__hospital_id=hospital_id)

    # 福利列表：抢光福利需沉底
    order_sql = "case when(( ifnull(sell_num_limit,100)<=0) or api_service.end_time< now()) then 0 else 1 end"
    services = services.distinct().extra(
        select={"order_sql": order_sql}
    ).order_by('is_sink', '-order_sql', 'ordering')

    services = rpc.db_opt.add_related(
        services,
        Service.get_service_info,
    )

    for service in services[start_num:start_num + count]:
        try:
            info = service.get_service_info()
            from api.manager import coupon_manager
            info["coupon_gifts"] = coupon_manager.get_top_coupon_gift_infos(service_id=service.id)
            result.append(info)
        except Exception as e:
            logging_exception()
            continue

    return result


@bind('api/service/multi_extend_tips')
def get_multi_service_extend_tips(id_list=[]):
    try:
        tips = ExtendTips.objects.filter(id__in=id_list).values('id', 'tip_name')
    except:
        tips = []

    result = [item for item in tips]
    return result


@bind('api/service/multi_detail')
def service_multiget_detail(id_list=[], filters={}, is_flag=False):
    """get service info with service id_list"""

    services = Service.objects.filter(id__in=id_list, is_online=True)
    service_info_list = []

    for service_id in id_list:
        for service in services:
            if service_id == service.id:
                info = service.get_service_info()
                info["coupon_gifts"] = coupon_manager.get_top_coupon_gift_infos(service_id=service.id)
                service_info_list.append(info)

    if is_flag:  # 医生和机构返回格式不同
        service_filter_result = filter_service(filters=filters)
        rank_mode = service_filter_result['rank_mode']
        result = {
            'total_count': service_filter_result['total_count'],
            'service_list': service_info_list,
            'rank_mode': rank_mode
        }
    else:
        result = service_info_list

    return result


def get_service_multiget_zone(id_list=None):
    """获取活动美购分区信息
    """
    if id_list is None:
        id_list = []
    service_info_list = get_toc_spu_info_list_mapping_by_spu_ids(id_list)
    for info in service_info_list.values():
        # 兼容字段
        info['id'] = info['service_id']
        info['title'] = info['service_name']
        info['image'] = get_full_path(info['service_image'], '-half')
        info['market_price'] = info.get('default_price', 0)
        info['gengmei_price'] = info.get('groupbuy_price', 0) or info.get('seckill_price', 0) or \
                                info.get('gengmei_price', 0)
        info['is_price_range'] = info.get('is_price_range', True)
        # 兼容字段
        info['label'] = settings.PROMOTION_ZONE_LABEL_PIC

    result = []
    for id in id_list:
        if str(id) in service_info_list:
            result.append(service_info_list[str(id)])
    return result


@bind_context('api/service/get_toc_spu_sell_amount_display_by_spu_ids')
def api_service_get_toc_spu_sell_amount_display_by_spu_ids(
        ctx,
        service_ids=None,
):
    """

    :param service_ids: [ 2, -1, 2, "1", 3 ] 假设 id为3 的数据不存在
    :return: { "2": 0, "1": 66 }  注意只有key部分的id变成了str
    """
    result = {}
    if service_ids:
        all_service_models = list(Service.objects.filter(id__in=service_ids).only('id', 'fake_sold_num'))
        result = {str(s.id): s.sell_amount_display if s.sell_amount_display > 0 else 0 for s in all_service_models}

    return result


@bind_context('api/service/get_toc_spu_info_list_mapping_by_spu_ids')
def api_service_get_toc_spu_info_list_mapping_by_spu_ids(
        ctx,
        service_ids=[],
):
    """

    :param service_ids: [ 2, -1, 2, "1", 3 ] 假设 id为3 的数据不存在
    :param get_diary_num:
    :param get_coupon_info:
    :return: { "2": { ...}, "1": {} }  注意只有key部分的id变成了str
    """
    result = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
        service_ids=service_ids
    )

    return result


@bind_context('api/service/get_toc_sku_info_list_mapping_by_sku_ids')
def api_service_get_toc_sku_info_list_mapping_by_sku_ids(
        ctx,
        service_item_ids=[],
):
    """

    :param service_item_ids: [ 2, -1, 2, "1", 3 ] 假设 id为3 的数据不存在
    :param get_diary_num:
    :param get_coupon_info:
    :return: { "2": { ...}, "1": {} }  注意只有key部分的id变成了str
    """
    result = service_info_manager.get_toc_sku_info_list_mapping_by_sku_ids(
        service_item_ids=service_item_ids,
    )
    return result


@bind_context('api/service/sku_price_info_cache_warming')
def api_service_sku_price_info_cache_warming(
        ctx,
        service_item_ids=None,
):
    # 又是hack...
    service_info_manager.pre_warming_get_service_sku_price_info(
        service_item_ids=service_item_ids,
        now=datetime.datetime.now()
    )
    return "DONE!"


@bind_context('api/service/filter_v2/seckill_list_for_special')
@list_interface(offset_name='offset', limit_name='count')
def api_filter_service_v2_seckill_list_for_special(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        service_id=None,
        use_tagv3=False,
):
    result = service_info_manager.filter_service_v2_seckill_list_for_special(
        ctx=ctx,
        order_by=order_by,
        filters=filters,
        extra_filters=extra_filters,
        offset=offset,
        count=count,
        sort_params=sort_params,
        current_city_id=current_city_id,
        use_tagv3=use_tagv3,
    )

    return result


@bind_context('api/service/filter_v2')
@list_interface(offset_name='offset', limit_name='count',
                element_model=Service, element_func_list=[Service.get_service_info])
def api_filter_service_v2(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        get_diary_num=False,
        simple_data_for_list=False,  # 应该已经被废弃了
        use_new_service_info_data=False,
        coupon_info_id=None,
        use_tagv3=False,
):
    special_id = filters.get('special_id', None) if filters else None
    user = ctx.session.user

    couponinfo_id = ""
    coupon_desc = ""

    if coupon_info_id:
        couponinfo = CouponInfo.objects.select_related('coupon').filter(id=coupon_info_id).first()
        if couponinfo and couponinfo.user == user:
            couponinfo_id = str(coupon_info_id)

            c = couponinfo.coupon

            # 7780添加
            coupon_desc_1 = ""
            benefit_type = c.benefit_type
            if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                coupon_desc_1 = "以下商品可使用直减{}元美券".format(c.value)
            elif benefit_type == BENEFIT_TYPE.MANJIAN:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.value)
            elif benefit_type == BENEFIT_TYPE.DISCOUNT:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.upper_limit)

            # 7780 折扣券注释
            # coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(
            #    c.prepay_threshold, c.value) if c.has_threshold else "以下商品可使用直减{}元美券".format(c.value)
            coupon_desc_2 = "预付款抵扣" if c.coupon_type == COUPON_TYPES.PLATFORM else "尾款抵扣"
            coupon_desc = "{}（{}）".format(coupon_desc_1, coupon_desc_2)

            spids = couponinfo.coupon_restrict_special_ids
            doctor_ids = couponinfo.coupon_restrict_doctor_ids
            sku_ids = couponinfo.coupon_restrict_sku_ids
            service_ids = set(ServiceItem.objects.filter(
                parent_id=0, id__in=sku_ids, is_delete=False
            ).values_list('service_id', flat=True)) if sku_ids else []

            if spids:
                filters['special_ids_and'] = list(spids)

            if doctor_ids:
                filters['doctor_ids'] = list(doctor_ids)

            if service_ids:
                filters['service_ids'] = list(service_ids)

    filter_result = api_filter_service(
        ctx=ctx,
        order_by=order_by,
        filters=filters,
        extra_filters=extra_filters,
        offset=offset,
        count=count,
        sort_params=sort_params,
        current_city_id=current_city_id,
        use_tagv3=use_tagv3,
    )

    service_id_list = filter_result['service_id_list']
    need_adver_info = filter_result['need_adver_info']
    service_adver_id_map = filter_result['service_adver_id_map']
    total_count = filter_result['total_count']
    rank_mode = filter_result['rank_mode']

    result = get_result_for_service(
        user=user, special_id=special_id,
        get_diary_num=get_diary_num,
        service_id_list=service_id_list,
        need_adver_info=need_adver_info,
        service_adver_id_map=service_adver_id_map,
        total_count=total_count,
        use_new_service_info_data=use_new_service_info_data,
    )

    result['coupon_info_id'] = couponinfo_id
    result['coupon_desc'] = coupon_desc
    result['rank_mode'] = rank_mode

    return result


@bind_context('api/service/filter_v3')
@list_interface(offset_name='offset', limit_name='count',
                element_model=Service, element_func_list=[Service.get_service_info])
def api_filter_service_v3(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        get_diary_num=False,
        simple_data_for_list=False,  # 应该已经被废弃了
        use_new_service_info_data=False,
        coupon_info_id=None,
        use_tagv3=False,
):
    special_id = filters.get('special_id', None) if filters else None
    user = ctx.session.user

    couponinfo_id = ""
    coupon_desc = ""

    if coupon_info_id:
        couponinfo = CouponInfo.objects.select_related('coupon').filter(id=coupon_info_id).first()
        if couponinfo and couponinfo.user == user:
            couponinfo_id = str(coupon_info_id)

            c = couponinfo.coupon

            # 7780添加
            coupon_desc_1 = ""
            benefit_type = c.benefit_type
            if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                coupon_desc_1 = "以下商品可使用直减{}元美券".format(c.value)
            elif benefit_type == BENEFIT_TYPE.MANJIAN:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.value)
            elif benefit_type == BENEFIT_TYPE.DISCOUNT:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.upper_limit)

            # 7780 折扣券注释
            # coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(
            #    c.prepay_threshold, c.value) if c.has_threshold else "以下商品可使用直减{}元美券".format(c.value)
            coupon_desc_2 = "预付款抵扣" if c.coupon_type == COUPON_TYPES.PLATFORM else "尾款抵扣"
            coupon_desc = "{}（{}）".format(coupon_desc_1, coupon_desc_2)

            spids = couponinfo.coupon_restrict_special_ids
            doctor_ids = couponinfo.coupon_restrict_doctor_ids
            sku_ids = couponinfo.coupon_restrict_sku_ids
            service_ids = set(ServiceItem.objects.filter(
                parent_id=0, id__in=sku_ids, is_delete=False
            ).values_list('service_id', flat=True)) if sku_ids else []

            if spids:
                filters['special_ids_and'] = list(spids)

            if doctor_ids:
                filters['doctor_ids'] = list(doctor_ids)

            if service_ids:
                filters['service_ids'] = list(service_ids)
    try:
        filter_result = api_filter_service(
            ctx=ctx,
            order_by=order_by,
            filters=filters,
            extra_filters=extra_filters,
            offset=offset,
            count=count,
            sort_params=sort_params,
            current_city_id=current_city_id,
            use_tagv3=use_tagv3,
        )
    except:
        import traceback
        info_logger.info('api_filter_service_v3------error--{}'.format(traceback.format_exc()))
        logging_exception()
    info_logger.info('api_filter_service_v3-filter_result--{}--------------'.format(filter_result))

    service_id_list = filter_result['service_id_list']
    need_adver_info = filter_result['need_adver_info']
    service_adver_id_map = filter_result['service_adver_id_map']
    total_count = filter_result['total_count']
    rank_mode = filter_result['rank_mode']

    result = {
        'total_count': total_count,
        'service_list': service_id_list,
    }

    result['coupon_info_id'] = couponinfo_id
    result['coupon_desc'] = coupon_desc
    result['rank_mode'] = rank_mode

    return result
#         ctx,
#         order_by=SERVICE_ORDER_TYPE.DEFAULT,
#         filters=None,
#         extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
#         offset=0, count=10,
#         sort_params=None,
#         current_city_id=None,
#         get_diary_num=False,
#         simple_data_for_list=False,  # 应该已经被废弃了
#         use_new_service_info_data=False,
#         coupon_info_id=None,
#         use_tagv3=False,
# ):
#     _, province_tag_id, city_tag_id = get_geo_related_tags(current_city_id)
#     if not city_tag_id:
#         return {}
#     if filters:
#         filters['advertise_user_city_tag_id'] = city_tag_id
#     # special_id = filters.get('special_id', None) if filters else None
#     user = ctx.session.user
#
#     couponinfo_id = ""
#     coupon_desc = ""
#
#     if coupon_info_id:
#         couponinfo = CouponInfo.objects.select_related('coupon').filter(id=coupon_info_id).first()
#         if couponinfo and couponinfo.user == user:
#             couponinfo_id = str(coupon_info_id)
#
#             c = couponinfo.coupon
#
#             # 7780添加
#             coupon_desc_1 = ""
#             benefit_type = c.benefit_type
#             if benefit_type == BENEFIT_TYPE.ZHIJIAN:
#                 coupon_desc_1 = "以下商品可使用直减{}元美券".format(c.value)
#             elif benefit_type == BENEFIT_TYPE.MANJIAN:
#                 coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.value)
#             elif benefit_type == BENEFIT_TYPE.DISCOUNT:
#                 coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.upper_limit)
#
#             # 7780 折扣券注释
#             # coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(
#             #    c.prepay_threshold, c.value) if c.has_threshold else "以下商品可使用直减{}元美券".format(c.value)
#             coupon_desc_2 = "预付款抵扣" if c.coupon_type == COUPON_TYPES.PLATFORM else "尾款抵扣"
#             coupon_desc = "{}（{}）".format(coupon_desc_1, coupon_desc_2)
#
#             spids = couponinfo.coupon_restrict_special_ids
#             doctor_ids = couponinfo.coupon_restrict_doctor_ids
#             sku_ids = couponinfo.coupon_restrict_sku_ids
#             service_ids = set(ServiceItem.objects.filter(
#                 parent_id=0, id__in=sku_ids, is_delete=False
#             ).values_list('service_id', flat=True)) if sku_ids else []
#
#             if spids:
#                 filters['special_ids_and'] = list(spids)
#
#             if doctor_ids:
#                 filters['doctor_ids'] = list(doctor_ids)
#
#             if service_ids:
#                 filters['service_ids'] = list(service_ids)
#
#     filter_result = api_filter_service(
#         ctx=ctx,
#         order_by=order_by,
#         filters=filters,
#         extra_filters=extra_filters,
#         offset=offset,
#         count=count,
#         sort_params=sort_params,
#         current_city_id=current_city_id,
#         use_tagv3=use_tagv3,
#     )
#
#     service_id_list = filter_result['service_id_list']
#     need_adver_info = filter_result['need_adver_info']
#     service_adver_id_map = filter_result['service_adver_id_map']
#     total_count = filter_result['total_count']
#     rank_mode = filter_result['rank_mode']
#
#     result = {
#         'total_count':total_count,
#         'service_list':service_id_list
#     }
#
#     # result = get_result_for_service(
#     #     user=user, special_id=special_id,
#     #     get_diary_num=get_diary_num,
#     #     service_id_list=service_id_list,
#     #     need_adver_info=need_adver_info,
#     #     service_adver_id_map=service_adver_id_map,
#     #     total_count=total_count,
#     #     use_new_service_info_data=use_new_service_info_data,
#     # )
#
#     result['coupon_info_id'] = couponinfo_id
#     result['coupon_desc'] = coupon_desc
#     result['rank_mode'] = rank_mode
#
#     return result


@bind_context("api/service/filter", deprecated="please use api/service/filter_v2")
@list_interface(offset_name='offset', limit_name='count',
                element_model=Service, element_func_list=[Service.get_service_info])
def api_filter_service_v1(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        offset=0, count=10,
        sort_params=None,
        current_city_id=None
):
    """
    this interface is DEPRECATED,
    use v2 instead
    """
    special_id = filters.get('special_id', None)
    user = ctx.session.user

    get_diary_num = False

    filter_result = api_filter_service(
        ctx=ctx,
        order_by=order_by,
        filters=filters,
        offset=offset,
        count=count,
        sort_params=sort_params,
        current_city_id=current_city_id
    )

    service_id_list = filter_result['service_id_list']
    need_adver_info = filter_result['need_adver_info']
    service_adver_id_map = filter_result['service_adver_id_map']
    total_count = filter_result['total_count']

    result = get_result_for_service(
        user=user, special_id=special_id,
        get_diary_num=get_diary_num,
        service_id_list=service_id_list,
        need_adver_info=need_adver_info,
        service_adver_id_map=service_adver_id_map,
        total_count=total_count,
        use_new_service_info_data=False,
    )

    return result['service_list']


def api_filter_service_extract_params(user, filters, extra_filters, sort_params, current_city_id):
    filters = filters or {}
    sort_params = sort_params or {}
    extra_filters = extra_filters or ()

    user_city_tag_id = get_user_city_tagid(user.id)
    if user_city_tag_id is not None:
        user_city_tag_id = assert_uint(user_city_tag_id)

    country_tagid, city_tagid = get_location_tag_id_by_city_id(current_city_id)
    user_city_tag_id = city_tagid and city_tagid or user_city_tag_id
    if user_city_tag_id is not None:
        sort_params['user_city_tag_id'] = user_city_tag_id

    # 2016.04.12 不再使用本国id作为筛选  人在中国 看韩国专场时 美购列表为空
    # if 'area_tag_id' not in filters and country_tagid:
    #     filters['area_tag_id'] = country_tagid

    efs = ExtraFilterSet()
    efs.add_key_string_list(extra_filters)
    efs.make_query_inplace(filters)  # will modify filters

    return {
        'filters': filters,
        'sort_params': sort_params,
    }


def api_filter_service(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=None,  # http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        use_tagv3=False,
):
    user = ctx.session.user
    offset = assert_uint(offset, 0)
    count = assert_uint(count, 0)

    params = api_filter_service_extract_params(
        user=user, filters=filters, extra_filters=extra_filters,
        sort_params=sort_params, current_city_id=current_city_id)
    sort_params = params['sort_params']
    filters = params['filters']

    # 如果是广告筛选
    if 'adver_position' in filters:
        need_adver_info = True
        sort_params['adver_position'] = filters['adver_position']
        if 'adver_word' in filters:
            sort_params['adver_word'] = filters['adver_word']
    else:
        need_adver_info = False

    service_filter_result = filter_service(
        offset=offset,
        size=count,
        sort_type=order_by,
        filters=filters,
        sort_params=sort_params,
        use_tagv3=use_tagv3,
    )
    rank_mode = service_filter_result['rank_mode']

    service_adver_id_map = {}
    service_id_list = service_filter_result['service_ids']
    # 如果是广告筛选
    if need_adver_info:
        service_adver_id_map = service_filter_result['adver_id_map']

    return {'service_id_list': service_id_list, 'need_adver_info': need_adver_info,
            'service_adver_id_map': service_adver_id_map,
            'total_count': service_filter_result['total_count'],
            'rank_mode': rank_mode}


def get_result_for_service(
        user, special_id, get_diary_num,
        service_id_list, need_adver_info, service_adver_id_map, total_count,
        use_new_service_info_data
):
    service_map_ad = {}
    if len(service_adver_id_map) > 0:
        for k_serviceid, v_adid in service_adver_id_map.items():
            ad_subject_info = AdvertiseManagement.objects.filter(id=v_adid).first()
            if ad_subject_info:
                service_map_ad.update({k_serviceid: {
                    'business_type': ad_subject_info.business_type,
                    'ordering': ad_subject_info.ordering
                }})

    if use_new_service_info_data:
        sid_to_service_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
            service_ids=service_id_list,
        )
        service_info_list = []
        for sid in service_id_list:
            if str(sid) in sid_to_service_info:
                innser_serive = sid_to_service_info[str(sid)]
                if len(service_map_ad) > 0:
                    innser_serive.update(service_map_ad.get(sid, {}))
                service_info_list.append(innser_serive)

    else:
        service_queryset = rpc.db_opt.add_related(
            Service.objects.all(),
            Service.get_service_info
        )

        service_list = get_objects_from_queryset_and_pk_list(
            service_queryset, service_id_list
        )

        service_info_list = []
        for service in service_list:
            if need_adver_info:
                advertise_id = service_adver_id_map[service.id] if service.id in service_adver_id_map else None
            else:
                advertise_id = None
            info = service.get_service_info(
                user=user, special_id=special_id, get_diary_num=get_diary_num,
                advertise_id=advertise_id)

            from api.manager import coupon_manager
            info["coupon_gifts"] = coupon_manager.get_top_coupon_gift_infos(service_id=service.id)
            service_info_list.append(info)

    result = {
        'total_count': total_count,
        'service_list': service_info_list,
    }

    return result


# @bind_context('api/service/filter_group_by')
# def api_filter_service_grpby(
#         ctx,
#         order_by=SERVICE_ORDER_TYPE.DEFAULT,
#         filters=None,
#         extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
#         offset=0, count=10,  # CAUTION: offset and count params here are about the "group by" field, not service
#         sort_params=None,
#         current_city_id=None,
#         group_by='doctor_id'
# ):
#     user = ctx.session.user
#     offset = assert_uint(offset, 0)
#     count = assert_uint(count, 0)
#
#     params = api_filter_service_extract_params(
#         user=user, filters=filters, extra_filters=extra_filters,
#         sort_params=sort_params, current_city_id=current_city_id)
#     filters = params['filters']
#     sort_params = params['sort_params']
#
#     service_filter_result = filter_service(
#         offset=offset,
#         size=count,
#         sort_type=order_by,
#         group_by=group_by,
#         filters=filters,
#         sort_params=sort_params)
#     rank_mode = service_filter_result['rank_mode']
#
#     service_id_list = []
#     for grp_service in service_filter_result['service_ids']:
#         # grp_service={'group_by_id':xx, 'service_ids':[]}
#         service_id_list += grp_service['service_ids']
#
#     service_queryset = rpc.db_opt.add_related(
#         Service.objects.all(),
#         Service.get_service_info
#     )
#
#     special_id = filters.get('special_id', None)
#     service_map = get_obj_map_from_queryset_and_pk_list(service_queryset, service_id_list)
#     doctor_ids = []
#
#     for grp_service in service_filter_result['service_ids']:
#         grp_service['service_list'] = []
#         for service_id in grp_service['service_ids']:
#             if service_id in service_map:
#                 grp_service['service_list'].append(
#                     service_map[service_id].get_service_info(user=user, special_id=special_id))
#         del grp_service['service_ids']
#         doctor_ids.append(grp_service['doctor_id'])
#
#     service_info_list = service_filter_result['service_ids']
#
#     result = {
#         'doctor_info': get_doctors_detail(doctor_ids)['doctor_info'],
#         'service_list': service_info_list,
#         'rank_mode': rank_mode
#     }
#
#     return result

@bind("api/service/richtext")
def get_service_richtext(service_id):
    """专门为优化美购详情页的接口, 单独获取美购富文本，提高效率"""
    try:
        richtext = Service.objects.only('photo_details_doctor', 'photo_details_operate').get(id=service_id).rich_text
        return richtext
    except:
        gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service")
@bind_context("api/service/get")
def get_service_info(ctx, id=None, is_new=False):
    """ 福利
        id: 福利ID
    """
    user = get_user_from_context(ctx)

    try:
        service = Service.objects.get(id=int(id))
        result = service.get_service_detail(user, is_new=is_new)
        return result
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service_v2")
def get_service_info_v2(ctx, id=None):
    """
    id: service_id
    """

    try:
        service = Service.objects.get(id=int(id))
        data = dict(wiki=service.wiki and {'id': service.wiki_id}, image_detail=service.image_detail,
                    richtext=service.rich_text,
                    extra_prompt={'has_extra_expense': service.have_extra_pay, 'introduction': service.extra_pay_info},
                    disclaimer=service.disclaimer, buy_notice=service.get_buy_notice,
                    services_count=service.get_all_services_count,
                    tags=service.get_tags())
        return data
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/wiki")
def get_service_wiki(ctx, service_id):
    """
    获取美购关联的百科详情
    :param ctx:
    :param service_id: 美购id
    :return:
    """
    try:
        from wiki.models import ItemTag, CollectTag, CollectItem

        service_tags = ServiceTag.objects.filter(
            service_id=int(service_id)
        ).values_list("tag_id", flat=True)

        wiki_items = ItemTag.objects.filter(
            tag_id__in=service_tags,
            item__is_online=True
        ).select_related('item')

        collect_ids = CollectTag.objects.filter(
            tag_id__in=service_tags
        ).values_list('collect_id', flat=True)
        wiki_collect = CollectItem.objects.filter(
            collect_id__in=collect_ids,
            is_online=True
        ).select_related('item')

        result = {}
        for wiki in wiki_items:
            result[wiki.item.id] = {
                'id': wiki.item.id,
                'name': wiki.item.name,
                'description': wiki.item.description,
                'icon': wiki.item.icon,
                'other_name': wiki.item.other_name,
                'is_hot': wiki.item.is_hot,
            }

        for wiki in wiki_collect:
            result[wiki.item.id] = {
                'id': wiki.item.id,
                'name': wiki.item.name,
                'description': wiki.item.description,
                'icon': wiki.item.icon,
                'other_name': wiki.item.other_name,
                'is_hot': wiki.item.is_hot,
            }

        return {
            'data': result.values()
        }

    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/services/batch_get")
def batch_get_services_info(ctx, ids=None, is_new=False):
    """ 美购
        ids: 美购ID列表
    """
    user = get_user_from_context(ctx)

    try:
        services = Service.objects.filter(id__in=ids)
        result = {}
        for service in services:
            service_id = service.id
            result[service_id] = service.get_service_detail(user, is_new=is_new)
        return result
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/item/create")
@bind_context("api/service/item_create")
def create_service_item(ctx, service_id, item_key, price):
    try:
        service = Service.objects.get(pk=service_id)
        if not service.is_multiattribute:
            return gen(CODES.OPERATION_NOT_SUPPORTED)
        try:
            item = ServiceItem.objects.get(service=service, key=item_key)
            return gen(CODES.SERVICE_EXIST)
        except ServiceItem.DoesNotExist:
            item = ServiceItem.objects.create(service=service, key=item_key, price=price)
            return dict(item_id=item.id)
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/item")
def get_service_item(ctx, item_key):
    try:
        item = ServiceItem.objects.get(key=item_key)
        return item.service_item_data()
    except ServiceItem.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/item/id")
def get_service_item_by_id(ctx, item_key, service_id):
    try:
        item = get_serviceitem_by_option_id(item_key, service_id)
        if not item:
            return gen(CODES.SERVICE_NOT_EXSIT)
        return item.service_item_data()
    except ServiceItem.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/items", login_required=False)
def get_service_items(ctx, item_keys):
    try:
        items = []
        for object in item_keys:
            try:
                item = ServiceItem.objects.get(key=object["key"])
                object.update(item.service_item_data())
                items.append(object)
            except ServiceItem.DoesNotExist:
                pass
        return dict(items=items)
    except ServiceItem.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/items/update_or_create")
@bind_context("api/service_items/update_or_create")
def update_or_create_service_item(ctx, service_id, items):
    try:
        service = Service.objects.get(pk=service_id)
        if not service.is_multiattribute:
            return gen(CODES.OPERATION_NOT_SUPPORTED)
        obj_items = set()
        for object in items:
            item, created = ServiceItem.objects.get_or_create(service=service, key=object["key"])
            item.gengmei_price = object.get("price", item.gengmei_price)
            item.discount = object.get("discount", item.discount)
            item.pre_payment_price = object.get("pre_payment_price", item.pre_payment_price)
            item.original_price = object.get("original_price", item.original_price)
            item.gengmei_price = object.get('gengmei_price', item.gengmei_price)
            item.cash_back_fee = object.get("cash_back_fee", item.cash_back_fee)
            item.cash_back_rate = object.get("cash_back_rate", item.cash_back_rate)
            item.self_support_discount = object.get("self_support_discount", item.self_support_discount)
            item.points_deduction_percent = object.get("points_deduction_percent", item.points_deduction_percent)

            item.save()
            obj_items.add(item)

        for del_item in (set(service.items.all()) - obj_items):
            del_item.delete()
        return None
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/item/update")
@bind_context("api/service_item/update")
def update_service_item(ctx, item_key, price):
    try:
        item = ServiceItem.objects.get(key=item_key)
        item.gengmei_price = price
        item.save()
        return None
    except ServiceItem.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/attr/create")
@bind_context("api/service_attr/create")
def create_service_attr(ctx, service_id, attr_name):
    try:
        service = Service.objects.get(pk=service_id)
        if not service.is_multiattribute:
            return gen(CODES.OPERATION_NOT_SUPPORTED)
        try:
            ServiceAttr.objects.get(service=service, name=attr_name)
            return gen(CODES.SERVICE_ATTR_EXIST)
        except ServiceAttr.DoesNotExist:
            attr = ServiceAttr.objects.create(service=service, name=attr_name)
            return dict(attr_id=attr.id, attr_name=attr.name)
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/attr/delete")
@bind_context("api/service_attr/delete")
def delete_service_attr(ctx, attr_id):
    try:
        attr = ServiceAttr.objects.get(pk=attr_id)
        attr.delete()
        ServiceItem.objects.filter(parent_id=0, service=attr.service).delete()
        return None
    except ServiceAttr.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/attr/update")
@bind_context("api/service_attr/update")
def update_service_attr(ctx, attr_id, attr_name):
    try:
        attr = ServiceAttr.objects.get(pk=attr_id)
        attr.name = attr_name
        attr.save()
        return dict(attr_id=attr.id, name=attr.name)
    except ServiceAttr.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/option/create")
@bind_context("api/service_option/create")
def create_service_option(ctx, attr_id, option_name):
    try:
        attr = ServiceAttr.objects.get(pk=attr_id)
        try:
            ServiceAttrOption.objects.get(service_attr=attr, name=option_name)
            return gen(CODES.SERVICE_OPTION_EXIST)
        except ServiceAttrOption.DoesNotExist:
            option = ServiceAttrOption.objects.create(service_attr=attr, name=option_name)
            return dict(option_id=option.id, option_name=option.name)
    except ServiceAttr.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/option/delete")
@bind_context("api/service_option/delete")
def delete_service_option(ctx, option_id):
    try:
        option = ServiceAttrOption.objects.get(pk=option_id)
        option.delete()
        ServiceItem.objects.filter(parent_id=0, service=option.service_attr.service).delete()
        return None
    except ServiceAttrOption.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/option/update")
@bind_context("api/service_option/update")
def update_service_option(ctx, option_id, option_name):
    try:
        option = ServiceAttrOption.objects.get(pk=option_id)
        option.name = option_name
        option.save()
        return None
    except ServiceAttrOption.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context("api/service/reserve", login_required=True)
def reserve_service(ctx, id=None):
    """ 福利预定
        id: 福利ID
    """
    user = get_user_from_context(ctx)

    try:
        service = Service.objects.get(id=int(id))
        reserve, created = ServiceReserve.objects.get_or_create(service=service, user=user)
        if created:
            reserve.save()
        else:
            if reserve.stat != '1':
                reserve.stat = '1'
                reserve.save()
        return None
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind_context('api/service/favorite/create', login_required=True)
@bind_context('api/service_favorite/create', login_required=True)
def api_service_favorite_create(ctx, service_id):
    """
    收藏一个福利
    """
    user = get_user_from_context(ctx)
    try:
        service = Service.objects.get(pk=service_id)
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_EXSIT)

    sf, _ = ServiceFavor.objects.get_or_create(user=user, service=service)
    if sf.is_deleted:
        sf.is_deleted = False
        sf.save()

    return list(service.tags.filter(is_online=True).values_list('id', flat=True))


@bind_context('api/service/favorite/delete', login_required=True)
@bind_context('api/service_favorite/delete', login_required=True)
def api_service_favorite_delete(ctx, service_id):
    """
    取消收藏福利
    """
    user = get_user_from_context(ctx)
    service = Service.objects.get(pk=service_id)
    ServiceFavor.objects.filter(user=user, service=service).update(is_deleted=True)


@bind('api/user_follow/service_count')
def get_user_follow_service_count(user_id):
    if not user_id:
        return gen(CODES.LOGIN_REQUIRED)
    service_ids = list(ServiceFavor.objects.filter(
        user_id=user_id, is_deleted=False).values_list('service_id', flat=True))
    count = Service.objects.filter(id__in=service_ids, is_online=True).count()
    return {
        'count': count
    }


@bind_context('api/service/favorite/get', login_required=True)
@list_interface(offset_name='start_num', limit_name='count', element_model=Service,
                element_func_list=[Service.get_service_info])
def api_service_favorite_get(ctx, count=10, start_num=0, use_new_service_info_data=False):
    result = []
    user = get_user_from_context(ctx)
    favors = ServiceFavor.objects.select_related('service')
    favors = favors.filter(
        user=user, is_deleted=False,
        service__is_online=True,
        service__doctor__is_online=True
    )
    favors = favors.order_by('-id')
    favors = favors[start_num: start_num + count]

    favors = rpc.db_opt.add_related(
        favors,
        Service.get_service_info,
        prefix='service__',
    )

    if use_new_service_info_data:
        sids = [f.service_id for f in favors]

        sid_to_service_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
            service_ids=sids,
        )
        for favor in favors:
            sid = str(favor.service_id)
            if sid in sid_to_service_info:
                info = sid_to_service_info[sid]
                result.append(info)
    else:
        for favor in favors:
            info = favor.service.get_service_info()
            from api.manager import coupon_manager
            info["coupon_gifts"] = coupon_manager.get_top_coupon_gift_infos(service_id=info['service_id'])
            result.append(info)

    return result


@bind_context('api/service_home/setting')
def api_service_home_setting(ctx):
    return ServiceHomeLayout.get()


@bind_context('api/service_home/activities')
def api_service_home_activities(ctx):
    """get service home activities."""
    activities = []
    activities.append(ServiceActivity.left())
    activities.append(ServiceActivity.top_right())
    activities.append(ServiceActivity.bottom_right())
    return activities


@bind('api/service_home/seckill_status')
def servicehome_seckill_status():
    """get seckill info for service index page.

    .. versionadded:: 5.5
    """
    return Special.get_seckill_info()


@bind('api/service_home/recommend_condition')
@cache_page(120)
def get_servicehome_recommend_condition(city_id=None):
    """所长美购推选 都属于同一个special_type
        获取美购首页 刷选所长推荐 所需要的条件数据
    """
    try:

        result = {
            'city_tag_id': None,
            'city_province_tag_id': None,
            'special_id': 232
        }

        _, province_tag_id, city_tag_id = get_geo_related_tags(city_id)
        result['city_tag_id'] = city_tag_id
        result['city_province_tag_id'] = province_tag_id
        return result
    except:
        return None


@bind_context('api/service/recommenddoctor', login_required=True)
def recommend_doctor(ctx, diary_id=None, item_name='', doctor_name='', hospital_name=''):
    """
    NOTE:
        美购不存在时，记录，然后当上线时，会推荐美购
        :param ctx:
        :return:
    """
    if not diary_id:
        return gen(CODES.DIARY_NOT_FOUND)

    user = get_user_from_context(ctx)
    if not user:
        return gen(CODES.USER_NOT_FOUND)

    diaries = ctx.rpc_remote['diary/get_num_and_cover_by_ids'](ids=[diary_id])
    if not diaries.unwrap():
        return gen(CODES.DIARY_NOT_FOUND)

    try:
        recommend_info = RecommendDoctor.objects.get(person=user.person, diary_id=diary_id)
    except RecommendDoctor.DoesNotExist:
        recommend_info = RecommendDoctor()

        recommend_info.person = user.person
        recommend_info.diary_id = diary_id
        recommend_info.raw_doctor = doctor_name
        recommend_info.raw_hospital = hospital_name
        recommend_info.item_name = item_name
        recommend_info.recommend_time = datetime.datetime.now()
        recommend_info.connected = False

        recommend_info.save()
        StockAlert.insert(recommend_info)

    return True


# todo 无调用 180315
# @bind('api/service/diary_amount')
# def api_get_service_related_diary_amount(service_id=None):
#     """
#     获取美购相关的日记本数量
#     """
#     if not service_id:
#         return gen(CODES.SERVICE_NOT_FOUND)
#
#     try:
#         Service.objects.get(pk=service_id)
#     except Service.DoesNotExist:
#         return gen(CODES.SERVICE_NOT_FOUND)
#
#     return get_service_related_diary_amount(service_id)


@bind('api/service/top_sale/with_tag')
@cache_page(120)
def get_top_sale(tag_id=None):
    DAYS_NUM = [1, 7, 30]
    result = {}

    def check_list(tag_list):
        result = []
        for tag in tag_list:
            if isinstance(tag, basestring):
                result.append(int(tag))
            elif isinstance(tag, (int, long)):
                result.append(tag)
        return result

    def top_tag_service(tag_top_sale_cache, days, tag_id=None):
        if tag_id:
            tag_key = 'tag_service:{tag_id}:{days}'.format(tag_id=tag_id, days=days)
        else:
            tag_key = 'no_tag_service:{days}'.format(days=days)
        cache_info = tag_top_sale_cache.get(tag_key)

        if not cache_info:
            return []

        try:
            cache_info = json.loads(cache_info)
        except:
            return []

        if cache_info.get('tag_service'):
            top_services = cache_info['tag_service']
        else:
            top_services = []

        return top_services

    if tag_id:
        if isinstance(tag_id, list) and tag_id:
            tag_ids = check_list(tag_id)
        elif isinstance(tag_id, basestring):
            tag_ids = [int(tag_id)]
        elif isinstance(tag_id, (int, long)):
            tag_ids = [tag_id]
    else:
        tag_ids = []

    for days in DAYS_NUM:
        if tag_ids:
            all_tag_service = []
            for tag_id in tag_ids:
                top_services = top_tag_service(tag_top_sale_cache, days, tag_id=tag_id)
                if not top_services:
                    continue

                all_tag_service.extend(top_services)

            services = Service.objects.filter(pk__in=all_tag_service)
            top_x_services = [service.get_service_info() for service in services]
            result[days] = top_x_services

        else:
            top_services = top_tag_service(tag_top_sale_cache, days)
            services = Service.objects.filter(pk__in=top_services)
            top_x_services = [service.get_service_info() for service in services]
            result[days] = top_x_services

    return result


@bind('api/service/count_order')
@cache_page(1200)
@list_interface(limit_name='limit', element_model=Service, element_func_list=[])
def get_service_count_order(days, limit=10):
    """
    获取几天内销量最高的美购
    """
    start_time = datetime.datetime.now() - datetime.timedelta(days=days)
    start_time.replace(hour=0, minute=0, second=0, microsecond=0)

    time_key = start_time.strftime("%Y-%m-%d-%H-%M")
    now_key = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M")

    # TODO 增加缓存 减少对次慢查询
    from rpc.cache import page_cache
    cache_key = 'limit_service_count_order_{}_{}'.format(time_key, now_key)

    service_ids = page_cache.get(cache_key)
    if not service_ids:
        orders = Order.objects.filter(pay_time__gte=start_time).values(
            'service'
        ).annotate(count=Count('service_id')).order_by('-count')[:1000]
        service_ids = [order['service'] for order in orders]
        page_cache.setex(cache_key, 86400, json.dumps(service_ids))
    else:
        service_ids = json.loads(service_ids)

    service_ids = service_ids[:limit]

    results = []
    sid_to_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
        service_ids=service_ids,
    )
    for service_id in service_ids:
        if str(service_id) in sid_to_info:
            d = sid_to_info[str(service_id)]
            d['id'] = d['service_id']
            results.append(d)

    return results


@bind('api/service/service_advertise')
@cache_page(120)
def get_service_advertise(current_city_id=None, limit=10, position='', tag_id=None):
    """
    美购广告，目前只有Ship在调用，而且从逻辑上来说应该是要被废弃了
    """
    today = datetime.datetime.today()
    query = Q(show_position__contains=position)
    query &= Q(show_city__id=current_city_id) | Q(show_city__isnull=True)
    query &= Q(is_online=True) & Q(start_time__lt=today) & Q(end_time__gt=today)
    advertises = AdvertiseManagement.objects.filter(query).select_related('service').order_by("start_time")
    try:
        tag = Tag.objects.get(id=tag_id)
        if tag.tag_type == TAG_TYPE.BODY_PART or tag.tag_type == TAG_TYPE.BODY_PART_SUB_ITEM:
            try:
                tags = tag_child_tags_cache.get(str(tag.id))
                tags = json.loads(tags)
                tags = tags['tag_ids']
            except:
                tags = []
        elif tag.tag_type == TAG_TYPE.ITEM_WIKI:
            tags = [int(tag_id)]
    except:
        tags = 'all'

    result = {}
    service_info_list = []
    service_ids = []
    data_count = 0
    for advertise in advertises:
        if hasattr(advertise, 'service'):
            if tags == 'all':
                if hasattr(advertise, 'extend_tip'):
                    service = advertise.service.get_data_for_list(None, None)
                    service_ids.append(advertise.service.id)
                    service['ad_str'] = advertise.extend_tip.tip_name
                    service_info_list.append(service)
                    data_count = data_count + 1
            elif hasattr(advertise.service, 'tags'):
                service_tags_count = advertise.service.tags.filter(id__in=tags).count()
                if service_tags_count:
                    if hasattr(advertise, 'extend_tip'):
                        service = advertise.service.get_data_for_list(None, None)
                        service_ids.append(advertise.service.id)
                        service['ad_str'] = advertise.extend_tip.tip_name
                        service_info_list.append(service)
                        data_count = data_count + 1

        if data_count >= limit:
            break

    # 为美购添加礼包信息
    for service_info in service_info_list:
        from api.manager import coupon_manager
        service_info["coupon_gifts"] = coupon_manager.get_top_coupon_gift_infos(service_id=service_info['service_id'])

    result['advertise_service'] = service_info_list
    result['advertise_service_ids'] = service_ids
    return result


@bind('api/service/sitemap')
def get_servicelist_sitemap(count=settings.BUILD_SITEMAP_COUNT):
    service_list = Service.objects.filter(is_online=True, doctor__is_online=True)
    service_list = service_list.order_by('-update_time')
    service_list = service_list[0:count]
    service_info = []
    for service in service_list:
        service_info.append({
            'id': service.id,
            'update_time': get_timestamp_or_none(service.update_time),
        })
    return service_info


@bind('api/service/sitemap/html')
def get_services_html_sitemap(rating, count=settings.BUILD_SITEMAP_COUNT):
    service_list = Service.objects.using(settings.SLAVE_DB_NAME).filter(is_online=True, rating__gte=rating,
                                                                        rating__lt=rating + 1)
    service_list = service_list.only("id", "name", "update_time").order_by('-update_time')[0:count]
    service_info = []
    for service in service_list:
        service_info.append({
            'id': service.id,
            "title": service.name,
            'created_time': get_strftime(service.update_time),
            "type": "service",
        })
    return service_info


@bind('api/service/by_id')
def service_by_id(id):
    model = Service
    try:
        m = model.objects.get(id=id)
        if hasattr(m, 'to_dict'):
            return m.to_dict()
        else:
            return to_dict(m)
    except model.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind('api/service/tag_ids')
def service_tag_ids(id):
    model = Service
    try:
        m = model.objects.get(id=id)
        tags = m.tags.order_by('pk')
        ts = list(tags.values_list('id', flat=True))
        return ts
    except model.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)


@bind('api/service/by_ids')
def service_by_ids(ids):
    model = Service
    ms = model.objects.filter(id__in=ids).select_related('doctor__hospital')
    result = []
    for m in ms:
        if hasattr(m, 'to_dict'):
            d = m.to_dict()
        else:
            d = to_dict(m)
        # patch with doctor info
        d['doctor_id'] = m.doctor_id
        if m.doctor:
            d['doctor'] = {
                'id': m.doctor_id,
                'name': m.doctor.name,
            }
            if m.doctor.hospital:
                d['doctor']['hospital'] = {
                    'id': m.doctor.hospital_id,
                    'name': m.doctor.hospital.name,
                }
        result.append(d)
    return result


@bind('api/service/doctor_info')
def get_service_doctor_info(service_id):
    """get serivce's doctor info.

    return:
        {
            'doctor_id':
            'doctor_user_id':
        }
    """
    try:
        service = Service.objects.select_related('doctor').get(id=service_id)
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_EXSIT)

    return {
        'doctor_id': service.doctor and service.doctor_id or None,
        'doctor_user_id': service.doctor and service.doctor.user_id or None,
    }


def get_service_card_info_by_ids(ids):
    services = Service.objects.select_related('doctor').filter(id__in=ids, is_online=True)
    if services is None:
        return []

    result = []
    for s in services:
        result.append({
            'id': s.id,
            'short_desc': s.short_description,
            'image': s.image_header,
            'gengmei_prices': s.gengmei_price_list,
            'doctor_name': s.doctor.name or '',
            'hospital_name': s.doctor.hospital.name or '',
        })
    return result


@bind('api/service/get_info_for_diary_detail')
def service_info_for_diary_detail(service_id, order_id=None):
    """
    此函数用于获取日记本、帖子等需要美购相关信息的接口, 请勿用于美购展示等接口
    v 7.6.30 改版 增加 is_online 美购在线状态
    """
    try:
        service = Service.objects.get(pk=service_id)
    except Service.DoesNotExist:
        return gen(CODES.SERVICE_NOT_FOUND)

    service_id_list = add_double_eleven_image()
    double_eleven_image = ''
    if service.id in service_id_list:
        double_eleven_image = 'https://heras.igengmei.com/serviceactivity/2019/10/21/d9c715646c'

    info = {
        'id': service.id,
        'doctor_id': None,
        'doctor_name': None,
        'doctor_city': None,
        'hospital_id': None,
        'hospital_name': None,
        'hospital_city': None,
        'short_description': service.short_description,
        'is_online': service.is_online,
        'double_eleven_image': double_eleven_image,
    }

    if service.doctor_id:
        doctor = service.doctor
        doctor_id = doctor.id
        doctor_name = doctor.name
        city = doctor.hospital.city
        if city:
            city_name = city.name
        else:
            city_name = ''
        info['doctor_id'] = doctor_id
        info['doctor_name'] = doctor_name
        info['doctor_city'] = city_name

        hospital = service.doctor.hospital
        hospital_id = hospital.id
        hospital_name = hospital.name
        city = hospital.city
        if city:
            city_name = city.name
        else:
            city_name = ''
        info['hospital_id'] = hospital_id
        info['hospital_name'] = hospital_name
        info['hospital_city'] = city_name

    gengmei_prices = service.gengmei_price_list
    tag = service.tags.first()
    if tag:
        service_tag_name = tag.name
    else:
        service_tag_name = ''

    image = service.image_header
    info['gengmei_prices'] = gengmei_prices
    info['service_tag_name'] = service_tag_name
    info['image'] = image

    if order_id:
        user_comment = ServiceComment.objects.filter(service=service_id, order__id=order_id)
        if user_comment:
            user_comment = user_comment[0]
            info['comment_content'] = user_comment.content if not user_comment.default_rating else ''
            info['user_rating'] = user_comment.rating
    else:
        info['comment_content'] = ''
        info['user_rating'] = 0
    info['comment_rate'] = service.rating

    return info


@bind('api/service/recommend_for_diary')
def recommend_for_diary(service_id, tag_ids=None, city_id=None):
    """
        日记本详情页推荐美购
        v 7.6.30 改版 新加 tag_ids city_id
        排序规则由
            SERVICE_ORDER_TYPE.DIARY_RECOMMENDED (日记本关联美购)
                改为
            SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES (销量最高)
    :param service_id: 美购id
    :param tag_ids: list类型 标签 三级、二级、一级的id 列表
    :param city_id: 城市id 目前传入的是当前用户定位的城市
    :return:
    """
    # 位置二 日记本关联 美购 对应医生/医院 热销 No.1 医生没有找医院 去重 1个
    sort_params = {}
    filters = {}
    max_service_id = None

    if service_id:
        try:
            service = Service.objects.get(pk=service_id)
        except Service.DoesNotExist:
            return gen(CODES.SERVICE_NOT_FOUND)

        doctor_id = service.doctor_id
        f_result_ids = filter_service(0, 2, filters={'doctor_id': doctor_id},
                                      sort_type=SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES)['service_ids']
        if not f_result_ids:
            f_result_ids = filter_service(0, 2, filters={'hospital_id': service.doctor.hospital.id},
                                          sort_type=SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES)['service_ids']

        if f_result_ids and service_id not in f_result_ids:
            max_service_id = f_result_ids[0]

        elif f_result_ids and service_id in f_result_ids:
            f_result_ids.remove(service_id)
            max_service_id = f_result_ids[0] if f_result_ids else None

        # 位置三 日记本关联 美购 的同城同tag top10
        service_tag_ids = [t.id for t in service.tags.all()]
        if service_tag_ids:
            filters.update({
                'tag_ids': [max(service_tag_ids)],  # 取id最大的
            })

        if service and service.city:
            city = service.city
        else:
            city = None

        if city:
            filters.update({
                'city_tag_id': city.tag_id,
            })

    else:
        #  位置三 日记本未关联美购 当前定位城市 日记本关联 tag
        if city_id:
            _, city_tag_id = get_location_tag_id_by_city_id(str(city_id))
            if city_tag_id:
                filters = {
                    "city_tag_id": city_tag_id,
                }
        if tag_ids:
            filters.update({
                "tag_ids": [max(tag_ids)],
            })

    #  位置三 根据筛选条件过滤 需要与service_id 和 位置二 去重 <=10个
    filter_data = filter_service(
        offset=0,
        size=12,
        sort_params=sort_params,
        sort_type=SERVICE_ORDER_TYPE.HIGHEST_SALES_FOR_SMART_RANK2,
        filters=filters,
    )
    service_ids = filter_data['service_ids']
    rank_mode = filter_data['rank_mode']
    if service_id and service_id in service_ids:
        service_ids.remove(service_id)

    if not max_service_id:
        sorted_service_ids = service_ids[:10]
    else:
        if max_service_id in service_ids:
            service_ids.remove(max_service_id)
        sorted_service_ids = [max_service_id] + service_ids[:10]

    services_query_set = Service.objects.filter(id__in=sorted_service_ids)
    services = get_objects_from_queryset_and_pk_list(services_query_set, sorted_service_ids)

    result = []
    service_id_list = add_double_eleven_image()
    for s in services:
        #  注掉评分，以防改回来
        # try:
        #     rating_lst = ServiceComment.objects.filter(service=s.id).values_list('rating', flat=True)
        #     five_rating = 0
        #     for item in rating_lst:
        #         five_rating += 1 if int(item) == 5 else 0
        #     if rating_lst:
        #         comment_rate = '{0:.0%}'.format(float(five_rating) / len(rating_lst))
        #     else:
        #         comment_rate = 0
        # except ServiceComment.DoesNotExist:
        #     comment_rate = 0  # 五星评级数／评价数
        double_eleven_image = ''
        if s.id in service_id_list:
            double_eleven_image = 'https://heras.igengmei.com/serviceactivity/2019/10/21/d9c715646c'
        result.append(
            {
                'id': s.id,
                'tag_name': (s.tags.first() or '') and s.tags.first().name,
                'doctor_name': s.doctor and s.doctor.name,
                'doctor_is_officer': s.doctor and s.doctor.doctor_type == DOCTOR_TYPE.OFFICER or False,
                'hospital_name': s.doctor.hospital and s.doctor.hospital.name,
                'gengmei_prices': s.gengmei_price_list,
                'image_header': s.image_header,
                'city_name': s.city_name,
                'original_price': s.lowest_original_price,
                # 'comment_rate': comment_rate,
                'short_description': s.short_description,
                'rank_mode': rank_mode,
                'double_eleven_image': double_eleven_image,
            }
        )

    return result


@bind("api/service/recommend_for_community")
def recommend_for_diary_v2(service_id=None, related_tag_ids=None, tag_v3_ids=None, current_city_id=None,
                           doctor_id=None, hospital_id=None, use_tagv3=False, require_num=2):
    """
    add in v 7.7.35 推荐美购逻辑变更
    选取顺序：医院下该日记标签->医生下该日记标签->城市下该日记标签
    :param service_id:
    :param related_tag_ids: [] 标签1.0
    :param tag_v3_ids:[] 标签3.0
    :param current_city_id: 城市
    :param doctor_id 医生ID
    :param hospital_id 医院ID
    :param use_tagv3: 是否通过标签3.0召回
    :param require_num: 需要返回的推荐的个数
    :return:
    """

    result = {
        "recommend_services": [],
    }

    filter_service_sort_list = ['hospital', 'doctor', 'city']  # 选取顺序
    filter_service_param_dic = dict()
    filter_base = {"is_online": True}
    if related_tag_ids:  # 添加tag关联
        filter_base['tag_ids'] = related_tag_ids
    filter_base["tagv3_ids"] = tag_v3_ids if tag_v3_ids else []

    recommend_service_ids = list()

    # 医院下相关美购的查找
    if hospital_id:
        hospital_filter = {'hospital_id': hospital_id}
        hospital_filter.update(filter_base)
        filter_service_param_dic['hospital'] = dict(
            offset=0,
            size=require_num + 1,  # 避免因重复而未取到
            filters=hospital_filter,
            sort_type=SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES
        )

    # 医生下相关美购的查找
    if doctor_id:
        doctor_filter = {'doctor_id': doctor_id}
        doctor_filter.update(filter_base)
        filter_service_param_dic['doctor'] = dict(
            offset=0,
            size=require_num + 1,  # 避免因重复而未取到
            filters=doctor_filter,
            sort_type=SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES
        )

    # 城市下相关美购的查找
    city_filter = {}
    city_filter.update(filter_base)
    if current_city_id:
        _, city_tag_id = get_location_tag_id_by_city_id(current_city_id)
        if city_tag_id:
            city_filter["city_tag_id"] = city_tag_id
    filter_service_param_dic['city'] = dict(
        offset=0,
        size=require_num + 1,  # 避免因重复而未取到
        filters=city_filter,
        sort_type=SERVICE_ORDER_TYPE.DEFAULT
    )

    for service_key in filter_service_sort_list:
        filter_param = filter_service_param_dic.get(service_key, {})
        if filter_param:
            filter_param["use_tagv3"] = use_tagv3  # 是否使用新标签
            try:
                service_ids = filter_service_v2(**filter_param)["service_ids"]
                recommend_service_ids.extend([i for i in service_ids if i not in recommend_service_ids])
            except:
                logging_exception()

            if service_id in recommend_service_ids:
                recommend_service_ids.remove(service_id)

            if (require_num - len(recommend_service_ids)) <= 0:
                break

    services_info_dic = get_toc_spu_info_list_mapping_by_spu_ids(recommend_service_ids[:require_num])
    result["recommend_services"] = [services_info_dic[str(sid)]
                                    for sid in recommend_service_ids if str(sid) in services_info_dic]

    result['rank_mode'] = RANK_MODE.CPC
    return result


@bind('api/service/diary_related_online_service_id')
def get_diary_detail_related_service_id(service_id, order_id):
    """
    顺序1：关联美购
    如果关联美购下线，取顺序2
    顺序2：关联订单的医生名下同项目tag的美购（在线美购）
    若都没有，此部分不显示
    """
    online_service_id = None
    if service_id is not None:
        try:
            service = Service.objects.get(id=service_id)
        except Service.DoesNotExist:
            return None

        if service.is_online:
            online_service_id = service_id
        elif order_id is not None:
            try:
                order = Order.objects.get(id=order_id)
            except Order.DoesNotExist:
                return None

            doctor = order.service.doctor
            self_service_tags = list(service.tags.all().values_list('id', flat=True))
            if self_service_tags:
                for s in doctor.services.filter(is_online=True):
                    _tags = list(s.tags.all().values_list('id', flat=True))
                    jump = False
                    for tag in _tags:
                        if tag and tag in self_service_tags:
                            online_service_id = s.id
                            jump = True
                            break
                    if jump:
                        break
            else:
                online_service_id = None
        else:
            online_service_id = None
    else:
        online_service_id = None

    return online_service_id


@bind('api/service/get_diary_service_data')
def get_diary_service_data(service_id, order_id, tag_id, need_old_data=False):
    """
    根据日记本相关的美购ID 获取美购的详细信息
    """
    rs = {}
    if not service_id:
        return rs

    try:
        service = Service.objects.get(id=service_id, is_online=True)
    except Service.DoesNotExist:
        return rs

    price_data = get_toc_spu_info_list_mapping_by_spu_ids([service_id])
    if not price_data:
        return rs

    service_price = price_data.get(str(service_id))
    if not service_price:
        return rs
    """
    多买需求之前优先级：拼团，秒杀，更美,
    多买需求重订优先级，底层指定外漏价格
    """
    # now_price, default_price = service_price.get('gengmei_price'), service_price.get('default_price')
    # is_groupbuy, is_seckill = service_price.get('is_groupbuy'), service_price.get('is_seckill')
    #
    # if is_seckill:
    #     now_price = service_price.get('seckill_price')
    # elif is_groupbuy:
    #     now_price = service_price.get('groupbuy_price', 0)

    price = {
        'default_price': service_price.get('default_price'),
        'gengmei_price': service_price.get('gengmei_price'),
        'is_groupbuy': service_price.get('is_groupbuy'),
        'is_seckill': service_price.get('is_seckill'),
        'seckill_price': service_price.get("seckill_price", 0),
        'groupbuy_price': service_price.get("groupbuy_price", 0),
        'show_price_key': service_price.get('show_price_key'),
        'multibuy_price': service_price.get('multibuy_price')
    }

    hospital_data = {
        'hospital_id': service_price.get("hospital_id", ""),
        'hospital_name': service_price.get("hospital_name", ""),
    }

    tag_id = service.get_tags()[0]['tag_id'] if service.get_tags() else tag_id
    service_count = Service.objects.filter(tags__in=[tag_id]).count() if tag_id else 0
    good_rating_number = ServiceComment.objects.filter(service_id=service_id, is_effect=True,
                                                       rating__gte=4.0).count()  # >=4.5分好评个数

    author_rating = 0
    if order_id:
        author_service = ServiceComment.objects.filter(order_id=order_id, is_effect=True, is_online=True).first()
        author_rating = author_service.rating if author_service else 0

    data = {
        'id': service_id,
        'name': service.name,
        'img_header': service.image_header,
        'author_rating': author_rating,
        'five_rating_number': good_rating_number,
        'service_count': service_count,
        'order_id': order_id,
        'service_tags': service.get_tags(),
        'sku_id': price_data.get('service_item_id') if price_data else None,
        'service_name': service_price.get("service_name", ''),
        'service_id': service_id,
        'short_description': service_price.get("short_description", ""),
        'operate_tag': service_price.get("operate_tag", []),
        'sell_amount': service_price.get("sell_amount", 0),
        'city': service_price.get('city', ''),
        'double_eleven_image': service_price.get('double_eleven_image', ''),
        'doctor_user_id': service.doctor and service.doctor.user and service.doctor.user.id or '',
        'doctor_accept_private_msg': service.doctor and service.doctor.accept_private_msg or False
    }

    if need_old_data:
        tag = service.tags.first()
        service_tag_name = tag.name if tag else ''
        old_data = {
            'comment_rating': service.rating,  # 兼容日记帖老数据结构取得值
            'prices': service.gengmei_price_list,  # 兼容老数据结构取得值
            'doctor_name': service.doctor.name,  # 兼容老数据结构取得值
            'service_tag_name': service_tag_name,  # 兼容老数据结构取得值
        }
        data['old_data'] = old_data

    data.update(price)
    data.update(hospital_data)

    return data


@bind('api/service/get_diary_show_info_by_service_ids')
def get_hospital_and_doctor_names_by_service_ids(service_ids):
    """
    返回美购对应的城市名、医生名、医院名、城市标签id
    """
    services = Service.objects.filter(id__in=service_ids).select_related('doctor__hospital__city')
    result = []
    for service in services:
        city = service.city and service.city.name or None
        hospital = service.hospital and service.hospital.name or None
        doctor = service.doctor.name
        city_tag_id = service.city and service.city.tag_id or None
        data = {
            'id': service.id,
            'city_name': city,
            'hospital_name': hospital,
            'doctor_name': doctor,
            "city_tag_id": city_tag_id,
            "tags": service.get_tags(),
        }
        result.append(data)
    return result


@bind('api/service/get_service_info_by_hospital')
def get_service_info_by_hospital_id(hospital_id, q=None):
    services = Service.get_service_infos_by_hospital_id(hospital_id, q)
    return services


@bind('api/service/get_service_info_id')
def get_service_info_by_id(service_id):
    try:
        service = Service.objects.get(id=service_id).get_service_info()
        return service
    except Service.DoesNotExist:
        pass


@bind('api/service/list_by_ids')
def list_service_by_ids(service_ids):
    """
    get service info batch
    :param service_ids:
    :return:
    """
    queryset = Service.objects.filter(pk__in=service_ids)
    data = {str(pk): None for pk in service_ids}
    for service in queryset:
        try:
            data[str(service.id)] = service.get_service_info()
        except IndexError:
            pass
    return data


@bind('api/service/list_by_ids_for_index')
def list_by_ids_for_index(service_ids):
    queryset = Service.objects.filter(pk__in=service_ids).select_related('doctor__hospital__city')
    data = {str(pk): None for pk in service_ids}
    for service in queryset:
        hospital_name = service.doctor.hospital.name
        try:
            data[str(service.id)] = {
                'service_id': service.id,
                'service_name': service.name,
                'city': service.city_name,
                'hospital_name': hospital_name,
                'tags': service.get_tags(),
                'hospital': service.hospital_name,
                'gengmei_price': service.gengmei_price_display,
                'is_online': service.is_online,
                'is_multiattribute': service.is_multiattribute,
            }
        except IndexError:
            pass
    return data


@bind('api/service/get_service_tags_by_ids')
def get_service_tags_by_id(service_ids):
    services = Service.objects.filter(id__in=service_ids)
    result = []
    for service in services:
        data = {
            'id': service.id,
            'tag': service.project_type.name,
        }
        result.append(data)
    return result


@bind('api/category/all')
def get_category_gadget():
    # [{'icon': '', 'tags': [],'category_name':''},]
    re_data = []
    image_data = CategoryGadget.objects.filter(is_online=True, start_time__lte=timezone.now()
                                               ).values_list('image_data', flat=True).order_by('-start_time')
    if image_data:
        image_data = image_data[0]
    else:
        return re_data

    image_json = json.loads(image_data)
    # 获取标签名称
    for item in image_json[:-1]:
        name = Tag.objects.filter(id__in=item.get('tags')).values_list('name', flat=True)
        tmp_dict = {
            'icon': item.get('icon'),
            'new_icon': item.get('new_icon', ''),
            'category_name': item.get('category_name')
        }
        if len(name) > 1:
            # 多标签的过滤
            tmp_dict['name'] = '/'.join(name)
            tmp_dict['tag_id'] = ''
            tmp_dict['tag_ids'] = [int(i.encode('utf-8')) for i in item.get('tags')]
        else:
            tmp_dict['name'] = name[0]
            tmp_dict['tag_id'] = item.get('tags')[0]
            tmp_dict['tag_ids'] = ''
        re_data.append(tmp_dict)
    re_data.append({
        'icon': image_json[-1]['icon'],
        'new_icon': image_json[-1].get('new_icon', ''),
        'name': u'更多',
        'tag_id': 'all',
        'tag_ids': '',
        'category_name': u'全部'
    })
    return re_data


@bind_context('api/meigou_home/seckill')
def get_meigou_home_seckill(ctx, city_id, sort_params):
    ret_dict = {}
    re_status = SeckillDeploy.get()

    if re_status and re_status.get('is_show'):
        try:
            query = Q(is_seckill=True) & Q(is_online=True)
            current_time = datetime.datetime.now()
            query &= Q(start_time__lt=current_time)
            query &= Q(end_time__gte=current_time)
            special_info = Special.objects.filter(query).order_by('-start_time')[0]
        except:
            return ret_dict

        service_data = get_seckill_info_for_service_home_list(ctx, special_info.id, city_id, sort_params)
        if service_data:
            data_list = []
            count_down = (special_info.end_time - timezone.now()).total_seconds()
            for item in service_data:
                service_item = ServiceItem.objects.get(id=item['service_item_id'])
                service = service_item.service
                # 查询案例数量
                try:
                    tmp = {
                        'img': service.image_header,
                        'user_case_count': service.diaries_count,
                        'title': '【' + service.project_type.name + '】' + ''.join(service_item.items_name),
                        'city': service.doctor.hospital.city.name,
                        'gengmei_price': service_item.original_price,
                        'seckill_price': service_item.gengmei_price,
                        'is_multiattribute': service.is_multiattribute,
                        'service_item_id': service_item.id,
                        'special_id': special_info.id,
                    }

                    if service.doctor.name:
                        tmp['name'] = service.doctor.name
                        # 关联的医生如果是机构管理者，那么展示机构名,客户端不展示地区
                        if service.doctor.doctor_type == DOCTOR_TYPE.OFFICER:
                            tmp['is_hospital_officer'] = True
                        else:
                            tmp['is_hospital_officer'] = False
                    elif service.hospital.name:
                        tmp['name'] = service.hospital.name
                        tmp['is_hospital_officer'] = True
                    data_list.append(tmp)
                except:
                    logging_exception()
            data_list.append({
                'img': '',
                'user_case_count': 0,
                'title': '',
                'city': '',
                'gengmei_price': 0,
                'seckill_price': 0,
                'is_multiattribute': True,
                'service_item_id': 0,
                'special_id': special_info.id,
                'name': '',
                'is_hospital_officer': True,
            })
            ret_dict['welfares'] = data_list
            ret_dict['countdown'] = count_down
            ret_dict['seckill_bg'] = re_status.get('bg_img')
            ret_dict['seckill_alert'] = re_status.get('sckill_alert')
    return ret_dict


@bind('api/meigou_home/scenebutton')
def get_meigou_home_scenebutton():
    ret_lst = []
    re_status = SceneButton.get()
    if re_status and re_status.get('is_show'):
        ret_lst = re_status.get('button_list')
    return ret_lst


@bind('api/meigou_home/scene_buttons')
def get_meigou_home_scene_buttons():
    ret_dict = {}
    re_status = SceneButton.get()
    if re_status and re_status.get('is_show'):
        ret_dict['button_list'] = re_status.get('button_list')
        ret_dict['icon_img'] = re_status.get('icon_img') if re_status.get('is_img') else ''
        ret_dict['title_color'] = re_status.get('title_color')
        ret_dict['dec_color'] = re_status.get('dec_color')

    return ret_dict


# @desc 给出推荐美购
# http://wiki.gengmei.cc/pages/viewpage.action?pageId=4150998
# @rd 郑伟
# @param  device_id 用户手机设备id
# @param  current_city_tag_id 用户美购首页选择城市tag_id
# @param  size 一次推荐多少条美购
# @date 20170624
@bind("api/service/recommend_service")
def get_reommend_service(device_id, current_city_id=None, size=10, use_new_service_info_data=False):
    country_tagid, user_city_tag_id = get_location_tag_id_by_city_id(current_city_id)
    rpc_client = get_rpc_remote_invoker()
    services = rpc_client['doris/recommend/service'](
        device_id=device_id, current_city_tag_id=user_city_tag_id, size=size
    ).unwrap()
    service_ids = services["service_ids"]

    sid_to_service_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
        service_ids=service_ids,
    )
    items = []
    for sid in service_ids:
        if str(sid) in sid_to_service_info:
            items.append(sid_to_service_info[str(sid)])

    return {"service_list": items}


@bind("api/service/willbuy_service")
def get_willbuy_service(device_id=None, current_city_id=None):
    '''
    个人中心页，猜你喜欢列表，
    :param device_id:
    :return:
    '''
    re_data = []
    if device_id:
        country_tagid, user_city_tag_id = get_location_tag_id_by_city_id(current_city_id)

        rpc_client = get_rpc_remote_invoker()

        size = 20

        services = rpc_client['doris/recommend/service'](
            device_id=device_id, current_city_tag_id=user_city_tag_id, size=size
        ).unwrap()
        service_ids = services["service_ids"]

        sid_to_service_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
            service_ids=service_ids,
        )
        for s in service_ids:
            if str(s) in sid_to_service_info:
                info = sid_to_service_info[str(s)]
                re_data.append(info)

    return re_data


@bind("api/service/willbuy")
def get_willbuy_service_v2(device_id=None, current_city_id=None, offset=0, size=10):
    '''
    :param device_id str: 设备号ID
    :param current_city_id int: 城市ID
    :param offset int: 偏移量
    :param size int: 数量
    :return: 美购ID
    :rtype: List[int]
    '''
    service_ids = []
    if device_id:
        country_tagid, user_city_tag_id = get_location_tag_id_by_city_id(current_city_id)

        rpc_client = get_rpc_remote_invoker()

        services = rpc_client['doris/willbuy/service'](
            device_id=device_id, current_city_tag_id=user_city_tag_id, size=size, offset=offset
        ).unwrap()
        service_ids = services["service_ids"]

    return service_ids


@bind("api/service/options_to_serviceitemid")
def charge_options_to_service_item_id(service_id, options):
    from api.tool.service_tool import get_serviceitem_by_option_id
    options = '-'.join(sorted(options.split('-'), key=lambda x: int(x)))
    result = get_serviceitem_by_option_id(options, service_id)
    if result is None:
        return gen(CODES.SERVICE_NOT_FOUND)
    return result


def _api_filter_service_v3(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=None,  # http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        or_filters=[]
):
    user = ctx.session.user
    offset = assert_uint(offset, 0)
    count = assert_uint(count, 0)

    params = api_filter_service_extract_params(
        user=user, filters=filters, extra_filters=extra_filters,
        sort_params=sort_params, current_city_id=current_city_id)
    sort_params = params['sort_params']
    filters = params['filters']

    # 如果是广告筛选
    if 'adver_position' in filters:
        need_adver_info = True
        sort_params['adver_position'] = filters['adver_position']
        if 'adver_word' in filters:
            sort_params['adver_word'] = filters['adver_word']
    else:
        need_adver_info = False

    service_filter_result = filter_service_v2(
        offset=offset,
        size=count,
        sort_type=order_by,
        filters=filters,
        sort_params=sort_params,
        or_filters=or_filters
    )

    service_adver_id_map = {}
    service_id_list = service_filter_result['service_ids']
    # 如果是广告筛选
    if need_adver_info:
        service_adver_id_map = service_filter_result['adver_id_map']

    return {'service_id_list': service_id_list, 'need_adver_info': need_adver_info,
            'service_adver_id_map': service_adver_id_map, 'total_count': service_filter_result['total_count']}


@bind_context('api/service/filter_with_scatter')
@list_interface(
    offset_name='offset', limit_name='count',
    element_model=Service, element_func_list=[Service.get_service_info]
)
def api_filter_service_scatter(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        use_new_service_info_data=False,
        use_tagv3=False,
):
    # added at 2018-02-13
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    """
    todo list:

    switch参数不在使用,后续迭代开发可删除
    """

    # 美购首页走老打散，新打散逻辑完善后删除
    return _api_filter_service_scatter_for_service_index(
        ctx=ctx,
        order_by=order_by, filters=filters,
        extra_filters=extra_filters, offset=offset, count=count,
        sort_params=sort_params, current_city_id=current_city_id,
        coupon_info_id=coupon_info_id, switch=switch,
        use_new_service_info_data=use_new_service_info_data, use_tagv3=use_tagv3)


@bind('api/service/tags/distribution')
def get_service_tags_distribution_by_id(doctor_id=None, hospital_id=None):
    """获取医生或者医院美购tag分布

    :param doctor_id:
    :param hospital_id:
    :return:
    """
    if doctor_id:
        doctor = Doctor.get_online_doctor_by_id(doctor_id)
        if doctor:
            return doctor.get_service_tags_distribution()
    elif hospital_id:
        hospital = Hospital.get_online_hospital_by_id(hospital_id)
        if hospital:
            return hospital.get_service_tags_distribution()
    return []


# TODO: 美购推荐页打散逻辑临时回退。处理后删除
def _api_filter_service_scatter_for_service_index(
        ctx=None,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        use_new_service_info_data=False,
        use_tagv3=False,
):
    user = ctx.session.user

    couponinfo_id = ""
    coupon_desc = ""
    coupon_prepay_threshold = None

    if coupon_info_id:
        couponinfo = CouponInfo.objects.select_related('coupon').filter(id=coupon_info_id).first()
        if couponinfo and couponinfo.user == user:
            couponinfo_id = str(coupon_info_id)

            c = couponinfo.coupon
            coupon_prepay_threshold = c.has_threshold and c.prepay_threshold or None
            # 7780添加
            coupon_desc_1 = ""
            benefit_type = c.benefit_type
            if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                coupon_desc_1 = "以下商品可使用直减{}元美券".format(c.value)
            elif benefit_type == BENEFIT_TYPE.MANJIAN:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.value)
            elif benefit_type == BENEFIT_TYPE.DISCOUNT:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.upper_limit)

            # 7780折扣券注释
            # coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(
            #    c.prepay_threshold, c.value) if c.has_threshold else "以下商品可使用直减{}元美券".format(c.value)
            coupon_desc_2 = "预付款抵扣" if c.coupon_type == COUPON_TYPES.PLATFORM else "尾款抵扣"
            coupon_desc = "{}（{}）".format(coupon_desc_1, coupon_desc_2)

            spids = couponinfo.coupon_restrict_special_ids
            doctor_ids = couponinfo.coupon_restrict_doctor_ids
            sku_ids = couponinfo.coupon_restrict_sku_ids
            service_ids = set(ServiceItem.objects.filter(
                parent_id=0, id__in=sku_ids, is_delete=False
            ).values_list('service_id', flat=True)) if sku_ids else []

            if spids:
                filters['special_ids_and'] = list(spids)

            if doctor_ids:
                filters['doctor_ids'] = list(doctor_ids)

            if service_ids:
                filters['service_ids'] = list(service_ids)

    # filter service scattered by merchant
    offset = assert_uint(offset, 0)
    count = assert_uint(count, 0)
    order_by = assert_uint(order_by, SERVICE_ORDER_TYPE.DEFAULT)

    params = api_filter_service_extract_params(
        user=user,
        filters=filters,
        extra_filters=extra_filters,
        sort_params=sort_params,
        current_city_id=current_city_id
    )
    sort_params = params['sort_params']
    filters = params['filters']

    next_page = offset / 10

    if order_by == SERVICE_ORDER_TYPE.DEFAULT and next_page < 8 and 'doctor_id' not in filters and \
            'hospital_id' not in filters:
        user_city_tag_id = sort_params.get('user_city_tag_id', -1)
        rpc_client = get_rpc_remote_invoker()
        if user_city_tag_id != -1:
            try:
                obj = City.objects.only('id', 'tag_id').get(tag_id=user_city_tag_id)
                rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[obj.id]).unwrap()
                sort_params['in_whitelist'] = int(bool(rs))
            except (City.DoesNotExist, City.MultipleObjectsReturned):
                sort_params['in_whitelist'] = 0
        else:
            sort_params['in_whitelist'] = 0

        service_filter_result = filter_service(
            offset=0,
            size=80,
            sort_type=order_by,
            filters=filters,
            sort_params=sort_params,
            use_tagv3=use_tagv3,
        )
        sku_ids = service_filter_result['service_ids']
        doctor_ids = service_filter_result['doctor_ids']
        if len(sku_ids) != len(doctor_ids):
            service_filter_result['service_ids'] = sku_ids
        else:
            doctor_merchant_dict = doctor_merchant_map(doctor_ids)
            hospital_ids = [doctor_merchant_dict[i] for i in doctor_ids]

            items = []
            sku_list = service_filter_result['sku_list']
            for idx in range(len(sku_ids)):
                item = {
                    'sku_id': sku_ids[idx],
                    'merchant_id': hospital_ids[idx],
                    'city_tag_id': sku_list[idx]['city_tag_id'],
                    'nearby_city_tags': map(itemgetter('tag_id'), sku_list[idx]['nearby_city_tags'])
                }
                items.append(item)
            in_whitelist = sort_params.get('in_whitelist', 0)
            data = service_scatter_by_city(items, user_city_tag_id, 10, merge_nearby=not in_whitelist)
            service_filter_result['service_ids'] = map(itemgetter('sku_id'), data)[offset:offset + count]

    else:
        service_filter_result = filter_service(
            offset=offset,
            size=count,
            sort_type=order_by,
            filters=filters,
            sort_params=sort_params,
            use_tagv3=use_tagv3
        )
    service_id_list = service_filter_result['service_ids']
    total_count = service_filter_result['total_count']
    special_id = filters.get('special_id', None) if filters else None
    result = get_result_for_service(
        user=user,
        special_id=special_id,
        get_diary_num=False,
        service_id_list=service_id_list,
        need_adver_info=False,
        service_adver_id_map={},
        total_count=total_count,
        use_new_service_info_data=use_new_service_info_data,
    )

    # if coupon_prepay_threshold is not None:
    #     coupon_service_list = []
    #     service_list = result.get('service_list') or []
    #     for service_info in service_list:
    #         price_key = service_info.get('ori_show_price_key')
    #         price = service_info.get(price_key) or None
    #         if price is not None:
    #             if coupon_prepay_threshold <= price:
    #                 coupon_service_list.append(service_info)
    #     result['service_list'] = coupon_service_list

    result['coupon_info_id'] = couponinfo_id
    result['coupon_desc'] = coupon_desc
    result['rank_mode'] = service_filter_result['rank_mode']

    return result


@bind('api/services/tags/by_service_ids')
def get_services_tag_name_by_service_ids(service_ids):
    """根据美购id获取用于日记本标题的tag name。"""

    services_tags = ServiceTag.objects.filter(service_id__in=service_ids, tag__is_online=True,
                                              tag__tag_type__in=[TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM,
                                                                 TAG_TYPE.ITEM_WIKI]).order_by("service_id", "tag_id"). \
        select_related('tag')

    res = {}
    for item in services_tags:
        if str(item.service_id) not in res:
            res[str(item.service_id)] = item.tag.name

    return res


@bind('api/batch_get_service_basic_info')
def batch_get_service_basic_info(service_ids, with_fields=None):
    """ 批量获取美购基本信息， 不是美购基本的信息的，千万不要加进来

    :param service_ids:
    :param with_fields:
    :return:
    """
    if with_fields is None:
        with_fields = []

    result = {sid: {} for sid in service_ids}

    select_fields = []
    if 'meta' in with_fields:
        select_fields.extend(['id', 'name', 'is_online', 'short_description'])
    if 'content' in with_fields:
        select_fields.extend(['detail_description', 'start_time', 'end_time', 'rating'])
    if 'image' in with_fields:
        select_fields.extend(['image_header', 'image_detail', 'image_bigpic'])

    services = Service.objects.only(*select_fields).filter(id__in=service_ids)
    for service in services:
        if 'meta' in with_fields:
            result[service.id]['meta'] = {
                'id': service.id,
                'name': service.name,
                'is_online': service.is_online,
                'short_description': service.short_description,
            }
        if 'content' in with_fields:
            result[service.id]['content'] = {
                'detail_description': service.detail_description,
                'start_time': str(service.start_time) if service.start_time else '',
                'end_time': str(service.end_time) if service.end_time else '',
                'rating': service.rating
            }
        if 'image' in with_fields:
            result[service.id]['image'] = {
                'image_header': service.image_header,
                'image_detail': service.image_detail,
                'image_bigpic': service.image_bigpic
            }
    return result


@bind('api/service/get_service_item_info_by_order_ids')
def get_service_item_info_by_order_ids(order_ids):
    """
    通过订单id，获取美购 sku 信息
    :param order_ids:
    :return:
    """
    result = {}
    if not order_ids:
        return result
    order_info = Order.objects.filter(id__in=order_ids).values("id", "service_id", "service_item_id")
    service_item_ids = list(filter(None, [item.get("service_item_id", 0) for item in order_info]))
    # 需要校验 sku 的状态
    service_items = ServiceItem.objects.filter(
        parent_id=0, pk__in=service_item_ids, sku_stock_lte_zero=False, is_delete=False).values("id", "service_id")
    service_item_dic = {service_item["id"]: service_item for service_item in service_items}
    service_item_names = ServiceItem.get_items_name(list(service_item_dic.keys()))  # 美购 SKU 对应的名字

    for order in order_info:
        service_id = order.get("service_id", 0)
        service_item_id = order.get("service_item_id", 0)

        if service_item_id in service_item_dic:
            _service_item_dic = service_item_dic.get(service_item_id, {})
            if _service_item_dic.get("service_id", 0) != service_id:  # 需要美购校验
                continue
            _service_item_name_list = service_item_names.get(service_item_id, [])
            _data = dict(order)
            _data["order_id"] = _data.pop("id")
            _data["service_item_name"] = _service_item_name_list[0] if _service_item_name_list else ""
            result[order["id"]] = _data

    return result


@bind_context('api/services/get_side_button_info')
def get_side_button_info(ctx):
    user = get_user_from_context(ctx)
    data = SideButton.get()
    if is_new_service_user(user):
        result = {
            'is_show': data.get('new_is_show'),
            'img': data.get('new_img'),
            'url': data.get('new_url'),
            'name': u'新用户',
        }
    else:
        result = {
            'is_show': data.get('old_is_show'),
            'img': data.get('old_img'),
            'url': data.get('old_url'),
            'name': u'老用户',
        }
    return result


@bind('api/service/base_info/by_id')
def get_service_info_by_id(service_id):
    """
    根据service_id获取service的基础信息
    请注意，不要随意的添加字段
    :param service_id:
    :return:
    """
    service = Service.objects.get(id=service_id)
    return {
        'service_name': service.name
    }


@bind_context("api/order/recommend_by_user/service/list", login_required=True)
def recommend_service_by_user_list(ctx, settlement_id, require_num=2):
    """
    根据用户购买记录推荐美购
    :param ctx:
    :param settlement_id:
    :param start_num:
    :param size:
    :return:
    """
    from api.models import SettlementItem, Order
    user = get_user_from_context(ctx)
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    filter_base = {"is_online": True}
    hospital_ids, has_purchase_service_ids, recommend_service_ids = [], [], []

    filter_param = dict(
        offset=0,
        size=require_num,
        filters=filter_base,
        sort_type=SERVICE_ORDER_TYPE.ORDER_LOWEST_PRICE
    )
    try:
        order_ids = SettlementItem.objects.filter(
            settlement_id=settlement_id).values_list('order_id', flat=True)
        for order_id in order_ids:
            try:
                order = Order.objects.get(id=order_id)
                hospital_ids.append(order.service.doctor.hospital_id)
                has_purchase_service_ids.append(order.service_id)
            except Order.DoesNotExist:
                continue
    except SettlementItem.DoesNotExist:
        logging_exception()

    for hospital_id in hospital_ids:
        try:
            filter_param['filters'].update({'hospital_id': hospital_id})
            service_ids = filter_service_v2(**filter_param)["service_ids"]

            # 去除已经购买过的spu, 每个机构只取一个展示
            duplicate_service_ids = list(set(service_ids) - set(has_purchase_service_ids))
            if len(duplicate_service_ids) >= 2:
                recommend_service_ids.append(duplicate_service_ids[0])
            else:
                recommend_service_ids.extend(duplicate_service_ids)
        except:
            logging_exception()

    # 获取已经购买订单中spu最低价格
    has_purchase_service_dic = get_toc_spu_info_list_mapping_by_spu_ids(has_purchase_service_ids)
    has_purchase_service_info = [has_purchase_service_dic[str(sid)]
                                 for sid in has_purchase_service_ids if str(sid) in has_purchase_service_dic]
    has_purchase_service_info.sort(key=lambda x: x['gengmei_price'])
    lowest_gengmei_price = 0
    if has_purchase_service_info:
        lowest_gengmei_price = int(has_purchase_service_info[0].get('gengmei_price', 0))

    recommend_service_ids = list(set(recommend_service_ids))
    # 获取机构下最低价格spu
    services_info_dic = get_toc_spu_info_list_mapping_by_spu_ids(recommend_service_ids)
    recommend_services = [services_info_dic[str(sid)]
                          for sid in recommend_service_ids if str(sid) in services_info_dic]

    # 比较机构推荐的spu与已经购买的spu
    ret_data = []
    for services in recommend_services:
        if int(services.get('gengmei_price', 0)) > lowest_gengmei_price:
            continue
        else:
            ret_data.append(services)

    return {
        'recommend_services_infos': ret_data,
    }


@bind_context("api/order/recommend_by_ctr/service/list", login_required=True)
def recommend_service_by_ctr_list(ctx, settlement_id, start_num=0, size=10, current_city_id=None, device_id=None):
    """
    根据ctr推荐美购
    :param ctx:
    :param settlement_id:
    :param start_num:
    :param size:
    :return:
    """
    from api.models import SettlementItem, Order
    user, city_tag_id, _service_ids = get_user_from_context(ctx), None, []

    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    if current_city_id:
        _, city_tag_id = get_location_tag_id_by_city_id(current_city_id)

    try:
        order_ids = SettlementItem.objects.filter(
            settlement_id=settlement_id).values_list('order_id', flat=True)
        for order_id in order_ids:
            try:
                order = Order.objects.get(id=order_id)
                _service_ids.append(order.service_id)
            except Order.DoesNotExist:
                continue
    except SettlementItem.DoesNotExist:
        logging_exception()

    # 调用策略接口
    rpc_client = get_rpc_remote_invoker()
    query_params = {
        "user_city_tag_id": city_tag_id,
        "user_id": user.id,
        "start_num": start_num,
        "size": size,
        "cl_id": device_id,
    }
    try:
        rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[current_city_id, ]).unwrap()
        in_whitelist = int(bool(rs))
    except:
        in_whitelist = 0

    query_params["in_whitelist"] = in_whitelist
    recommend_service_ids_info = {}
    try:
        recommend_service_ids_info = rpc_client['doris/recommend/order/service'](
            service_ids=_service_ids, query_params=query_params).unwrap()
    except Exception:
        logging_exception()

    recommend_service_ids = recommend_service_ids_info.get('service_ids', [])
    services_info_dic = get_toc_spu_info_list_mapping_by_spu_ids(recommend_service_ids)
    recommend_services = [services_info_dic[str(sid)]
                          for sid in recommend_service_ids if str(sid) in services_info_dic]
    return {
        'recommend_services_infos': recommend_services,
    }


@bind_context("api/order/recommend_by_ctr/service/list_v2", login_required=True)
def recommend_service_by_ctr_list_v2(ctx, settlement_id, start_num=0, size=10, current_city_id=None, device_id=None):
    """
    根据ctr推荐美购,只返回service_id
    :param ctx:
    :param settlement_id:
    :param start_num:
    :param size:
    :return:
    """
    from api.models import SettlementItem, Order
    user, city_tag_id, _service_ids = get_user_from_context(ctx), None, []

    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    if current_city_id:
        _, city_tag_id = get_location_tag_id_by_city_id(current_city_id)

    try:
        order_ids = SettlementItem.objects.filter(
            settlement_id=settlement_id).values_list('order_id', flat=True)
        for order_id in order_ids:
            try:
                order = Order.objects.get(id=order_id)
                _service_ids.append(order.service_id)
            except Order.DoesNotExist:
                continue
    except SettlementItem.DoesNotExist:
        logging_exception()

    # 调用策略接口
    rpc_client = get_rpc_remote_invoker()
    query_params = {
        "user_city_tag_id": city_tag_id,
        "user_id": user.id,
        "start_num": start_num,
        "size": size,
        "cl_id": device_id,
    }
    try:
        rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[current_city_id, ]).unwrap()
        in_whitelist = int(bool(rs))
    except:
        in_whitelist = 0

    query_params["in_whitelist"] = in_whitelist
    recommend_service_ids_info = {}
    try:
        recommend_service_ids_info = rpc_client['doris/recommend/order/service'](
            service_ids=_service_ids, query_params=query_params).unwrap()
    except Exception:
        logging_exception()

    recommend_service_ids = recommend_service_ids_info.get('service_ids', [])
    return {
        'recommend_service_ids': recommend_service_ids,
    }


@bind_context("api/topic/to_service_list", login_required=False)
def recommend_service_by_topic_list(ctx, service_ids):
    '''
        发帖关联美购
    '''
    services = Service.objects.filter(id__in=service_ids)

    def get_last_lever_tagname(service):
        '''获取最底层的标帖'''
        tag_name, tag_id = "", 0

        bodypart_subitem = service.bodypart_subitem
        if bodypart_subitem:
            tag_name = bodypart_subitem.name
            tag_id = bodypart_subitem.tag_id
            return tag_name, tag_id

        if service.tags:
            tag = service.tags.first()
            tag_name, tag_id = tag.name, tag.id
        return tag_name, tag_id

    # 按输入的ids顺序排序
    services = sorted(services, key=lambda x: service_ids.index(x.id))

    results = []
    service_id_list = add_double_eleven_image()
    for service in services:
        tag_name, tag_id = get_last_lever_tagname(service)

        hospital_name = ""
        try:
            hospital_name = service.doctor.hospital.name
        except Exception as e:
            info_logger.info(e)

        service_items = ServiceItem.objects.filter(parent_id=0, service_id=service.id, is_delete=False)
        # prices=Service.get_default_price_info_by_service_item_ids(service_item_ids=si_ids)

        price_list = [item.get_show_price().get("gengmei_price") for item in service_items]

        info_logger.info("*" * 150)
        gengmei_price = min(price_list)
        info_logger.info(gengmei_price)
        double_eleven_image = ''
        if service.id in service_id_list:
            double_eleven_image = 'https://heras.igengmei.com/serviceactivity/2019/10/21/d9c715646c'

        result = {
            "service_id": service.id,
            "gm_url": gm.get_service_detail(service.id),
            "service_name": service.name,
            "image_header": service.image_header,
            "hospital_name": hospital_name,
            "gengmei_price": gengmei_price,
            "tag_id": tag_id,
            "tag_name": tag_name,
            "double_eleven_image": double_eleven_image,
        }
        results.append(result)
    return results


@bind_context("api/service_list_tag")
def service_list_tag(ctx, service_ids):
    '''
        通过service_ids拿到相关的tags
    '''
    result = {}
    services = Service.objects.filter(id__in=service_ids)
    for item in services:
        result.update({item.id: [tag.id for tag in item.tags.all()]})

    return result


@bind_context("api/service/recommend_purchase", login_required=True)
def recommend_purchase_by_ctr_list(ctx, start_num=0, size=10, current_city_id=None, device_id=None, check_in=False):
    """
    美分商城首页  推荐美购
    :param ctx:
    :param start_num:
    :param size:
    :return:
    """
    user, city_tag_id, _service_ids = get_user_from_context(ctx), None, []

    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    if current_city_id:
        _, city_tag_id = get_location_tag_id_by_city_id(current_city_id)

    # 调用策略接口
    rpc_client = get_rpc_remote_invoker()
    query_params = {
        "user_city_tag_id": city_tag_id,
        "user_id": user.id,
        "start_num": start_num,
        "size": size,
        "cl_id": device_id,
        'check_in': check_in,
    }
    try:
        rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[current_city_id, ]).unwrap()
        in_whitelist = int(bool(rs))
    except:
        in_whitelist = 0

    query_params["in_whitelist"] = in_whitelist
    recommend_service_ids_info = {}
    try:
        recommend_service_ids_info = rpc_client['doris/recommend/shopcart/service'](
            service_ids=_service_ids, query_params=query_params).unwrap()
    except Exception:
        logging_exception()

    recommend_service_ids = recommend_service_ids_info.get('service_ids', [])
    services_info_dic = get_toc_spu_info_list_mapping_by_spu_ids(recommend_service_ids)
    recommend_services = [services_info_dic[str(sid)]
                          for sid in recommend_service_ids if str(sid) in services_info_dic]
    return {
        'recommend_services_infos': recommend_services,
    }


@bind_context('api/service/filter_with_scatter_personalized_recommendation')
@list_interface(
    offset_name='offset', limit_name='count',
    element_model=Service, element_func_list=[Service.get_service_info]
)
def api_filter_service_scatter_personalized_recommendation(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        device_id=None,
        use_new_service_info_data=False,
        use_tagv3=False,
        is_check_in=False,
):
    # added at 2018-02-13
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    """
    todo list:

    switch参数不在使用,后续迭代开发可删除
    """

    # 美购首页走老打散，新打散逻辑完善后删除
    return _api_filter_service_scatter_for_service_index_v2(
        ctx=ctx,
        order_by=order_by, filters=filters,
        extra_filters=extra_filters, offset=offset, count=count,
        sort_params=sort_params, current_city_id=current_city_id,
        coupon_info_id=coupon_info_id, switch=switch, device_id=device_id,
        use_new_service_info_data=use_new_service_info_data,
        use_tagv3=use_tagv3, is_check_in=is_check_in
    )


@bind_context('api/service/filter_with_scatter_personalized_recommendation_v2')
@list_interface(
    offset_name='offset', limit_name='count',
    element_model=Service, element_func_list=[Service.get_service_info]
)
def api_filter_service_scatter_personalized_recommendation_v2(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        device_id=None,
        use_new_service_info_data=False,
        use_tagv3=False,
        is_check_in=False,
):
    # added at 2018-02-13
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # FOR SERVICEHOME SERVICE CATEGORY AREA ONLY !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    # DO NOT USE THIS FOR OTHER PURPOSE !!!
    """
    todo list:

    switch参数不在使用,后续迭代开发可删除
    """

    # 美购首页走老打散，新打散逻辑完善后删除
    return _api_filter_service_scatter_for_service_index_v3(
        ctx=ctx,
        order_by=order_by, filters=filters,
        extra_filters=extra_filters, offset=offset, count=count,
        sort_params=sort_params, current_city_id=current_city_id,
        coupon_info_id=coupon_info_id, switch=switch, device_id=device_id,
        use_new_service_info_data=use_new_service_info_data,
        use_tagv3=use_tagv3, is_check_in=is_check_in
    )


def _api_filter_service_scatter_for_service_index_v3(
        ctx=None,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        device_id=None,
        use_new_service_info_data=False,
        use_tagv3=False,
        is_check_in=False,
):
    user = ctx.session.user

    couponinfo_id = ""
    coupon_desc = ""

    # filter service scattered by merchant
    offset = assert_uint(offset, 0)
    count = assert_uint(count, 0)
    order_by = assert_uint(order_by, SERVICE_ORDER_TYPE.DEFAULT)

    params = api_filter_service_extract_params(
        user=user,
        filters=filters,
        extra_filters=extra_filters,
        sort_params=sort_params,
        current_city_id=current_city_id
    )
    sort_params = params['sort_params']
    filters = params['filters']

    next_page = offset / 10

    rpc_client = get_rpc_remote_invoker()

    if order_by == SERVICE_ORDER_TYPE.DEFAULT and next_page < 8 and 'doctor_id' not in filters and \
            'hospital_id' not in filters:
        user_city_tag_id = sort_params.get('user_city_tag_id', -1)

        if user_city_tag_id != -1:
            try:
                obj = City.objects.only('id', 'tag_id').get(tag_id=user_city_tag_id)
                rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[obj.id]).unwrap()
                sort_params['in_whitelist'] = int(bool(rs))
            except (City.DoesNotExist, City.MultipleObjectsReturned):
                sort_params['in_whitelist'] = 0
        else:
            sort_params['in_whitelist'] = 0
        personalized_recommendation_result = doris_service_recommend(
            device_id=device_id, filters=filters, order_by=order_by, offset=0,
            size=80, sort_params=sort_params, use_tagv3=use_tagv3,
            user_city_tag_id=user_city_tag_id, is_check_in=is_check_in
        )

        user_data = personalized_recommendation_result['user_portrait_data']
        service_filter_result = personalized_recommendation_result['normal_sort_data']
        sku_ids = service_filter_result['service_ids']
        doctor_ids = service_filter_result['doctor_ids']
        if len(sku_ids) != len(doctor_ids):
            service_filter_result['service_ids'] = sku_ids
        else:
            doctor_merchant_dict = doctor_merchant_map(doctor_ids)
            hospital_ids = [doctor_merchant_dict[i] for i in doctor_ids]

            items = []
            sku_list = service_filter_result['sku_list']
            for idx in range(len(sku_ids)):
                item = {
                    'sku_id': sku_ids[idx],
                    'merchant_id': hospital_ids[idx],
                    'city_tag_id': sku_list[idx]['city_tag_id'],
                    'nearby_city_tags': map(itemgetter('tag_id'), sku_list[idx]['nearby_city_tags'])
                }
                items.append(item)
            in_whitelist = sort_params.get('in_whitelist', 0)
            data = service_scatter_by_city(items, user_city_tag_id, 10, merge_nearby=not in_whitelist)
            service_filter_result['service_ids'] = map(itemgetter('sku_id'), data)[offset:offset + count]

    else:
        personalized_recommendation_result = doris_service_recommend(
            device_id=device_id, filters=filters, order_by=order_by,
            offset=offset, size=count, sort_params=sort_params,
            use_tagv3=use_tagv3, user_city_tag_id=None, is_check_in=is_check_in
        )

        user_data = personalized_recommendation_result['user_portrait_data']
        service_filter_result = personalized_recommendation_result['normal_sort_data']
    if user_data and offset == 0:
        service_filter_result['service_ids'] = user_data['service_ids'] + service_filter_result['service_ids']
        service_filter_result['doctor_ids'] = user_data['doctor_ids'] + service_filter_result['doctor_ids']
        service_filter_result['sku_list'] = user_data['sku_list'] + service_filter_result['sku_list']
        service_filter_result['total_count'] = user_data['total_count'] + service_filter_result['total_count']
        service_filter_result['highlight'] = user_data['highlight'] + service_filter_result['highlight']

    service_id_list = service_filter_result['service_ids']
    total_count = service_filter_result['total_count']
    special_id = filters.get('special_id', None) if filters else None
    # result = get_result_for_service(
    #     user=user,
    #     special_id=special_id,
    #     get_diary_num=True,
    #     service_id_list=service_id_list,
    #     need_adver_info=False,
    #     service_adver_id_map={},
    #     total_count=total_count,
    #     use_new_service_info_data=use_new_service_info_data,
    # )
    result = {
        'total_count':total_count,
        'service_list':service_id_list
    }
    result['coupon_info_id'] = couponinfo_id
    result['coupon_desc'] = coupon_desc
    result['rank_mode'] = service_filter_result['rank_mode']

    # service_list_dict = {item['service_id']: item for item in result['service_list']}
    # service_ids_list = []
    # for service_id in service_id_list:
    #     if service_list_dict.get(service_id):
    #         service_ids_list.append(service_list_dict.get(service_id))

    # return result

    return result


def _api_filter_service_scatter_for_service_index_v2(
        ctx=None,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),  # TODO: add reference to http://wiki.gengmei.cc/pages/viewpage.action?pageId=1050558
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        device_id=None,
        use_new_service_info_data=False,
        use_tagv3=False,
        is_check_in=False,
):
    user = ctx.session.user

    couponinfo_id = ""
    coupon_desc = ""

    # filter service scattered by merchant
    offset = assert_uint(offset, 0)
    count = assert_uint(count, 0)
    order_by = assert_uint(order_by, SERVICE_ORDER_TYPE.DEFAULT)

    params = api_filter_service_extract_params(
        user=user,
        filters=filters,
        extra_filters=extra_filters,
        sort_params=sort_params,
        current_city_id=current_city_id
    )
    sort_params = params['sort_params']
    filters = params['filters']

    next_page = offset / 10

    rpc_client = get_rpc_remote_invoker()

    if order_by == SERVICE_ORDER_TYPE.DEFAULT and next_page < 8 and 'doctor_id' not in filters and \
            'hospital_id' not in filters:
        user_city_tag_id = sort_params.get('user_city_tag_id', -1)

        if user_city_tag_id != -1:
            try:
                obj = City.objects.only('id', 'tag_id').get(tag_id=user_city_tag_id)
                rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[obj.id]).unwrap()
                sort_params['in_whitelist'] = int(bool(rs))
            except (City.DoesNotExist, City.MultipleObjectsReturned):
                sort_params['in_whitelist'] = 0
        else:
            sort_params['in_whitelist'] = 0
        personalized_recommendation_result = doris_service_recommend(
            device_id=device_id, filters=filters, order_by=order_by, offset=0,
            size=80, sort_params=sort_params, use_tagv3=use_tagv3,
            user_city_tag_id=user_city_tag_id, is_check_in=is_check_in
        )

        user_data = personalized_recommendation_result['user_portrait_data']
        service_filter_result = personalized_recommendation_result['normal_sort_data']
        sku_ids = service_filter_result['service_ids']
        doctor_ids = service_filter_result['doctor_ids']
        if len(sku_ids) != len(doctor_ids):
            service_filter_result['service_ids'] = sku_ids
        else:
            doctor_merchant_dict = doctor_merchant_map(doctor_ids)
            hospital_ids = [doctor_merchant_dict[i] for i in doctor_ids]

            items = []
            sku_list = service_filter_result['sku_list']
            for idx in range(len(sku_ids)):
                item = {
                    'sku_id': sku_ids[idx],
                    'merchant_id': hospital_ids[idx],
                    'city_tag_id': sku_list[idx]['city_tag_id'],
                    'nearby_city_tags': map(itemgetter('tag_id'), sku_list[idx]['nearby_city_tags'])
                }
                items.append(item)
            in_whitelist = sort_params.get('in_whitelist', 0)
            data = service_scatter_by_city(items, user_city_tag_id, 10, merge_nearby=not in_whitelist)
            service_filter_result['service_ids'] = map(itemgetter('sku_id'), data)[offset:offset + count]

    else:
        personalized_recommendation_result = doris_service_recommend(
            device_id=device_id, filters=filters, order_by=order_by,
            offset=offset, size=count, sort_params=sort_params,
            use_tagv3=use_tagv3, user_city_tag_id=None, is_check_in=is_check_in
        )

        user_data = personalized_recommendation_result['user_portrait_data']
        service_filter_result = personalized_recommendation_result['normal_sort_data']
    if user_data and offset == 0:
        service_filter_result['service_ids'] = user_data['service_ids'] + service_filter_result['service_ids']
        service_filter_result['doctor_ids'] = user_data['doctor_ids'] + service_filter_result['doctor_ids']
        service_filter_result['sku_list'] = user_data['sku_list'] + service_filter_result['sku_list']
        service_filter_result['total_count'] = user_data['total_count'] + service_filter_result['total_count']
        service_filter_result['highlight'] = user_data['highlight'] + service_filter_result['highlight']

    service_id_list = service_filter_result['service_ids']
    total_count = service_filter_result['total_count']
    special_id = filters.get('special_id', None) if filters else None
    result = get_result_for_service(
        user=user,
        special_id=special_id,
        get_diary_num=True,
        service_id_list=service_id_list,
        need_adver_info=False,
        service_adver_id_map={},
        total_count=total_count,
        use_new_service_info_data=use_new_service_info_data,
    )

    result['coupon_info_id'] = couponinfo_id
    result['coupon_desc'] = coupon_desc
    result['rank_mode'] = service_filter_result['rank_mode']

    service_list_dict = {item['service_id']: item for item in result['service_list']}
    service_ids_list = []
    for service_id in service_id_list:
        if service_list_dict.get(service_id):
            service_ids_list.append(service_list_dict.get(service_id))
    result['service_list'] = service_ids_list

    return result


def doris_service_recommend(
        device_id, filters, order_by, offset, size,
        sort_params, use_tagv3, user_city_tag_id=None, is_check_in=False
):
    rpc_client = get_rpc_remote_invoker()

    if is_check_in:
        rpc_endpoint = "doris/recommend/views/service_check_in"
        personalized_recommendation_result = rpc_client[rpc_endpoint](
            device_id=device_id,
            offset=offset,
            size=size,
            user_city_tag_id=user_city_tag_id,
        ).unwrap()
    else:
        rpc_endpoint = "doris/search/service_home_recommend"
        personalized_recommendation_result = rpc_client[rpc_endpoint](
            device_id=device_id,
            offset=offset,
            size=size,
            sort_type=order_by,
            filters=filters,
            sort_params=sort_params,
            use_tagv3=use_tagv3
        ).unwrap()

    info_logger.info(dict(
        url=rpc_endpoint,
        device_id=device_id,
        offset=offset,
        size=size,
        sort_type=order_by,
        filters=filters,
        sort_params=sort_params,
        use_tagv3=use_tagv3,
        user_city_tag_id=user_city_tag_id,
        is_check_in=is_check_in,
        res=personalized_recommendation_result,
    ))
    return personalized_recommendation_result


@bind_context("api/my_order/recommend/service/list", login_required=True)
def recommend_service_by_ctr_list(ctx, status=ORDER_STATUS_FILTER.ALL, start_num=0, size=10, current_city_id=None,
                                  device_id=None):
    """
    根据用户订单推荐美购
    :param ctx:
    :param settlement_id:
    :param start_num:
    :param size:
    :return:
    """
    from api.models import Order
    user, city_tag_id, _service_ids = get_user_from_context(ctx), None, []

    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    if current_city_id:
        _, city_tag_id = get_location_tag_id_by_city_id(current_city_id)

    if status == ORDER_STATUS_FILTER.ALL:  # 全部
        _service_ids = list(set(Order.objects.filter(user=user).values_list('service_id', flat=True)))
    elif status == ORDER_STATUS_FILTER.NOT_PAID:  # 未支付
        _service_ids = Order.objects.filter(
            status__in=[ORDER_STATUS_FILTER.CANCEL, ORDER_STATUS_FILTER.NOT_PAID],
            user=user).values_list('service_id', flat=True)
    elif status == ORDER_STATUS_FILTER.PAID:  # 已付款
        _service_ids = list(set(Order.objects.filter(
            status=ORDER_STATUS_FILTER.PAID, user=user).values_list('service_id', flat=True)))
    elif status == ORDER_STATUS_FILTER.USED:  # 已使用
        _service_ids = list(set(Order.objects.filter(
            status=ORDER_STATUS_FILTER.USED, user=user).values_list('service_id', flat=True)))
    elif status == ORDER_STATUS_FILTER.REFUNDED:  # 已退款
        _service_ids = list(set(Order.objects.filter(
            status=ORDER_STATUS_FILTER.REFUNDED, user=user).values_list('service_id', flat=True)))

    # 调用策略接口
    rpc_client = get_rpc_remote_invoker()
    query_params = {
        "user_city_tag_id": city_tag_id,
        "user_id": user.id,
        "start_num": start_num,
        "size": size,
        "cl_id": device_id,
    }
    try:
        rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[current_city_id, ]).unwrap()
        in_whitelist = int(bool(rs))
    except:
        in_whitelist = 0

    query_params["in_whitelist"] = in_whitelist
    recommend_service_ids_info = {}
    try:
        recommend_service_ids_info = rpc_client['doris/recommend/order/service'](
            service_ids=_service_ids, query_params=query_params).unwrap()
    except Exception:
        logging_exception()

    recommend_service_ids = recommend_service_ids_info.get('service_ids', [])
    services_info_dic = get_toc_spu_info_list_mapping_by_spu_ids(recommend_service_ids)
    recommend_services = [services_info_dic[str(sid)]
                          for sid in recommend_service_ids if str(sid) in services_info_dic]
    return {
        'recommend_services_infos': recommend_services,
    }


@bind('api/service_items/visual_detail')
def doctor_service_items(sku_list, sku_id="", spu_id="", hospital_name="", region_name="", province_name="",
                         city_name=""):
    """
        可视化获取sku相关信息
    """

    service_items = ServiceItem.objects.filter(id__in=sku_list)
    if sku_id:
        service_items = service_items.filter(id=sku_id)
    if spu_id:
        service_items = service_items.filter(service_id=spu_id)

    sku_names = ServiceItem.get_items_name([item.id for item in service_items])
    # 获取sku价格信息
    item2price_map = Service.get_current_price_info_by_service_item_ids(sku_list)

    service_ids = set([i.service_id for i in service_items])
    services = Service.objects.filter(id__in=service_ids)

    if spu_id:
        services = services.filter(id=spu_id)
    if hospital_name:
        services = services.filter(doctor__hospital__name__contains=hospital_name)
    if region_name:
        services = services.filter(doctor__hospital__city__province__region__id=region_name)
    if province_name:
        services = services.filter(doctor__hospital__city__province__id=province_name)
    if city_name:
        services = services.filter(doctor__hospital__city__id=city_name)

    services = services \
        .select_related("doctor__name") \
        .select_related("doctor__hospital__name") \
        .select_related("doctor__hospital__city__name") \
        .select_related("doctor__hospital__city__province__name") \
        .select_related("doctor__hospital__city__province__region__name") \
        .values("id",
                "name",
                "image_header",
                "doctor__name",
                "doctor__hospital__name",
                "doctor__hospital__city__name",
                "doctor__hospital__city__province__name",
                "doctor__hospital__city__province__region__name")

    spu_dic = {}
    sku_dic = {}
    for i in services:
        spu_dic[i.get('id')] = {
            'service_id': i.get('id'),
            'service_name': i.get('name', ""),
            'image_header': i.get('image_header', ""),
            'doctor_name': i.get('doctor__name', ""),
            'hospital_name': i.get('doctor__hospital__name', ""),
            'region_name': i.get("doctor__hospital__city__province__region__name", ""),
            "province_name": i.get("doctor__hospital__city__province__name", ""),
            "city_name": i.get("doctor__hospital__city__name", ""),
        }
    if spu_dic:
        service_item_ids = [item.id for item in service_items]
        service_item_id_to_sku_name_info = get_sku_id_to_sku_name_info(service_item_ids, False)
        for item in service_items:
            service_item_name = service_item_id_to_sku_name_info.get(str(item.id), {}).get('name', '')
            if not service_item_name:
                service_item_name = item.service.name
            if spu_dic.get(item.service_id):
                price_info = item2price_map.get(item.id)
                sku_dic[item.id] = dict(
                    service_item_id=item.id,
                    # service_item_name=u'+'.join(sku_names.get(item.id, [])),
                    service_item_name=service_item_id_to_sku_name_info.get(str(item.id), {}).get('name', ''),
                    gengmei_price=price_info['gengmei_price'],
                    pre_payment_price=price_info['pre_payment_price'],
                )
                sku_dic[item.id].update(spu_dic.get(item.service_id))
            else:
                pass
    return sku_dic


@bind('api/service/visual_detail')
def doctor_service_items(service_ids, spu_id="", hospital_name="", region_name="", province_name="", city_name=""):
    """
        可视化获取spu相关信息
    """
    data = {}
    services = Service.objects.filter(id__in=service_ids)
    if spu_id:
        services = services.filter(id=spu_id)
    if hospital_name:
        services = services.filter(doctor__hospital__name__contains=hospital_name)
    if region_name:
        services = services.filter(doctor__hospital__city__province__region__id=region_name)
    if province_name:
        services = services.filter(doctor__hospital__city__province__id=province_name)
    if city_name:
        services = services.filter(doctor__hospital__city__id=city_name)

    services = services \
        .select_related("doctor__name") \
        .select_related("doctor__hospital__name") \
        .select_related("doctor__hospital__city__name") \
        .select_related("doctor__hospital__city__province__name") \
        .select_related("doctor__hospital__city__province__region__name") \
        .values("id",
                "name",
                "image_header",
                "doctor__name",
                "doctor__hospital__name",
                "doctor__hospital__city__name",
                "doctor__hospital__city__province__name",
                "doctor__hospital__city__province__region__name")

    service_item_ids = [item['id'] for item in services]
    service_item_id_to_sku_name_info = get_sku_id_to_sku_name_info(service_item_ids, False)
    for s in services:
        item_ids = ServiceItem.objects.filter(service_id=s.get("id"), is_delete=False).values_list('id', flat=True)
        price_info = Service.get_price_range_from_item_ids(item_ids)
        data[s.get("id")] = {
            'service_id': s.get("id"),
            'service_name': service_item_id_to_sku_name_info.get(str(s["id"]), {}).get('name', ''),
            'image_header': s.get("image_header"),
            'doctor_name': s.get("doctor__name"),
            'hospital_name': s.get("doctor__hospital__name", ""),
            'gengmei_price': '{}-{}'.format(price_info['gengmei_price'][0], price_info['gengmei_price'][1]),
            "pre_payment_price": '{}-{}'.format(price_info['pre_payment_price'][0], price_info['pre_payment_price'][1]),
            'region_name': s.get("doctor__hospital__city__province__region__name", ""),
            "province_name": s.get("doctor__hospital__city__province__name", ""),
            "city_name": s.get("doctor__hospital__city__name", ""),
        }

    return data


def _api_filter_service_scatter_for_service_index_v4(
        ctx=None,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        use_new_service_info_data=False,
        use_tagv3=False,
):
    user = ctx.session.user
    couponinfo_id = ""
    coupon_desc = ""
    coupon_prepay_threshold = None
    if coupon_info_id:
        couponinfo = CouponInfo.objects.select_related('coupon').filter(id=coupon_info_id).first()
        if couponinfo and couponinfo.user == user:
            couponinfo_id = str(coupon_info_id)

            c = couponinfo.coupon
            coupon_prepay_threshold = c.has_threshold and c.prepay_threshold or None
            # 7780添加
            coupon_desc_1 = ""
            benefit_type = c.benefit_type
            if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                coupon_desc_1 = "以下商品可使用直减{}元美券".format(c.value)
            elif benefit_type == BENEFIT_TYPE.MANJIAN:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.value)
            elif benefit_type == BENEFIT_TYPE.DISCOUNT:
                coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(c.prepay_threshold, c.upper_limit)

            # 7780折扣券注释
            # coupon_desc_1 = "以下商品可使用满{}-{}元美券".format(
            #    c.prepay_threshold, c.value) if c.has_threshold else "以下商品可使用直减{}元美券".format(c.value)
            coupon_desc_2 = "预付款抵扣" if c.coupon_type == COUPON_TYPES.PLATFORM else "尾款抵扣"
            coupon_desc = "{}（{}）".format(coupon_desc_1, coupon_desc_2)

            spids = couponinfo.coupon_restrict_special_ids
            doctor_ids = couponinfo.coupon_restrict_doctor_ids
            sku_ids = couponinfo.coupon_restrict_sku_ids
            service_ids = set(ServiceItem.objects.filter(
                parent_id=0, id__in=sku_ids, is_delete=False
            ).values_list('service_id', flat=True)) if sku_ids else []
            if spids:
                filters['special_ids_and'] = list(spids)
            if doctor_ids:
                filters['doctor_ids'] = list(doctor_ids)
            if service_ids:
                filters['service_ids'] = list(service_ids)
    # filter service scattered by merchant
    offset = assert_uint(offset, 0)
    count = assert_uint(count, 0)
    order_by = assert_uint(order_by, SERVICE_ORDER_TYPE.DEFAULT)
    params = api_filter_service_extract_params(
        user=user,
        filters=filters,
        extra_filters=extra_filters,
        sort_params=sort_params,
        current_city_id=current_city_id
    )
    sort_params = params['sort_params']
    filters = params['filters']
    next_page = offset / 10
    if order_by == SERVICE_ORDER_TYPE.DEFAULT and next_page < 8 and 'doctor_id' not in filters and \
            'hospital_id' not in filters:
        user_city_tag_id = sort_params.get('user_city_tag_id', -1)
        rpc_client = get_rpc_remote_invoker()
        if user_city_tag_id != -1:
            try:
                obj = City.objects.only('id', 'tag_id').get(tag_id=user_city_tag_id)
                rs = rpc_client['doris/hera/in_city_whitelist'](city_ids=[obj.id]).unwrap()
                sort_params['in_whitelist'] = int(bool(rs))
            except (City.DoesNotExist, City.MultipleObjectsReturned):
                sort_params['in_whitelist'] = 0
        else:
            sort_params['in_whitelist'] = 0
        service_filter_result = filter_service(
            offset=0,
            size=80,
            sort_type=order_by,
            filters=filters,
            sort_params=sort_params,
            use_tagv3=use_tagv3,
        )
        sku_ids = service_filter_result['service_ids']
        doctor_ids = service_filter_result['doctor_ids']
        if len(sku_ids) != len(doctor_ids):
            service_filter_result['service_ids'] = sku_ids
        else:
            doctor_merchant_dict = doctor_merchant_map(doctor_ids)
            hospital_ids = [doctor_merchant_dict[i] for i in doctor_ids]
            items = []
            sku_list = service_filter_result['sku_list']
            for idx in range(len(sku_ids)):
                item = {
                    'sku_id': sku_ids[idx],
                    'merchant_id': hospital_ids[idx],
                    'city_tag_id': sku_list[idx]['city_tag_id'],
                    'nearby_city_tags': map(itemgetter('tag_id'), sku_list[idx]['nearby_city_tags'])
                }
                items.append(item)
            in_whitelist = sort_params.get('in_whitelist', 0)
            data = service_scatter_by_city(items, user_city_tag_id, 10, merge_nearby=not in_whitelist)
            service_filter_result['service_ids'] = map(itemgetter('sku_id'), data)[offset:offset + count]
    else:
        service_filter_result = filter_service(
            offset=offset,
            size=count,
            sort_type=order_by,
            filters=filters,
            sort_params=sort_params,
            use_tagv3=use_tagv3
        )
    service_id_list = service_filter_result['service_ids']
    total_count = service_filter_result['total_count']
    result = dict()
    result['coupon_info_id'] = couponinfo_id
    result['coupon_desc'] = coupon_desc
    result['rank_mode'] = service_filter_result['rank_mode']
    result['service_list'] = service_id_list
    result['total_count'] = total_count
    return result


@bind_context('api/service/filter_with_scatter_v2')
@list_interface(
    offset_name='offset', limit_name='count',
    element_model=Service, element_func_list=[Service.get_service_info]
)
def api_filter_service_scatter_v2(
        ctx,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        filters=None,
        extra_filters=(),
        offset=0, count=10,
        sort_params=None,
        current_city_id=None,
        coupon_info_id=None,
        switch=False,
        use_new_service_info_data=False,
        use_tagv3=False,
):
    """
    2.0只返回id
    :param ctx:
    :param order_by:
    :param filters:
    :param extra_filters:
    :param offset:
    :param count:
    :param sort_params:
    :param current_city_id:
    :param coupon_info_id:
    :param switch:
    :param use_new_service_info_data:
    :param use_tagv3:
    :return:
    """
    return _api_filter_service_scatter_for_service_index_v4(
        ctx=ctx,
        order_by=order_by, filters=filters,
        extra_filters=extra_filters, offset=offset, count=count,
        sort_params=sort_params, current_city_id=current_city_id,
        coupon_info_id=coupon_info_id, switch=switch,
        use_new_service_info_data=use_new_service_info_data, use_tagv3=use_tagv3)