# -*- coding: UTF-8 -*-
from __future__ import absolute_import
import math
import random
import datetime
from django.db import IntegrityError
from django.db.models import Q
from django.db import transaction
from django.conf import settings
from gm_types.error import ERROR

from hera.service_utils import service_is_activity, sku_is_activity
from hera.tasks.special import add_purchase_list, add_specialresource, \
    add_speciallayout, create_new_special_floor_data, create_serviceitem_prices
from rpc.exceptions import GaiaRPCFaultException, UniError

from gm_types.gaia import DOCTOR_SECKILL_APPLY_STATUS, ACTIVITY_TYPE_ENUM, SPECIAL_MODULE
from gm_types.gaia import SECKILL_TYPE, SPECIAL_RULE_SET
from hera.models import UserPerm
from hera.queries.specialservice import SpecialServiceDQ
from hera.utils import check_business
from rpc.decorators import bind_context, bind
from rpc.exceptions import (
    RPCIntegrityError, RPCNotFoundException,
)
from rpc.tool.error_code import gen, CODES
from rpc.tool.log_tool import info_logger, exception_logger, logging_exception
from rpc.tool.dict_mixin import to_dict
from api.tool.datetime_tool import itv2time
from api.models import SpecialSeckillButton, DoctorSeckillApply, SKUPriceRule, GroupBuySpecial, SeckillPolymerButton, \
    SpecialRegion, SpecialRelatedTagV3
from api.models import Special, SpecialItem, Service, SpecialGadget, ServiceItem, Share, SpecialStorey, ServiceItemPrice
from api.models import DoctorSeckillApplyRecord, SpecialRelatedTag, SpecialRule
from api.models import SpecialLayout, SpecialExterior, City, Region, PhotoStorage, SpecialResource
from api.models import SpecialExteriorCity, SpecialExteriorRegion, SpecialExteriorService, SeckillPolymer, \
    SeckillRelatedSpecial, SeckillPolymerLayout
from api.models import Doctor
from point.models.mall import PointActivity

from ..datatables import SpecialDT
from ..queries.special import SpecialDQ, SpecialSeckillButtonDQ, DoctorSeckillApplyDQ
from ..queries.specialexterior import SpecialExteriorrDQ
import json
from utils.time_tools import strtime2datetime
from api.tasks.service_task import refresh_service_cache_with_special_ids
from api.manager.service_info_manager import get_sku_id_to_sku_name_info

uri_pre = 'hera/special'


@bind_context(uri_pre + '/choices')
def special_choices(ctx, q='', filter_type=None, page=1, num=30, initial=None, is_online=False, is_new_special=0):
    page = int(page)
    num = int(num)

    if initial is not None:
        if isinstance(initial, (list, tuple)):
            qry = Q(id__in=initial)
        else:
            qry = Q(id=initial)
    else:
        qry = Q(id__contains=q) | Q(title__contains=q)
    if is_online:
        qry &= Q(is_online=True)
    if is_new_special:
        qry &= Q(is_new_special=1)
    if filter_type:
        if isinstance(type, (list, tuple)):
            for t in filter_type:
                qry &= Q(**{'{}'.format(t): True})
        else:
            qry &= Q(**{'{}'.format(filter_type): True})
    query = Special.objects.using(settings.SLAVE_DB_NAME).filter(qry)
    total_count = 0
    start_pos = (page - 1) * num
    start_pos = start_pos if start_pos >= 0 else 0
    results = [
        {
            'id': obj.id,
            'text': u'{}:{}'.format(obj.id, obj.title),
        } for obj in query[start_pos: start_pos + num]
        ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(uri_pre + '/query')
def special_query(ctx, options):
    dqobj = SpecialDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/list')
def special_datatable(ctx, req_data):
    dtobj = SpecialDT(Special)
    return dtobj.process(req_data, ['id', 'title'])


@bind_context(uri_pre + '/gadget/get')
def special_gadget_detail(ctx, special_id, options=None):
    special_gadgets = SpecialGadget.objects.filter(special__id=special_id).all()
    options = {
        'fields': None,
        'excludes': None,
        'expands': None,
    }
    data = []
    for special_gadget in special_gadgets:
        special_gadget.image_data = json.loads(special_gadget.image_data)
        data.append(to_dict(special_gadget, **options))
    return data


@bind_context(uri_pre + '/gadget/edit')
def special_gadget_edit(ctx, special_id, count_num=0, special_gadgets=None, is_show=False):
    # image_datas = special_gadgets['image_data']
    res_validate = []
    for image_datas in special_gadgets:
        for image_data in image_datas['image_data']:
            if image_data.get('url_type') == '2':
                res = service_is_activity(image_data.get('url_params', {}).get('id', ''), image_datas['special_id'])
                if res['code'] == 0:
                    res_validate.append(res)
            if image_data.get('url_type') == '29':
                res = sku_is_activity(image_data.get('url_params', {}).get('id', ''), image_datas['special_id'])
                if res['code'] == 0:
                    res_validate.append(res)
    if res_validate:
        return {
            'code': 0,
            'message': (u','.join([item['message'] for item in res_validate if item['code'] == 0]))
        }

    if special_id:
        SpecialGadget.objects.filter(special__id=special_id).delete()
    photo_query = PhotoStorage.objects.filter(is_online=1)
    max_photo_count = photo_query.count()
    max_photo_id = photo_query.values('id')
    if int(count_num) <= max_photo_count:
        samples = random.sample([pg['id'] for pg in max_photo_id], int(count_num))
        page_obj_container = []
        for page_id in samples:
            page_obj = PhotoStorage.objects.get(id=page_id)
            page_obj_container.append(page_obj)
        for special_gadget in special_gadgets:
            specialgadget = SpecialGadget()
            specialgadget.ordering = special_gadget["order"]
            for img_data in special_gadget['image_data']:
                if not img_data.get('image'):
                    img_obj = (random.sample(page_obj_container, 1))[0]
                    img_data['image'] = img_obj.image
                    page_obj_container.remove(img_obj)
            specialgadget.image_data = json.dumps(special_gadget["image_data"])
            specialgadget.special_id = special_gadget["special_id"]
            specialgadget.save()
    special = Special.objects.get(id=special_id)
    special.is_show_gadget = is_show
    special.save()
    return {
        'code': 1,
        'message': u'保存成功！'
    }


@bind_context(uri_pre + '/gadget/content')
def special_content_edit(ctx, special_id, content=None, is_show=False):
    if special_id:
        special = Special.objects.get(id=special_id)
        special.content = content
        special.is_show_content = is_show
        special.save()
    return 1


@bind_context(uri_pre + '/gadget/coupons')
def special_coupons_edit(ctx, special_id, coupons=None, is_show=False):
    if special_id:
        special = Special.objects.get(id=special_id)
        special.coupons = coupons
        special.is_show_coupons = is_show
        special.save()
    return 1


@bind_context(uri_pre + '/get')
def special_detail(ctx, special_id, options=None, seckill=False):
    try:
        special = Special.objects.get(id=special_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    special_data = to_dict(special, **options)
    special_data['content'] = special.content
    special_data['sp_items'] = []
    rank_position_list = []
    if special.rank_position:
        rank_position_dict = json.loads(special.rank_position)
        sorted_rank_position = sorted(rank_position_dict.items(), key=lambda d: int(d[0]))
        for rank, val_rank in sorted_rank_position:
            rank_position_list.append(str(val_rank[1]))
        special_data['rank_position'] = rank_position_list
    if special.service_zone_content:
        special_data['service_zone_content'] = json.loads(special.service_zone_content)

    # 加入拼团相关数据
    if special.groupbuy:
        groupbuy_info = {
            "groupbuy_type": special.groupbuy.groupbuy_type,
            'active_type':special.groupbuy.active_type,
            "nums": special.groupbuy.nums,
            "countdown": itv2time(special.groupbuy.countdown),
            'share_wechat_images': json.loads(special.groupbuy.share_wechat_images)
            if special.groupbuy.share_wechat_images else [],
        }
        special_data.update(groupbuy_info)

    special_data['layouts'] = {}
    for layout in special.speciallayout_set.all():
        if layout.related:
            related = json.loads(layout.related)
        else:
            related = ''
        if layout.module == SPECIAL_MODULE.RECOMMEND_DOCTOR:
            for data in related:
                doctor = Doctor.objects.get(id=data['doctor_id'])
                data['name'] = doctor.name
                data['title'] = doctor.title
                data['hospital_name'] = getattr(doctor.hospital, 'name', '')
        layout_data = {
            'id': layout.id,
            'is_visible': layout.is_visible,
            'related': related,
        }
        special_data['layouts'][layout.module] = layout_data
    # 获取专题活动的分享信息
    try:
        share_info = Share.objects.get(id=special.share.id)
        special_data["share_title"] = share_info.title
        special_data["share_content"] = share_info.content
        special_data["share_moments"] = share_info.moments
        special_data["share_weibo"] = share_info.weibo
        special_data["share_image"] = share_info.image
    except:
        info_logger.info(__import__('traceback').format_exc())
    return special_data


@bind_context(uri_pre + '/seckill_get')
def sekill_detail_seckill(ctx, special_id, options=None, seckill=False):
    """
    获取秒杀的详情页
    create by oldman at 2017-01-08 sku重构相关秒杀专题改变
    """
    try:
        special = Special.objects.get(id=special_id, is_seckill=True)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    special_data = to_dict(special, **options)
    special_data['content'] = special.content
    special_data['layouts'] = {}
    special_data['order'] = special_data['rank']
    for layout in special.speciallayout_set.all():
        if layout.related:
            related = json.loads(layout.related)
        else:
            related = ''
        layout_data = {
            'id': layout.id,
            'is_visible': layout.is_visible,
            'related': related,
        }
        special_data['layouts'][layout.module] = layout_data
    try:
        share_info = Share.objects.get(id=special.share.id)
        special_data["share_title"] = share_info.title
        special_data["share_content"] = share_info.content
        special_data["share_moments"] = share_info.moments
        special_data["share_weibo"] = share_info.weibo
        special_data["share_image"] = share_info.image
    except:
        info_logger.info(__import__('traceback').format_exc())
    return special_data


@bind_context(uri_pre + '/edit')
@transaction.atomic
def special_edit(ctx, special_id=None, special_info=None, seckill=False):
    """
    专题的编辑
    :param ctx:
    :param special_id:
    :param special_info:
    :param seckill:
    :return:
    """

    region = special_info.pop('region', None)
    if special_info is None:
        return None
    share_info = {}
    if 'share_title' in special_info:
        share_info = {
            "title": special_info.pop("share_title"),
            "content": special_info.pop("share_content"),
            "moments": special_info.pop("share_moments"),
            "weibo": special_info.pop("share_weibo"),
            "image": special_info.pop("share_image"),
        }
    if 'campaign' in special_info:  # 秒杀专场不关联活动
        special_info['campaign_id'] = special_info.pop('campaign')
        # 判断是否可以设置为超值
        is_overflow = special_info.get('is_overflow', False)
        campaign_id = special_info['campaign_id']
        if is_overflow and campaign_id:
            overflow_special_ids = Special.objects.filter(
                campaign_id=campaign_id, is_overflow=True,
            ).exclude(id=special_id).values_list('id', flat=True)
            if overflow_special_ids and special_id not in overflow_special_ids:
                # 该活动已有超值专场
                raise gen(CODES.CAMPAIGN_HAD_OVERFLOW)
    if special_id is None:
        try:
            special = Special.objects.create(**special_info)
            # set skupricerule
            if special.is_seckill:
                activity_type = ACTIVITY_TYPE_ENUM.SECKILL
            elif special.is_more_buy:
                activity_type = ACTIVITY_TYPE_ENUM.MOREBUY
            else:
                activity_type = ACTIVITY_TYPE_ENUM.SPECIAL
            SKUPriceRule.objects.create(
                name=special.title,
                is_enable=False,
                start_time=special.start_time,
                end_time=special.end_time,
                activity_type=activity_type,
                activity_id=special.id,
                more_buy_count=special.more_buy_count,
            )
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            special = Special.objects.get(id=special_id)
            if 'rank_position' in special_info:
                special.rank_position = special_info['rank_position']
            if SpecialLayout.objects.filter(module=SPECIAL_MODULE.TURNTABLE, special_id=special_id):
                related_data = SpecialLayout.objects.get(module=SPECIAL_MODULE.TURNTABLE, special_id=special_id).related
                lottery_id = json.loads(related_data)['lottery_id']
                point_obj = PointActivity.objects.get(id=lottery_id)
                special_end_time = strtime2datetime(special_info['end_time'])
                if point_obj.end_time < special_end_time:
                    raise
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in special_info.iteritems():
            setattr(special, k, v)
        special.save()
        ### 专题时间调整, 意味着价格规则调整, 意味着会生成新的价格策略, 需要进行验证
        detected_keys = ['is_online', 'start_time', 'end_time']
        price_detected = False
        for dk in detected_keys:
            if special_info.get(dk) is not None:
                price_detected = True
                break
        # set skupricerule
        activity_type = ACTIVITY_TYPE_ENUM.SPECIAL
        if special.is_seckill:
            activity_type = ACTIVITY_TYPE_ENUM.SECKILL
        elif special.is_more_buy:
            activity_type = ACTIVITY_TYPE_ENUM.MOREBUY

        skupricerule = SKUPriceRule.objects.filter(
            activity_type=activity_type,
            activity_id=special.id
        ).first()
        if skupricerule:
            skupricerule.is_enable = special.is_online
            skupricerule.start_time = special.start_time
            skupricerule.end_time = special.end_time
            skupricerule.save()
        # 价格校验, 美购列表数据过多，导致保存超时
        # 需要使用新的方式来校验
        # if price_detected:
        #     ServiceItem.validate_sku_price_for_special(special.id)

    # set special share info
    if special.share_id:
        share = Share.objects.get(id=special.share_id)
        for key, value in share_info.iteritems():
            setattr(share, key, value)
        share.save()
    else:
        share = Share.objects.create(**share_info)
        special.share_id = share.id
        special.save()
    refresh_service_cache_with_special_ids(special_ids=[special.id])
    return {'id': special.id}


def replication_special_layout(old_special_id, new_special_id):
    """
    :param old_special_id:
    :param new_special_id:
    :return:
    """
    layout_objs_list = []
    layouts = SpecialLayout.objects.filter(special_id=old_special_id)
    for layout in layouts:
        layout_data = to_dict(layout, excludes=['special', 'id', 'related'])
        layout_data['special_id'] = new_special_id
        layout_data['related'] = layout.related
        layout_objs_list.append(SpecialLayout(**layout_data))
    SpecialLayout.objects.bulk_create(layout_objs_list)


def replication_special_floor(old_special_id, new_special_id):
    """
    创建子楼层数据，楼层数据复制挺复杂的
    1. 复制父级楼层数据
    2. 复制子级楼层数据
    3. 复制父级美购列表数据
    4. 复制父级关联的标签数据
    5. 复制子级美购列表数据
    6. 复制子级标签数据
    :param old_special_id:
    :param new_special_id:
    :return:
    """
    has_child_storey_related_dict, has_no_child_storey_related_dict = {}, {}
    specialstoreys = SpecialStorey.objects.filter(
        special_id=old_special_id, parent_id__isnull=True
    )

    def replication_parent_service_list(old_special_id, new_special_id):
        """
        复制父级美购列表数据
        :param old_special_id:
        :param new_special_id:
        :return:
        """
        specialitem_objs = []
        specialitems = SpecialItem.objects.filter(special_id=old_special_id, floor_id__isnull=True)

        if not specialitems:
            return
        for specialitem in specialitems:
            new_special_item_data = to_dict(
                specialitem,
                fields=['name', 'subtitle', 'image_url', 'doctor_id',
                        'topic_id', 'diary_id', 'service_id',
                        'rank', 'position', 'is_recommend', 'serviceitem_id',
                        'doctorseckillapply_id', 'sell_num', 'tip']
            )
            new_special_item_data['special_id'] = new_special_id
            specialitem_objs.append(SpecialItem(**new_special_item_data))
        SpecialItem.objects.bulk_create(specialitem_objs)

    def replication_parent_tag(old_special_id, new_special_id):
        """
        复制父级关联的标签数据
        :param old_special_id:
        :param new_special_id:
        :return:
        """
        tag_ids = list(SpecialRelatedTag.objects.filter(
            special_id=old_special_id, floor_id__isnull=True
        ).values_list('tag_id'))

        if not tag_ids:
            return

        objs = [
            SpecialRelatedTag(special_id=new_special_id, tag_id=tag_id)
            for tag_id in tag_ids
        ]
        new_objs = SpecialRelatedTag.objects.bulk_create(objs)
        return [obj.id for obj in new_objs]

    replication_parent_service_list(old_special_id, new_special_id)
    replication_parent_tag(old_special_id, new_special_id)
    with transaction.atomic():
        try:
            for old_parent_storey in specialstoreys:
                # 先创建父楼层数据
                new_storey_data = to_dict(old_parent_storey, excludes=['related', 'special_id', 'id'])
                new_storey_data['related'] = old_parent_storey.related
                new_storey_data['special_id'] = new_special_id
                new_parent_storey = SpecialStorey.objects.create(**new_storey_data)  # 创建父楼层

                # 创建父楼层对应的子楼层的数据
                for old_child_storey in SpecialStorey.objects.filter(special_id=old_special_id, parent_id=old_parent_storey.id):
                    new_pstorey_data = to_dict(old_child_storey, excludes=['related', 'special_id', 'id'])
                    new_pstorey_data['related'] = old_child_storey.related
                    new_pstorey_data['special_id'] = new_special_id
                    new_pstorey_data['parent_id'] = new_parent_storey.id
                    new_child_storey = SpecialStorey.objects.create(**new_pstorey_data)
                    has_child_storey_related_dict[(old_special_id, old_child_storey.id)] = (new_special_id, new_child_storey.id)
                else:
                    has_no_child_storey_related_dict[(old_special_id, old_parent_storey.id)] = (new_special_id, new_parent_storey.id)
        except Exception as e:
            logging_exception()
            raise e
    # 存在子楼层
    if has_child_storey_related_dict:
        create_new_special_floor_data.delay(has_child_storey_related_dict)

    if has_no_child_storey_related_dict:
        create_new_special_floor_data.delay(has_no_child_storey_related_dict)

    if SpecialItem.objects.filter(special_id=new_special_id, floor_id__isnull=True).exists():
        create_serviceitem_prices.delay(new_special_id)



@bind_context(uri_pre + '/replication_new_special')
def replication_new_special(ctx, special_id):
    """复制新专题"""
    sp = None
    special = Special.objects.get(id=special_id)
    sku_objs = SKUPriceRule.objects.filter(activity_id=special.id).first()
    old_rule_info, share_info, share, sp = {}, {}, None, None
    special_info = to_dict(
        special,
        fields=[
            "title",
            "desc",
            "content",
            "type",
            "style_type",
            "theme",
            "start_time",
            "end_time",
            "doctor_name",
            "hospital_name",
            "price_1",
            "price_2",
            "coupons",
            "is_show_coupons",
            "is_show_content",
            "is_show_gadget",
            "is_online",
            "rank",
            "image",
            "icon",
            "recommend",
            "is_seckill",
            "is_overflow",
            "purchase_limit",
            "is_servicehome",
            "is_recommendpool",
            "is_advertisement",
            "is_show_service_zone",
            "service_zone_content",
            "seckill_type",
            "is_show_resource",
            "rank_position",
            "xiaochengxu_img",
            "polymer_backgrond_image",
            "polymer_unselected_image",
            "activity_rules",
            "promotion_image",
            "is_new_special",
            "is_show_address",
            "is_more_buy",
            "more_buy_count",
        ]
    )
    if sku_objs:
        old_rule_info = to_dict(sku_objs, fields=["name",
                                                  "is_enable",
                                                  "start_time",
                                                  "end_time",
                                                  "activity_type",
                                                  "refund_anytime",
                                                  "can_use_points",
                                                  "share_get_cashback",
                                                  "groupbuy_type",
                                                  "active_type",
                                                  "groupbuy_nums",
                                                  "groupbuy_countdown",
                                                  "more_buy_count",])
    if special.share:
        share_info = to_dict(
            Share.objects.get(id=special.share_id),
            fields=['title', 'content', 'moments', 'weibo', 'image'],
        )
    with transaction.atomic():
        try:
            sp = Special.objects.create(**special_info)
            if share_info:
                share = Share.objects.create(**share_info)
            if old_rule_info:
                sku = SKUPriceRule.objects.create(**old_rule_info)
                sku.activity_id = sp.id
                sku.save()
            sp.share_id = share.id if share else None
            sp.tags = list(special.tags.values_list('id', flat=True))
            sp.services = list(special.services.values_list('id', flat=True))
            sp.save()
        except Exception as e:
            logging_exception()
            raise e

    replication_special_layout(special_id, sp.id)
    replication_special_floor(special_id, sp.id)
    refresh_service_cache_with_special_ids(special_ids=[sp.id])
    return {'id': sp.id}



@bind_context(uri_pre + '/resource/get')
def special_resource_detail(ctx, special_id, options=None):
    resource_data = SpecialResource.objects.filter(special__id=special_id).all()
    options = {
        'fields': None,
        'excludes': None,
        'expands': None,
    }
    data = []
    for resource in resource_data:
        resource.image_data = json.loads(resource.image_data)
        data.append(to_dict(resource, **options))
    return data


@bind_context(uri_pre + '/resource/edit')
def seckill_resource_edit(ctx, special_id, special_resource=None, is_show=False):
    if special_id:
        SpecialResource.objects.filter(special_id=special_id).delete()
    for resource in special_resource:
        obj = SpecialResource.objects.create(
            special_id=special_id,
            rank=resource['rank'],
            image_data=json.dumps(resource['image_data']),
        )
    special = Special.objects.get(id=special_id)
    special.is_show_resource = is_show
    special.save()
    return 1


@bind_context(uri_pre + '/seckill_edit')
@transaction.atomic
def special_edit_seckill(ctx, special_id=None, special_info=None, share_info=None):
    if special_info is None:
        return None
    if share_info is None:
        return None
    # 专题不能有重复的
    special_info['rank'] = special_info.pop('order')
    seckill_type = special_info['seckill_type']
    ids = Special.objects.exclude(id=special_id). \
        exclude(Q(end_time__lte=special_info['start_time']) | Q(start_time__gte=special_info['end_time'])). \
        filter(seckill_type=SECKILL_TYPE.GENGMEI).values_list('id').filter(is_seckill=True).distinct()
    if len(ids) > 0 and seckill_type == SECKILL_TYPE.GENGMEI:
        return {'error': '秒杀专场时间重合:' + ', '.join(['%d' % i[0] for i in ids])}
    if special_info['rank'] != 0 and special_info['seckill_type'] == SECKILL_TYPE.GENGMEI:
        return {'error': '更美秒杀排序只能为0'}
    if special_id is None:
        try:
            special = Special.objects.create(**special_info)
            # set skupricerule
            if special.is_seckill:
                activity_type = ACTIVITY_TYPE_ENUM.SECKILL
            elif special.is_more_buy:
                activity_type = ACTIVITY_TYPE_ENUM.MOREBUY
            else:
                activity_type = ACTIVITY_TYPE_ENUM.SPECIAL
            SKUPriceRule.objects.create(
                name=special.title,
                is_enable=False,
                start_time=special.start_time,
                end_time=special.end_time,
                activity_type=activity_type,
                activity_id=special.id,
                more_buy_count=special.more_buy_count,
            )
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            special = Special.objects.get(id=special_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        special_info.pop('share')

        update_fields = {}
        for k, v in special_info.iteritems():
            if getattr(special, k) != v:
                update_fields[k] = v
                setattr(special, k, v)
        if update_fields:
            Special.objects.filter(id=special_id).update(**update_fields)

        ### 专题时间调整, 意味着价格规则调整, 意味着会生成新的价格策略, 需要进行验证
        detected_keys = ['is_online', 'start_time', 'end_time']
        price_detected = False
        for dk in detected_keys:
            if special_info.get(dk) is not None:
                price_detected = True
                break

        # set skupricerule
        skupricerule = SKUPriceRule.objects.get(
            activity_type=ACTIVITY_TYPE_ENUM.SECKILL if special.is_seckill else ACTIVITY_TYPE_ENUM.SPECIAL,
            activity_id=special.id
        )
        skupricerule.is_enable = special.is_online
        skupricerule.start_time = special.start_time
        skupricerule.end_time = special.end_time
        skupricerule.save()
        if price_detected:
            ServiceItem.validate_sku_price_for_special(special.id)

    if not share_info.get('share_id', None):
        try:
            share_info.pop('share_id')
            share = Share.objects.create(**share_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            share = Share.objects.get(id=int(share_info.pop('share_id')))
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in share_info.iteritems():
            setattr(share, k, v)
        share.save()
    special.share_id = share.id
    Special.objects.filter(id=special.id).update(share_id=share.id)
    refresh_service_cache_with_special_ids.delay(special_ids=[special.id])
    return {'id': special.id}


@bind_context(uri_pre + '/get_servicesitems_from_ids')
def get_servicesitems_from_ids(ctx, ids):
    """
    :param ids: 申请秒杀list列表
    create by oldman 2017-01-08 根据申请ID向秒杀专场添加sku
    """
    services_data = []
    doctorseckillapply = DoctorSeckillApply.objects.filter(id__in=ids, status=DOCTOR_SECKILL_APPLY_STATUS.PASS)
    for apply in doctorseckillapply:
        # @产品: 邓光宇
        # @需求文档: http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=28907071
        # if not settings.DOCTOR_APPLY_IN_SPECIALS and SpecialItem.objects.filter(
        #         doctorseckillapply_id=apply.id).exists():  # 一个医生提报只能绑定一个专题
        #     continue
        price_info = apply.service_item.get_default_price_info()
        data = {
            'apply_id': apply.id,
            'is_apply': True,
            'serviceitem_id': apply.service_item.id,
            'service_id': apply.service_item.service.id,
            'service_name': apply.service_item.service.name,
            'project_name': ''.join(apply.service_item.items_name),
            'rank': 0,
            'gengmei_price': price_info.get('gengmei_price', 0),
            'active_price': apply.seckill_price,
            'pre_payment_price': apply.pre_payment_price,  # 设置为秒杀价默认值
            # 'sell_num_limit': apply.stock,
            'sell_num_limit': apply.left_stock,
            'total_num_limit': apply.left_stock,
            'service_tip': u'',
            'discount': apply.commission,  # 抽成默认为医生设置秒杀价的百分之十,向上取整
            'add_stock': 0,
        }
        services_data.append(data)
    return services_data


@bind_context(uri_pre + '/listupdate')
def special_listupdate(ctx, items):
    info = []
    for obj in items:
        special = Special.objects.get(pk=obj['key'])

        need_refresh = False
        if special.is_online != obj['is_online']:
            need_refresh = True

        special.is_online = obj['is_online']
        special.save()

        if need_refresh:
            refresh_service_cache_with_special_ids(special_ids=[special.id])
        info.append(obj['key'])
    return info


@bind_context(uri_pre + '/update_online_status')
def special_update_online_status(ctx, id, is_online):
    try:
        special = Special.objects.get(id=id)
    except Special.DoesNotExist:
        raise RPCNotFoundException
    need_refresh = False
    if special.is_online != obj['is_online']:
        need_refresh = True
    special.is_online = is_online
    special.save()

    if need_refresh:
        refresh_service_cache_with_special_ids(special_ids=[special.id])


@bind_context(uri_pre + '/button/query')
def special_seckill_button_query(ctx, options):
    dqobj = SpecialSeckillButtonDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/button/get')
def special_seckill_button_get(ctx, button_id):
    try:
        button = SpecialSeckillButton.objects.get(id=button_id)
    except:
        raise RPCNotFoundException
    button_data = to_dict(button)
    return button_data


@bind_context(uri_pre + '/button/edit')
def special_seckill_button_edit(ctx, button_id, button_info):
    if button_info is None:
        return None
    tags = button_info.pop('tags')
    cities = button_info.pop('cities')
    if button_id is None:
        try:
            button = SpecialSeckillButton.objects.create(**button_info)
            button.tags = tags
            button.cities = cities
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            button = SpecialSeckillButton.objects.get(id=button_id)
            button.tags = tags
            button.cities = cities
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in button_info.iteritems():
            setattr(button, k, v)
        button.save()
    return button.id


@bind_context(uri_pre + '/doctor_apply/query')
def doctor_apply_query(ctx, options):
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        user = ctx.session.user
        members = UserPerm.members(user)
        init_q = Q(service_item__service__doctor__business_partener__in=members)
    else:
        init_q = None

    dqobj = DoctorSeckillApplyDQ(init_q=init_q)
    return dqobj.process(**options)


@bind_context(uri_pre + '/add/service_zone')
def add_service_zone(ctx, date):
    special = Special.objects.get(id=date['special_id'])
    res_validate = []
    date_jsons = json.loads(date['content'])
    for date_json in date_jsons[1:]:
        for items in date_json[1:]:
            for item in items[1:]:
                res = service_is_activity(item, date['special_id'])
                if res['code'] == 0:
                    res_validate.append(res)
    if res_validate:
        return {
            'code': 0,
            'message': (u','.join([item['message'] for item in res_validate if item['code'] == 0]))
        }
    special.is_show_service_zone = date['is_show']
    special.service_zone_content = date['content']
    special.save()
    return {
        'code': 1,
        'message': '修改成功！',
    }


@bind_context(uri_pre + '/apply/get')
def upload_button_apply_get(ctx, apply_id=None):
    if apply_id is None:
        return None
    apply = DoctorSeckillApply.objects.get(id=apply_id)
    apply_date = to_dict(apply)
    service = apply.service_item.service
    apply_date['service_name'] = service.name
    service_item_id_to_sku_name_info = get_sku_id_to_sku_name_info([apply.service_item.id], False)
    item_name = service_item_id_to_sku_name_info[str(apply.service_item.id)].get('name', '')
    apply_date['item_name'] = item_name
    apply_date['doctor_name'] = service.doctor.name
    apply_date['doctor_id'] = service.doctor.id
    apply_date['button_document'] = apply.seckill_button.button_document
    apply_date['hospital_name'] = service.doctor.hospital.name
    apply_date['bussiness_partner'] = getattr(service.doctor.business_partener, 'username', '')
    apply_date['service_lock'] = u'是' if service.is_lock else u'否'
    from talos.models.diary import Diary
    apply_date['diary_num'] = Diary.objects.filter(service_id=service.id, topics__is_online=True).count()
    apply_date['photo_details_doctor'] = service.photo_details_doctor or ''
    return apply_date


def _upload_button_apply_audit(person, apply_id, is_pass, reason):
    apply = DoctorSeckillApply.objects.get(id=apply_id)
    if apply.status == DOCTOR_SECKILL_APPLY_STATUS.PASS or apply.status == DOCTOR_SECKILL_APPLY_STATUS.REJECT:
        return {
            'code': 0,
            'apply_id': apply.id
        }
    if is_pass:
        price_info = apply.service_item.get_default_price_info()
        seckill_price = apply.seckill_price
        self_support_discount_rate = price_info['self_support_discount'] * 1.00 / price_info['gengmei_price']
        if seckill_price > 500:
            pre_payment_rate = price_info['pre_payment_price'] * 1.00 / price_info['gengmei_price']
            commission_rate = price_info['discount'] * 1.00 / price_info['gengmei_price']
            pre_payment_price = math.ceil(apply.seckill_price * pre_payment_rate)
            commission = math.ceil(apply.seckill_price * commission_rate)
        else:
            from doctor.tool.service_tool import count_pre_payment
            result = count_pre_payment(seckill_price)
            pre_payment_price = result['pre_payment_price_int']
            commission = result['discount']
        apply.status = DOCTOR_SECKILL_APPLY_STATUS.PASS
        apply.available_num = apply.stock
        # http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=28907071
        apply.left_stock = apply.stock
        apply.pre_payment_price = pre_payment_price
        apply.commission = commission
        apply.self_support_discount = math.ceil(apply.seckill_price * self_support_discount_rate)
        apply.pass_time = datetime.datetime.now()
    else:
        apply.status = DOCTOR_SECKILL_APPLY_STATUS.REJECT
    apply.save()

    record_date = {
        'doctorseckillapply_id': apply_id,
        'is_pass': is_pass,
        'seckill_price': apply.seckill_price,
        'comment': reason or '',
        'person': person
    }
    record = DoctorSeckillApplyRecord.objects.create(**record_date)
    return {
        'code': 1,
        'apply_id': apply.id
    }


@bind_context(uri_pre + '/apply/record')
def upload_button_apply_audit(ctx, apply_id, is_pass, reason):
    person = ctx.session.user.person
    return _upload_button_apply_audit(person, apply_id, is_pass, reason)


@bind_context(uri_pre + '/applys/audit')
def upload_button_applys_audit(ctx, items):
    person = ctx.session.user.person
    is_pass = True if items['action'] == 'pass' else False
    reason = items['reason']
    audit_apply_ids = []
    for apply_id in items['arr']:
        result = _upload_button_apply_audit(person, apply_id, is_pass, reason)
        if result['code'] == 1:
            audit_apply_ids.append(result['apply_id'])
    return {
        'audit_apply_ids': audit_apply_ids,
    }


@bind_context(uri_pre + '/one_key_audit')
def doctorseckillapply_one_key_audit(ctx, seckill_button__id=None):
    if not seckill_button__id:
        return
    person = ctx.session.user.person
    is_pass = True
    reason = ''
    audit_apply_ids = []
    doctor_seckill_apply = list(DoctorSeckillApply.objects.filter(seckill_button_id=seckill_button__id, status__in=[DOCTOR_SECKILL_APPLY_STATUS.DEFAULT, DOCTOR_SECKILL_APPLY_STATUS.RE_APPLY]).values_list('id', flat=True))
    if doctor_seckill_apply:
        for apply_id in doctor_seckill_apply:
            result = _upload_button_apply_audit(person, apply_id, is_pass, reason)
            if result['code'] == 1:
                audit_apply_ids.append(result['apply_id'])
    return {
        'audit_apply_ids': audit_apply_ids,
    }


@bind_context(uri_pre + '/apply/listupdate')
def upload_button_apply_listupdate(ctx, items):
    apply_ids = []
    for item in items:
        try:
            apply = DoctorSeckillApply.objects.get(id=item['id'])
            apply.pre_payment_price = item['pre_payment_price']
            apply.commission = item['commission']
            if item.get('self_support_discount'):
                apply.self_support_discount = item['self_support_discount']
            apply.save()
            apply_ids.append(apply.id)
        except:
            continue
    return apply_ids


@bind_context(uri_pre + '/get_from_applies')
def get_serviceitem_info_from_applies(ctx, apply_ids):
    """
    专场根据申请ID获取serviceitem信息 by pengli 2016-11-36
    modify at 2017-01-01 by oldman content:一个医生提报只能绑定一个专题
    """
    services_data = []
    doctorseckillapply = DoctorSeckillApply.objects.filter(id__in=apply_ids, status=DOCTOR_SECKILL_APPLY_STATUS.PASS)
    for item in doctorseckillapply:
        if not settings.DOCTOR_APPLY_IN_SPECIALS and SpecialItem.objects.filter(
                doctorseckillapply_id=item.id).exists():  # 一个医生提报只能绑定一个专题
            continue
        price_info = item.service_item.get_default_price_info()
        data = {
            'apply_id': item.id,
            'item_id': item.service_item.id,
            'service_id': item.service_item.service.id,
            'serviceitem_id': item.service_item.id,
            'service_name': item.service_item.service.name,
            'project_name': ''.join(item.service_item.items_name),
            'gengmei_price': price_info.get('gengmei_price', 0),
            'active_price': item.seckill_price,
            'pre_payment_price': item.pre_payment_price,
            'discount': item.commission,
            'sell_num_limit': item.stock,  # 限制数量
            'sell_num': item.stock - item.available_num,  # 已售卖数量
            'rank': 0,
            'is_apply': True,
        }
        services_data.append(data)
    return services_data


@bind_context(uri_pre + '/get_from_ids')
def service_get_from_ids(ctx, ids):
    """
    :param ids: serviceitem列表根据美购ID添加(普通专题)
    date: 2016-11-26 by pengli
    """
    services_data = []
    services = Service.objects.filter(id__in=ids)
    for service in services:
        for item in service.items.all().filter(is_delete=False, parent_id=0):
            price_info = item.get_default_price_info()
            data = {
                'apply_id': '',
                'item_id': item.id,
                'is_apply': False,
                'service_id': service.id,
                'serviceitem_id': item.id,
                'service_name': service.name,
                'project_name': ''.join(item.items_name),
                'gengmei_price': price_info.get('gengmei_price', 0),
                'active_price': price_info.get('gengmei_price', 0),
                'pre_payment_price': price_info.get('pre_payment_price', 0),
                'discount': price_info.get('discount', 0),
                'sell_num_limit': '',
                'sell_num': 0,
                'rank': 0,
            }
            services_data.append(data)
    return services_data


@bind_context(uri_pre + '/get_from_serviceitem_ids')
def get_from_serviceitem_ids(ctx, serviceitem_ids):
    """
    专场根据skuID获取serviceitem信息
    """
    services_data = []
    for serviceitem_id in serviceitem_ids:
        serviceitem = ServiceItem.objects.get(id=serviceitem_id, parent_id=0)
        price_info = serviceitem.get_default_price_info()
        data = {
            'apply_id': '',
            'item_id': serviceitem.id,
            'service_id': serviceitem.service.id,
            'serviceitem_id': serviceitem.id,
            'service_name': serviceitem.service.name,
            'project_name': ''.join(serviceitem.items_name),
            'gengmei_price': price_info.get('gengmei_price', 0),
            'active_price': price_info.get('gengmei_price', 0),
            'pre_payment_price': price_info.get('pre_payment_price', 0),
            'discount': price_info.get('discount', 0),
            'sell_num_limit': '',
            'sell_num': 0,
            'rank': 0,
        }
        services_data.append(data)
    return services_data


@bind_context(uri_pre + '/layout/edit')
def layout_edit(ctx, layout_id=None, layout_info=None):
    if layout_info is None:
        return None
    if layout_info.get('module', -1) == SPECIAL_MODULE.FLOOR:
        floors = json.loads(layout_info.get('related', {}))['floor']
        res_validate = []
        for floor in floors:
            contents = floor['content']
            for content in contents:
                for message in content['message']:
                    if message.get('jump_type') == '2':
                        res = service_is_activity(message.get('jump_url', ''), layout_info['special_id'])
                        if res['code'] == 0:
                            res_validate.append(res)
                    if message.get('jump_type') == '29':
                        res = sku_is_activity(message.get('jump_url', ''), layout_info['special_id'])
                        if res['code'] == 0:
                            res_validate.append(res)
        if res_validate:
            return {
                'code': 0,
                'message': (u','.join([item['message'] for item in res_validate if item['code'] == 0]))
            }

    if SpecialLayout.objects.filter(module=layout_info['module'], special_id=layout_info['special_id']):
        try:
            layout = SpecialLayout.objects.get(id=layout_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        if layout.module == SPECIAL_MODULE.TURNTABLE:
            lottery_id = json.loads(layout_info['related'])['lottery_id']
            point_obj = PointActivity.objects.get(id=lottery_id)
            special_obj = Special.objects.get(id=layout_info['special_id'])

            if point_obj.end_time < special_obj.end_time:
                return {
                    'code': 0,
                    'message': "转盘活动结束时间不能小于专场结束时间"
                }
        for k, v in layout_info.iteritems():
            setattr(layout, k, v)
        layout.save()
    else:
        try:
            layout_info.pop('id')
            layout = SpecialLayout.objects.create(**layout_info)
        except IntegrityError:
            raise RPCIntegrityError
        except:
            raise
    return {
        'code': 1,
        'message': u'修改成功！'
    }


@bind_context(uri_pre + '/seckill_layout/edit')
def layout_edit(ctx, seckill_layout_id=None, seckill_layout_info=None):
    if seckill_layout_info is None:
        return None
    print(seckill_layout_info)
    if SpecialSeckillLayout.objects.filter(special_id=seckill_layout_info['special_id']):
        try:
            layout = SpecialSeckillLayout.objects.get(id=seckill_layout_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in seckill_layout_info.iteritems():
            setattr(layout, k, v)
        layout.save()
    else:
        try:
            seckill_layout_info.pop('id')
            layout = SpecialSeckillLayout.objects.create(**seckill_layout_info)
        except IntegrityError:
            raise RPCIntegrityError
        except:
            raise
    return layout.id


@bind_context(uri_pre + '/service_query')
def service_query(ctx, options):
    init_q = None
    flag = False
    filters = options.get('filters', [])
    for items in filters:
        if items.get('field') != 'floor_id':
            continue
        else:
            flag = True
    if not flag:
        init_q = Q(floor_id__isnull=True)
    dqobj = SpecialServiceDQ(init_q=init_q)
    return dqobj.process(**options)


def delete_specialitem(specialitem):
    specialitem.disable_price()
    specialitem.delete()


@transaction.atomic()
@bind_context(uri_pre + '/service/delete')
def service_delete(ctx, specialitem_id):
    """
    根据ID删除一个specialitem
    """
    specialitem = SpecialItem.objects.get(pk=specialitem_id)
    delete_specialitem(specialitem)
    refresh_service_cache_with_special_ids(special_ids=[specialitem.special_id])


@transaction.atomic()
@bind_context(uri_pre + '/delete_all_items')
def service_delete(ctx, special_id):
    """
    根据specail_id删除一个专题下所有美购项目
    """
    specialitems = SpecialItem.objects.filter(special_id=special_id)
    map(delete_specialitem, specialitems)

    refresh_service_cache_with_special_ids(special_ids=[special_id])
    return list(specialitems.values_list('id', flat=True))


@bind_context(uri_pre + '/service/list_update')
def service_list_update(ctx, items):
    """
    批量修改一批specialitem
    """
    data_result = []
    errmsg = []
    for item in items:
        try:
            specialitem = SpecialItem.objects.get(pk=item['specialitem_id'])
            specialitem.set_position()
            data_result.append(specialitem.id)
        except GaiaRPCFaultException as e:
            errmsg.append(e.message)
        except:
            info_logger.info(__import__('traceback').format_exc())
            continue
    if errmsg:
        return {'status': 'fail', 'errmsg': errmsg}
    return data_result


def _create_more_buy_serviceitem(items):
    # 创建多买SKU
    if not items:
        return items

    for item in items:
        parent = ServiceItem.objects.get(id=int(item.get('serviceitem_id')))
        serviceitem, created = ServiceItem.objects.get_or_create(
            service_id=parent.service_id,
            key=parent.key,
            total_num=parent.total_num,
            sku_stock=parent.sku_stock,
            sku_stock_lte_zero=parent.sku_stock_lte_zero,
            topic_cash_back_limit=parent.topic_cash_back_limit,
            sort=parent.sort,
            is_delete=parent.is_delete,
            cost_price=parent.cost_price,
            city_id=parent.city_id,
            parent_id=parent.id,
        )

        price = ServiceItemPrice.objects.filter(service_item=serviceitem, is_default_price=True).first()
        if not price:
            parent_price = ServiceItemPrice.objects.filter(service_item=parent, is_default_price=True).first()
            default_price, created = ServiceItemPrice.objects.get_or_create(
                service_item_id=serviceitem.id,
                is_enable=True,
                sale_limit=parent_price.sale_limit,
                sale_limit_lte_zero=parent_price.sale_limit_lte_zero,
                total_num=parent_price.total_num,
                total_num_fake=parent_price.total_num_fake,
                is_default_price=True,
                selling_rule_id=None,
                discount=parent_price.discount,
                pre_payment_price=parent_price.pre_payment_price,
                original_price=parent_price.original_price,
                gengmei_price=parent_price.gengmei_price,
                cash_back_rate=parent_price.cash_back_rate,
                cash_back_fee=parent_price.cash_back_fee,
                self_support_discount=parent_price.self_support_discount,
                single_user_buy_limit=parent_price.single_user_buy_limit,
                more_buy_price=parent_price.gengmei_price,
            )
        item['serviceitem_id'] = serviceitem.id
    return items


@bind_context(uri_pre + '/service/add_items')
def service_add_items(ctx, items, special_id, floor_id=None):
    """
    批量添加一批specialitem到普通专场
    create by oldman 2017-01-01
    :param ctx:
    :param items:list
    :param special_id:
    :return:
    """
    if not special_id:
        return []

    special = Special.objects.get(pk=special_id)
    if special.is_more_buy and special.more_buy_count:
        # 判断专题是否为多买 若多买则进行新的流程 创建多买SKU
        items = _create_more_buy_serviceitem(items)

    errmsg = []
    data_result = {}
    suc_sitem_ids = []
    fail_item_ids = []
    for item in items:
        try:
            with transaction.atomic():
                # 保存时增加判断
                if item['apply_id']:
                    specialitems = SpecialItem.objects.filter(doctorseckillapply_id=item['apply_id']). \
                        exclude(special_id=special_id)
                    if not settings.DOCTOR_APPLY_IN_SPECIALS and specialitems.exists():
                        continue
                if floor_id:
                    specialitem, created = SpecialItem.objects.get_or_create(serviceitem_id=item['serviceitem_id'],
                                                                             special_id=special_id, floor_id=floor_id)
                else:
                    specialitem, created = SpecialItem.objects.get_or_create(serviceitem_id=item['serviceitem_id'],
                                                                    special_id=special_id)
                specialitem.doctorseckillapply_id = item['apply_id']
                specialitem.service_id = specialitem.serviceitem.service.id
                specialitem.save()
                # specialitem.set_position()
                # 更新该specialitem的专场库存价格信息
                specialitem.update_or_create_price_info()

                suc_sitem_ids.append(specialitem.id)
        except GaiaRPCFaultException as e:
            errmsg.append(e.message)
        except:
            info_logger.info(__import__('traceback').format_exc())
            fail_item_ids.append(item['serviceitem_id'])
            continue
    if errmsg:
        raise GaiaRPCFaultException(error=ERROR.UNIVERSAL, message='error')
    if fail_item_ids:
        SpecialItem.objects.filter(id__in=fail_item_ids).delete()

    refresh_service_cache_with_special_ids(special_ids=[special_id])
    data_result['suc_sitem_ids'] = suc_sitem_ids
    data_result['fail_item_ids'] = fail_item_ids
    return data_result


@bind_context(uri_pre + '/seckill/service/add_items')
def seckill_service_add_items(ctx, items, special_id):
    """
    批量添加一批specialitem到秒杀专场
    create by oldman 2017-01-08
    :param ctx:
    :param items:list
    :param special_id:
    :return:
    """
    if not special_id:
        return []
    data_result = {}
    suc_sitem_ids = []
    fail_item_ids = []
    stock_unavaliable = []
    for item in items:
        try:
            with transaction.atomic():
                # 保存时增加判断
                # 2019年10月15号: http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=28907071
                # 个人是不愿意改的.....
                # if item['apply_id']:
                #     specialitems = SpecialItem.objects.filter(doctorseckillapply_id=item['apply_id']). \
                #         exclude(special_id=special_id)
                #     if not settings.DOCTOR_APPLY_IN_SPECIALS and specialitems.exists():
                #         continue
                if item['apply_id']:
                    specialitems = SpecialItem.objects.filter(doctorseckillapply_id=item['apply_id'],
                                                              special_id=special_id)
                    if specialitems.exists():
                        continue
                specialitem = SpecialItem.objects.get_or_create(serviceitem_id=item['serviceitem_id'],
                                                                special_id=special_id)[0]
                # specialitem.position = item['position']
                specialitem.doctorseckillapply_id = item['apply_id']
                specialitem.service_id = specialitem.serviceitem.service.id
                specialitem.tip = item['tip']
                specialitem.save()
                # 更新该specialitem的专场库存价格信息
                specialitem.update_or_create_price_info(0 if not item.get('add_stock', 0) else
                                                        item.get('add_stock', 0))

                suc_sitem_ids.append(specialitem.id)
        except AssertionError as e:
            info_logger.info(__import__('traceback').format_exc())
            stock_unavaliable.append('sku_id'+ str(item['serviceitem_id']) + '------' +e.message)
            continue
        except:
            info_logger.info(__import__('traceback').format_exc())
            fail_item_ids.append(item['serviceitem_id'])
            continue
    if fail_item_ids:
        SpecialItem.objects.filter(id__in=fail_item_ids).delete()

    refresh_service_cache_with_special_ids(special_ids=[special_id])

    data_result['suc_sitem_ids'] = suc_sitem_ids
    data_result['fail_item_ids'] = fail_item_ids
    data_result['stock_unavaliable'] = stock_unavaliable
    return data_result


@bind_context(uri_pre + '/exterior/query')
def specialexterior_query(ctx, options):
    dqobj = SpecialExteriorrDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/exterior/get')
def specialexterior_edit(ctx, specialexterior_id):
    try:
        specialexterior = SpecialExterior.objects.get(id=specialexterior_id)
    except:
        raise RPCNotFoundException
    data = to_dict(specialexterior)
    if specialexterior.aggregate_type == 1:
        data['seckill_polymer_id'] = specialexterior.special_polymer_id
        data['special_polymer_id'] = ''
    elif specialexterior.aggregate_type == 4:
        data['visualpage_id'] = specialexterior.special_polymer_id
        data['special_polymer_id'] = ''
    data['city'] = list(specialexterior.cities.values_list('id', flat=True))
    data['region'] = list(specialexterior.regions.values_list('id', flat=True))
    data['services'] = list(specialexterior.services.values_list('id', flat=True))
    return data


@transaction.atomic()
@bind_context(uri_pre + '/exterior/edit')
def specialexterior_edit(ctx, specialexterior_id, specialexterior_info=None):
    if specialexterior_info is None:
        return None
    city = specialexterior_info.pop('city')
    region = specialexterior_info.pop('region')
    service = specialexterior_info.pop('services', None)
    special_id = specialexterior_info.pop('special')
    special_polymer_id = specialexterior_info.pop('special_polymer_id', None)
    specialexterior_info['start_time'] = specialexterior_info['start_time'] or None
    specialexterior_info['end_time'] = specialexterior_info['end_time'] or None
    specialexterior_info['special_polymer_id'] = special_polymer_id or None
    seckill_polymer_id = specialexterior_info.pop('seckill_polymer_id', None)
    visualpage_id = specialexterior_info.pop('visualpage_id', None)
    if special_polymer_id:
        specialexterior_info['aggregate_type'] = 2
    if seckill_polymer_id:
        specialexterior_info['special_polymer_id'] = seckill_polymer_id
        specialexterior_info['aggregate_type'] = 1
    if visualpage_id:
        specialexterior_info['special_polymer_id'] = visualpage_id
        specialexterior_info['aggregate_type'] = 4

    if special_id:
        special = Special.objects.get(id=special_id)
        specialexterior_info['special_id'] = special_id
        specialexterior_info['aggregate_type'] = 3
    else:
        special = None
        specialexterior_info['special_id'] = ''
    if special and special.start_time and special.end_time and specialexterior_info['start_time'] and specialexterior_info[
        'end_time']:
        start_time = datetime.datetime.strptime(specialexterior_info['start_time'], "%Y-%m-%d %H:%M:%S")
        end_time = datetime.datetime.strptime(specialexterior_info['end_time'], "%Y-%m-%d %H:%M:%S")
        special = Special.objects.get(id=specialexterior_info['special_id'])
        if not (special.start_time <= start_time and special.end_time >= end_time):
            return {
                'code': 0,
                'id': specialexterior_id
            }

    if specialexterior_id is None:
        try:
            specialexterior = SpecialExterior.objects.create(**specialexterior_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            specialexterior = SpecialExterior.objects.get(id=specialexterior_id)
            for k, v in specialexterior_info.iteritems():
                setattr(specialexterior, k, v)
            specialexterior.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException

    old_city = set(specialexterior.cities.all())
    new_city = set(City.objects.filter(id__in=city))
    for city in new_city - old_city:
        SpecialExteriorCity.objects.get_or_create(special_exterior=specialexterior, city=city)
    SpecialExteriorCity.objects.filter(special_exterior=specialexterior,
                                       city__in=(old_city - new_city)).delete()

    old_region = set(specialexterior.regions.all())
    new_region = set(Region.objects.filter(id__in=region))
    for region in new_region - old_region:
        SpecialExteriorRegion.objects.get_or_create(special_exterior=specialexterior, region=region)
    SpecialExteriorRegion.objects.filter(special_exterior=specialexterior,
                                         region__in=(old_region - new_region)).delete()
    if service is not None:
        old_service = set(specialexterior.services.all())
        new_service = set(Service.objects.filter(id__in=service))
        for service in new_service - old_service:
            SpecialExteriorService.objects.get_or_create(special_exterior=specialexterior, service=service)
        SpecialExteriorService.objects.filter(special_exterior=specialexterior,
                                              service__in=(old_service - new_service)).delete()

    return {
        'code': 1,
        'id': specialexterior.id
    }

@transaction.atomic()
@bind_context(uri_pre + '/exterior/replication')
def specialexterior_replication(ctx, specialexterior_id, specialexterior_info=None):
    # 专题美购外显复制功能
    old_specialexterior = SpecialExterior.objects.get(id=specialexterior_id)
    with transaction.atomic():
        specialexterior = SpecialExterior.objects.create(
            name=old_specialexterior.name,
            image=old_specialexterior.image,
            start_time=old_specialexterior.start_time,
            end_time=old_specialexterior.end_time,
            rank=old_specialexterior.rank,
            is_online=old_specialexterior.is_online,
            special=old_specialexterior.special,
            show_position=old_specialexterior.show_position,
            user_type=old_specialexterior.user_type,
            payment_type=old_specialexterior.payment_type,
            new_image=old_specialexterior.new_image,
            new_big_image=old_specialexterior.new_big_image,
            special_polymer_id=old_specialexterior.special_polymer_id,
            promotion_id=old_specialexterior.promotion_id,
            aggregate_type=old_specialexterior.aggregate_type,
            device_type=old_specialexterior.device_type
        )
        regions_list = list(old_specialexterior.regions.values_list('id', flat=True))
        cities_list = list(old_specialexterior.cities.values_list('id', flat=True))
        services_list = list(old_specialexterior.services.values_list('id', flat=True))
        if regions_list:
            SpecialExteriorRegion.objects.bulk_create([
                SpecialExteriorRegion(special_exterior=specialexterior, region_id=region)
                for region in regions_list]
            )
        if cities_list:
            SpecialExteriorCity.objects.bulk_create([
                SpecialExteriorCity(special_exterior=specialexterior, city_id=city)
                for city in cities_list]
            )
        if services_list:
            SpecialExteriorService.objects.bulk_create([
                SpecialExteriorService(special_exterior=specialexterior, service_id=service)
                for service in services_list]
            )
    return {
        'code': 1,
        'id': specialexterior.id
    }

@bind_context(uri_pre + '/exterior/listupdate')
def specialexterior_edit(ctx, items):
    success_list = []
    for item in items:
        try:
            specialexterior = SpecialExterior.objects.get(id=item['id'])
            specialexterior.is_online = item['is_online']
            specialexterior.rank = item['rank']
            specialexterior.save()
            success_list.append(specialexterior.id)
        except:
            continue
    return success_list


@bind_context(uri_pre + '/rule/get')
def rule_get(ctx, special_id):
    """
    获取专题的规则
    :param ctx:
    :param special_id: 专题ID
    :return: ID的价格规则
    """
    if special_id is None:
        return None
    try:
        special = Special.objects.get(id=special_id)

        if special.is_seckill:
            activity_type = ACTIVITY_TYPE_ENUM.SECKILL
        elif special.groupbuy_id:
            activity_type = ACTIVITY_TYPE_ENUM.GROUPBUY
        elif special.is_more_buy and special.more_buy_count:
            activity_type = ACTIVITY_TYPE_ENUM.MOREBUY
        else:
            activity_type = ACTIVITY_TYPE_ENUM.SPECIAL

        rule = SKUPriceRule.objects.get(activity_id=special_id, activity_type=activity_type)
        refund_anytime = rule.refund_anytime
        can_use_points = rule.can_use_points
        share_get_cashback = rule.share_get_cashback

        rule_data = {
            "activity_type": rule.activity_type,
            "refund_anytime": SPECIAL_RULE_SET.NONE if refund_anytime is None else int(refund_anytime),
            "can_use_points": SPECIAL_RULE_SET.NONE if can_use_points is None else int(can_use_points),
            "share_get_cashback": SPECIAL_RULE_SET.NONE if share_get_cashback is None else int(share_get_cashback),
        }
    except:
        info_logger.info(__import__('traceback').format_exc())
        rule_data = None
    return rule_data


@bind_context(uri_pre + '/rule/edit')
def rule_edit(ctx, special_id, data_info):
    """
    编辑专题的规则
    :param data_info: 规则数据信息
    :param ctx:
    :param special_id: 专题ID
    :return: ID的价格规则
    """
    if special_id is None:
        return None
    try:
        special = Special.objects.get(id=special_id)

        if special.is_seckill:
            activity_type = ACTIVITY_TYPE_ENUM.SECKILL
        elif special.groupbuy_id:
            activity_type = ACTIVITY_TYPE_ENUM.GROUPBUY
            data_info['can_use_points'] = False  # 禁用美分
        else:
            activity_type = ACTIVITY_TYPE_ENUM.SPECIAL

        rule = SKUPriceRule.objects.get_or_create(activity_id=special_id,
                                                  activity_type=activity_type)[0]
        for key, value in data_info.iteritems():
            if int(value) == SPECIAL_RULE_SET.NONE:
                value = None
            else:
                value = int(value)
            setattr(rule, key, value)
        rule.save()
        rule_data = to_dict(rule)
    except:
        info_logger.info(__import__('traceback').format_exc())
        rule_data = None

    refresh_service_cache_with_special_ids(special_ids=[special_id])
    return rule_data


@bind_context(uri_pre + '/artemis_special_info')
def get_artemis_special_info(ctx, special_id):
    try:
        special = Special.objects.get(id=special_id)
        return {
            'special_id': special.id,
            'name': special.title
        }
    except Exception:
        return None


@bind(uri_pre + '/set_position')
def set_position(special_item_id, position):
    position = int(position)
    with transaction.atomic():
        items = SpecialItem.objects.filter(id=special_item_id).select_for_update()
        item = items.first()
        if not item:
            return
        if position != 0:
            city = item.service.doctor.hospital.city
            q = Q(service__doctor__hospital__city=city, position=position, special_id=item.special_id)
            if item.special.is_new_special:
                q &= Q(floor_id=item.floor_id)
            if SpecialItem.objects.filter(q).exists():
                raise UniError(u'该位置已有相同城市')
        item.position = position
        item.save()
        # refresh_service_cache_with_special_ids(special_ids=[item.special_id])
    return


@bind_context(uri_pre + '/groupbuy_edit')
def groupbuy_edit(ctx, special_id=None, special_info=None, share_info=None, groupbuy_info=None):
    """
    拼团创建/编辑
    :param ctx:
    :param special_id:
    :param special_info: 拼团信息
    :param share_info: 分享信息
    :param groupbuy_info: 拼团信息
    :return:
    """
    if special_info is None:
        return {}

    if special_id is None:
        try:

            groupbuy = GroupBuySpecial.objects.create(**groupbuy_info)

            special = Special.objects.create(
                groupbuy=groupbuy,
                **special_info
            )
            if special.is_more_buy:
                activity_type=ACTIVITY_TYPE_ENUM.MOREBUY
            else:
                activity_type = ACTIVITY_TYPE_ENUM.GROUPBUY
            SKUPriceRule.objects.create(
                name=special.title,
                is_enable=False,
                start_time=special.start_time,
                end_time=special.end_time,
                activity_type=activity_type,
                activity_id=special.id,
                groupbuy_nums=groupbuy.nums,
                groupbuy_type=groupbuy.groupbuy_type,
                active_type=groupbuy.active_type,
                groupbuy_countdown=groupbuy.countdown,
                more_buy_count=special.more_buy_count,
            )
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            special = Special.objects.get(id=special_id)
            if 'rank_position' in special_info:
                special.rank_position = special_info['rank_position']
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in special_info.iteritems():
            setattr(special, k, v)
        special.save()
        ### 专题时间调整, 意味着价格规则调整, 意味着会生成新的价格策略, 需要进行验证
        detected_keys = ['is_online', 'start_time', 'end_time']
        price_detected = False
        for dk in detected_keys:
            if special_info.get(dk) is not None:
                price_detected = True
                break

        # set skupricerule
        skupricerule = SKUPriceRule.objects.get(
            activity_type=ACTIVITY_TYPE_ENUM.GROUPBUY,
            activity_id=special.id
        )
        skupricerule.is_enable = special.is_online
        skupricerule.start_time = special.start_time
        skupricerule.end_time = special.end_time
        skupricerule.save()

        if price_detected:
            ServiceItem.validate_sku_price_for_special(special.id)
        # set share_wechat_images
        special.groupbuy.share_wechat_images = json.dumps(groupbuy_info['share_wechat_images'])
        special.groupbuy.save()

    if special.share_id:
        share = Share.objects.get(id=special.share_id)
        for key, value in share_info.iteritems():
            setattr(share, key, value)
        share.save()
    else:
        share = Share.objects.create(**share_info)
        special.share_id = share.id
        special.save()
    refresh_service_cache_with_special_ids(special_ids=[special.id])
    return {'id': special.id}


@bind_context(uri_pre + '/groupbuy/service/add_items')
def seckill_service_add_items(ctx, items, special_id):
    """
    批量添加一批specialitem到拼团专场
    :param ctx:
    :param items:list
    :param special_id:
    :return:
    """
    if not special_id:
        return []
    data_result = {}
    suc_sitem_ids = []
    fail_item_ids = []
    for item in items:
        try:
            # 保存时增加判断
            if item['doctorseckillapply_id']:
                specialitems = SpecialItem.objects.filter(doctorseckillapply_id=item['doctorseckillapply_id']). \
                    exclude(special_id=special_id)
                if not settings.DOCTOR_APPLY_IN_SPECIALS and specialitems.exists():
                    continue

            defaults = {
                "tip": item['tip'],
                "doctorseckillapply_id": item['doctorseckillapply_id'],
                "service_id": item['service_id']
            }
            specialitem, created = SpecialItem.objects.update_or_create(
                serviceitem_id=item['serviceitem_id'],
                special_id=special_id,
                defaults=defaults
            )
            # 更新该specialitem的专场库存价格信息
            specialitem.update_or_create_price_info(0 if not item.get('add_stock', 0) else
                                                    item.get('add_stock', 0))
            suc_sitem_ids.append(specialitem.id)
        except Exception as e:

            info_logger.info(__import__('traceback').format_exc())
            fail_item_ids.append(item['serviceitem_id'])
            continue
    if fail_item_ids:
        SpecialItem.objects.filter(id__in=fail_item_ids).delete()
    data_result['suc_sitem_ids'] = suc_sitem_ids
    data_result['fail_item_ids'] = fail_item_ids
    refresh_service_cache_with_special_ids(special_ids=[special_id])
    return data_result


@bind_context(uri_pre + '/new_layout/edit')
def layout_edit(ctx, layout_id=None, layout_info=None):
    res_validate = []
    if not layout_info:
        return None

    if not layout_info['related']:
        return None

    related_data = json.loads(layout_info['related'])

    if layout_info.get('module', '-1') == SPECIAL_MODULE.TURNTABLE:
        s = Special.objects.get(id=layout_info['special_id'])
        for lottery_data in related_data:
            # 判断专题活动是否失效了
            lottery_id = lottery_data.get('lottery_id')
            p = PointActivity.objects.get(id=lottery_id)
            if p.end_time < s.end_time:
                return {
                    'code': 0,
                    'message': "转盘活动结束时间不能小于专场结束时间"
                }
    if layout_info.get('module', '-1') == SPECIAL_MODULE.GADGET_TEMPLATE:
        for each in related_data:
            for p in each['data']:
                if p.get('jump_type') =='2':
                    # 处理spu
                    res = service_is_activity(p.get('url_params', ''), layout_info['special_id'])
                    if res['code'] == 0:
                        res_validate.append(res)
                if p.get('jump_type') == '29':
                    # 处理sku
                    res = sku_is_activity(p.get('url_params', ''), layout_info['special_id'])
                    if res['code'] == 0:
                        res_validate.append(res)
        if res_validate:
            return {
                'code': 0,
                'message': (u','.join([item['message'] for item in res_validate if item['code'] == 0]))
            }
    if layout_info.get('module', -1) == SPECIAL_MODULE.FLOOR:
        s = Special.objects.get(id=layout_info['special_id'])
        for each in related_data['data']:
            for d in each['data']:
                if int(related_data.get('has_child_storey', 0)):
                    for ss in d.get('data', []):
                        if  ss.get('type_id', -1) == SPECIAL_MODULE.GADGET_TEMPLATE:
                            for ss in d['data']:
                                if ss.get('jump_type') == '2':
                                    # 处理spu
                                    res = service_is_activity(ss.get('url_params', ''), layout_info['special_id'])
                                    if res['code'] == 0:
                                        res_validate.append(res)
                                if ss.get('jump_type') == '29':
                                    # 处理sku
                                    res = sku_is_activity(ss.get('url_params', ''), layout_info['special_id'])
                                    if res['code'] == 0:
                                        res_validate.append(res)
                        if ss.get('type_id', -1) == SPECIAL_MODULE.TURNTABLE:
                            lottery_id = ss.get('lottery_id')
                            p = PointActivity.objects.get(id=lottery_id)
                            if p.end_time < s.end_time:
                                return {
                                    'code': 0,
                                    'message': "转盘id:{id}活动结束时间不能小于专场结束时间".format(id=lottery_id)
                                }
                else:
                    if d.get('type_id', -1) == SPECIAL_MODULE.GADGET_TEMPLATE:
                        for d in d['data']:
                            if d.get('jump_type') == '2':
                                # 处理spu
                                res = service_is_activity(d.get('url_params', ''), layout_info['special_id'])
                                if res['code'] == 0:
                                    res_validate.append(res)
                            if d.get('jump_type') == '29':
                                # 处理sku
                                res = sku_is_activity(d.get('url_params', ''), layout_info['special_id'])
                                if res['code'] == 0:
                                    res_validate.append(res)
                    if d.get('type_id', -1) == SPECIAL_MODULE.TURNTABLE:
                        lottery_id = d.get('lottery_id')
                        p = PointActivity.objects.get(id=lottery_id)
                        if p.end_time < s.end_time:
                            return {
                                'code': 0,
                                'message': "转盘id:{id}活动结束时间不能小于专场结束时间".format(id=lottery_id)
                            }
        if res_validate:
            return {
                'code': 0,
                'message': (u','.join([item['message'] for item in res_validate if item['code'] == 0]))
            }

    if layout_id is None:
        try:
            if layout_info.get('module', -1) == SPECIAL_MODULE.FLOOR:
                related = json.loads(layout_info['related'])
                layout_data = related.pop('data') # 把子楼层的数据单独出来
                layout_info['related'] = json.dumps(related)
                SpecialLayout.objects.create(**layout_info) # 全局配置信息
                for kk in layout_data:
                    objs = []
                    sort = kk.pop('ordering', 0)
                    inner_data = kk.pop('data', [])
                    d = json.dumps(kk)
                    parent_storey = SpecialStorey.objects.create(
                        sort=sort,
                        special_id=layout_info['special_id'],
                        related=d,
                    )
                    for dd in inner_data:
                        objs.append(SpecialStorey(
                            sort=dd.pop('ordering', 0),
                            special_id=layout_info['special_id'],
                            related=json.dumps(dd),
                            parent_id=parent_storey.id
                        ))
                    SpecialStorey.objects.bulk_create(objs)
            else:
                SpecialLayout.objects.create(**layout_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        # 编辑
        if layout_info.get('module', -1) == SPECIAL_MODULE.FLOOR:
            layout = SpecialLayout.objects.get(id=layout_id)
            related = json.loads(layout_info['related'])
            layout_data = related.pop('data', [])  # 把子楼层的数据单独出来
            has_child_storey = bool(int(related.get('has_child_storey', False)))
            layout_info['related'] = json.dumps(related)
            for k, v in layout_info.iteritems():
                setattr(layout, k, v)
            layout.save()

            if not layout_data:
                SpecialStorey.objects.filter(special_id=layout_info['special_id']).delete()

            for dd in layout_data:
                inner_data = dd.pop('data', [])
                parent_id = dd.pop('parent_id')
                sort = dd.pop('ordering', 0)
                related = json.dumps(dd)
                if parent_id:
                    if not has_child_storey:
                        related = json.loads(related)
                        related.update({'data': inner_data})
                        related = json.dumps(related)
                    SpecialStorey.objects.filter(id=parent_id).update(sort=sort, related=related)
                else:
                    if not has_child_storey:
                        related = json.loads(related)
                        related.update({'data': inner_data})
                        related = json.dumps(related)
                    s = SpecialStorey.objects.create(sort=sort, special_id=layout_info['special_id'], related=related)
                    parent_id = s.id

                if not inner_data:
                    SpecialStorey.objects.filter(special_id=layout_info['special_id'], parent_id=parent_id).delete()

                if has_child_storey:
                    # 有子楼层
                    for ss in inner_data:
                        floor_id = ss.pop('floor_id', None)
                        sort = ss.pop('ordering', -1)
                        related = json.dumps(ss)
                        if floor_id:
                           SpecialStorey.objects.filter(id=floor_id).update(sort=sort, related=related)
                        else:
                            SpecialStorey.objects.create(sort=sort, special_id=layout_info['special_id'], related=related, parent_id=parent_id)

        else:
            layout = SpecialLayout.objects.get(id=layout_id)
            for k, v in layout_info.iteritems():
                setattr(layout, k, v)
            layout.save()

    return {
        'code': 1,
        'message': u'修改成功！'
    }


@bind_context(uri_pre + '/new_get')
def special_detail(ctx, special_id, options=None, seckill=False):
    try:
        special = Special.objects.get(id=special_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    special_data = to_dict(special, **options)
    special_data['content'] = special.content

    # 确定全局是否含有美购列表
    si_flag = SpecialItem.objects.filter(special_id=special.id, floor_id__isnull=True).exists()
    sr_flag = SpecialRule.objects.filter(special_id=special_id).exists()
    special_data["has_special_sku_list"] = si_flag or sr_flag

    # 确定楼层里面是否含有美购列表
    target_flag_one = SpecialItem.objects.filter(special_id=special.id, floor_id__isnull=False).exists()
    target_flag_two = SpecialRelatedTag.objects.filter(special_id=special_id).exists()
    special_data["has_child_sku_list"] = target_flag_one or target_flag_two
    special_data['sp_items'] = []

    special_data['layouts'] = {}
    for layout in special.speciallayout_set.all():
        if layout.related:
            related = json.loads(layout.related)
        else:
            related = ''
        layout_data = {
            'id': layout.id,
            'is_visible': layout.is_visible,
            'related': related,
        }
        if layout.module == SPECIAL_MODULE.FLOOR:
            parent_data = {}
            related['data'] = []
            ss = SpecialStorey.objects.filter(special_id=special_id)
            spe = json.loads(layout.related)
            has_child_storey = bool(int(spe.get('has_child_storey', False)))
            for parent_d in ss.filter(parent_id__isnull=True):
                if parent_d:
                    parent_data = json.loads(parent_d.related)
                    parent_data['ordering'] = parent_d.sort
                    parent_data['parent_id'] = parent_d.id

                if parent_data and has_child_storey:
                    parent_data['data'] = []
                    for child in ss.filter(parent_id=parent_d.id):
                        child_data = json.loads(child.related)
                        child_data['ordering'] = child.sort
                        child_data['floor_id'] = child.id
                        # 判断子楼层是否有美购列表
                        flag_si = SpecialItem.objects.filter(special_id=special_id, floor_id=child.id).exists()
                        flag_sr = SpecialRelatedTag.objects.filter(special_id=special_id, floor_id=child.id).exists()
                        child_data['has_sku_list'] = flag_si or flag_sr
                        parent_data['data'].append(child_data)
                # elif parent_data and not has_child_storey:
                #     child = ss.filter(parent_id=parent_d.id).first()
                #     if child:
                #         child_data = json.loads(child.related)
                #     else:
                #         child_data = []
                #     parent_data['data'] = child_data

                related['data'].append(parent_data)
        special_data['layouts'][layout.module] = layout_data
    # 获取专题活动的分享信息
    try:
        share_info = Share.objects.get(id=special.share.id)
        special_data["share_title"] = share_info.title
        special_data["share_content"] = share_info.content
        special_data["share_moments"] = share_info.moments
        special_data["share_weibo"] = share_info.weibo
        special_data["share_image"] = share_info.image
        specialregion = SpecialRegion.objects.filter(special=special).first()
        if specialregion:
            special_data["region"] = specialregion.region.id
    except:
        info_logger.info(__import__('traceback').format_exc())
    return special_data


@bind_context(uri_pre + '/new_edit')
@transaction.atomic
def special_edit(ctx, special_id=None, special_info=None, seckill=False):
    """
    专题的编辑
    :param ctx:
    :param special_id:
    :param special_info:
    :param seckill:
    :return:
    """
    if special_info is None:
        return None
    share_info = {}
    region_id = special_info.pop("region", None)

    if 'share_title' in special_info:
        share_info = {
            "title": special_info.pop("share_title"),
            "content": special_info.pop("share_content"),
            "moments": special_info.pop("share_moments"),
            "weibo": special_info.pop("share_weibo"),
            "image": special_info.pop("share_image"),
        }

    if 'campaign' in special_info:  # 秒杀专场不关联活动
        special_info['campaign_id'] = special_info.pop('campaign')
        # 判断是否可以设置为超值
        is_overflow = special_info.get('is_overflow', False)
        campaign_id = special_info['campaign_id']
        if is_overflow and campaign_id:
            overflow_special_ids = Special.objects.filter(
                campaign_id=campaign_id, is_overflow=True,
            ).exclude(id=special_id).values_list('id', flat=True)
            if overflow_special_ids and special_id not in overflow_special_ids:
                # 该活动已有超值专场
                raise gen(CODES.CAMPAIGN_HAD_OVERFLOW)

    from django.db import transaction
    with transaction.atomic():
        if special_id is None:
            special = Special.objects.create(**special_info)
        else:
            special = Special.objects.get(id=special_id)
            update_fields = {}

            for k, v in special_info.iteritems():
                if getattr(special, k) != v:
                    update_fields[k] = v
            if update_fields:
               Special.objects.filter(id=special_id).update(**update_fields)

            special = Special.objects.get(id=special_id)

        # set skupricerule
        activity_type = ACTIVITY_TYPE_ENUM.SPECIAL
        if special.is_seckill:
            activity_type = ACTIVITY_TYPE_ENUM.SECKILL
        elif special.is_more_buy:
            activity_type = ACTIVITY_TYPE_ENUM.MOREBUY

        skupricerule = SKUPriceRule.objects.filter(
            activity_type=activity_type,
            activity_id=special.id
        ).first()
        if not skupricerule:
            SKUPriceRule.objects.create(
                name=special.title,
                is_enable=False,
                start_time=special.start_time,
                end_time=special.end_time,
                activity_type=activity_type,
                activity_id=special.id,
                more_buy_count=special.more_buy_count,
            )
        else:
            skupricerule.is_enable = special.is_online
            skupricerule.start_time = special.start_time
            skupricerule.end_time = special.end_time
            skupricerule.save()

        if region_id:
            if SpecialRegion.objects.filter(special_id=special.id):
                if not SpecialRegion.objects.filter(region_id=region_id, special_id=special.id):
                    SpecialRegion.objects.filter(special_id=special.id).update(region_id=region_id)
            else:
                SpecialRegion.objects.create(special_id=special.id, region_id=region_id)
        else:
            SpecialRegion.objects.filter(special_id=special.id).delete()

        # set special share info
        if special.share_id and share_info:
            share = Share.objects.get(id=special.share_id)
            for key, value in share_info.iteritems():
                setattr(share, key, value)
            share.save()
        elif share_info:
            share = Share.objects.create(**share_info)
            Special.objects.filter(id=special.id).update(share_id=share.id)

        refresh_service_cache_with_special_ids.delay(special_ids=[special.id])
        return {'id': special.id}


@bind_context(uri_pre + '/delete_floor')
def special_delete_floor(ctx, floor_id=None, types=None):
    """
    删除子楼层/楼层, 如果里面存在美购列表, 则需要把楼层关联的美购列表也删除掉
    :param ctx:
    :param floor_id:
    :param special_id:
    :param types:
    :return:
    """
    if not floor_id or not types:
        return
    if types == '1':
        SpecialStorey.objects.filter(id=floor_id).delete()
        SpecialItem.objects.filter(floor_id=floor_id).delete()

    elif types == '0':  # 这个是删除整个楼层
        # 删除子楼层
        child_floors = SpecialStorey.objects.filter(parent_id=floor_id)
        for c in child_floors:
            SpecialItem.objects.filter(floor_id=c.id).delete()
        child_floors.delete()
        # 删除父楼层
        SpecialStorey.objects.filter(id=floor_id).delete()
        # 删除楼层里面的美购列表
        SpecialItem.objects.filter(floor_id=floor_id)



@bind_context(uri_pre + '/delete_floor_service')
def special_delete_floor_service_list(ctx, special_id=None, floor_id=None):
    if not floor_id or not special_id:
        return

    # 删除子楼层的时候, 同时清空该楼层对应的美购列表
    s = None
    SpecialItem.objects.filter(special_id=special_id, floor_id=floor_id).delete()
    SpecialRelatedTag.objects.filter(special_id=special_id, floor_id=floor_id).delete()
    try:
        s = SpecialStorey.objects.get(id=floor_id)
        related = json.loads(s.related)
        res_data = []
        for _d in related.get('data', []):
            if _d.get('type') == 'service':
                continue
            else:
                res_data.append(_d)

        related['data'] = res_data
        s.related = json.dumps(related)
        s.save()
    except s.DoesNotExist:
        exception_logger.info(__import__('traceback').format_exc())



@bind_context(uri_pre + '/related_tags')
def special_related_tags(ctx, special_id, floor_id):
    if not floor_id or not special_id:
        return []
    return list(
        SpecialRelatedTag.objects.filter(special_id=special_id, floor_id=floor_id).values_list('tag_id', flat=True)
    )

@bind_context(uri_pre + '/related_new_tags')
def special_related_new_tags(ctx, special_id, floor_id):
    if not floor_id or not special_id:
        return []
    return list(
        SpecialRelatedTagV3.objects.filter(special_id=special_id, floor_id=floor_id).values_list('tagv3_id', flat=True)
    )


@bind_context(uri_pre + '/seckill_polymer/edit')
@transaction.atomic
def seckill_polymer_edit(ctx, seckill_polymer_id=None, seckill_polymer_info=None, share_info=None):
    """
    秒杀聚合
    :param ctx:
    :param special_id:
    :param special_info:
    :param seckill:
    :return:
    """
    related_seckill_ids = seckill_polymer_info.pop('related_seckill_id', [])
    if seckill_polymer_info is None:
        return {}

    if share_info is None:
        return {}

    if seckill_polymer_id is None:
        seckillpolymer = SeckillPolymer.objects.create(**seckill_polymer_info)
    else:
        try:
            seckillpolymer = SeckillPolymer.objects.get(id=seckill_polymer_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException

        for k, v in seckill_polymer_info.iteritems():
            setattr(seckillpolymer, k, v)
        seckillpolymer.save()

    sec_rel_objs = []
    old_special_ids = list(SeckillRelatedSpecial.objects.filter(
        seckillpolymer_id=seckillpolymer.id).values_list('special_id', flat=True))
    # 删除历史
    SeckillRelatedSpecial.objects.filter(
        special_id__in=set(old_special_ids) - set(related_seckill_ids),
        seckillpolymer=seckillpolymer
    ).delete()
    # 添加新
    for seckill_id in set(related_seckill_ids) - set(old_special_ids):
        sec_rel_objs.append(SeckillRelatedSpecial(special_id=seckill_id, seckillpolymer=seckillpolymer))
    SeckillRelatedSpecial.objects.bulk_create(sec_rel_objs)

    if not share_info.get('share_id', None):
        try:
            share_info.pop('share_id')
            share = Share.objects.create(**share_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            share = Share.objects.get(id=int(share_info.pop('share_id')))
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in share_info.iteritems():
            setattr(share, k, v)
        share.save()

    seckillpolymer.share = share
    seckillpolymer.save()

    return {'id': seckillpolymer.id}


@bind_context(uri_pre + '/seckill_polymer/get')
def seckill_polymer_get(ctx, seckill_polymer_id):
    """
    秒杀聚合页
    """
    if not seckill_polymer_id:
        return {}
    try:
        layouts_data = {}
        seckillpolymer = SeckillPolymer.objects.get(id=seckill_polymer_id)
        layouts = SeckillPolymerLayout.objects.filter(seckill_polymer_id=seckillpolymer.id)
        for layout in layouts:
            layouts_data[str(layout.module)] = {
                "id": layout.id,
                "is_visible": layout.is_visible,
                "related": json.loads(layout.related)
            }
        share_info = Share.objects.get(id=seckillpolymer.share.id)

        seckillpolymerbutton = SeckillPolymerButton.objects.filter(seckill_polymer_id=seckill_polymer_id)
        layouts_data['2'] = []
        for s in seckillpolymerbutton:
            specialregion = SpecialRegion.objects.filter(special=s.special).first()
            region = specialregion.region.name if specialregion else ''
            layouts_data['2'].append({
                'selected_image': s.selected_image,
                'unselected_image': s.unselected_image,
                'special_id': str(s.special_id),
                'region': region
            })
    except SeckillPolymer.DoesNotExist:
        raise RPCNotFoundException
    return {
        "id": seckillpolymer.id,
        "title": seckillpolymer.title,
        "image": seckillpolymer.image,
        "rules": seckillpolymer.rules,
        "buttons": seckillpolymer.tool_bar_json,
        "is_online": seckillpolymer.is_online,
        "related_seckill_id": list(seckillpolymer.seckill_polymers.values_list("special_id", flat=True)),
        "layouts_data": layouts_data,
        "share": share_info.id,
        "share_title": share_info.title,
        "share_content": share_info.content,
        "share_moments": share_info.moments,
        "share_weibo": share_info.weibo,
        "share_image": share_info.image,
        "polymer_type": seckillpolymer.polymer_type,
        "button_selection": seckillpolymer.button_selection,
    }



@bind_context(uri_pre + '/seckill_polymer/layout_edit')
def seckill_layout_edit(ctx, layout_id, layout_info):
    """
    秒杀聚合页配置
    """
    if layout_id is None:
        layout = SeckillPolymerLayout.objects.create(**layout_info)
    else:
        try:
            layout = SeckillPolymerLayout.objects.get(id=layout_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in layout_info.iteritems():
            setattr(layout, k, v)
            layout.save()

    return {
        "id": layout.id,
    }

@bind_context(uri_pre + '/seckill_polymer/button_edit')
def seckill_button_edit(ctx, layout_id, layout_info):
    """
    秒杀聚合页配置
    """
    related = json.loads(layout_info['related'])
    region_dict = {}
    for idx, button in enumerate(related):
        specialregion = SpecialRegion.objects.filter(special_id=button['special_id']).first()
        if specialregion:
            region_dict[idx] = specialregion.region.name
    if len(region_dict.values()) != len(set(region_dict.values())):
        new_dict = {}
        for k, v in region_dict.items():
            new_dict.setdefault(v, []).append(k)
        for k, v in new_dict.items():
            if len(v) > 1:
                return {
                    "err": "专场{}与专场{}地域属性重复".format(v[0]+1, v[1]+1),
                }

    seckillpolymerbutton = SeckillPolymerButton.objects.filter(seckill_polymer_id=layout_info['seckill_polymer_id']).delete()
    SeckillPolymerButton.objects.bulk_create([
        SeckillPolymerButton(
            seckill_polymer_id=layout_info['seckill_polymer_id'],
            selected_image=button['selected_image'],
            unselected_image=button['unselected_image'],
            special_id=button['special_id'],
            module=layout_info['module'],
        )
        for button in related
    ])
    return {
        "id": layout_info['seckill_polymer_id'],
    }


@bind_context(uri_pre + '/replication_special')
def replication_special_edit(ctx, special_id):
    """
    秒杀专题复制创建
    """
    special_info = Special.objects.filter(id=special_id).first()
    share_id = special_info.share_id
    if share_id:
        share = Share.objects.get(id=share_id)
    else:
        share = None

    with transaction.atomic():
            if share:
                new_share = Share.objects.create(
                    title=share.title,
                    content=share.content,
                    moments=share.moments,
                    weibo=share.weibo,
                    image=share.image,
                )

            special = Special.objects.create(
                title=special_info.title,
                desc=special_info.desc,
                content=special_info.content,
                type=special_info.type,
                style_type=special_info.style_type,
                theme=special_info.theme,
                start_time=special_info.start_time + datetime.timedelta(days=365),
                end_time=special_info.end_time + datetime.timedelta(days=365),
                doctor_name=special_info.doctor_name,
                hospital_name=special_info.hospital_name,
                price_1=special_info.price_1,
                price_2=special_info.price_2,
                coupons=special_info.coupons,
                is_show_coupons=special_info.is_show_coupons,
                is_show_content=special_info.is_show_content,
                is_show_gadget=special_info.is_show_gadget,
                is_online=special_info.is_online,
                rank=special_info.rank,
                image=special_info.image,
                icon=special_info.icon,
                recommend=special_info.recommend,
                is_seckill=special_info.is_seckill,
                is_overflow=special_info.is_overflow,
                campaign=special_info.campaign,
                purchase_limit=special_info.purchase_limit,
                is_servicehome=special_info.is_servicehome,
                is_recommendpool=special_info.is_recommendpool,
                is_advertisement=special_info.is_advertisement,
                share=new_share if share else special_info.share,
                is_show_service_zone=special_info.is_show_service_zone,
                service_zone_content=special_info.service_zone_content,
                seckill_type=special_info.seckill_type,
                is_show_resource=special_info.is_show_resource,
                rank_position=special_info.rank_position,
                xiaochengxu_img=special_info.xiaochengxu_img,
                polymer_backgrond_image=special_info.polymer_backgrond_image,
                polymer_unselected_image=special_info.polymer_unselected_image,
                activity_rules=special_info.activity_rules,
                groupbuy=special_info.groupbuy,
                promotion_image=special_info.promotion_image,
                is_new_special=special_info.is_new_special,
                is_show_address=special_info.is_show_address,
            )
            # set skupricerule
            old_skupricerule = SKUPriceRule.objects.filter(activity_id=special_id).first()
            SKUPriceRule.objects.get_or_create(
                activity_type=ACTIVITY_TYPE_ENUM.SECKILL,
                activity_id=special.id,
                defaults={
                    "name":special.title,
                    "is_enable": False,
                    "start_time": special.start_time,
                    "end_time": special.end_time,
                    "refund_anytime": old_skupricerule.refund_anytime,
                    "can_use_points": old_skupricerule.can_use_points,
                    "share_get_cashback": old_skupricerule.share_get_cashback,
                    "groupbuy_type": old_skupricerule.groupbuy_type,
                    "active_type": old_skupricerule.active_type,
                    "groupbuy_nums": old_skupricerule.groupbuy_nums,
                    "groupbuy_countdown": old_skupricerule.groupbuy_countdown,
                }
            )
            add_purchase_list.delay(special_id, special, old_skupricerule)
            add_specialresource.delay(special_id, special)
            add_speciallayout.delay(special_id, special)

    refresh_service_cache_with_special_ids(special_ids=[special.id])
    return {'id': special.id}


@transaction.atomic()
@bind_context(uri_pre + '/batch_specialitem_delete')
def batch_specialitem_delete(ctx, special_id, floor_id=None, apply_ids=None, services_ids=None, serviceitem_ids=None):
    """
    删除专题美购列表
    :param ctx:
    :param special_id:
    :param floor_id:
    :param apply_ids:
    :param services_ids:
    :param serviceitem_ids:
    :return:
    """
    if not special_id:
        return []

    init_q = Q(special_id=special_id)

    if apply_ids and isinstance(apply_ids, list):
        if floor_id:
            init_q &= Q(floor_id=floor_id)
        for apply_id in apply_ids:
            specialitem = SpecialItem.objects.filter(init_q, doctorseckillapply_id=apply_id).first()
            if not specialitem:
                continue
            delete_specialitem(specialitem)

    if services_ids and isinstance(services_ids, list):
        if floor_id:
            init_q &= Q(floor_id=floor_id)
        for service_id in services_ids:
            specialitems = SpecialItem.objects.filter(init_q, service_id=service_id)
            for specialitem in specialitems:
                delete_specialitem(specialitem)

    if serviceitem_ids and isinstance(serviceitem_ids, list):
        if floor_id:
            init_q &= Q(floor_id=floor_id)
        for serviceitem_id in serviceitem_ids:
            specialitem = SpecialItem.objects.filter(init_q, serviceitem_id=serviceitem_id).first()
            if not specialitem:
                continue
            delete_specialitem(specialitem)
    refresh_service_cache_with_special_ids(special_ids=[special_id])
    return []
