import hmac
import threading
import traceback
import uuid
import hashlib
import xmltodict
import time
import logging
import requests
import os
import secrets
import random
import string
from calendar import monthrange
from collections import defaultdict
from decimal import Decimal
from datetime import date, datetime, timedelta
from django.utils import timezone
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator
from django.db import models, transaction, IntegrityError
from django.db.models import Q, Count, Sum, F, Exists, OuterRef
from gvsdsdk.fluent import db, func, FQ
from django.db.models.functions import TruncDate, TruncMonth, TruncYear
from django.contrib.auth.hashers import make_password
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser

# 工具类导入
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
from backend.utils import (
    update_dashou_daily_by_action,
    update_shangjia_daily
)
from jituan.services.club_context import filter_club_char_field, filter_queryset_by_club, filter_user_related_by_club, filter_user_qs_by_club, orders_for_request
from jituan.services.catalog_scope import block_non_group_catalog_scope
from jituan.services.club_penalty import (
    filter_penalty_qs, resolve_penalty_club_id, filter_gsfenhong_qs,
    forbid_penalty_out_of_scope, resolve_penalty_record_club_id,
)
from jituan.services.club_user_access import forbid_if_user_out_of_scope, list_response_meta
from shop.utils import update_dianpu_daily_stat
from products.utils import update_shangpin_daily_stat
from orders.notice_tasks import dingdan_guangbo
from ..utils import (
    verify_kefu_permission,
    PERMISSION_TO_SHENFEN,
    SHENFEN_PROFILE_MAP,
    has_fadan_view_permission,
    has_merchant_order_permission,
    check_fadan_permission,
    pick_order_id,
    pick_penalty_create_params,
    write_xiugai_log,
    XIUGAI_LEIXING_DASHOU,
    XIUGAI_LEIXING_GUANSHI,
    XIUGAI_LEIXING_SHANGJIA,
    XIUGAI_LEIXING_ZUZHANG,
    XIUGAI_LEIXING_LABEL,
)

# models 集中导入
## gvsdsdk RBAC 模型（使用 PascalCase 字段名，匹配实际数据库表结构）
from gvsdsdk.models import Role, Permission, RolePermission, UserRole
## backend
from backend.models import WithdrawalDailyStats

## users
from users.models import (
    UserGuanshi, UserBoss, UserZuzhang,
    UserKefu, UserDashou, Xiugaijilu, UserShangjia, UserShenheguan
)
from users.business_models import User

## orders
from orders.models import (
    CommissionRate, PlayerDeliveryImage, PenaltyRecord,
    OrderPlayerHistory, Order, RefundRecord,
    MerchantOrderExt, Penalty, PenaltyAppealImage, PenaltyBonus
)

## shop
from shop.models import Dianpu, DianpuMorenPeizhi, YonghuPingzheng, ShangpinLeixingDianpu, DianpuShangpinShenheShezhi

## products
from products.models import (
    Shangpin, ShangpinLeixing, ShangpinZhuanqu, Huiyuan,
    DuociFenhong, Czjilu, Huiyuangoumai, Gsfenhong
)

## config
from config.models import (
    WithdrawConfig, ShangjiaLianjie, AccountPermission,
    TixianQuotaDefault,
    DailyIncomeStat, DailyPayoutStat, PopupPage, PopupConfig, PopupImage
)

## rank
from rank.models import (
    KaoheguanBankuai, Bankuai, Chenghao, KaoheCishuFeiyong,
    YonghuChenghao, ShenheJilu, KaoheJiluFeiyong, KaoheJujueJilu
)

# 序列化器
from ..serializers import PopupPageSerializer

# 全局常量、日志对象
logger = logging.getLogger('houtai')
SHOP_PRODUCT_PERMS = ['1199ab', '1199abc', '1199abd']

class CaiwuView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        username_from_frontend = request.data.get('phone', None)
        kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)

        if kefu_obj is None:
            return permissions

        if 'caiwu' not in permissions:
            return Response({'code': 403, 'msg': '无财务查看权限'}, status=403)

        from jituan.services.caiwu_stats import build_caiwu_payload

        try:
            data = build_caiwu_payload(request)
            return Response({'code': 0, 'msg': 'ok', 'data': data})
        except Exception:
            logger.exception('CaiwuView 异常')
            return Response({'code': 99, 'msg': '统计失败'}, status=500)


class CwhybkhqView(APIView):
    """
    获取所有板块及对应会员列表
    POST /houtai/cwhybkhq
    Body: { "phone": "管理员手机号" }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions

            if 'huiyuancaiwu' not in permissions:
                return Response({'code': 403, 'msg': '无会员财务查看权限'}, status=403)

            all_bankuai = list(Bankuai.query.all())
            bankuai_ids = [bk.bankuai_id for bk in all_bankuai]
            all_members = Huiyuan.query.filter(bankuai_id__in=bankuai_ids).values('huiyuan_id', 'jieshao', 'bankuai_id')
            members_by_bankuai = defaultdict(list)
            for m in all_members:
                members_by_bankuai[m['bankuai_id']].append(dict(m))
            bankuai_list = []
            for bk in all_bankuai:
                bankuai_list.append({
                    'bankuai_id': bk.bankuai_id,
                    'mingcheng': bk.mingcheng,
                    'members': members_by_bankuai.get(bk.bankuai_id, [])
                })

            return Response({'code': 0, 'msg': 'ok', 'data': bankuai_list})
        except Exception as e:
            logger.exception("CwhybkhqView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)


class HybkjtsjView(APIView):
    """
    会员详细统计数据
    POST /houtai/hybkjtsj
    Body: {
        "phone": "管理员手机号",
        "huiyuan_id": "会员ID",
        "granularity": "day" | "month" | "year",
        "year": 2026,
        "month": 5,
        "include_guanshi": true,
        "include_zuzhang": true,
        "summary_date": "2026-05-16"
    }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions

            if 'huiyuancaiwu' not in permissions:
                return Response({'code': 403, 'msg': '无会员财务查看权限'}, status=403)

            huiyuan_id = request.data.get('huiyuan_id')
            granularity = request.data.get('granularity', 'day')
            year = request.data.get('year')
            month = request.data.get('month')
            include_guanshi = request.data.get('include_guanshi', False)
            include_zuzhang = request.data.get('include_zuzhang', False)
            summary_date = request.data.get('summary_date')

            if not huiyuan_id:
                return Response({'code': 1, 'msg': '缺少会员ID'}, status=400)

            # 🔧 直接使用 date.today() 避免模块名冲突
            now = date.today()

            # ---------- 构造时间范围 ----------
            if granularity == 'day':
                if not year or not month:
                    return Response({'code': 1, 'msg': '按日统计需要年份和月份'}, status=400)
                start_date = date(year, month, 1)
                if month == 12:
                    end_date = date(year + 1, 1, 1)
                else:
                    end_date = date(year, month + 1, 1)
                trunc_func = TruncDate('CreateTime')
                if not summary_date:
                    summary_date = now.strftime('%Y-%m-%d')
                summary_q = datetime.strptime(summary_date, '%Y-%m-%d').date()
            elif granularity == 'month':
                if not year:
                    return Response({'code': 1, 'msg': '按月统计需要年份'}, status=400)
                start_date = date(year, 1, 1)
                end_date = date(year + 1, 1, 1)
                trunc_func = TruncMonth('CreateTime')
                if not summary_date:
                    summary_date = now.strftime('%Y-%m')
                summary_q = datetime.strptime(summary_date + '-01', '%Y-%m-%d').date()
            elif granularity == 'year':
                year = year or now.year
                start_date = date(year, 1, 1)
                end_date = date(year + 1, 1, 1)
                trunc_func = TruncYear('CreateTime')
                if not summary_date:
                    summary_date = str(year)
                summary_q = date(year, 1, 1)
            else:
                return Response({'code': 1, 'msg': '无效的颗粒度'}, status=400)

            # 生成 naive datetime（避免 USE_TZ=False 报错）
            start_dt = datetime.combine(start_date, datetime.min.time())
            end_dt = datetime.combine(end_date, datetime.min.time())

            # ---------- 充值过滤 ----------
            chongzhi_filter = {
                'huiyuan_id': huiyuan_id,       # 会员ID筛选
                'leixing': 1,
                'zhuangtai': 3,
                'CreateTime__gte': start_dt,
                'CreateTime__lt': end_dt
            }

            # ---------- 分红过滤 ----------
            fenhong_filter = {
                'huiyuan_id': huiyuan_id,       # 分红表会员ID筛选
                'CreateTime__gte': start_dt,
                'CreateTime__lt': end_dt
            }

            # 充值分组查询
            chongzhi_qs = filter_queryset_by_club(
                Czjilu.query.filter(**chongzhi_filter), request,
            ) \
                .annotate(period=trunc_func) \
                .values('period') \
                .annotate(
                    chongzhi_count=Count('id'),
                    chongzhi_amount=Sum('jine')
                ).order_by('period')

            # 分红分组查询
            fenhong_qs = None
            if include_guanshi or include_zuzhang:
                fenhong_agg = {}
                if include_guanshi:
                    fenhong_agg['guanshi_amount'] = Sum('fenhong')
                if include_zuzhang:
                    fenhong_agg['zuzhang_amount'] = Sum('zuzhang_fenhong')
                fenhong_qs = filter_gsfenhong_qs(
                    Gsfenhong.query.filter(**fenhong_filter), request,
                ) \
                    .annotate(period=trunc_func) \
                    .values('period') \
                    .annotate(**fenhong_agg) \
                    .order_by('period')

            # 合并数据
            data_map = {}
            for item in chongzhi_qs:
                period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \
                             item['period'].strftime('%Y-%m') if granularity == 'month' else \
                             item['period'].strftime('%Y')
                data_map[period_str] = {
                    'chongzhi_count': item['chongzhi_count'],
                    'chongzhi_amount': float(item['chongzhi_amount'] or 0)
                }

            if fenhong_qs:
                for item in fenhong_qs:
                    period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \
                                 item['period'].strftime('%Y-%m') if granularity == 'month' else \
                                 item['period'].strftime('%Y')
                    if period_str not in data_map:
                        data_map[period_str] = {'chongzhi_count': 0, 'chongzhi_amount': 0.0}
                    if include_guanshi:
                        data_map[period_str]['guanshi_amount'] = float(item.get('guanshi_amount') or 0)
                    if include_zuzhang:
                        data_map[period_str]['zuzhang_amount'] = float(item.get('zuzhang_amount') or 0)

            time_series = []
            for period_str, vals in data_map.items():
                guanshi = vals.get('guanshi_amount', 0) if include_guanshi else 0
                zuzhang = vals.get('zuzhang_amount', 0) if include_zuzhang else 0
                vals['shouyi'] = round(vals['chongzhi_amount'] - guanshi - zuzhang, 2)
                vals['date'] = period_str
                time_series.append(vals)
            time_series.sort(key=lambda x: x['date'])

            # ---------- 汇总数据 ----------
            summary_chongzhi_filter = chongzhi_filter.copy()
            if granularity == 'day':
                summary_chongzhi_filter['CreateTime__date'] = summary_q
            elif granularity == 'month':
                summary_chongzhi_filter['CreateTime__year'] = summary_q.year
                summary_chongzhi_filter['CreateTime__month'] = summary_q.month
            else:
                summary_chongzhi_filter['CreateTime__year'] = summary_q.year

            summary_chongzhi = Czjilu.query.filter(**summary_chongzhi_filter).aggregate(
                total_count=Count('id'),
                total_amount=Sum('jine')
            )
            total_chongzhi_count = summary_chongzhi['total_count'] or 0
            total_chongzhi_amount = float(summary_chongzhi['total_amount'] or 0)

            total_guanshi = 0.0
            total_zuzhang = 0.0
            if include_guanshi or include_zuzhang:
                summary_fenhong_filter = fenhong_filter.copy()
                if granularity == 'day':
                    summary_fenhong_filter['CreateTime__date'] = summary_q
                elif granularity == 'month':
                    summary_fenhong_filter['CreateTime__year'] = summary_q.year
                    summary_fenhong_filter['CreateTime__month'] = summary_q.month
                else:
                    summary_fenhong_filter['CreateTime__year'] = summary_q.year

                fenhong_agg = {}
                if include_guanshi:
                    fenhong_agg['guanshi_sum'] = Sum('fenhong')
                if include_zuzhang:
                    fenhong_agg['zuzhang_sum'] = Sum('zuzhang_fenhong')
                summary_fenhong = Gsfenhong.query.filter(**summary_fenhong_filter).aggregate(**fenhong_agg)
                total_guanshi = float(summary_fenhong.get('guanshi_sum') or 0)
                total_zuzhang = float(summary_fenhong.get('zuzhang_sum') or 0)

            total_shouyi = round(total_chongzhi_amount - total_guanshi - total_zuzhang, 2)

            return Response({
                'code': 0,
                'msg': 'ok',
                'data': {
                    'time_series': time_series,
                    'summary': {
                        'total_chongzhi_count': total_chongzhi_count,
                        'total_chongzhi_amount': total_chongzhi_amount,
                        'total_guanshi_fenhong': total_guanshi,
                        'total_zuzhang_fenhong': total_zuzhang,
                        'total_shouyi': total_shouyi
                    }
                }
            })
        except Exception as e:
            logger.exception("HybkjtsjView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)





class SzxxView(APIView):
    """
    每日收支详细统计数据
    POST /houtai/szxx
    只读 daily_income_stat / daily_payout_stat，按 date 字段汇总。
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            from jituan.services.szxx_stats import build_szxx_payload

            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions

            if 'caiwu' not in permissions:
                return Response({'code': 403, 'msg': '无财务查看权限'}, status=403)

            granularity = request.data.get('granularity', 'day')
            year = request.data.get('year')
            month = request.data.get('month')
            summary_date = request.data.get('summary_date')

            data, err = build_szxx_payload(request, granularity, year, month, summary_date)
            if err:
                return Response({'code': 1, 'msg': err}, status=400)

            return Response({'code': 0, 'msg': 'ok', 'data': data})

        except Exception as e:
            logger.exception("SzxxView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)    






     

class CwddhqlxView(APIView):
    """
    获取订单类型（商品类型）用于筛选
    POST /houtai/cwddhqlx
    Body: { "phone": "管理员手机号" }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions

            if 'dingdancaiwu' not in permissions:
                return Response({'code': 403, 'msg': '无订单财务查看权限'}, status=403)

            # 获取非审核状态（shenhezhuangtai=1）的商品类型，只返回ID和名称
            types = ShangpinLeixing.query.filter(shenhezhuangtai=1).values('id', 'jieshao')
            return Response({
                'code': 0,
                'msg': 'ok',
                'data': list(types)
            })
        except Exception as e:
            logger.exception("CwddhqlxView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)




class CwhqjtddsjView(APIView):
    """
    订单详细统计数据（修正版）
    POST /houtai/cwhqjtddsj
    Body: {
        "phone": "管理员手机号",
        "granularity": "day" | "month" | "year",
        "year": 2026,
        "month": 5,
        "type_id": 1,            # 商品类型ID，0表示全部
        "order_source": 1,       # 1=平台单，2=商家单，0=全部
        "summary_date": "2026-05-16"
    }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions
            if 'dingdancaiwu' not in permissions:
                return Response({'code': 403, 'msg': '无订单财务查看权限'}, status=403)

            granularity = request.data.get('granularity', 'day')
            year = request.data.get('year')
            month = request.data.get('month')
            type_id = request.data.get('type_id', 0)
            order_source = request.data.get('order_source', 0)
            summary_date = request.data.get('summary_date')

            now = date.today()

            # ---------- 时间范围 ----------
            if granularity == 'day':
                if not year or not month:
                    return Response({'code': 1, 'msg': '按日统计需要年份和月份'}, status=400)
                start_date = date(year, month, 1)
                end_date = date(year, month + 1, 1) if month < 12 else date(year + 1, 1, 1)
                trunc_func = TruncDate('CreateTime')
                if not summary_date:
                    summary_date = now.strftime('%Y-%m-%d')
                summary_q = datetime.strptime(summary_date, '%Y-%m-%d').date()
            elif granularity == 'month':
                if not year:
                    return Response({'code': 1, 'msg': '按月统计需要年份'}, status=400)
                start_date = date(year, 1, 1)
                end_date = date(year + 1, 1, 1)
                trunc_func = TruncMonth('CreateTime')
                if not summary_date:
                    summary_date = now.strftime('%Y-%m')
                summary_q = datetime.strptime(summary_date + '-01', '%Y-%m-%d').date()
            elif granularity == 'year':
                year = year or now.year
                start_date = date(year, 1, 1)
                end_date = date(year + 1, 1, 1)
                trunc_func = TruncYear('CreateTime')
                if not summary_date:
                    summary_date = str(year)
                summary_q = date(year, 1, 1)
            else:
                return Response({'code': 1, 'msg': '无效的颗粒度'}, status=400)

            start_dt = datetime.combine(start_date, datetime.min.time())
            end_dt = datetime.combine(end_date, datetime.min.time())

            # ---------- 基础过滤 ----------
            filters = {
                'CreateTime__gte': start_dt,
                'CreateTime__lt': end_dt,
                'Status__in': [1, 2, 3, 4, 5, 6, 7, 8]  # 只统计有效订单状态
            }
            if int(type_id) != 0:
                filters['ProductTypeID'] = type_id
            if int(order_source) in [1, 2]:
                filters['Platform'] = order_source

            # ---------- 时间序列分组查询 ----------
            order_base = filter_queryset_by_club(
                Order.query.filter(**filters), request, club_field='ClubID',
            )
            qs = order_base \
                .annotate(period=trunc_func) \
                .values('period') \
                .annotate(
                # 订单总量（所有符合条件的状态）
                order_count=Count('id'),
                # 订单总额
                order_amount=Sum('Amount'),
                # 成交单量 (Status=3)
                success_count=Count('id', filter=Q(Status=3)),
                # 成交订单总额
                success_amount=Sum('Amount', filter=Q(Status=3)),
                # 退款单量 (Status=5)
                refund_count=Count('id', filter=Q(Status=5)),
                # 打手分成总额（仅成交订单）
                PlayerCommission_sum=Sum('PlayerCommission', filter=Q(Status=3)),
                # 平台成交订单的店铺分红
                dianpu_fenhong_sum=Sum('pingtai_kuozhan__ShopIncome',
                                       filter=Q(Status=3, Platform=1)),
                # 成交订单中：平台单金额、商家单金额、各自打手分成
                platform_success_amount=Sum('Amount', filter=Q(Status=3, Platform=1)),
                merchant_success_amount=Sum('Amount', filter=Q(Status=3, Platform=2)),
                platform_dashou=Sum('PlayerCommission', filter=Q(Status=3, Platform=1)),
                merchant_dashou=Sum('PlayerCommission', filter=Q(Status=3, Platform=2)),
            ).order_by('period')

            time_series = []
            for item in qs:
                period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \
                    item['period'].strftime('%Y-%m') if granularity == 'month' else \
                        item['period'].strftime('%Y')

                # 平台收益计算
                platform_profit = (item['platform_success_amount'] or 0) \
                                  - (item['platform_dashou'] or 0) \
                                  - (item['dianpu_fenhong_sum'] or 0)
                merchant_profit = (item['merchant_success_amount'] or 0) \
                                  - (item['merchant_dashou'] or 0)
                total_profit = platform_profit + merchant_profit

                time_series.append({
                    'date': period_str,
                    'order_count': item['order_count'],
                    'order_amount': float(item['order_amount'] or 0),
                    'success_count': item['success_count'],
                    'success_amount': float(item['success_amount'] or 0),
                    'refund_count': item['refund_count'],
                    'dashou_fencheng': float(item['PlayerCommission_sum'] or 0),
                    'dianpu_fenhong': float(item['dianpu_fenhong_sum'] or 0),
                    'profit': round(total_profit, 2)
                })

            # ---------- 汇总数据 ----------
            summary_qs = filter_queryset_by_club(
                Order.query.filter(**filters), request, club_field='ClubID',
            )
            agg = summary_qs.aggregate(
                total_order_count=Count('id'),
                total_order_amount=Sum('Amount'),
                total_success_count=Count('id', filter=Q(Status=3)),
                total_success_amount=Sum('Amount', filter=Q(Status=3)),
                total_refund_count=Count('id', filter=Q(Status=5)),
                total_dashou=Sum('PlayerCommission', filter=Q(Status=3)),
                total_dianpu_fenhong=Sum('pingtai_kuozhan__ShopIncome', filter=Q(Status=3, Platform=1)),
                platform_amount=Sum('Amount', filter=Q(Status=3, Platform=1)),
                merchant_amount=Sum('Amount', filter=Q(Status=3, Platform=2)),
                platform_dashou_sum=Sum('PlayerCommission', filter=Q(Status=3, Platform=1)),
                merchant_dashou_sum=Sum('PlayerCommission', filter=Q(Status=3, Platform=2)),
            )
            total_order_count = agg['total_order_count'] or 0
            total_order_amount = agg['total_order_amount'] or 0
            total_success_count = agg['total_success_count'] or 0
            total_success_amount = agg['total_success_amount'] or 0
            total_refund_count = agg['total_refund_count'] or 0
            total_dashou = agg['total_dashou'] or 0
            total_dianpu_fenhong = agg['total_dianpu_fenhong'] or 0
            platform_amount = agg['platform_amount'] or 0
            merchant_amount = agg['merchant_amount'] or 0
            platform_dashou_sum = agg['platform_dashou_sum'] or 0
            merchant_dashou_sum = agg['merchant_dashou_sum'] or 0

            profit_platform = platform_amount - platform_dashou_sum - total_dianpu_fenhong
            profit_merchant = merchant_amount - merchant_dashou_sum
            total_profit = profit_platform + profit_merchant

            summary = {
                'total_order_count': total_order_count,
                'total_order_amount': round(float(total_order_amount), 2),
                'total_success_count': total_success_count,
                'total_success_amount': round(float(total_success_amount), 2),
                'total_refund_count': total_refund_count,
                'total_PlayerCommission': round(float(total_dashou), 2),
                'total_dianpu_fenhong': round(float(total_dianpu_fenhong), 2),
                'total_profit': round(total_profit, 2)
            }

            return Response({
                'code': 0,
                'msg': 'ok',
                'data': {
                    'time_series': time_series,
                    'summary': summary
                }
            })
        except Exception as e:
            logger.exception("CwhqjtddsjView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)










class CwqtczhqView(APIView):
    """
    其他充值/罚款数据统计（非会员充值）
    POST /houtai/cwqtczhq
    Body: {
        "phone": "管理员手机号",
        "granularity": "day" | "month" | "year",
        "year": 2026,
        "month": 5,                         // day时必填
        "types": [2,3,4,5,6],               // 2=押金,3=积分,4=商家,5=考核,6=罚款
        "summary_date": "2026-05-17"        // 汇总日期，格式随颗粒度（day:YYYY-MM-DD, month:YYYY-MM, year:YYYY）
    }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions
            if 'qtczcaiwu' not in permissions:
                return Response({'code': 403, 'msg': '无其他充值财务查看权限'}, status=403)

            from jituan.services.finance_detail_stats import build_chongzhi_finance_payload

            granularity = request.data.get('granularity', 'day')
            year = request.data.get('year')
            month = request.data.get('month')
            types = request.data.get('types', [2, 3, 4, 5, 6])
            summary_date = request.data.get('summary_date')

            if isinstance(types, list) and len(types) == 0:
                types = [2, 3, 4, 5, 6]

            data, err = build_chongzhi_finance_payload(
                request, granularity, year, month, types, summary_date,
            )
            if err:
                return Response({'code': 1, 'msg': err}, status=400)

            return Response({'code': 0, 'msg': 'ok', 'data': data})
        except Exception as e:
            logger.exception("CwqtczhqView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)

'''class CwqtczhqView_LEGACY(APIView):
    """
    其他充值/罚款数据统计（非会员充值）
    POST /houtai/cwqtczhq
    Body: {
        "phone": "管理员手机号",
        "granularity": "day" | "month" | "year",
        "year": 2026,
        "month": 5,                         // day时必填
        "types": [2,3,4,5,6],               // 2=押金,3=积分,4=商家,5=考核,6=罚款
        "summary_date": "2026-05-17"        // 汇总日期，格式随颗粒度（day:YYYY-MM-DD, month:YYYY-MM, year:YYYY）
    }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            username_from_frontend = request.data.get('phone', None)
            kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
            if kefu_obj is None:
                return permissions
            if 'qtczcaiwu' not in permissions:
                return Response({'code': 403, 'msg': '无其他充值财务查看权限'}, status=403)

            granularity = request.data.get('granularity', 'day')
            year = request.data.get('year')
            month = request.data.get('month')
            types = request.data.get('types', [2, 3, 4, 5, 6])   # 默认全部
            summary_date = request.data.get('summary_date')

            if isinstance(types, list) and len(types) == 0:
                types = [2, 3, 4, 5, 6]

            now = date.today()

            # ---------- 时间范围（用于曲线图） ----------
            if granularity == 'day':
                if not year or not month:
                    return Response({'code': 1, 'msg': '按日统计需要年份和月份'}, status=400)
                start_date = date(year, month, 1)
                end_date = date(year, month + 1, 1) if month < 12 else date(year + 1, 1, 1)
                trunc_func = TruncDate('CreateTime')
                # 默认汇总日期
                if not summary_date:
                    summary_date = now.strftime('%Y-%m-%d')
                summary_q = datetime.strptime(summary_date, '%Y-%m-%d').date()
            elif granularity == 'month':
                if not year:
                    return Response({'code': 1, 'msg': '按月统计需要年份'}, status=400)
                start_date = date(year, 1, 1)
                end_date = date(year + 1, 1, 1)
                trunc_func = TruncMonth('CreateTime')
                if not summary_date:
                    summary_date = now.strftime('%Y-%m')
                summary_q = datetime.strptime(summary_date + '-01', '%Y-%m-%d').date()
            elif granularity == 'year':
                year = year or now.year
                start_date = date(year, 1, 1)
                end_date = date(year + 1, 1, 1)
                trunc_func = TruncYear('CreateTime')
                if not summary_date:
                    summary_date = str(year)
                summary_q = date(year, 1, 1)
            else:
                return Response({'code': 1, 'msg': '无效的颗粒度'}, status=400)

            start_dt = datetime.combine(start_date, datetime.min.time())
            end_dt = datetime.combine(end_date, datetime.min.time())

            # ---------- 构建时间序列 ----------
            time_series_map = {}   # period_str -> { type_code: {count, amount, profit, fenhong_count, fenhong_amount} }

            def ensure_date(ds):
                if ds not in time_series_map:
                    time_series_map[ds] = {}
                return time_series_map[ds]

            # 1. 处理充值记录（Czjilu）
            cz_types = [t for t in types if t in [2, 3, 4, 5]]
            if cz_types:
                cz_qs = Czjilu.query.filter(
                    CreateTime__gte=start_dt,
                    CreateTime__lt=end_dt,
                    leixing__in=cz_types,
                    zhuangtai=3                     # 只统计支付成功的
                ).annotate(
                    period=trunc_func
                ).values('period', 'leixing').annotate(
                    total_count=Count('id'),
                    total_amount=Sum('jine')
                ).order_by('period', 'leixing')

                for item in cz_qs:
                    period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \
                                 item['period'].strftime('%Y-%m') if granularity == 'month' else \
                                 item['period'].strftime('%Y')
                    lx = item['leixing']
                    amount = float(item['total_amount'] or 0)
                    count = item['total_count']
                    # 收益：积分(3)收益为金额本身，其他为0
                    profit = amount if lx == 3 else 0.0

                    day_data = ensure_date(period_str)
                    day_data[str(lx)] = {
                        'count': count,
                        'amount': amount,
                        'profit': profit,
                        'fenhong_count': 0,      # 充值类型无分红
                        'fenhong_amount': 0.0
                    }

            # 2. 处理罚款（Penalty）—— 包含分红
            if 6 in types:
                # 罚款记录
                penalty_qs = Penalty.query.filter(
                    CreateTime__gte=start_dt,
                    CreateTime__lt=end_dt,
                    Status=2                     # 已缴纳
                ).annotate(
                    period=trunc_func
                ).values('period').annotate(
                    penalty_count=Count('id'),
                    penalty_amount=Sum('FineAmount')
                ).order_by('period')

                # 分红记录
                fenhong_qs = PenaltyBonus.query.filter(
                    CreateTime__gte=start_dt,
                    CreateTime__lt=end_dt
                ).annotate(
                    period=trunc_func
                ).values('period').annotate(
                    fh_count=Count('id'),
                    fh_amount=Sum('WithdrawableAmount')
                ).order_by('period')

                # 合并
                for item in penalty_qs:
                    period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \
                                 item['period'].strftime('%Y-%m') if granularity == 'month' else \
                                 item['period'].strftime('%Y')
                    day_data = ensure_date(period_str)
                    day_data['6'] = {
                        'count': item['penalty_count'],
                        'amount': float(item['penalty_amount'] or 0),
                        'profit': 0.0,   # 先设为0，后续计算
                        'fenhong_count': 0,
                        'fenhong_amount': 0.0
                    }

                for item in fenhong_qs:
                    period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \
                                 item['period'].strftime('%Y-%m') if granularity == 'month' else \
                                 item['period'].strftime('%Y')
                    day_data = ensure_date(period_str)
                    if '6' not in day_data:
                        day_data['6'] = {'count': 0, 'amount': 0.0, 'profit': 0.0, 'fenhong_count': 0, 'fenhong_amount': 0.0}
                    day_data['6']['fenhong_count'] += item['fh_count']
                    day_data['6']['fenhong_amount'] += float(item['fh_amount'] or 0)

                # 计算罚款收益 = 罚款金额 - 分红金额
                for period_str, day_data in time_series_map.items():
                    if '6' in day_data:
                        penalty = day_data['6']
                        penalty['profit'] = round(penalty['amount'] - penalty['fenhong_amount'], 2)

            # 构造有序时间序列列表
            time_series = []
            for period_str in sorted(time_series_map.keys()):
                entry = {'date': period_str, 'types': {}}
                for type_code in sorted(time_series_map[period_str].keys()):
                    entry['types'][type_code] = time_series_map[period_str][type_code]
                time_series.append(entry)

            # ---------- 汇总指定日期 ----------
            summary_result = {'by_type': {}, 'total_count': 0, 'total_amount': 0.0, 'total_profit': 0.0}

            # 汇总充值类型
            for lx in cz_types:
                filter_kwargs = {
                    'leixing': lx,
                    'zhuangtai': 3,
                }
                if granularity == 'day':
                    filter_kwargs['CreateTime__date'] = summary_q
                elif granularity == 'month':
                    filter_kwargs['CreateTime__year'] = summary_q.year
                    filter_kwargs['CreateTime__month'] = summary_q.month
                else:
                    filter_kwargs['CreateTime__year'] = summary_q.year
                agg = filter_queryset_by_club(
                    Czjilu.query.filter(**filter_kwargs), request,
                ).aggregate(count=Count('id'), amount=Sum('jine'))
                count = agg['count'] or 0
                amount = float(agg['amount'] or 0)
                profit = amount if lx == 3 else 0.0
                summary_result['by_type'][str(lx)] = {
                    'count': count,
                    'amount': round(amount, 2),
                    'profit': round(profit, 2),
                    'fenhong_count': 0,
                    'fenhong_amount': 0.0
                }
                summary_result['total_count'] += count
                summary_result['total_amount'] += amount
                summary_result['total_profit'] += profit

            # 汇总罚款
            if 6 in types:
                penalty_filter = {
                    'Status': 2,
                }
                fenhong_filter = {}
                if granularity == 'day':
                    penalty_filter['CreateTime__date'] = summary_q
                    fenhong_filter['CreateTime__date'] = summary_q
                elif granularity == 'month':
                    penalty_filter['CreateTime__year'] = summary_q.year
                    penalty_filter['CreateTime__month'] = summary_q.month
                    fenhong_filter['CreateTime__year'] = summary_q.year
                    fenhong_filter['CreateTime__month'] = summary_q.month
                else:
                    penalty_filter['CreateTime__year'] = summary_q.year
                    fenhong_filter['CreateTime__year'] = summary_q.year

                penalty_agg = Penalty.query.filter(**penalty_filter).aggregate(
                    count=Count('id'), amount=Sum('FineAmount'))
                penalty_count = penalty_agg['count'] or 0
                penalty_amount = float(penalty_agg['amount'] or 0)

                fenhong_agg = PenaltyBonus.query.filter(**fenhong_filter).aggregate(
                    fh_count=Count('id'), fh_amount=Sum('WithdrawableAmount'))
                fh_count = fenhong_agg['fh_count'] or 0
                fh_amount = float(fenhong_agg['fh_amount'] or 0)

                profit = round(penalty_amount - fh_amount, 2)
                summary_result['by_type']['6'] = {
                    'count': penalty_count,
                    'amount': round(penalty_amount, 2),
                    'profit': profit,
                    'fenhong_count': fh_count,
                    'fenhong_amount': round(fh_amount, 2)
                }
                summary_result['total_count'] += penalty_count
                summary_result['total_amount'] += penalty_amount
                summary_result['total_profit'] += profit

            summary_result['total_amount'] = round(summary_result['total_amount'], 2)
            summary_result['total_profit'] = round(summary_result['total_profit'], 2)

            return Response({
                'code': 0,
                'msg': 'ok',
                'data': {
                    'time_series': time_series,
                    'summary': summary_result
                }
            })
        except Exception as e:
            logger.exception("CwqtczhqView 接口错误")
            return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)'''





# ==================== 后端接口 ====================
# 文件：views.py （添加以下两个视图类）



