# peizhi/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.throttling import AnonRateThrottle
from django.db import models

from houtai.utils import update_shangjia_daily
from peizhi.models import Gonggao, Lunbo
import logging
from rest_framework.permissions import AllowAny  # 新增这行

# 配置日志
logger = logging.getLogger(__name__)






# peizhi/views.py 或新建 views_config.py
import logging
from rest_framework.views import APIView

from rest_framework.permissions import AllowAny
from rest_framework.parsers import JSONParser
from .models import ClubConfig


class GetDynamicConfigView(APIView):


    permission_classes = [AllowAny]
    parser_classes = [JSONParser]

    def post(self, request):
        try:
            # 查询我方俱乐部配置（is_self=1），取第一条（通常只有一条）
            club_config = ClubConfig.objects.filter(is_self=1).only(
                'storage_bucket_domain',
                'storage_bucket_name',
                'chat_api_key',
                'customer_service_api_url',
                'customer_service_corp_id'
            ).first()

            # 默认值（防止空数据导致前端报错）
            cos_bucket = ''
            cos_region = 'ap-shanghai'
            cos_oss_url = ''
            goeasy_host = 'hangzhou.goeasy.io'
            goeasy_appkey = ''
            kefu_link = ''
            kefu_enterprise_id = ''
            morentouxiang = 'a_long/morentouxiang.jpg'
            dashouguize = 'a_long/dashouguize.jpg'

            if club_config:
                # 存储桶配置
                bucket = club_config.storage_bucket_name or ''
                region = 'ap-shanghai'  # 若需要动态，可单独存储
                cos_oss_url = club_config.storage_bucket_domain or ''
                if cos_oss_url and not cos_oss_url.endswith('/'):
                    cos_oss_url += '/'
                cos_bucket = bucket
                cos_region = region

                # GoEasy 配置
                if club_config.chat_api_key:
                    goeasy_appkey = club_config.chat_api_key
                # host 如果需要可动态，这里固定或从其他字段获取

                # 客服配置
                if club_config.customer_service_api_url:
                    kefu_link = club_config.customer_service_api_url
                if club_config.customer_service_corp_id:
                    kefu_enterprise_id = club_config.customer_service_corp_id

                # 其他配置可在此扩展

            # 构造返回数据
            data = {
                "code": 0,
                "data": {
                    "cos": {
                        "bucket": cos_bucket,
                        "region": cos_region,
                        "ossImageUrl": cos_oss_url,
                        "uploadPathPrefix": "order/"
                    },
                    "goEasy": {
                        "host": goeasy_host,
                        "appkey": goeasy_appkey
                    },
                    "otherConfig": {
                        "morentouxiang": morentouxiang,
                        "dashouguize": dashouguize
                    },
                    "kefu": {
                        "link": kefu_link,
                        "enterpriseId": kefu_enterprise_id
                    }
                }
            }
            return Response(data)

        except Exception as e:
            logger.error(f"获取动态配置失败: {str(e)}", exc_info=True)
            return Response({"code": 500, "msg": "服务器内部错误"}, status=500)




class ShangpinGonggaoView(APIView):
    """
    商品公告和轮播图接口
    获取类型为1的公告和轮播图
    """

    # 应用节流（限制频率）
    throttle_classes = [AnonRateThrottle]
    # 新增：允许所有用户访问（不需要认证）
    permission_classes = [AllowAny]

    def post(self, request):
        """
        处理POST请求，返回公告和轮播图数据

        请求: POST /api/peizhi/shangpingonggao/
        返回: {
            "shangpingonggao": "公告内容字符串",
            "shangpinlunbo": ["轮播图URL1", "轮播图URL2"]
        }
        """
        try:
            logger.info("收到商品公告和轮播图请求")

            # 1. 查询公告（类型为1）
            gonggao_obj = Gonggao.objects.filter(leixing=1).first()

            # 获取公告内容，如果没有则返回空字符串
            gonggao_content = gonggao_obj.jieshao if gonggao_obj else ""

            # 2. 查询轮播图（类型为1，按显示顺序排序）
            lunbo_queryset = Lunbo.objects.filter(leixing=1)

            # 提取轮播图URL列表
            lunbo_urls = [lunbo.tupian_url for lunbo in lunbo_queryset]

            # 3. 构建返回数据
            response_data = {
                "shangpingonggao": gonggao_content,
                "shangpinlunbo": lunbo_urls
            }

            logger.info(f"返回数据：公告长度{len(gonggao_content)}，轮播图数量{len(lunbo_urls)}")

            # 4. 返回响应
            return Response(response_data, status=status.HTTP_200_OK)

        except Exception as e:
            # 记录异常
            logger.error(f"获取商品公告和轮播图时发生错误：{str(e)}")

            # 返回错误响应
            error_data = {
                "error": "服务器内部错误",
                "detail": "获取数据失败，请稍后重试"
            }
            return Response(error_data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)





# peizhi/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, permissions
from rest_framework.parsers import MultiPartParser, FormParser
from django.db import transaction
from yonghu.models import UserMain, AdminProfile
from .models import Tupianpeizhi, Qunpeizhi
from dingdan.models import Lilubiao
import json
import os
import uuid
from utils.oss_utils import upload_to_oss, delete_from_oss, validate_image

class AdminConfigQueryView(APIView):
    """
    管理员获取系统配置接口
    URL: /peizhi/adpzhq
    方法: POST
    权限: JWT Token认证 + 管理员权限
    参数: zhanghao (管理员账号)
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        try:
            # 获取前端传递的账号
            zhanghao = request.data.get('zhanghao')
            if not zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不能为空',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 获取当前用户（UserMain实例）
            user_main = request.user

            # 验证账号：比较user_main的phone与前端传递的zhanghao
            if user_main.phone != zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不匹配',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            # 验证用户类型是否为管理员
            if user_main.user_type != 'admin':
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            # 验证管理员扩展表是否存在
            try:
                admin_profile = user_main.admin_profile
            except AdminProfile.DoesNotExist:
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            # 查询图片配置
            rule_image = Tupianpeizhi.objects.filter(leixing=1).first()
            default_avatar = Tupianpeizhi.objects.filter(leixing=2).first()

            # 查询群配置
            qun_configs = Qunpeizhi.objects.all()
            qq_groups = []
            for config in qun_configs:
                qq_groups.append({
                    'peizhiid': config.peizhiid,
                    'neirong': config.neirong if config.neirong else '',
                    'qunid': config.qunid if config.qunid else '',
                    'jieshao': config.jieshao if config.jieshao else ''
                })

            # 查询利率配置
            platform_rate_obj = Lilubiao.objects.filter(fadanpingtai='1').first()
            shop_rate_obj = Lilubiao.objects.filter(fadanpingtai='3').first()
            # 🔴【新增】打手提现抽成利率（字符5）
            dashou_withdraw_obj = Lilubiao.objects.filter(fadanpingtai='5').first()
            # 🔴【新增】管事提现抽成利率（字符6）
            guanshi_withdraw_obj = Lilubiao.objects.filter(fadanpingtai='6').first()

            # 构建返回数据
            data = {
                'images': {
                    'rule_image': rule_image.tupian if rule_image and rule_image.tupian else '',
                    'default_avatar': default_avatar.tupian if default_avatar and default_avatar.tupian else ''
                },
                'qq_groups': qq_groups,
                'rates': {
                    'platform_rate': float(platform_rate_obj.lilu) if platform_rate_obj and platform_rate_obj.lilu else 0.0,
                    'shop_rate': float(shop_rate_obj.lilu) if shop_rate_obj and shop_rate_obj.lilu else 0.0,
                    # 🔴【新增】打手提现抽成利率
                    'dashou_withdraw_rate': float(
                        dashou_withdraw_obj.lilu) if dashou_withdraw_obj and dashou_withdraw_obj.lilu else 0.0,
                    # 🔴【新增】管事提现抽成利率
                    'guanshi_withdraw_rate': float(
                        guanshi_withdraw_obj.lilu) if guanshi_withdraw_obj and guanshi_withdraw_obj.lilu else 0.0
                }
            }

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

        except Exception as e:
            return Response({
                'code': 99,
                'msg': f'系统错误: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class AdminUploadImageView(APIView):
    """
    管理员上传配置图片接口
    URL: /peizhi/adxgtp
    方法: POST
    权限: JWT Token认证 + 管理员权限
    参数: zhanghao, leixing (1:打手规则图片, 2:默认头像图片), image (文件)
    """
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request):
        try:
            zhanghao = request.data.get('zhanghao')
            leixing = request.data.get('leixing')
            image_file = request.FILES.get('image')

            if not zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不能为空',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            if not leixing:
                return Response({
                    'code': 2,
                    'msg': '图片类型不能为空',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            if not image_file:
                return Response({
                    'code': 3,
                    'msg': '请选择图片文件',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 验证用户权限
            user_main = request.user
            if user_main.phone != zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不匹配',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            if user_main.user_type != 'admin':
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            try:
                admin_profile = user_main.admin_profile
            except AdminProfile.DoesNotExist:
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            # 验证图片类型
            if leixing not in ['1', '2']:
                return Response({
                    'code': 4,
                    'msg': '图片类型错误',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 验证图片文件
            is_valid, error_msg = validate_image(image_file)
            if not is_valid:
                return Response({
                    'code': 5,
                    'msg': f'图片验证失败: {error_msg}',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 根据类型确定固定相对URL和文件名
            if leixing == '1':
                # 打手规则图片
                fixed_relative_url = 'a_long/dashouguize.jpg'
                oss_file_name = 'dashouguize.jpg'
            else:
                # 默认头像图片
                fixed_relative_url = 'a_long/morentouxiang.jpg'
                oss_file_name = 'morentouxiang.jpg'

            # 构建OSS文件路径
            oss_file_path = fixed_relative_url  # a_long/dashouguize.jpg 或 a_long/morentouxiang.jpg

            # 上传到OSS（覆盖原有文件）
            image_file.seek(0)  # 重置文件指针
            full_url = upload_to_oss(image_file, oss_file_path)

            if not full_url:
                return Response({
                    'code': 6,
                    'msg': '图片上传到存储桶失败',
                    'data': None
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            # 更新数据库
            with transaction.atomic():
                # 获取或创建图片配置记录
                tupian_peizhi, created = Tupianpeizhi.objects.get_or_create(leixing=int(leixing))
                tupian_peizhi.tupian = fixed_relative_url
                tupian_peizhi.save()

            return Response({
                'code': 0,
                'msg': '图片上传成功',
                'data': {
                    'image_url': fixed_relative_url
                }
            })

        except Exception as e:
            return Response({
                'code': 99,
                'msg': f'系统错误: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class AdminUpdateConfigView(APIView):
    """
    管理员更新系统配置接口（QQ群、利率）- 已修复
    URL: /peizhi/adupdate
    方法: POST
    权限: JWT Token认证 + 管理员权限
    参数:
        - zhanghao: 管理员账号
        - qq_groups: [{"peizhiid": 1, "neirong": "", "qunid": "", "jieshao": ""}, ...] (可选)
        - rates: {"platform_rate": 0.1, "shop_rate": 0.2} (可选)
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        try:
            zhanghao = request.data.get('zhanghao')
            if not zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不能为空',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 验证用户权限
            user_main = request.user
            if user_main.phone != zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不匹配',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            if user_main.user_type != 'admin':
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            try:
                admin_profile = user_main.admin_profile
            except AdminProfile.DoesNotExist:
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            response_data = {}

            # 🔴 修复：更新QQ群配置 - 确保根据peizhiid正确更新对应的记录
            qq_groups = request.data.get('qq_groups')
            if qq_groups and isinstance(qq_groups, list):
                updated_groups = []
                for group_data in qq_groups:
                    peizhiid = group_data.get('peizhiid')
                    if not peizhiid:
                        continue

                    # 🔴 修复：使用filter()确保查询到正确的记录
                    qun_configs = Qunpeizhi.objects.filter(peizhiid=peizhiid)

                    if qun_configs.exists():
                        # 如果存在多个相同peizhiid的记录，取第一个（应该只有一个）
                        qun_config = qun_configs.first()

                        # 更新字段
                        neirong = group_data.get('neirong')
                        qunid = group_data.get('qunid')
                        jieshao = group_data.get('jieshao')

                        if neirong is not None:
                            qun_config.neirong = neirong or ''
                        if qunid is not None:
                            qun_config.qunid = qunid or ''
                        if jieshao is not None:
                            qun_config.jieshao = jieshao or ''

                        qun_config.save()
                    else:
                        # 如果不存在，创建新的（使用提供的peizhiid）
                        qun_config = Qunpeizhi.objects.create(
                            peizhiid=peizhiid,
                            neirong=group_data.get('neirong', '') or '',
                            qunid=group_data.get('qunid', '') or '',
                            jieshao=group_data.get('jieshao', '') or ''
                        )

                    updated_groups.append({
                        'peizhiid': qun_config.peizhiid,
                        'neirong': qun_config.neirong,
                        'qunid': qun_config.qunid,
                        'jieshao': qun_config.jieshao
                    })

                if updated_groups:
                    response_data['qq_groups'] = updated_groups

            # 🔴 修复：更新利率配置 - 确保更新现有记录而不是创建新记录
            rates = request.data.get('rates')
            if rates and isinstance(rates, dict):
                updated_rates = {}

                # 平台发单利率（类型1，fadanpingtai='1'）
                platform_rate = rates.get('platform_rate')
                if platform_rate is not None:
                    try:
                        platform_rate_float = float(platform_rate)
                        if 0 <= platform_rate_float <= 1:
                            # 🔴 修复：查询现有记录，如果不存在则创建
                            platform_rate_obj = Lilubiao.objects.filter(fadanpingtai='1').first()

                            if platform_rate_obj:
                                # 更新现有记录
                                platform_rate_obj.lilu = platform_rate_float
                                platform_rate_obj.save()
                            else:
                                # 创建新记录
                                platform_rate_obj = Lilubiao.objects.create(
                                    lilu=platform_rate_float,
                                    fadanpingtai='1'
                                )

                            updated_rates['platform_rate'] = platform_rate_float
                        else:
                            return Response({
                                'code': 3,
                                'msg': '平台发单利率必须在0-1之间',
                                'data': None
                            }, status=status.HTTP_400_BAD_REQUEST)
                    except ValueError:
                        return Response({
                            'code': 4,
                            'msg': '平台发单利率格式错误',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                # 商家发单利率（类型2，fadanpingtai='2'）
                shop_rate = rates.get('shop_rate')
                if shop_rate is not None:
                    try:
                        shop_rate_float = float(shop_rate)
                        if 0 <= shop_rate_float <= 1:
                            # 🔴 修复：查询现有记录，如果不存在则创建
                            shop_rate_obj = Lilubiao.objects.filter(fadanpingtai='3').first()

                            if shop_rate_obj:
                                # 更新现有记录
                                shop_rate_obj.lilu = shop_rate_float
                                shop_rate_obj.save()
                            else:
                                # 创建新记录
                                shop_rate_obj = Lilubiao.objects.create(
                                    lilu=shop_rate_float,
                                    fadanpingtai='3'
                                )

                            updated_rates['shop_rate'] = shop_rate_float
                        else:
                            return Response({
                                'code': 5,
                                'msg': '商家发单利率必须在0-1之间',
                                'data': None
                            }, status=status.HTTP_400_BAD_REQUEST)
                    except ValueError:
                        return Response({
                            'code': 6,
                            'msg': '商家发单利率格式错误',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                # AdminUpdateConfigView 类中的 post 方法，在 rates 部分

                # 🔴【新增】打手提现抽成利率（类型5，fadanpingtai='5'）
                dashou_withdraw_rate = rates.get('dashou_withdraw_rate')
                if dashou_withdraw_rate is not None:
                    try:
                        dashou_withdraw_rate_float = float(dashou_withdraw_rate)
                        if 0 <= dashou_withdraw_rate_float <= 1:
                            # 查询现有记录，如果不存在则创建
                            dashou_withdraw_obj = Lilubiao.objects.filter(fadanpingtai='5').first()

                            if dashou_withdraw_obj:
                                # 更新现有记录
                                dashou_withdraw_obj.lilu = dashou_withdraw_rate_float
                                dashou_withdraw_obj.save()
                            else:
                                # 创建新记录
                                dashou_withdraw_obj = Lilubiao.objects.create(
                                    lilu=dashou_withdraw_rate_float,
                                    fadanpingtai='5'
                                )

                            updated_rates['dashou_withdraw_rate'] = dashou_withdraw_rate_float
                        else:
                            return Response({
                                'code': 7,  # 注意：这里需要调整错误码，不要重复
                                'msg': '打手提现抽成利率必须在0-1之间',
                                'data': None
                            }, status=status.HTTP_400_BAD_REQUEST)
                    except ValueError:
                        return Response({
                            'code': 8,
                            'msg': '打手提现抽成利率格式错误',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                # 🔴【新增】管事提现抽成利率（类型6，fadanpingtai='6'）
                guanshi_withdraw_rate = rates.get('guanshi_withdraw_rate')
                if guanshi_withdraw_rate is not None:
                    try:
                        guanshi_withdraw_rate_float = float(guanshi_withdraw_rate)
                        if 0 <= guanshi_withdraw_rate_float <= 1:
                            # 查询现有记录，如果不存在则创建
                            guanshi_withdraw_obj = Lilubiao.objects.filter(fadanpingtai='6').first()

                            if guanshi_withdraw_obj:
                                # 更新现有记录
                                guanshi_withdraw_obj.lilu = guanshi_withdraw_rate_float
                                guanshi_withdraw_obj.save()
                            else:
                                # 创建新记录
                                guanshi_withdraw_obj = Lilubiao.objects.create(
                                    lilu=guanshi_withdraw_rate_float,
                                    fadanpingtai='6'
                                )

                            updated_rates['guanshi_withdraw_rate'] = guanshi_withdraw_rate_float
                        else:
                            return Response({
                                'code': 9,
                                'msg': '管事提现抽成利率必须在0-1之间',
                                'data': None
                            }, status=status.HTTP_400_BAD_REQUEST)
                    except ValueError:
                        return Response({
                            'code': 10,
                            'msg': '管事提现抽成利率格式错误',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                if updated_rates:
                    response_data['rates'] = updated_rates

            # 如果没有任何更新
            if not response_data:
                return Response({
                    'code': 7,
                    'msg': '没有需要更新的数据',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            return Response({
                'code': 0,
                'msg': '更新成功',
                'data': response_data
            })

        except Exception as e:
            return Response({
                'code': 99,
                'msg': f'系统错误: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

'''class AdminUpdateConfigView(APIView):
    """
    管理员更新系统配置接口（QQ群、利率）
    URL: /peizhi/adupdate
    方法: POST
    权限: JWT Token认证 + 管理员权限
    参数:
        - zhanghao: 管理员账号
        - qq_groups: [{"peizhiid": 1, "neirong": "", "qunid": "", "jieshao": ""}, ...] (可选)
        - rates: {"platform_rate": 0.1, "shop_rate": 0.2} (可选)
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        try:
            zhanghao = request.data.get('zhanghao')
            if not zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不能为空',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 验证用户权限
            user_main = request.user
            if user_main.phone != zhanghao:
                return Response({
                    'code': 1,
                    'msg': '账号不匹配',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            if user_main.user_type != 'admin':
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            try:
                admin_profile = user_main.admin_profile
            except AdminProfile.DoesNotExist:
                return Response({
                    'code': 1,
                    'msg': '无权限',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            response_data = {}

            # 更新QQ群配置
            qq_groups = request.data.get('qq_groups')
            if qq_groups and isinstance(qq_groups, list):
                updated_groups = []
                for group_data in qq_groups:
                    peizhiid = group_data.get('peizhiid')
                    if not peizhiid:
                        continue

                    # 查找配置
                    qun_config = Qunpeizhi.objects.filter(peizhiid=peizhiid).first()
                    if not qun_config:
                        # 如果不存在，创建新的
                        qun_config = Qunpeizhi(peizhiid=peizhiid)

                    # 更新字段（peizhiid不可改）
                    qun_config.neirong = group_data.get('neirong', qun_config.neirong) or ''
                    qun_config.qunid = group_data.get('qunid', qun_config.qunid) or ''
                    qun_config.jieshao = group_data.get('jieshao', qun_config.jieshao) or ''
                    qun_config.save()

                    updated_groups.append({
                        'peizhiid': qun_config.peizhiid,
                        'neirong': qun_config.neirong,
                        'qunid': qun_config.qunid,
                        'jieshao': qun_config.jieshao
                    })

                if updated_groups:
                    response_data['qq_groups'] = updated_groups

            # 更新利率配置
            rates = request.data.get('rates')
            if rates and isinstance(rates, dict):
                updated_rates = {}

                # 平台发单利率（类型1）
                platform_rate = rates.get('platform_rate')
                if platform_rate is not None:
                    try:
                        platform_rate_float = float(platform_rate)
                        if 0 <= platform_rate_float <= 1:
                            platform_rate_obj = Lilubiao.objects.filter(fadanpingtai='1').first()
                            if not platform_rate_obj:
                                platform_rate_obj = Lilubiao.objects.create(
                                    lilu=platform_rate_float,
                                    fadanpingtai='1'
                                )
                            else:
                                platform_rate_obj.lilu = platform_rate_float
                                platform_rate_obj.save()

                            updated_rates['platform_rate'] = platform_rate_float
                        else:
                            return Response({
                                'code': 3,
                                'msg': '平台发单利率必须在0-1之间',
                                'data': None
                            }, status=status.HTTP_400_BAD_REQUEST)
                    except ValueError:
                        return Response({
                            'code': 4,
                            'msg': '平台发单利率格式错误',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                # 商家发单利率（类型2）
                shop_rate = rates.get('shop_rate')
                if shop_rate is not None:
                    try:
                        shop_rate_float = float(shop_rate)
                        if 0 <= shop_rate_float <= 1:
                            shop_rate_obj = Lilubiao.objects.filter(fadanpingtai='2').first()
                            if not shop_rate_obj:
                                shop_rate_obj = Lilubiao.objects.create(
                                    lilu=shop_rate_float,
                                    fadanpingtai='3'
                                )
                            else:
                                shop_rate_obj.lilu = shop_rate_float
                                shop_rate_obj.save()

                            updated_rates['shop_rate'] = shop_rate_float
                        else:
                            return Response({
                                'code': 5,
                                'msg': '商家发单利率必须在0-1之间',
                                'data': None
                            }, status=status.HTTP_400_BAD_REQUEST)
                    except ValueError:
                        return Response({
                            'code': 6,
                            'msg': '商家发单利率格式错误',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                if updated_rates:
                    response_data['rates'] = updated_rates

            # 如果没有任何更新
            if not response_data:
                return Response({
                    'code': 7,
                    'msg': '没有需要更新的数据',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            return Response({
                'code': 0,
                'msg': '更新成功',
                'data': response_data
            })

        except Exception as e:
            return Response({
                'code': 99,
                'msg': f'系统错误: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)'''

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django.db.models import Q
from decimal import Decimal
import logging

from yonghu.models import UserMain, UserDashou, UserBoss, UserShangjia, UserGuanshi

logger = logging.getLogger(__name__)


class DashouPaihangView(APIView):
    """
    排行榜接口
    路径：/peizhi/dsph
    方法：POST
    认证：JWT Token认证

    请求参数：
    - leixing: 1=今日收入，2=今月收入
    - yonghu_leixing: 1=打手，2=管事，3=商家

    返回数据：
    - code: 0=成功，其他=失败
    - msg: 提示信息
    - data: {
        xianshi: True/False,  # 是否展示真实数据
        paihang_list: [  # 排行榜列表
          {
            uid: '用户ID',
            touxiang: '头像相对URL',
            nicheng: '用户昵称',
            jine: 收入金额,
            mingci: 排名
          },
          ...
        ]
      }
    """

    # =============== 可配置参数 ===============
    # 每次查询返回的最大记录数（可修改）
    ZUI_DAO_CHA_XUN_SHU_LIANG = 50

    # 最少需要多少人才能展示（可修改）
    ZUI_SHAO_REN_SHU_YAO_QIU = 1

    # 第一名最低金额要求（单位：元）（可修改）
    DI_YI_MING_ZUI_DI_JIN_E = Decimal('10.00')

    # 管事第一名最低数量要求（可修改）
    DI_YI_MING_ZUI_DI_SHU_LIANG = 1
    # =============== 可配置参数结束 ===============

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 获取请求参数 - 修复参数类型问题
            leixing_str = request.data.get('leixing', '1')
            yonghu_leixing_str = request.data.get('yonghu_leixing', '1')

            # 转换为整数
            try:
                leixing = int(leixing_str)
            except (ValueError, TypeError):
                leixing = 1

            try:
                yonghu_leixing = int(yonghu_leixing_str)
            except (ValueError, TypeError):
                yonghu_leixing = 1

            # 参数验证
            if leixing not in [1, 2]:
                return Response({
                    'code': 400,
                    'msg': '参数错误：leixing必须为1(今日)或2(今月)',
                    'data': {'xianshi': False}
                })

            if yonghu_leixing not in [1, 2, 3]:
                return Response({
                    'code': 400,
                    'msg': '参数错误：yonghu_leixing必须为1(打手)、2(管事)或3(商家)',
                    'data': {'xianshi': False}
                })

            # 根据用户类型调用不同的查询方法
            if yonghu_leixing == 1:
                # 打手排行榜
                paihang_list, xianshi_flag = self.chaxun_dashou_paihang(leixing)
            elif yonghu_leixing == 2:
                # 管事排行榜
                paihang_list, xianshi_flag = self.chaxun_guanshi_paihang(leixing)
            else:
                # 商家排行榜
                paihang_list, xianshi_flag = self.chaxun_shangjia_paihang(leixing)

            # 返回结果
            return Response({
                'code': 0,
                'msg': '获取成功',
                'data': {
                    'xianshi': xianshi_flag,
                    'paihang_list': paihang_list
                }
            })

        except Exception as e:
            logger.error(f"排行榜查询失败: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '服务器内部错误',
                'data': {'xianshi': False}
            })

    def chaxun_dashou_paihang(self, leixing):
        """
        查询打手排行榜
        :param leixing: 1=今日收入，2=今月收入
        :return: (排行榜列表, 是否展示标志)
        """
        # 确定查询的字段
        if leixing == 1:
            # 今日收入
            shouyi_ziduan = 'jinrishouyi'
        else:
            # 今月收入
            shouyi_ziduan = 'jinyueshouyi'

        # 查询条件：收入大于0，打手账号状态正常（1=正常）
        # 注意：打手表中的zhanghaozhuangtai字段
        chaxun_tiaojian = Q(**{f'{shouyi_ziduan}__gt': 0}) & Q(zhanghaozhuangtai=1)

        # 查询打手扩展表，并关联用户主表
        dashou_list = UserDashou.objects.select_related('user').filter(
            chaxun_tiaojian
        ).order_by(f'-{shouyi_ziduan}')[:self.ZUI_DAO_CHA_XUN_SHU_LIANG]

        # 检查是否满足展示条件
        if len(dashou_list) < self.ZUI_SHAO_REN_SHU_YAO_QIU:
            # 人数不足，不展示
            return [], False

        # 检查第一名金额是否达到最低要求
        if leixing == 1:
            di_yi_ming_jine = dashou_list[0].jinrishouyi
        else:
            di_yi_ming_jine = dashou_list[0].jinyueshouyi

        if di_yi_ming_jine < self.DI_YI_MING_ZUI_DI_JIN_E:
            # 第一名金额不足，不展示
            return [], False

        # 构建返回数据
        paihang_list = []
        for index, dashou in enumerate(dashou_list):
            # 获取用户主表信息
            user_main = dashou.user

            # 打手的昵称直接使用打手扩展表的nicheng字段
            dashou_nicheng = dashou.nicheng or '未设置昵称'

            # 获取头像（相对URL）
            touxiang_url = user_main.avatar or ''

            # 获取收入金额
            if leixing == 1:
                shouyi_jine = dashou.jinrishouyi
            else:
                shouyi_jine = dashou.jinyueshouyi

            paihang_list.append({
                'uid': user_main.yonghuid,
                'touxiang': touxiang_url,
                'nicheng': dashou_nicheng,
                'jine': float(shouyi_jine),  # 转换为float方便前端处理
                'mingci': index + 1
            })

        return paihang_list, True

    def chaxun_guanshi_paihang(self, leixing):
        """
        查询管事排行榜
        :param leixing: 1=今日，2=今月
        :return: (排行榜列表, 是否展示标志)
        """
        # 确定查询的字段
        if leixing == 1:
            # 今日充值打手数量
            chaxun_ziduan = 'jinrichongzhi'
        else:
            # 今月充值打手数量
            chaxun_ziduan = 'jinyuechongzhi'

        # 查询条件：数量大于0，管事状态正常（状态1为正常）
        # 注意：管手表中的zhuangtai字段
        chaxun_tiaojian = Q(**{f'{chaxun_ziduan}__gt': 0}) & Q(zhuangtai=1)

        # 查询管事扩展表，并关联用户主表
        guanshi_list = UserGuanshi.objects.select_related('user').filter(
            chaxun_tiaojian
        ).order_by(f'-{chaxun_ziduan}')[:self.ZUI_DAO_CHA_XUN_SHU_LIANG]

        # 检查是否满足展示条件
        if len(guanshi_list) < self.ZUI_SHAO_REN_SHU_YAO_QIU:
            # 人数不足，不展示
            return [], False

        # 检查第一名数量是否达到最低要求
        if leixing == 1:
            di_yi_ming_shuliang = guanshi_list[0].jinrichongzhi
        else:
            di_yi_ming_shuliang = guanshi_list[0].jinyuechongzhi

        if di_yi_ming_shuliang < self.DI_YI_MING_ZUI_DI_SHU_LIANG:
            # 第一名数量不足，不展示
            return [], False

        # 构建返回数据
        paihang_list = []
        for index, guanshi in enumerate(guanshi_list):
            # 获取用户主表信息
            user_main = guanshi.user

            # 管事的昵称需要查询老板扩展表
            boss_profile = UserBoss.objects.filter(user=user_main).first()
            guanshi_nicheng = boss_profile.nickname if boss_profile else '未设置昵称'

            # 获取头像（相对URL）
            touxiang_url = user_main.avatar or ''

            # 获取数量
            if leixing == 1:
                chongzhi_shuliang = guanshi.jinrichongzhi
            else:
                chongzhi_shuliang = guanshi.jinyuechongzhi

            paihang_list.append({
                'uid': user_main.yonghuid,
                'touxiang': touxiang_url,
                'nicheng': guanshi_nicheng,
                'jine': float(chongzhi_shuliang),  # 这里jine字段实际存储的是数量
                'mingci': index + 1
            })

        return paihang_list, True

    def chaxun_shangjia_paihang(self, leixing):
        """
        查询商家排行榜
        :param leixing: 1=今日流水，2=今月流水
        :return: (排行榜列表, 是否展示标志)
        """
        # 确定查询的字段
        if leixing == 1:
            # 今日流水
            liushui_ziduan = 'jinriliushui'
        else:
            # 今月流水
            liushui_ziduan = 'jinyueliushui'

        # 查询条件：流水大于0，商家状态正常（状态1为正常）
        # 注意：商家表中的zhuangtai字段
        chaxun_tiaojian = Q(**{f'{liushui_ziduan}__gt': 0}) & Q(zhuangtai=1)

        # 查询商家扩展表，并关联用户主表
        shangjia_list = UserShangjia.objects.select_related('user').filter(
            chaxun_tiaojian
        ).order_by(f'-{liushui_ziduan}')[:self.ZUI_DAO_CHA_XUN_SHU_LIANG]

        # 检查是否满足展示条件
        if len(shangjia_list) < self.ZUI_SHAO_REN_SHU_YAO_QIU:
            # 人数不足，不展示
            return [], False

        # 检查第一名金额是否达到最低要求
        if leixing == 1:
            di_yi_ming_liushui = shangjia_list[0].jinriliushui
        else:
            di_yi_ming_liushui = shangjia_list[0].jinyueliushui

        if di_yi_ming_liushui < self.DI_YI_MING_ZUI_DI_JIN_E:
            # 第一名流水不足，不展示
            return [], False

        # 构建返回数据
        paihang_list = []
        for index, shangjia in enumerate(shangjia_list):
            # 获取用户主表信息
            user_main = shangjia.user

            # 商家的昵称需要查询老板扩展表
            boss_profile = UserBoss.objects.filter(user=user_main).first()
            shangjia_nicheng = boss_profile.nickname if boss_profile else '未设置昵称'

            # 获取头像（相对URL）
            touxiang_url = user_main.avatar or ''

            # 获取流水金额
            if leixing == 1:
                liushui_jine = shangjia.jinriliushui
            else:
                liushui_jine = shangjia.jinyueliushui

            paihang_list.append({
                'uid': user_main.yonghuid,
                'touxiang': touxiang_url,
                'nicheng': shangjia_nicheng,
                'jine': float(liushui_jine),
                'mingci': index + 1
            })

        return paihang_list, True




# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db import connection
from django.db.models import Q
import time

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db import connection
from django.db.models import Count
import time

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db import connection
import time

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db import connection
from django.db.models import Q
import time


class GetAdminZhanghaoView(APIView):
    """
    获取管理员账号列表接口
    路径：/peizhi/adzhhq
    方法：POST
    权限：JWT Token + 管理员验证
    参数：
        - zhanghao: 当前登录的管理员账号（字符串）
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 🔥 1. 身份验证（100%按表模型）
            user = request.user

            # 获取当前用户的手机号（CharField，字符串类型）
            current_phone = getattr(user, 'phone', '')
            if not current_phone:
                return Response({
                    'code': 401,
                    'message': '用户手机号信息不完整',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            # 检查前端传递的账号是否匹配（都是字符串）
            frontend_zhanghao = request.data.get('zhanghao', '').strip()
            if frontend_zhanghao != current_phone:
                return Response({
                    'code': 403,
                    'message': '账号不匹配，无权限访问',
                    'data': None
                }, status=status.HTTP_403_FORBIDDEN)

            # 🔥 2. 验证是否为管理员（user_type是CharField，值是'admin'字符串）
            user_type = getattr(user, 'user_type', '')
            if user_type != 'admin':
                # 检查是否有管理员扩展表
                if not hasattr(user, 'admin_profile'):
                    return Response({
                        'code': 403,
                        'message': '无管理员权限',
                        'data': None
                    }, status=status.HTTP_403_FORBIDDEN)

            # 🔥 3. 查询管理员账号列表（全部按字符串查询）
            with connection.cursor() as cursor:
                # 🔥 查询所有管理员账号（user_type='admin' 字符串）
                # 🔥 phone和yonghuid都是CharField，xiugaiid也是CharField
                sql = """
                    SELECT 
                        um.id,
                        um.phone,
                        um.yonghuid,
                        um.user_type,
                        COALESCE(COUNT(xjl.id), 0) as jilushuliang
                    FROM user_main um
                    LEFT JOIN xiugaijilu xjl ON um.phone = xjl.xiugaiid
                    WHERE um.user_type = 'admin'
                    GROUP BY um.id, um.phone, um.yonghuid, um.user_type
                    ORDER BY um.create_time DESC
                """

                cursor.execute(sql)
                results = cursor.fetchall()

            # 🔥 4. 处理查询结果（全部保持字符串类型）
            zhanghao_list = []
            for row in results:
                zhanghao_list.append({
                    #'id': row[0],
                    'id': str(row[1]) if row[1] else str(row[2]),  # 🔥 保持字符串
                    'zhanghao':str(row[1]) if row[1] else str(row[2]),  # 🔥 保持字符串
                    'yonghuid': str(row[2]) if row[2] else '',  # 🔥 保持字符串
                    'user_type': str(row[3]) if row[3] else '',  # 🔥 保持字符串
                    'jilushuliang': int(row[4])  # 只有这个是数字
                })

            return Response({
                'code': 0,
                'message': '获取成功',
                'data': zhanghao_list
            }, status=status.HTTP_200_OK)

        except Exception as e:
            print(f"获取管理员账号列表失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return Response({
                'code': 500,
                'message': '系统错误',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class GetXiugaiJiluView(APIView):
    """
    获取修改记录列表接口
    路径：/peizhi/adhqxg
    方法：POST
    权限：JWT Token + 管理员验证
    参数：
        - zhanghao: 当前登录的管理员账号（字符串）
        - xiugaizhe_zhanghaoid: 选中的管理员账号ID（字符串，对应phone或yonghuid）
        - page: 页码（整数）
        - pagesize: 每页条数（整数）
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 🔥 1. 身份验证（100%按表模型）
            user = request.user

            # 获取当前用户的手机号（CharField，字符串）
            current_phone = getattr(user, 'phone', '')
            if not current_phone:
                return Response({
                    'code': 401,
                    'message': '用户手机号信息不完整',
                    'data': None
                }, status=status.HTTP_401_UNAUTHORIZED)

            # 检查前端传递的账号是否匹配（字符串比较）
            frontend_zhanghao = request.data.get('zhanghao', '')
            if not isinstance(frontend_zhanghao, str):
                frontend_zhanghao = str(frontend_zhanghao)
            frontend_zhanghao = frontend_zhanghao.strip()

            if frontend_zhanghao != current_phone:
                return Response({
                    'code': 403,
                    'message': '账号不匹配，无权限访问',
                    'data': None
                }, status=status.HTTP_403_FORBIDDEN)

            # 🔥 2. 验证是否为管理员（user_type='admin'字符串）
            user_type = getattr(user, 'user_type', '')
            if user_type != 'admin':
                if not hasattr(user, 'admin_profile'):
                    return Response({
                        'code': 403,
                        'message': '无管理员权限',
                        'data': None
                    }, status=status.HTTP_403_FORBIDDEN)

            # 🔥 3. 获取参数（100%按实际类型）
            # 🔥 xiugaizhe_zhanghaoid 是字符串（对应xiugaiid字段，CharField）
            xiugaizhe_zhanghaoid_param = request.data.get('xiugaizhe_zhanghaoid', '')


            # 🔥 强制转为字符串（因为数据库是CharField）
            if xiugaizhe_zhanghaoid_param is None:
                xiugaizhe_zhanghaoid = ''
            else:
                # 无论如何都转为字符串
                xiugaizhe_zhanghaoid = str(xiugaizhe_zhanghaoid_param).strip()


            # 🔥 分页参数转为整数
            try:
                page = int(request.data.get('page', 1))
            except:
                page = 1

            try:
                pagesize = int(request.data.get('pagesize', 10))
            except:
                pagesize = 10

            # 🔥 4. 参数验证
            if not xiugaizhe_zhanghaoid:
                return Response({
                    'code': 400,
                    'message': '修改者账号ID不能为空',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            if page < 1:
                page = 1
            if pagesize < 1 or pagesize > 50:
                pagesize = 10

            # 🔥 5. 高效查询（100%按表字段类型）
            with connection.cursor() as cursor:
                # 🔥 5.1 查询总记录数（xiugaiid是CharField，用字符串查询）
                count_sql = """
                    SELECT COUNT(*) 
                    FROM xiugaijilu 
                    WHERE xiugaiid = %s
                """
                cursor.execute(count_sql, [xiugaizhe_zhanghaoid])
                total_count = cursor.fetchone()[0]

                # 🔥 5.2 计算分页
                offset = (page - 1) * pagesize

                # 🔥 5.3 查询当前页数据（使用参数化查询，防止SQL注入）
                data_sql = """
                    SELECT 
                        xjl.id,
                        xjl.yonghuid,           -- CharField
                        xjl.xiugaiid,           -- CharField
                        xjl.leixing,            -- IntegerField
                        xjl.xiugaitijiao,       -- DecimalField
                        xjl.xiugaitijiaoq,      -- DecimalField
                        xjl.jifen,              -- IntegerField
                        xjl.yjifen,             -- IntegerField
                        xjl.yajin,              -- DecimalField
                        xjl.yyajin,             -- DecimalField
                        xjl.shangjiayue,        -- DecimalField
                        xjl.shangjiayueq,       -- DecimalField
                        xjl.guanshiyue,         -- DecimalField
                        xjl.guanshiyueq,        -- DecimalField
                        xjl.huiyuants,          -- IntegerField
                        xjl.huiyuan_id,         -- CharField
                        xjl.qitashuoming,       -- TextField
                        xjl.create_time,        -- DateTimeField
                        um.avatar               -- CharField
                    FROM xiugaijilu xjl
                    LEFT JOIN user_main um ON xjl.yonghuid = um.yonghuid
                    WHERE xjl.xiugaiid = %s
                    ORDER BY xjl.create_time DESC
                    LIMIT %s OFFSET %s
                """

                cursor.execute(data_sql, [xiugaizhe_zhanghaoid, pagesize, offset])
                records = cursor.fetchall()

            # 🔥 6. 处理查询结果（严格按照字段类型处理）
            jilu_list = []
            for record in records:
                # 获取修改者账号信息（都是字符串）
                xiugaizhe_zhanghao = ''
                with connection.cursor() as cursor2:
                    # 🔥 phone和yonghuid都是CharField，用字符串查询
                    cursor2.execute(
                        "SELECT phone, yonghuid FROM user_main WHERE phone = %s OR yonghuid = %s LIMIT 1",
                        [xiugaizhe_zhanghaoid, xiugaizhe_zhanghaoid]
                    )
                    xiugaizhe_info = cursor2.fetchone()
                    if xiugaizhe_info:
                        # 🔥 都是字符串
                        xiugaizhe_zhanghao = str(xiugaizhe_info[0]) if xiugaizhe_info[0] else str(xiugaizhe_info[1]) if \
                        xiugaizhe_info[1] else xiugaizhe_zhanghaoid

                # 获取会员名称（如果有）
                huiyuan_mingzi = ''
                if record[15]:  # huiyuan_id是CharField
                    with connection.cursor() as cursor3:
                        cursor3.execute(
                            "SELECT jieshao FROM huiyuan WHERE huiyuan_id = %s LIMIT 1",
                            [str(record[15])]  # 🔥 转为字符串查询
                        )
                        huiyuan_info = cursor3.fetchone()
                        if huiyuan_info:
                            huiyuan_mingzi = str(huiyuan_info[0]) if huiyuan_info[0] else ''

                # 🔥 严格按照前端字段名映射，保持正确类型
                jilu_data = {
                    # 基础字段（字符串）
                    'id': record[0],
                    'beixiugai_yonghuid': str(record[1]) if record[1] else '',  # yonghuid
                    'xiugaizhe_id': str(record[2]) if record[2] else '',  # xiugaiid
                    'xiugaizhe_zhanghao': xiugaizhe_zhanghao,  # 字符串
                    'beixiugai_yonghuleixing': record[3],  # leixing是整数

                    # 打手字段（Decimal转float，Integer转int）
                    'xiugaihou_dashoutixianjine': float(record[4]) if record[4] is not None else 0.00,
                    'xiugaiqian_dashoutixianjine': float(record[5]) if record[5] is not None else 0.00,
                    'xiugaihou_dashoujifen': int(record[6]) if record[6] is not None else 0,
                    'xiugaiqian_dashoujifen': int(record[7]) if record[7] is not None else 0,
                    'xiugaihou_dashouyajin': float(record[8]) if record[8] is not None else 0.00,
                    'xiugaiqian_dashouyajin': float(record[9]) if record[9] is not None else 0.00,

                    # 商家字段
                    'xiugaihou_shangjiayue': float(record[10]) if record[10] is not None else 0.00,
                    'xiugaiqian_shangjiayue': float(record[11]) if record[11] is not None else 0.00,

                    # 管事字段
                    'xiugaihou_guanshiyue': float(record[12]) if record[12] is not None else 0.00,
                    'xiugaiqian_guanshiyue': float(record[13]) if record[13] is not None else 0.00,

                    # 会员字段
                    'tianjia_huiyuantianshu': int(record[14]) if record[14] is not None else 0,
                    'huiyuan_id': str(record[15]) if record[15] else '',  # 字符串
                    'huiyuan_mingzi': huiyuan_mingzi,  # 字符串

                    # 其他字段
                    'qita_shuoming': str(record[16]) if record[16] else '',  # 字符串
                    'xiugai_time': record[17].strftime('%Y-%m-%d %H:%M:%S') if record[17] else None,
                    'touxiang': str(record[18]) if record[18] else ''  # avatar是字符串
                }

                jilu_list.append(jilu_data)

            # 🔥 7. 分页控制
            queried_records = len(jilu_list)
            has_more = False

            if queried_records == pagesize:
                total_queried = (page - 1) * pagesize + queried_records
                has_more = total_queried < total_count

            return Response({
                'code': 0,
                'message': '获取成功',
                'data': {
                    'list': jilu_list,
                    'zongshuliang': total_count,
                    'dangqian_page': page,
                    'page_size': pagesize,
                    'has_more': has_more,
                    'shijichaxundediaoshuliang': queried_records
                }
            }, status=status.HTTP_200_OK)

        except ValueError as e:
            print(f"参数错误: {str(e)}")
            return Response({
                'code': 400,
                'message': '参数错误',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        except Exception as e:
            print(f"获取修改记录失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return Response({
                'code': 500,
                'message': '系统错误',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)







# views/shangjia_moban.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django.db import transaction
from decimal import Decimal
import re

from .models import ShangjiaMoban
from shangpin.models import ShangpinLeixing  # 假设商品模型路径


'''class ShangjiaTianjiaMobanView(APIView):
    """
    商家添加订单模板接口
    POST /peizhi/sjtjddmb
    权限: JWT认证用户
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取用户ID
            user = request.user
            yonghu_id = user.yonghuid

            # 2. 验证并获取请求数据
            leixing_id = request.data.get('shangpinTypeId')
            jieshao = request.data.get('jieshao', '').strip()
            jiage = request.data.get('jiage', '')

            # 3. 验证必填字段
            if not all([leixing_id, jieshao, jiage]):
                return Response({
                    'code': 400,
                    'msg': '缺少必填字段',
                    'data': {}
                }, status=400)

            # 4. 验证价格格式
            try:
                jiage_decimal = Decimal(jiage)
                if jiage_decimal <= 0 or jiage_decimal > 10000:
                    return Response({
                        'code': 400,
                        'msg': '价格必须在0.1-10000元之间',
                        'data': {}
                    }, status=400)
            except (ValueError, TypeError):
                return Response({
                    'code': 400,
                    'msg': '价格格式错误',
                    'data': {}
                }, status=400)

            # 5. 验证介绍长度
            if len(jieshao) > 100:
                return Response({
                    'code': 400,
                    'msg': '订单介绍不能超过100字',
                    'data': {}
                }, status=400)

            # 6. 验证商品类型是否存在且可用
            try:
                # 使用select_related优化查询，只获取需要的字段
                shangpin_obj = ShangpinLeixing.objects.filter(
                    id=leixing_id,
                    yaoqiuleixing=1  # 假设1为正常状态
                ).only('id', 'yaoqiuleixing').first()

                if not shangpin_obj:
                    return Response({
                        'code': 404,
                        'msg': '商品类型不存在或已禁用',
                        'data': {}
                    }, status=404)

                # 检查抢单要求类型
                if shangpin_obj.yaoqiuleixing != 1:
                    return Response({
                        'code': 400,
                        'msg': '该商品类型不支持创建模板',
                        'data': {}
                    }, status=400)

            except ShangpinLeixing.DoesNotExist:
                return Response({
                    'code': 404,
                    'msg': '商品类型不存在',
                    'data': {}
                }, status=404)

            # 7. 创建模板（使用事务保证数据一致性）
            with transaction.atomic():
                # 检查是否已存在相同介绍（根据unique_together约束）
                existing_moban = ShangjiaMoban.objects.filter(
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    moban_jieshao=jieshao,
                    #zhuangtai=1  # 只检查正常状态的模板
                ).exists()

                if existing_moban:
                    return Response({
                        'code': 400,
                        'msg': '已存在相同介绍的模板',
                        'data': {}
                    }, status=400)

                # 创建新模板
                moban = ShangjiaMoban.objects.create(
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    moban_jieshao=jieshao,
                    jiage=jiage_decimal,
                    fabu_shuliang=0,
                    #zhuangtai=1
                )

            # 8. 返回成功响应（与前端字段对应）
            return Response({
                'code': 200,
                'msg': '模板创建成功',
                'data': {
                    'mobanId': moban.id,  # 模板ID
                    'shangpinTypeId': leixing_id,  # 商品类型ID
                    'jieshao': moban.moban_jieshao,  # 模板介绍
                    'jiage': str(moban.jiage),  # 价格（转为字符串）
                    'fabushuliang': moban.fabu_shuliang,  # 发布数量
                    'createTime': moban.create_time.strftime('%Y-%m-%d %H:%M:%S')  # 创建时间
                }
            })

        except Exception as e:
            # 记录错误日志
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"创建商家模板失败: {str(e)}", exc_info=True)

            return Response({
                'code': 500,
                'msg': '服务器内部错误',
                'data': {}
            }, status=500)


# views/shangjia_moban.py
from django.core.paginator import Paginator
from django.db.models import Q


class ShangjiaMobanListView(APIView):
    """
    商家订单模板列表接口
    POST /peizhi/sjddmblb
    权限: JWT认证用户
    功能: 支持普通分页获取和搜索两种模式
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取用户ID和基础参数
            user = request.user
            yonghu_id = user.yonghuid

            # 2. 获取请求参数
            leixing_id = request.data.get('shangpinTypeId')
            get_type = request.data.get('getType', 2)  # 1=搜索，2=普通获取
            page = int(request.data.get('page', 1))
            page_size = int(request.data.get('pageSize', 50))
            search_keyword = request.data.get('searchKeyword', '').strip()

            # 3. 验证必填字段
            if not leixing_id:
                return Response({
                    'code': 400,
                    'msg': '商品类型ID不能为空',
                    'data': {}
                }, status=400)

            if get_type not in [1, 2]:
                return Response({
                    'code': 400,
                    'msg': '获取类型参数错误',
                    'data': {}
                }, status=400)

            # 4. 构建查询条件
            query_conditions = Q(
                yonghu_id=yonghu_id,
                leixing_id=leixing_id,
                #zhuangtai=1  # 只查询正常状态的模板
            )

            # 5. 搜索模式处理
            if get_type == 1 and search_keyword:
                # 搜索模式：按模板ID搜索
                try:
                    moban_id = int(search_keyword)
                    query_conditions &= Q(id=moban_id)
                except ValueError:
                    return Response({
                        'code': 400,
                        'msg': '搜索关键词必须是有效的模板ID',
                        'data': {}
                    }, status=400)

            # 6. 执行查询（使用values优化性能）
            queryset = ShangjiaMoban.objects.filter(query_conditions)

            # 7. 计算总数和分页
            total_count = queryset.count()

            # 搜索模式不进行分页，直接返回所有匹配结果
            if get_type == 1:
                moban_list = queryset.values(
                    'id',  # 模板ID
                    'leixing_id',  # 商品类型ID
                    'moban_jieshao',  # 模板介绍
                    'jiage',  # 价格
                    'fabu_shuliang',  # 发布数量
                    'create_time'  # 创建时间
                )

                # 格式化数据（与前端字段对应）
                formatted_list = []
                for item in moban_list:
                    formatted_list.append({
                        'mobanId': item['id'],
                        'shangpinTypeId': item['leixing_id'],
                        'jieshao': item['moban_jieshao'],
                        'jiage': str(item['jiage']),  # 转为字符串
                        'fabushuliang': item['fabu_shuliang'],
                        'createTime': item['create_time'].strftime('%Y-%m-%d %H:%M:%S') if item['create_time'] else ''
                    })

                return Response({
                    'code': 200,
                    'msg': '搜索成功',
                    'data': {
                        'list': formatted_list,
                        'total': len(formatted_list),
                        'hasMore': False,  # 搜索模式不分页，没有更多数据
                        'currentPage': 1,
                        'pageSize': len(formatted_list)
                    }
                })

            # 8. 普通获取模式：分页处理
            else:
                # 使用Django分页器
                paginator = Paginator(queryset, page_size)

                # 检查页码是否有效
                if page < 1 or page > paginator.num_pages:
                    return Response({
                        'code': 400,
                        'msg': '页码超出范围',
                        'data': {}
                    }, status=400)

                # 获取当前页数据
                current_page = paginator.page(page)

                # 获取数据并格式化
                moban_list = current_page.object_list.values(
                    'id',  # 模板ID
                    'leixing_id',  # 商品类型ID
                    'moban_jieshao',  # 模板介绍
                    'jiage',  # 价格
                    'fabu_shuliang',  # 发布数量
                    'create_time'  # 创建时间
                )

                # 格式化数据（与前端字段对应）
                formatted_list = []
                for item in moban_list:
                    formatted_list.append({
                        'mobanId': item['id'],
                        'shangpinTypeId': item['leixing_id'],
                        'jieshao': item['moban_jieshao'],
                        'jiage': str(item['jiage']),  # 转为字符串
                        'fabushuliang': item['fabu_shuliang'],
                        'createTime': item['create_time'].strftime('%Y-%m-%d %H:%M:%S') if item['create_time'] else ''
                    })

                # 判断是否有更多数据
                has_more = current_page.has_next()

                # 特殊逻辑：如果当前页数据量不足page_size，说明没有更多数据
                if len(formatted_list) < page_size:
                    has_more = False

                return Response({
                    'code': 200,
                    'msg': '获取成功',
                    'data': {
                        'list': formatted_list,
                        'total': total_count,
                        'hasMore': has_more,  # 是否允许上拉刷新
                        'currentPage': page,
                        'pageSize': page_size,
                        'totalPages': paginator.num_pages
                    }
                })

        except Exception as e:
            # 记录错误日志
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"获取商家模板列表失败: {str(e)}", exc_info=True)

            return Response({
                'code': 500,
                'msg': '服务器内部错误',
                'data': {}
            }, status=500)


# views/shangjia_moban.py
class ShangjiaGengxinMobanView(APIView):
    """
    商家更新订单模板接口
    POST /peizhi/sjddmbgx
    权限: JWT认证用户
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取用户ID
            user = request.user
            yonghu_id = user.yonghuid

            # 2. 获取请求参数
            moban_id = request.data.get('mobanId')
            leixing_id = request.data.get('shangpinTypeId')
            new_jieshao = request.data.get('jieshao', '').strip()
            new_jiage = request.data.get('jiage', '')

            # 3. 验证必填字段
            if not all([moban_id, leixing_id]):
                return Response({
                    'code': 400,
                    'msg': '模板ID和商品类型ID不能为空',
                    'data': {}
                }, status=400)

            # 4. 检查是否有修改内容
            if not new_jieshao and not new_jiage:
                return Response({
                    'code': 400,
                    'msg': '没有修改内容',
                    'data': {}
                }, status=400)

            # 5. 验证修改内容
            update_fields = {}

            # 验证介绍
            if new_jieshao:
                if len(new_jieshao) > 100:
                    return Response({
                        'code': 400,
                        'msg': '订单介绍不能超过100字',
                        'data': {}
                    }, status=400)
                update_fields['moban_jieshao'] = new_jieshao

            # 验证价格
            if new_jiage:
                try:
                    jiage_decimal = Decimal(new_jiage)
                    if jiage_decimal <= 0 or jiage_decimal > 10000:
                        return Response({
                            'code': 400,
                            'msg': '价格必须在0.1-10000元之间',
                            'data': {}
                        }, status=400)
                    update_fields['jiage'] = jiage_decimal
                except (ValueError, TypeError):
                    return Response({
                        'code': 400,
                        'msg': '价格格式错误',
                        'data': {}
                    }, status=400)

            # 6. 查询模板是否存在且属于当前用户
            try:
                moban = ShangjiaMoban.objects.get(
                    id=moban_id,
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    #zhuangtai=1  # 只修改正常状态的模板
                )
            except ShangjiaMoban.DoesNotExist:
                return Response({
                    'code': 404,
                    'msg': '模板不存在或无权修改',
                    'data': {}
                }, status=404)

            # 7. 检查介绍是否重复（如果修改了介绍）
            if 'moban_jieshao' in update_fields:
                existing_moban = ShangjiaMoban.objects.filter(
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    moban_jieshao=update_fields['moban_jieshao'],
                    #zhuangtai=1
                ).exclude(id=moban_id).exists()  # 排除自身

                if existing_moban:
                    return Response({
                        'code': 400,
                        'msg': '已存在相同介绍的模板',
                        'data': {}
                    }, status=400)

            # 8. 更新模板（使用update_fields优化，只更新修改的字段）
            with transaction.atomic():
                for field, value in update_fields.items():
                    setattr(moban, field, value)
                moban.save(update_fields=list(update_fields.keys()))

            # 9. 获取更新后的数据
            updated_moban = ShangjiaMoban.objects.filter(id=moban_id).values(
                'id', 'leixing_id', 'moban_jieshao', 'jiage', 'fabu_shuliang', 'update_time'
            ).first()

            # 10. 返回成功响应
            return Response({
                'code': 200,
                'msg': '模板修改成功',
                'data': {
                    'mobanId': updated_moban['id'],
                    'shangpinTypeId': updated_moban['leixing_id'],
                    'jieshao': updated_moban['moban_jieshao'],
                    'jiage': str(updated_moban['jiage']),
                    'fabushuliang': updated_moban['fabu_shuliang'],
                    'updateTime': updated_moban['update_time'].strftime('%Y-%m-%d %H:%M:%S') if updated_moban[
                        'update_time'] else ''
                }
            })

        except Exception as e:
            # 记录错误日志
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"修改商家模板失败: {str(e)}", exc_info=True)

            return Response({
                'code': 500,
                'msg': '服务器内部错误',
                'data': {}
            }, status=500)


# views/shangjia_moban.py
class ShangjiaShanchuMobanView(APIView):
    """
    商家删除订单模板接口
    POST /peizhi/sjddmbsc
    权限: JWT认证用户
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取用户ID
            user = request.user
            yonghu_id = user.yonghuid

            # 2. 获取请求参数
            moban_id = request.data.get('mobanId')
            leixing_id = request.data.get('shangpinTypeId')

            # 3. 验证必填字段
            if not all([moban_id, leixing_id]):
                return Response({
                    'code': 400,
                    'msg': '模板ID和商品类型ID不能为空',
                    'data': {}
                }, status=400)

            # 4. 查询模板是否存在且属于当前用户
            try:
                moban = ShangjiaMoban.objects.get(
                    id=moban_id,
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    #zhuangtai=1  # 只删除正常状态的模板
                )
            except ShangjiaMoban.DoesNotExist:
                return Response({
                    'code': 404,
                    'msg': '模板不存在或无权删除',
                    'data': {}
                }, status=404)

            # 5. 检查模板是否已被使用（有发布数量）
            if moban.fabu_shuliang > 0:
                # 可以选择软删除，将状态改为已删除
                moban.zhuangtai = 4  # 4=已删除状态
                moban.save()
                delete_type = 'soft'
            else:
                # 硬删除
                moban.delete()
                delete_type = 'hard'

            # 6. 返回成功响应
            return Response({
                'code': 200,
                'msg': '模板删除成功',
                'data': {
                    'mobanId': moban_id,
                    'deleteType': delete_type,
                    'shangpinTypeId': leixing_id
                }
            })

        except Exception as e:
            # 记录错误日志
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"删除商家模板失败: {str(e)}", exc_info=True)

            return Response({
                'code': 500,
                'msg': '服务器内部错误',
                'data': {}
            }, status=500)'''




from django.core.paginator import Paginator
from django.db.models import Q, Prefetch
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from peizhi.models import ShangjiaMoban
from dengji.models import Chenghao

class ShangjiaMobanListView(APIView):
    """
    商家订单模板列表接口
    POST /peizhi/sjddmblb
    权限: JWT认证
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            user = request.user
            yonghu_id = user.yonghuid

            leixing_id = request.data.get('shangpinTypeId')
            if not leixing_id:
                return Response({'code': 400, 'msg': '商品类型ID不能为空', 'data': {}}, status=400)

            get_type = int(request.data.get('getType', 2))
            page = int(request.data.get('page', 1))
            page_size = int(request.data.get('pageSize', 50))

            keyword = request.data.get('keyword', '')
            min_price = request.data.get('minPrice', '')
            label_id = request.data.get('labelId', None)
            if label_id is not None:
                try:
                    label_id = int(label_id)
                except (ValueError, TypeError):
                    label_id = None

            query = Q(yonghu_id=yonghu_id, leixing_id=leixing_id)

            if get_type == 1:  # 搜索模式
                if keyword:
                    keyword = str(keyword).strip()
                    try:
                        mid = int(keyword)
                        query &= Q(id=mid) | Q(moban_jieshao__icontains=keyword)
                    except ValueError:
                        query &= Q(moban_jieshao__icontains=keyword)
                if min_price:
                    try:
                        query &= Q(jiage__gte=Decimal(str(min_price)))
                    except:
                        pass
                if label_id:
                    query &= Q(chenghao_id=label_id)

                mobans = ShangjiaMoban.objects.filter(query).order_by('-id')
                formatted = []
                for moban in mobans:
                    texiao_json = ''
                    label_name = ''
                    if moban.chenghao_id:
                        try:
                            ch = Chenghao.objects.get(id=moban.chenghao_id)
                            label_name = ch.mingcheng
                            texiao_json = ch.texiao_miaoshu or ''
                        except Chenghao.DoesNotExist:
                            pass
                    formatted.append({
                        'mobanId': moban.id,
                        'shangpinTypeId': moban.leixing_id,
                        'jieshao': moban.moban_jieshao,
                        'jiage': str(moban.jiage),
                        'fabushuliang': moban.fabu_shuliang,
                        'commissionEnabled': moban.yongjin_qiangdan,
                        'commissionValue': str(moban.yongjin_jiage) if moban.yongjin_jiage is not None else None,
                        'labelId': moban.chenghao_id,
                        'labelName': label_name,
                        'texiaoJson': texiao_json,
                        'createTime': moban.create_time.strftime('%Y-%m-%d %H:%M:%S') if moban.create_time else ''
                    })
                return Response({'code': 200, 'msg': '搜索成功', 'data': {'list': formatted, 'total': len(formatted), 'hasMore': False}})

            # 分页模式
            queryset = ShangjiaMoban.objects.filter(query).order_by('-id')
            paginator = Paginator(queryset, page_size)
            if page < 1 or page > paginator.num_pages:
                return Response({'code': 400, 'msg': '页码超出范围', 'data': {}}, status=400)

            current_page = paginator.page(page)
            formatted = []
            for moban in current_page.object_list:
                texiao_json = ''
                label_name = ''
                if moban.chenghao_id:
                    try:
                        ch = Chenghao.objects.get(id=moban.chenghao_id)
                        label_name = ch.mingcheng
                        texiao_json = ch.texiao_miaoshu or ''
                    except Chenghao.DoesNotExist:
                        pass
                formatted.append({
                    'mobanId': moban.id,
                    'shangpinTypeId': moban.leixing_id,
                    'jieshao': moban.moban_jieshao,
                    'jiage': str(moban.jiage),
                    'fabushuliang': moban.fabu_shuliang,
                    'commissionEnabled': moban.yongjin_qiangdan,
                    'commissionValue': str(moban.yongjin_jiage) if moban.yongjin_jiage is not None else None,
                    'labelId': moban.chenghao_id,
                    'labelName': label_name,
                    'texiaoJson': texiao_json,
                    'createTime': moban.create_time.strftime('%Y-%m-%d %H:%M:%S') if moban.create_time else ''
                })

            has_more = current_page.has_next()
            if len(formatted) < page_size:
                has_more = False

            return Response({
                'code': 200,
                'msg': '获取成功',
                'data': {
                    'list': formatted,
                    'total': paginator.count,
                    'hasMore': has_more,
                    'currentPage': page,
                    'pageSize': page_size,
                    'totalPages': paginator.num_pages
                }
            })

        except Exception as e:
            logger.error(f"获取模板列表失败: {str(e)}", exc_info=True)
            return Response({'code': 500, 'msg': '服务器内部错误', 'data': {}}, status=500)

from dengji.models import  Chenghao

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.db.models import Prefetch
class ShangjiaTianjiaMobanView(APIView):
    """
    商家添加订单模板接口
    POST /peizhi/sjtjddmb
    权限: JWT认证
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取当前商家用户ID
            user = request.user
            yonghu_id = user.yonghuid

            # 2. 接收并清洗参数，避免类型错误
            leixing_id = request.data.get('shangpinTypeId')
            jieshao = request.data.get('jieshao', '').strip() if request.data.get('jieshao') else ''
            jiage = request.data.get('jiage', '')
            # 佣金相关
            commission_enabled = request.data.get('commissionEnabled', False)
            commission_value = request.data.get('commissionValue', None)
            # 标签ID（称号ID）
            label_id = request.data.get('labelId', None)

            # 3. 必填验证
            if not leixing_id or not jieshao or not jiage:
                logger.warning(f"参数缺失 - leixing_id:{leixing_id}, jieshao:{jieshao}, jiage:{jiage}")
                return Response({
                    'code': 400,
                    'msg': '缺少必填字段（订单类型、介绍、价格）',
                    'data': {}
                }, status=400)

            # 4. 价格格式验证
            try:
                jiage_decimal = Decimal(str(jiage))
                if jiage_decimal <= 0:
                    return Response({'code': 400, 'msg': '价格必须大于0', 'data': {}}, status=400)
                if jiage_decimal > 10000:
                    return Response({'code': 400, 'msg': '价格不能超过10000元', 'data': {}}, status=400)
            except (InvalidOperation, ValueError, TypeError):
                return Response({'code': 400, 'msg': '价格格式错误', 'data': {}}, status=400)

            # 5. 介绍长度验证
            if len(jieshao) > 100:
                return Response({'code': 400, 'msg': '订单介绍不能超过100字', 'data': {}}, status=400)

            # 6. 佣金验证（若启用）
            yongjin_qiangdan = False
            yongjin_jiage = None
            if commission_enabled:
                yongjin_qiangdan = True
                if not commission_value:
                    return Response({'code': 400, 'msg': '开启佣金时，佣金金额不能为空', 'data': {}}, status=400)
                try:
                    comm_decimal = Decimal(str(commission_value))
                    if comm_decimal <= 0:
                        return Response({'code': 400, 'msg': '佣金必须大于0', 'data': {}}, status=400)
                    yongjin_jiage = comm_decimal
                except (InvalidOperation, ValueError, TypeError):
                    return Response({'code': 400, 'msg': '佣金格式错误', 'data': {}}, status=400)

            # 7. 标签ID清洗（前端可能传数字或字符串）
            cleaned_label_id = None
            if label_id is not None and str(label_id).strip():
                try:
                    cleaned_label_id = int(label_id)
                except (ValueError, TypeError):
                    return Response({'code': 400, 'msg': '标签ID格式错误', 'data': {}}, status=400)

            # 8. 验证商品类型是否存在且可用
            try:
                shangpin_obj = ShangpinLeixing.objects.get(
                    id=leixing_id,
                    shenhezhuangtai=1        # 审核通过
                )
            except ShangpinLeixing.DoesNotExist:
                return Response({'code': 404, 'msg': '商品类型不存在或已禁用', 'data': {}}, status=404)

            # 9. 检查同一用户、同一类型下是否已存在相同介绍的模板
            if ShangjiaMoban.objects.filter(
                yonghu_id=yonghu_id,
                leixing_id=leixing_id,
                moban_jieshao=jieshao
            ).exists():
                return Response({'code': 400, 'msg': '已存在相同介绍的模板', 'data': {}}, status=400)

            # 10. 执行数据库创建（事务）
            with transaction.atomic():
                moban = ShangjiaMoban.objects.create(
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    moban_jieshao=jieshao,
                    jiage=jiage_decimal,
                    fabu_shuliang=0,
                    yongjin_qiangdan=yongjin_qiangdan,
                    yongjin_jiage=yongjin_jiage,
                    chenghao_id=cleaned_label_id
                )

            # 11. 返回成功数据（字段与前端对齐）
            return Response({
                'code': 200,
                'msg': '模板创建成功',
                'data': {
                    'mobanId': moban.id,
                    'shangpinTypeId': leixing_id,
                    'jieshao': moban.moban_jieshao,
                    'jiage': str(moban.jiage),
                    'fabushuliang': moban.fabu_shuliang,
                    'createTime': moban.create_time.strftime('%Y-%m-%d %H:%M:%S'),
                    'commissionEnabled': moban.yongjin_qiangdan,
                    'commissionValue': str(moban.yongjin_jiage) if moban.yongjin_jiage is not None else None,
                    'labelId': moban.chenghao_id
                }
            })

        except Exception as e:
            # 打印完整错误堆栈到日志
            logger.error(f"添加模板失败: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '服务器内部错误，添加模板失败，请联系管理员',
                'data': {}
            }, status=500)


from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from peizhi.models import ShangjiaMoban

class ShangjiaShanchuMobanView(APIView):
    """
    删除模板
    POST /peizhi/sjddmbsc
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            user = request.user
            yonghu_id = user.yonghuid

            moban_id = request.data.get('mobanId')
            leixing_id = request.data.get('shangpinTypeId')

            if not all([moban_id, leixing_id]):
                return Response({'code': 400, 'msg': '参数缺失', 'data': {}}, status=400)

            try:
                moban = ShangjiaMoban.objects.get(id=moban_id, yonghu_id=yonghu_id, leixing_id=leixing_id)
            except ShangjiaMoban.DoesNotExist:
                return Response({'code': 404, 'msg': '模板不存在', 'data': {}}, status=404)

            # 强制硬删除
            moban.delete()

            return Response({'code': 200, 'msg': '删除成功', 'data': {'mobanId': moban_id, 'shangpinTypeId': leixing_id}})
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"删除模板失败: {str(e)}", exc_info=True)
            return Response({'code': 500, 'msg': '服务器内部错误', 'data': {}}, status=500)




from decimal import Decimal, InvalidOperation
from django.db import transaction
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from peizhi.models import ShangjiaMoban

class ShangjiaGengxinMobanView(APIView):
    """
    商家更新订单模板接口
    POST /peizhi/sjddmbgx
    权限: JWT认证
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            user = request.user
            yonghu_id = user.yonghuid

            moban_id = request.data.get('mobanId')
            leixing_id = request.data.get('shangpinTypeId')
            new_jieshao = request.data.get('jieshao', '').strip()
            new_jiage = request.data.get('jiage', '').strip()
            # 新增字段
            commission_enabled = request.data.get('commissionEnabled')
            commission_value = request.data.get('commissionValue')
            label_id = request.data.get('labelId')

            if not all([moban_id, leixing_id]):
                return Response({'code': 400, 'msg': '模板ID和商品类型ID不能为空', 'data': {}}, status=400)

            # 查询模板
            try:
                moban = ShangjiaMoban.objects.get(
                    id=moban_id,
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id
                )
            except ShangjiaMoban.DoesNotExist:
                return Response({'code': 404, 'msg': '模板不存在或无权修改', 'data': {}}, status=404)

            update_fields = []

            # 修改介绍
            if new_jieshao:
                if len(new_jieshao) > 100:
                    return Response({'code': 400, 'msg': '订单介绍不能超过100字', 'data': {}}, status=400)
                if new_jieshao != moban.moban_jieshao:
                    moban.moban_jieshao = new_jieshao
                    update_fields.append('moban_jieshao')

            # 修改价格
            if new_jiage:
                try:
                    jiage_decimal = Decimal(new_jiage)
                    if jiage_decimal <= 0 or jiage_decimal > 10000:
                        return Response({'code': 400, 'msg': '价格必须在0.1-10000元之间', 'data': {}}, status=400)
                    if jiage_decimal != moban.jiage:
                        moban.jiage = jiage_decimal
                        update_fields.append('jiage')
                except (InvalidOperation, ValueError, TypeError):
                    return Response({'code': 400, 'msg': '价格格式错误', 'data': {}}, status=400)

            # 修改标签
            if label_id is not None:  # 允许传空字符串或不传，传0表示清空
                new_label = int(label_id) if label_id else None
                if new_label != moban.chenghao_id:
                    moban.chenghao_id = new_label
                    update_fields.append('chenghao_id')

            # 修改佣金设置
            if commission_enabled is not None:
                enabled = bool(commission_enabled)
                if enabled:
                    if not commission_value:
                        return Response({'code': 400, 'msg': '佣金价格不能为空', 'data': {}}, status=400)
                    try:
                        comm_decimal = Decimal(commission_value)
                        if comm_decimal < 0.1 or comm_decimal > 50:
                            return Response({'code': 400, 'msg': '佣金需在0.1-50元之间', 'data': {}}, status=400)
                        if moban.yongjin_qiangdan != True or moban.yongjin_jiage != comm_decimal:
                            moban.yongjin_qiangdan = True
                            moban.yongjin_jiage = comm_decimal
                            update_fields.extend(['yongjin_qiangdan', 'yongjin_jiage'])
                    except (InvalidOperation, ValueError, TypeError):
                        return Response({'code': 400, 'msg': '佣金格式错误', 'data': {}}, status=400)
                else:
                    if moban.yongjin_qiangdan != False or moban.yongjin_jiage is not None:
                        moban.yongjin_qiangdan = False
                        moban.yongjin_jiage = None
                        update_fields.extend(['yongjin_qiangdan', 'yongjin_jiage'])

            if not update_fields:
                return Response({'code': 400, 'msg': '没有修改内容', 'data': {}}, status=400)

            # 检查介绍唯一性
            if 'moban_jieshao' in update_fields:
                if ShangjiaMoban.objects.filter(
                    yonghu_id=yonghu_id,
                    leixing_id=leixing_id,
                    moban_jieshao=moban.moban_jieshao
                ).exclude(id=moban_id).exists():
                    return Response({'code': 400, 'msg': '已存在相同介绍的模板', 'data': {}}, status=400)

            with transaction.atomic():
                moban.save(update_fields=update_fields)

            # 返回最新数据
            return Response({
                'code': 200,
                'msg': '模板修改成功',
                'data': {
                    'mobanId': moban.id,
                    'shangpinTypeId': moban.leixing_id,
                    'jieshao': moban.moban_jieshao,
                    'jiage': str(moban.jiage),
                    'fabushuliang': moban.fabu_shuliang,
                    'commissionEnabled': moban.yongjin_qiangdan,
                    'commissionValue': str(moban.yongjin_jiage) if moban.yongjin_jiage else None,
                    'labelId': moban.chenghao_id,
                    'updateTime': moban.update_time.strftime('%Y-%m-%d %H:%M:%S')
                }
            })

        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"修改商家模板失败: {str(e)}", exc_info=True)
            return Response({'code': 500, 'msg': '服务器内部错误', 'data': {}}, status=500)



from dengji.models import  DingdanBiaoqian



import random          # 必须导入
import secrets

import os
import time
import random          # 必须导入
import hashlib
import secrets
import logging
from decimal import Decimal
from django.conf import settings
from django.db import transaction
from django.db.models import F
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from yonghu.models import UserShangjia

from shangpin.models import ShangpinLeixing

from dengji.models import Chenghao

logger = logging.getLogger(__name__)

class ShangjiaGenerateLinkView(APIView):
    """商家生成发单链接接口（支持模板佣金/标签）"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            user = request.user
            # 获取参数
            shangpin_type_id = request.data.get('shangpinTypeId')
            moban_id = request.data.get('mobanId')
            if not all([shangpin_type_id, moban_id]):
                return Response({'code': 400, 'msg': '缺少参数', 'data': {}}, status=400)

            try:
                shangpin_type_id = int(shangpin_type_id)
                moban_id = int(moban_id)
            except ValueError:
                return Response({'code': 400, 'msg': '参数格式错误', 'data': {}}, status=400)

            # 商家信息
            try:
                shangjia = user.shop_profile
            except UserShangjia.DoesNotExist:
                return Response({'code': 403, 'msg': '商家信息未完善', 'data': {}}, status=403)
            if shangjia.zhuangtai != 1:
                return Response({'code': 403, 'msg': '商家状态异常', 'data': {}}, status=403)

            # 商品类型
            try:
                shangpin_leixing = ShangpinLeixing.objects.get(id=shangpin_type_id, shenhezhuangtai=1)
            except ShangpinLeixing.DoesNotExist:
                return Response({'code': 400, 'msg': '商品类型不存在或已禁用', 'data': {}}, status=400)

            # 查询模板
            try:
                moban = ShangjiaMoban.objects.get(id=moban_id, yonghu_id=user.yonghuid, leixing_id=shangpin_type_id)
            except ShangjiaMoban.DoesNotExist:
                return Response({'code': 400, 'msg': '模板不存在', 'data': {}}, status=400)

            jiage = moban.jiage
            if jiage > shangjia.yue:
                return Response({'code': 400, 'msg': f'余额不足，当前余额{shangjia.yue}元', 'data': {}}, status=400)

            # 利率
            try:
                lilu = float(Lilubiao.objects.get(fadanpingtai='3').lilu) or 1.0
            except Lilubiao.DoesNotExist:
                lilu = 1.0
            dashou_fencheng = Decimal(str(round(float(jiage) * lilu, 2)))

            # 抢单要求：根据模板是否启用佣金
            chenghao_id = moban.chenghao_id
            if moban.yongjin_qiangdan and moban.yongjin_jiage is not None:
                final_yaoqiuleixing = 2          # 佣金模式
                final_yongjin = moban.yongjin_jiage
                final_huiyuan_id = ''
            else:
                final_yaoqiuleixing = shangpin_leixing.yaoqiuleixing or 1
                final_yongjin = None
                final_huiyuan_id = shangpin_leixing.huiyuan_id or ''

            # 获取标签名称
            label_name = ''
            if chenghao_id:
                try:
                    label_name = Chenghao.objects.get(id=chenghao_id).mingcheng
                except Chenghao.DoesNotExist:
                    pass

            # 生成ID和Token
            def generate_dingdan_id():
                timestamp = int(time.time() * 1000)
                process_id = os.getpid() % 1000
                random_num = random.randint(1000, 9999)   # 注意需要 import random
                return f"SJ{timestamp}{process_id:03d}{random_num}"

            def generate_secure_token(dd_id, uid):
                timestamp = int(time.time() * 1_000_000)
                salt = secrets.token_hex(8)
                raw = f"{timestamp}:{dd_id}:{uid}:{salt}"
                token = hashlib.sha256(raw.encode()).hexdigest()[:24]
                # 确保唯一性（极小概率重复再试一次）
                if ShangjiaLianjie.objects.filter(lianjie_token=token).exists():
                    return generate_secure_token(dd_id, uid)
                return token

            def generate_link_url(token):
                base = getattr(settings, 'H5_DOMAIN', 'https://h5.yourdomain.com').rstrip('/')
                return f"{base}/order/{token}"

            with transaction.atomic():
                dingdan_id = generate_dingdan_id()
                secure_token = generate_secure_token(dingdan_id, user.yonghuid)

                # 创建订单主表
                dingdan = Dingdan.objects.create(
                    dingdan_id=dingdan_id,
                    zhuangtai=13,
                    fadan_pingtai=2,
                    jine=jiage,
                    dashou_fencheng=dashou_fencheng,
                    leixing_id=shangpin_type_id,
                    yaoqiuleixing=final_yaoqiuleixing,
                    huiyuan_id=final_huiyuan_id,
                    yongjin=final_yongjin,
                    jieshao=moban.moban_jieshao[:2000],
                    user1_id=f"Sj{user.yonghuid}",
                    tupian=user.avatar or '',
                )

                # 商家扩展表
                DingdanShangjia.objects.create(
                    dingdan=dingdan,
                    shangjia_id=user.yonghuid,
                    sjnicheng=shangjia.nicheng or f"商家{user.yonghuid}",
                )

                # 关联标签
                if chenghao_id:
                    try:
                        ch = Chenghao.objects.get(id=chenghao_id)
                        DingdanBiaoqian.objects.create(dingdan=dingdan, chenghao=ch)
                    except Chenghao.DoesNotExist:
                        logger.warning(f"称号{chenghao_id}不存在，跳过关联")

                # 更新商家余额和统计
                UserShangjia.objects.filter(id=shangjia.id).update(
                    fabu=F('fabu') + 1,
                    yue=F('yue') - jiage,
                    jinridingdan=F('jinridingdan') + 1,
                    jinriliushui=F('jinriliushui') + jiage,
                    jinyuedingdan=F('jinyuedingdan') + 1,
                    jinyueliushui=F('jinyueliushui') + jiage,
                )

                # 生成链接并保存
                link_url = generate_link_url(secure_token)
                lianjie_obj = ShangjiaLianjie.objects.create(
                    yonghu_id=user.yonghuid,
                    leixing_id=shangpin_type_id,
                    moban_id=moban_id,
                    lianjie=link_url,
                    lianjie_token=secure_token,
                    dingdan_id=dingdan_id,
                    is_used=False,
                    youxiao_shijian=timezone.now() + timezone.timedelta(days=1)
                )

                # 更新模板发布数量
                ShangjiaMoban.objects.filter(id=moban_id).update(fabu_shuliang=F('fabu_shuliang') + 1)
                shangjia.refresh_from_db()

                logger.info(f"链接生成成功 - 订单ID: {dingdan_id}, 标签: {label_name}")

                return Response({
                    'code': 200,
                    'msg': '链接生成成功',
                    'data': {
                        'linkUrl': link_url,
                        'newBalance': str(shangjia.yue),
                        'dingdanId': dingdan_id,
                        'dingdanJieshao': moban.moban_jieshao,
                        'jiage': str(jiage),
                        'labelId': chenghao_id,
                        'labelName': label_name,
                        'youxiaoShijian': lianjie_obj.youxiao_shijian.strftime('%Y-%m-%d %H:%M:%S'),
                    }
                })

        except Exception as e:
            logger.error(f"生成链接事务失败: {str(e)}", exc_info=True)
            return Response({'code': 500, 'msg': '系统错误，生成链接失败', 'data': {}}, status=500)


from django.core.paginator import Paginator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from peizhi.models import ShangjiaLianjie

class ShangjiaLianjieListView(APIView):
    """获取已生成的链接列表（支持筛选是否使用）"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            user = request.user
            yonghu_id = user.yonghuid

            moban_id = request.data.get('mobanId')
            used = int(request.data.get('used', 0))          # 0未使用，1已使用
            page = int(request.data.get('page', 1))
            page_size = int(request.data.get('pageSize', 5))
            page_size = min(page_size, 50)

            if not moban_id:
                return Response({'code': 400, 'msg': '模板ID不能为空', 'data': {}}, status=400)

            # 校验模板归属
            try:
                moban = ShangjiaMoban.objects.get(id=moban_id, yonghu_id=yonghu_id)
            except ShangjiaMoban.DoesNotExist:
                return Response({'code': 404, 'msg': '模板不存在或无权查看', 'data': {}}, status=404)

            links = ShangjiaLianjie.objects.filter(
                yonghu_id=yonghu_id,
                moban_id=moban_id,
                is_used=(used == 1)
            ).order_by('-create_time')

            paginator = Paginator(links, page_size)
            if page < 1 or page > paginator.num_pages:
                return Response({'code': 400, 'msg': '页码超出范围', 'data': {}}, status=400)

            current_page = paginator.page(page)
            data_list = []
            for link in current_page.object_list:
                data_list.append({
                    'id': link.id,
                    'lianjie': link.lianjie,
                    'lianjie_token': link.lianjie_token,
                    'is_used': link.is_used,
                    'dingdan_id': link.dingdan_id,
                    'youxiao_shijian': link.youxiao_shijian.strftime('%Y-%m-%d %H:%M:%S') if link.youxiao_shijian else '',
                    'create_time': link.create_time.strftime('%Y-%m-%d %H:%M:%S')
                })

            return Response({
                'code': 200,
                'msg': '获取成功',
                'data': {
                    'list': data_list,
                    'total': paginator.count,
                    'hasMore': current_page.has_next(),
                    'currentPage': page,
                    'pageSize': page_size
                }
            })

        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"获取链接列表失败: {str(e)}", exc_info=True)
            return Response({'code': 500, 'msg': '服务器内部错误', 'data': {}}, status=500)



import logging

from peizhi.models import ShangjiaMoban, ShangjiaLianjie
from shangpin.models import ShangpinLeixing
from dingdan.models import Dingdan, DingdanShangjia
# 假设利率表模型路径（根据你的项目结构调整）
from dingdan.models import Lilubiao

logger = logging.getLogger(__name__)

# views/peizhi/sjljhq.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from django.db import transaction
from django.db.models import F
from django.conf import settings
from django.utils import timezone
import time
import random
import os
import logging
import hashlib
import secrets
from decimal import Decimal

logger = logging.getLogger(__name__)


'''class ShangjiaGenerateLinkView(APIView):
    """商家生成发单链接接口"""
    permission_classes = [IsAuthenticated]  # JWT Token认证

    def post(self, request):
        # 1. 记录请求日志
        logger.info(f"商家生成链接请求 - 用户ID: {request.user.yonghuid}, 数据: {request.data}")

        # 2. 获取前端参数
        data = request.data
        shangpin_type_id = data.get('shangpinTypeId')  # 商品类型ID
        moban_id = data.get('mobanId')  # 模板ID

        # 3. 基本参数验证
        if not all([shangpin_type_id, moban_id]):
            logger.warning(f"参数缺失 - shangpin_type_id: {shangpin_type_id}, moban_id: {moban_id}")
            return Response({
                'code': 400,
                'msg': '缺少必填参数：订单类型ID和模板ID',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        try:
            shangpin_type_id = int(shangpin_type_id)
            moban_id = int(moban_id)
        except ValueError:
            return Response({
                'code': 400,
                'msg': '参数格式错误，必须是数字',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 4. 获取商家扩展信息（使用反向查询，最高效的方式）
        try:
            shangjia = request.user.shop_profile
        except UserShangjia.DoesNotExist:
            logger.error(f"商家扩展信息不存在 - 用户ID: {request.user.yonghuid}")
            return Response({
                'code': 403,
                'msg': '您不是商家或商家信息未完善',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # 5. 验证商家状态
        if shangjia.zhuangtai != 1:
            logger.warning(f"商家状态异常 - 用户ID: {request.user.yonghuid}, 状态: {shangjia.zhuangtai}")
            return Response({
                'code': 403,
                'msg': '商家账号状态异常，请联系管理员',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # 6. 查询商品类型信息
        try:
            shangpin_leixing = ShangpinLeixing.objects.get(id=shangpin_type_id)
        except ShangpinLeixing.DoesNotExist:
            logger.error(f"商品类型不存在 - 类型ID: {shangpin_type_id}")
            return Response({
                'code': 400,
                'msg': '商品类型不存在',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 提取商品类型信息
        yaoqiuleixing = shangpin_leixing.yaoqiuleixing
        huiyuan_id = shangpin_leixing.huiyuan_id if shangpin_leixing.huiyuan_id else ''
        # yongjin 字段暂时不需要用到

        # 7. 查询商家订单模板（使用组合查询，最高效）
        try:
            moban = ShangjiaMoban.objects.get(
                yonghu_id=request.user.yonghuid,
                leixing_id=shangpin_type_id,
                id=moban_id
            )
        except ShangjiaMoban.DoesNotExist:
            logger.error(
                f"商家模板不存在 - 用户ID: {request.user.yonghuid}, 类型ID: {shangpin_type_id}, 模板ID: {moban_id}")
            return Response({
                'code': 400,
                'msg': '订单模板不存在',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 获取模板价格和介绍
        jiage = moban.jiage
        dingdan_jieshao = moban.moban_jieshao

        # 8. 验证商家余额
        if jiage > shangjia.yue:
            logger.warning(f"商家余额不足 - 用户ID: {request.user.yonghuid}, 余额: {shangjia.yue}, 价格: {jiage}")
            return Response({
                'code': 400,
                'msg': f'余额不足，当前余额{shangjia.yue}元',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 9. 查询利率（复用商家派单接口逻辑）
        try:
            lilu_obj = Lilubiao.objects.get(fadanpingtai='3')
            lilu = float(lilu_obj.lilu)
            if lilu <= 0:
                lilu = 1  # 默认值
            logger.info(f"获取到利率 - 平台: 3, 利率: {lilu}")
        except Lilubiao.DoesNotExist:
            logger.warning("利率表不存在，使用默认利率1")
            lilu = 1

        # 10. 计算打手分成
        # 方法1：最安全的方式，兼容所有情况
        dashou_fencheng = Decimal(str(round(float(jiage) * lilu, 2)))
        logger.info(f"计算打手分成 - 价格: {jiage}, 利率: {lilu}, 分成: {dashou_fencheng}")

        # 11. 生成订单ID（复用商家派单接口的逻辑）
        def generate_dingdan_id():
            """生成唯一订单ID"""
            timestamp = int(time.time() * 1000)
            process_id = os.getpid() % 1000
            random_num = random.randint(1000, 9999)
            return f"SJ{timestamp}{process_id:03d}{random_num}"

        # 🔴🆕【新增】生成安全Token的函数
        def generate_secure_token(dingdan_id, shangjia_id):
            """
            生成安全的Token
            算法：时间戳 + 订单ID + 商家ID + 随机数 → SHA256 → 截取前24位
            """
            timestamp = int(time.time() * 1_000_000)  # 纳秒级精度
            random_salt = secrets.token_hex(8)  # 16位随机盐值

            # 组合原始字符串
            raw_string = f"{timestamp}:{dingdan_id}:{shangjia_id}:{random_salt}"

            # 使用SHA256哈希
            hash_obj = hashlib.sha256(raw_string.encode('utf-8'))
            token = hash_obj.hexdigest()[:24]  # 取前24位作为Token

            # 确保唯一性（理论上极小概率重复，但还是要检查）
            try:
                if ShangjiaLianjie.objects.filter(lianjie_token=token).exists():
                    # 如果重复，重新生成（极低概率）
                    return generate_secure_token(dingdan_id, shangjia_id)
            except:
                pass

            return token

        # 🔴🆕【修改】生成链接函数 - 使用Token而不是订单ID
        def generate_link_url(token):
            """生成安全的H5页面链接 - 使用Token而不是订单ID"""
            base_url = getattr(settings, 'H5_DOMAIN', 'https://h5.yourdomain.com')
            # 确保base_url以/结尾
            if not base_url.endswith('/'):
                base_url += '/'
            # 🔴🆕【关键修改】生成使用Token的链接
            return f"{base_url}order/{token}"

        # 13. 使用事务确保数据一致性（这是最关键的步骤！）
        try:
            with transaction.atomic():
                # 生成订单ID
                dingdan_id = generate_dingdan_id()

                # 🔴🆕【新增】生成安全Token
                secure_token = generate_secure_token(dingdan_id, request.user.yonghuid)

                # 创建订单主表（状态为13：灰度状态，等待老板填写）
                dingdan = Dingdan.objects.create(
                    dingdan_id=dingdan_id,
                    zhuangtai=13,  # 🔴 核心：状态13表示灰度状态，等待老板填写
                    fadan_pingtai=2,  # 商家发单
                    jine=jiage,
                    dashou_fencheng=dashou_fencheng,
                    # 以下字段留空，等待老板填写
                    jiedan_dashou_id='',
                    dashou_liuyan='',
                    zhiding_id='',
                    shangpin_id=0,  # 暂时设为0
                    leixing_id=shangpin_type_id,
                    yaoqiuleixing=yaoqiuleixing if yaoqiuleixing else 1,
                    huiyuan_id=huiyuan_id,
                    yongjin=None,  # 暂时不需要
                    jieshao=dingdan_jieshao[:2000],  # 限制长度
                    beizhu='',  # 备注为空，等待老板填写
                    nicheng='',  # 🔴 游戏昵称为空，等待老板填写
                    tupian=request.user.avatar if request.user.avatar else '',
                    user1_id=f"Sj{request.user.yonghuid}",  # 商家标识
                    user2_id='',  # 打手标识为空
                )
                logger.info(f"创建订单成功 - 订单ID: {dingdan_id}")

                # 创建商家订单扩展表
                DingdanShangjia.objects.create(
                    dingdan=dingdan,
                    shangjia_id=request.user.yonghuid,
                    sjnicheng=shangjia.nicheng if shangjia.nicheng else f"商家{request.user.yonghuid}",
                    shangjia_pingjia='',
                    cfliyou='',
                    sqzhuangtai=0,
                    bhliyou=''
                )
                logger.info(f"创建商家订单扩展表成功 - 订单ID: {dingdan_id}")

                # 🔴 核心：更新商家统计信息和余额（使用F表达式，避免并发问题）
                UserShangjia.objects.filter(id=shangjia.id).update(
                    fabu=F('fabu') + 1,
                    yue=F('yue') - jiage,  # 🔴 扣除余额
                    jinridingdan=F('jinridingdan') + 1,
                    jinriliushui=F('jinriliushui') + jiage,
                    jinyuedingdan=F('jinyuedingdan') + 1,
                    jinyueliushui=F('jinyueliushui') + jiage,
                )
                logger.info(f"更新商家统计信息成功 - 用户ID: {request.user.yonghuid}, 扣除金额: {jiage}")

                # 🔴🆕【修改】生成链接 - 使用Token而不是订单ID
                link_url = generate_link_url(secure_token)
                logger.info(f"生成链接 - Token: {secure_token}, 链接: {link_url}")

                # 🔴🆕【修改】保存链接到商家链接表 - 添加Token字段
                lianjie_obj = ShangjiaLianjie.objects.create(
                    yonghu_id=request.user.yonghuid,
                    leixing_id=shangpin_type_id,
                    moban_id=moban_id,
                    lianjie=link_url,
                    lianjie_token=secure_token,  # 🔴🆕【新增】保存Token
                    dingdan_id=dingdan_id,
                    is_used=False,  # 未使用
                    youxiao_shijian=timezone.now() + timezone.timedelta(days=1)  # 24小时有效
                )
                logger.info(f"保存链接到数据库成功 - 链接ID: {lianjie_obj.id}, Token: {secure_token}")

                # 更新模板发布数量
                ShangjiaMoban.objects.filter(id=moban_id).update(
                    fabu_shuliang=F('fabu_shuliang') + 1,
                )
                logger.info(f"更新模板发布数量成功 - 模板ID: {moban_id}")

                # 重新获取商家最新数据（因为余额已更新）
                shangjia.refresh_from_db()

                # 记录成功日志
                logger.info(
                    f"商家生成链接成功 - 用户ID: {request.user.yonghuid}, 订单ID: {dingdan_id}, Token: {secure_token}, 链接: {link_url}")

                # 14. 返回成功响应（字段必须与前端对应）
                return Response({
                    'code': 200,
                    'msg': '链接生成成功',
                    'data': {
                        'linkUrl': link_url,  # 🔴 前端需要这个字段
                        'newBalance': str(shangjia.yue),  # 🔴 返回新的余额给前端更新
                        'dingdanId': dingdan_id,
                        'dingdanJieshao': dingdan_jieshao,
                        'jiage': str(jiage),
                        'youxiaoShijian': lianjie_obj.youxiao_shijian.strftime(
                            '%Y-%m-%d %H:%M:%S') if lianjie_obj.youxiao_shijian else '',
                        # 以下字段可根据前端需要添加
                        'jinriliushui': str(shangjia.jinriliushui),
                        'jinyueliushui': str(shangjia.jinyueliushui),
                        'jinridingdan': shangjia.jinridingdan,
                        'jinyuedingdan': shangjia.jinyuedingdan
                    }
                }, status=status.HTTP_200_OK)

        except Exception as e:
            logger.error(f"生成链接失败 - 用户ID: {request.user.yonghuid}, 错误: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '系统错误，生成链接失败',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)'''


# utils/ip_security.py
import logging
import re
from django.utils import timezone
from django.core.cache import cache
from rest_framework.throttling import SimpleRateThrottle

logger = logging.getLogger(__name__)


class IPUtils:
    """IP工具类"""

    @staticmethod
    def get_client_ip(request):
        """
        获取客户端真实IP，处理代理情况
        优先级: X-Real-IP > X-Forwarded-For > remote_addr
        """
        ip = None

        try:
            # 1. 尝试从 X-Real-IP 获取
            x_real_ip = request.META.get('HTTP_X_REAL_IP')
            if x_real_ip:
                ip = x_real_ip.strip()
                logger.info(f"从 X-Real-IP 获取IP: {ip}")
                return ip

            # 2. 尝试从 X-Forwarded-For 获取（处理代理链）
            x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
            if x_forwarded_for:
                # 可能有多个代理IP，第一个是真实IP
                ips = x_forwarded_for.split(',')
                for possible_ip in ips:
                    possible_ip = possible_ip.strip()
                    if possible_ip and possible_ip.lower() != 'unknown':
                        ip = possible_ip
                        logger.info(f"从 X-Forwarded-For 获取IP: {ip}")
                        return ip

            # 3. 使用 remote_addr
            ip = request.META.get('REMOTE_ADDR', '')
            logger.info(f"从 REMOTE_ADDR 获取IP: {ip}")

        except Exception as e:
            logger.error(f"获取IP失败: {str(e)}", exc_info=True)
            ip = '0.0.0.0'

        return ip

    @staticmethod
    def validate_ip(ip):
        """验证IP格式是否合法"""
        if not ip:
            return False

        # IPv4正则
        ipv4_pattern = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'

        # IPv6正则（简化版）
        ipv6_pattern = r'^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$'

        return bool(re.match(ipv4_pattern, ip) or re.match(ipv6_pattern, ip))

    @staticmethod
    def is_private_ip(ip):
        """判断是否为内网IP"""
        if not ip:
            return False

        # 内网IP段
        private_ranges = [
            ('10.0.0.0', '10.255.255.255'),
            ('172.16.0.0', '172.31.255.255'),
            ('192.168.0.0', '192.168.255.255'),
            ('127.0.0.0', '127.255.255.255'),
        ]

        try:
            # 将IP转换为整数比较
            def ip_to_int(ip):
                parts = ip.split('.')
                return (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])

            ip_num = ip_to_int(ip)
            for start, end in private_ranges:
                if ip_to_int(start) <= ip_num <= ip_to_int(end):
                    return True
        except:
            pass

        return False


class OrderLinkThrottle(SimpleRateThrottle):
    """订单链接访问频率限制"""

    scope = 'order_link'
    rate = '10/minute'  # 每分钟最多10次

    def get_cache_key(self, request, view):
        # 基于IP和接口路径进行限制
        ip = IPUtils.get_client_ip(request)
        if not ip or IPUtils.is_private_ip(ip):
            # 内网IP或不合法IP，使用默认
            ip = 'default'

        return f'throttle_{view.__class__.__name__}_{ip}'


# views/peizhi/khhqddlj.py
import logging
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.utils import timezone
from django.db.models import Q
from .models import ShangjiaLianjie
from dingdan.models import Dingdan, DingdanShangjia
from yonghu.models import UserMain, UserShangjia




from peizhi.models import ClubConfig
'''class KehuGetDingdanLianjieView(APIView):
    """
    客户获取订单链接信息接口
    路径：GET /peizhi/khhqddlj?token=xxx
    返回字段包括：订单信息、打手标识、商家标识、俱乐部配置（客服链接、存储桶、GoEasy AppKey等）
    """

    throttle_classes = [OrderLinkThrottle]
    permission_classes = []  # 无需认证，任何人都可访问

    def get(self, request):
        """
        处理GET请求，获取订单链接信息
        """
        # 1. 记录请求开始
        logger.info("=== 客户获取订单链接信息接口开始 ===")

        # 2. 获取客户端真实IP
        client_ip = IPUtils.get_client_ip(request)
        logger.info(f"客户端IP: {client_ip}")

        # 3. 验证IP合法性
        if not IPUtils.validate_ip(client_ip):
            logger.warning(f"IP格式不合法: {client_ip}")
            return Response({
                'code': 400,
                'msg': '网络连接异常',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 4. 获取Token参数
        token = request.query_params.get('token', '')
        if not token:
            logger.warning(f"Token参数缺失 - IP: {client_ip}")
            return Response({
                'code': 400,
                'msg': '缺少链接参数',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        logger.info(f"接收到Token: {token[:8]}...")

        # 5. 查询链接记录
        try:
            lianjie_obj = ShangjiaLianjie.objects.select_related().get(lianjie_token=token)
            logger.info(f"找到链接记录 - 链接ID: {lianjie_obj.id}, 商家ID: {lianjie_obj.yonghu_id}")
        except ShangjiaLianjie.DoesNotExist:
            logger.warning(f"链接不存在 - Token: {token[:8]}..., IP: {client_ip}")
            return Response({
                'code': 404,
                'msg': '链接不存在或已失效',
                'data': None
            }, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            logger.error(f"查询链接异常: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '系统错误，请稍后重试',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 6. 更新访问者IP（无论是否已使用，都记录最新访问IP）
        try:
            lianjie_obj.shiyongzhe_ip = client_ip
            lianjie_obj.save(update_fields=['shiyongzhe_ip'])
            logger.info(f"更新访问者IP成功: {client_ip}")
        except Exception as e:
            logger.warning(f"更新IP失败: {str(e)}")
            # 继续处理，不影响主要逻辑

        # 7. 检查链接有效期
        """now = timezone.now()
        if lianjie_obj.youxiao_shijian and lianjie_obj.youxiao_shijian < now:
            logger.warning(f"链接已过期 - 过期时间: {lianjie_obj.youxiao_shijian}, 当前: {now}")
            return Response({
                'code': 400,
                'msg': '链接已过期',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)"""

        # 8. 准备返回数据
        response_data = {
            'is_used': lianjie_obj.is_used,
            'dingdan_id': lianjie_obj.dingdan_id or '',
            'youxiao_shijian': lianjie_obj.youxiao_shijian.strftime(
                '%Y-%m-%d %H:%M:%S') if lianjie_obj.youxiao_shijian else '',
        }

        # 9. 获取订单信息
        if lianjie_obj.dingdan_id:
            try:
                # 查询订单主表
                dingdan_obj = Dingdan.objects.get(dingdan_id=lianjie_obj.dingdan_id)

                # 获取商家昵称
                shangjia_nicheng = ''
                try:
                    user_main = UserMain.objects.get(yonghuid=lianjie_obj.yonghu_id)
                    shangjia_obj = user_main.shop_profile
                    shangjia_nicheng = shangjia_obj.nicheng
                except (UserMain.DoesNotExist, UserShangjia.DoesNotExist):
                    logger.warning(f"商家信息不存在 - 用户ID: {lianjie_obj.yonghu_id}")

                # ========== 新增：确定俱乐部配置 ==========
                # 默认头像（相对路径）
                default_avatar = 'a_long/morentouxiang.jpg'
                # 初始化配置字段默认值
                kefu_link = ''
                oss_url = ''
                goeasy_appkey = ''

                # 判断是否为跨平台订单
                is_cross = dingdan_obj.is_cross if hasattr(dingdan_obj, 'is_cross') else 0

                # 默认使用我方配置
                use_self_config = True
                target_club_id = None

                if is_cross == 1:
                    # 跨平台订单
                    # 优先使用 partner_club_id
                    if dingdan_obj.partner_club_id:
                        target_club_id = dingdan_obj.partner_club_id
                        use_self_config = False
                        logger.info(f"跨平台订单，使用对方俱乐部ID: {target_club_id}")
                    # 其次，如果有对方订单ID但没有俱乐部ID，使用默认俱乐部ID 000001
                    elif dingdan_obj.partner_order_id:
                        target_club_id = '000001'
                        use_self_config = False
                        logger.info(f"跨平台订单有对方订单ID但无俱乐部ID，使用默认俱乐部ID: {target_club_id}")
                    else:
                        # 无任何对方信息，降级为我方配置
                        use_self_config = True
                        logger.warning(f"订单标记跨平台但无对方俱乐部ID和对方订单ID，使用我方配置")
                else:
                    # 非跨平台订单
                    use_self_config = True

                # 获取俱乐部配置
                club_config = None
                if use_self_config:
                    # 查询我方俱乐部配置 (is_self=1)
                    try:
                        club_config = ClubConfig.objects.filter(is_self=1).first()
                        if club_config:
                            logger.info(f"使用我方俱乐部配置，club_id={club_config.club_id}")
                        else:
                            logger.warning("未找到我方俱乐部配置（is_self=1）")
                    except Exception as e:
                        logger.error(f"查询我方俱乐部配置异常: {str(e)}", exc_info=True)
                else:
                    # 使用确定的俱乐部ID查询对方配置
                    if target_club_id:
                        try:
                            club_config = ClubConfig.objects.filter(club_id=target_club_id).first()
                            if club_config:
                                logger.info(f"使用对方俱乐部配置，club_id={target_club_id}")
                            else:
                                logger.warning(f"未找到对方俱乐部配置，club_id={target_club_id}")
                        except Exception as e:
                            logger.error(f"查询对方俱乐部配置异常: {str(e)}", exc_info=True)

                if club_config:
                    kefu_link = club_config.customer_service_api_url or ''
                    oss_url = club_config.storage_bucket_domain or ''
                    goeasy_appkey = club_config.chat_api_key or ''

                # ========== 构建订单信息 ==========
                dingdan_info = {
                    'jieshao': dingdan_obj.jieshao or '',
                    'jiage': str(dingdan_obj.jine) if dingdan_obj.jine is not None else '0.00',  # 金额字段名是 jine
                    'shangjia_mingcheng': shangjia_nicheng,
                    'zhuangtai': dingdan_obj.zhuangtai,
                    'is_cross': is_cross,
                    # 新增配置字段
                    'kefu_link': kefu_link,
                    'oss_url': oss_url,
                    'default_avatar': default_avatar,
                    'goeasy_appkey': goeasy_appkey,
                }

                # 如果已使用，返回已填写的信息
                if lianjie_obj.is_used:
                    dingdan_info.update({
                        'youxi_nicheng': dingdan_obj.nicheng or '',
                        'zhiding_dashou': dingdan_obj.zhiding_id or '',
                        'beizhu': dingdan_obj.beizhu or '',
                    })

                # 获取聊天相关标识
                # 9.1 获取接单打手ID
                jiedan_dashou_id = dingdan_obj.jiedan_dashou_id
                dashou_biaoshi = f'Ds{jiedan_dashou_id}' if jiedan_dashou_id else ''

                # 9.2 获取商家标识
                shangjia_biaoshi = ''
                try:
                    shangjia_kuozhan = DingdanShangjia.objects.select_related('dingdan').filter(
                        dingdan__dingdan_id=lianjie_obj.dingdan_id
                    ).first()

                    if shangjia_kuozhan and shangjia_kuozhan.shangjia_id:
                        shangjia_biaoshi = f'Sj{shangjia_kuozhan.shangjia_id}'
                        logger.info(f"生成商家标识: {shangjia_biaoshi}")
                    else:
                        logger.info(f"订单 {lianjie_obj.dingdan_id} 没有商家订单扩展信息")
                except Exception as e:
                    logger.error(f"查询商家订单扩展异常: {str(e)}", exc_info=True)

                # 添加聊天标识
                dingdan_info.update({
                    'dashou_biaoshi': dashou_biaoshi,
                    'shangjia_biaoshi': shangjia_biaoshi,
                    'you_dashou': bool(dashou_biaoshi),
                })

                response_data.update(dingdan_info)

            except Dingdan.DoesNotExist:
                logger.error(f"订单不存在 - 订单ID: {lianjie_obj.dingdan_id}")
                response_data.update({
                    'jieshao': '',
                    'jiage': '0.00',
                    'shangjia_mingcheng': '',
                    'zhuangtai': None,
                    'dashou_biaoshi': '',
                    'shangjia_biaoshi': '',
                    'you_dashou': False,
                    'is_cross': 0,
                    'kefu_link': '',
                    'oss_url': '',
                    'default_avatar': 'a_long/morentouxiang.jpg',
                    'goeasy_appkey': '',
                })
            except Exception as e:
                logger.error(f"获取订单信息异常: {str(e)}", exc_info=True)
                response_data.update({
                    'jieshao': '',
                    'jiage': '0.00',
                    'shangjia_mingcheng': '',
                    'zhuangtai': None,
                    'dashou_biaoshi': '',
                    'shangjia_biaoshi': '',
                    'you_dashou': False,
                    'is_cross': 0,
                    'kefu_link': '',
                    'oss_url': '',
                    'default_avatar': 'a_long/morentouxiang.jpg',
                    'goeasy_appkey': '',
                })
        else:
            # 如果订单ID为空
            logger.warning(f"链接记录中订单ID为空 - 链接ID: {lianjie_obj.id}")
            response_data.update({
                'jieshao': '',
                'jiage': '0.00',
                'shangjia_mingcheng': '',
                'zhuangtai': None,
                'dashou_biaoshi': '',
                'shangjia_biaoshi': '',
                'you_dashou': False,
                'is_cross': 0,
                'kefu_link': '',
                'oss_url': '',
                'default_avatar': 'a_long/morentouxiang.jpg',
                'goeasy_appkey': '',
            })

        # 10. 判断订单状态是否允许修改
        if not lianjie_obj.is_used and response_data.get('zhuangtai') == 13:
            response_data['allow_modify'] = True
        else:
            response_data['allow_modify'] = False

        # 11. 记录成功日志并返回
        logger.info(
            f"返回订单信息成功 - Token: {token[:8]}..., IP: {client_ip}, "
            f"是否允许修改: {response_data['allow_modify']}, "
            f"是否有打手: {response_data.get('you_dashou', False)}"
        )

        return Response({
            'code': 200,
            'msg': '成功',
            'data': response_data
        }, status=status.HTTP_200_OK)'''



class KehuGetDingdanLianjieView(APIView):
    """
    客户获取订单链接信息接口
    路径：GET /peizhi/khhqddlj?token=xxx
    返回字段包括：订单信息、打手标识、商家标识、俱乐部配置（客服链接、存储桶、GoEasy AppKey等）
    """

    throttle_classes = [OrderLinkThrottle]
    permission_classes = []  # 无需认证，任何人都可访问

    def get(self, request):
        """
        处理GET请求，获取订单链接信息
        """
        # 1. 记录请求开始
        logger.info("=== 客户获取订单链接信息接口开始 ===")

        # 2. 获取客户端真实IP
        client_ip = IPUtils.get_client_ip(request)
        logger.info(f"客户端IP: {client_ip}")

        # 3. 验证IP合法性
        if not IPUtils.validate_ip(client_ip):
            logger.warning(f"IP格式不合法: {client_ip}")
            return Response({
                'code': 400,
                'msg': '网络连接异常',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 4. 获取Token参数
        token = request.query_params.get('token', '')
        if not token:
            logger.warning(f"Token参数缺失 - IP: {client_ip}")
            return Response({
                'code': 400,
                'msg': '缺少链接参数',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        logger.info(f"接收到Token: {token[:8]}...")

        # 5. 查询链接记录
        try:
            lianjie_obj = ShangjiaLianjie.objects.select_related().get(lianjie_token=token)
            logger.info(f"找到链接记录 - 链接ID: {lianjie_obj.id}, 商家ID: {lianjie_obj.yonghu_id}")
        except ShangjiaLianjie.DoesNotExist:
            logger.warning(f"链接不存在 - Token: {token[:8]}..., IP: {client_ip}")
            return Response({
                'code': 404,
                'msg': '链接不存在或已失效',
                'data': None
            }, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            logger.error(f"查询链接异常: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '系统错误，请稍后重试',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 6. 更新访问者IP（无论是否已使用，都记录最新访问IP）
        try:
            lianjie_obj.shiyongzhe_ip = client_ip
            lianjie_obj.save(update_fields=['shiyongzhe_ip'])
            logger.info(f"更新访问者IP成功: {client_ip}")
        except Exception as e:
            logger.warning(f"更新IP失败: {str(e)}")
            # 继续处理，不影响主要逻辑

        # 7. 检查链接有效期
        '''now = timezone.now()
        if lianjie_obj.youxiao_shijian and lianjie_obj.youxiao_shijian < now:
            logger.warning(f"链接已过期 - 过期时间: {lianjie_obj.youxiao_shijian}, 当前: {now}")
            return Response({
                'code': 400,
                'msg': '链接已过期',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)'''

        # 8. 准备返回数据
        response_data = {
            'is_used': lianjie_obj.is_used,
            'dingdan_id': lianjie_obj.dingdan_id or '',
            'youxiao_shijian': lianjie_obj.youxiao_shijian.strftime(
                '%Y-%m-%d %H:%M:%S') if lianjie_obj.youxiao_shijian else '',
        }

        # 9. 获取订单信息
        if lianjie_obj.dingdan_id:
            try:
                # 查询订单主表
                dingdan_obj = Dingdan.objects.get(dingdan_id=lianjie_obj.dingdan_id)

                # 获取商家昵称
                shangjia_nicheng = ''
                try:
                    user_main = UserMain.objects.get(yonghuid=lianjie_obj.yonghu_id)
                    shangjia_obj = user_main.shop_profile
                    shangjia_nicheng = shangjia_obj.nicheng
                except (UserMain.DoesNotExist, UserShangjia.DoesNotExist):
                    logger.warning(f"商家信息不存在 - 用户ID: {lianjie_obj.yonghu_id}")

                # ========== 新增：确定俱乐部配置 ==========
                # 默认头像（相对路径）
                default_avatar = 'a_long/morentouxiang.jpg'
                # 初始化配置字段默认值
                kefu_link = ''
                oss_url = ''
                goeasy_appkey = ''

                # 判断是否为跨平台订单（保留，但不影响配置选择）
                is_cross = dingdan_obj.is_cross if hasattr(dingdan_obj, 'is_cross') else 0

                # 始终使用我方俱乐部配置
                try:
                    club_config = ClubConfig.objects.filter(is_self=1).first()
                    if club_config:
                        logger.info(f"使用我方俱乐部配置，club_id={club_config.club_id}")
                        kefu_link = club_config.customer_service_api_url or ''
                        oss_url = club_config.storage_bucket_domain or ''
                        goeasy_appkey = club_config.chat_api_key or ''
                    else:
                        logger.warning("未找到我方俱乐部配置（is_self=1）")
                except Exception as e:
                    logger.error(f"查询我方俱乐部配置异常: {str(e)}", exc_info=True)

                # ========== 构建订单信息 ==========
                dingdan_info = {
                    'jieshao': dingdan_obj.jieshao or '',
                    'jiage': str(dingdan_obj.jine) if dingdan_obj.jine is not None else '0.00',  # 金额字段名是 jine
                    'shangjia_mingcheng': shangjia_nicheng,
                    'zhuangtai': dingdan_obj.zhuangtai,
                    'is_cross': is_cross,
                    # 新增配置字段
                    'kefu_link': kefu_link,
                    'oss_url': oss_url,
                    'default_avatar': default_avatar,
                    'goeasy_appkey': goeasy_appkey,
                    # 🆕 前端用于连接GoEasy的自身用户ID（固定B前缀+商家ID）
                    'self_user_id': f"B{lianjie_obj.yonghu_id}" if lianjie_obj.yonghu_id else '',
                }

                # 如果已使用，返回已填写的信息
                if lianjie_obj.is_used:
                    dingdan_info.update({
                        'youxi_nicheng': dingdan_obj.nicheng or '',
                        'zhiding_dashou': dingdan_obj.zhiding_id or '',
                        'beizhu': dingdan_obj.beizhu or '',
                    })

                # 获取聊天相关标识
                # 9.1 获取接单打手ID
                jiedan_dashou_id = dingdan_obj.jiedan_dashou_id
                dashou_biaoshi = f'Ds{jiedan_dashou_id}' if jiedan_dashou_id else ''

                # 9.2 获取商家标识
                shangjia_biaoshi = ''
                try:
                    shangjia_kuozhan = DingdanShangjia.objects.select_related('dingdan').filter(
                        dingdan__dingdan_id=lianjie_obj.dingdan_id
                    ).first()

                    if shangjia_kuozhan and shangjia_kuozhan.shangjia_id:
                        shangjia_biaoshi = f'Sj{shangjia_kuozhan.shangjia_id}'
                        logger.info(f"生成商家标识: {shangjia_biaoshi}")
                    else:
                        logger.info(f"订单 {lianjie_obj.dingdan_id} 没有商家订单扩展信息")
                except Exception as e:
                    logger.error(f"查询商家订单扩展异常: {str(e)}", exc_info=True)

                # 添加聊天标识
                dingdan_info.update({
                    'dashou_biaoshi': dashou_biaoshi,
                    'shangjia_biaoshi': shangjia_biaoshi,
                    'you_dashou': bool(dashou_biaoshi),
                })

                response_data.update(dingdan_info)

            except Dingdan.DoesNotExist:
                logger.error(f"订单不存在 - 订单ID: {lianjie_obj.dingdan_id}")
                response_data.update({
                    'jieshao': '',
                    'jiage': '0.00',
                    'shangjia_mingcheng': '',
                    'zhuangtai': None,
                    'dashou_biaoshi': '',
                    'shangjia_biaoshi': '',
                    'you_dashou': False,
                    'is_cross': 0,
                    'kefu_link': '',
                    'oss_url': '',
                    'default_avatar': 'a_long/morentouxiang.jpg',
                    'goeasy_appkey': '',
                    'self_user_id': '',
                })
            except Exception as e:
                logger.error(f"获取订单信息异常: {str(e)}", exc_info=True)
                response_data.update({
                    'jieshao': '',
                    'jiage': '0.00',
                    'shangjia_mingcheng': '',
                    'zhuangtai': None,
                    'dashou_biaoshi': '',
                    'shangjia_biaoshi': '',
                    'you_dashou': False,
                    'is_cross': 0,
                    'kefu_link': '',
                    'oss_url': '',
                    'default_avatar': 'a_long/morentouxiang.jpg',
                    'goeasy_appkey': '',
                    'self_user_id': '',
                })
        else:
            # 如果订单ID为空
            logger.warning(f"链接记录中订单ID为空 - 链接ID: {lianjie_obj.id}")
            response_data.update({
                'jieshao': '',
                'jiage': '0.00',
                'shangjia_mingcheng': '',
                'zhuangtai': None,
                'dashou_biaoshi': '',
                'shangjia_biaoshi': '',
                'you_dashou': False,
                'is_cross': 0,
                'kefu_link': '',
                'oss_url': '',
                'default_avatar': 'a_long/morentouxiang.jpg',
                'goeasy_appkey': '',
                'self_user_id': '',
            })

        # 10. 判断订单状态是否允许修改
        if not lianjie_obj.is_used and response_data.get('zhuangtai') == 13:
            response_data['allow_modify'] = True
        else:
            response_data['allow_modify'] = False

        # 11. 记录成功日志并返回
        logger.info(
            f"返回订单信息成功 - Token: {token[:8]}..., IP: {client_ip}, "
            f"是否允许修改: {response_data['allow_modify']}, "
            f"是否有打手: {response_data.get('you_dashou', False)}"
        )

        return Response({
            'code': 200,
            'msg': '成功',
            'data': response_data
        }, status=status.HTTP_200_OK)





import threading
from utils.weixin_broadcast import WeixinBroadcastSender
from dingdan.utils import sync_order_to_partners
logger = logging.getLogger(__name__)


class KehuTianxieDingdanView(APIView):
    """
    客户填写订单信息接口
    路径：POST /peizhi/kehutx
    """

    # 添加频率限制（比查询接口更严格）
    throttle_classes = [OrderLinkThrottle]
    permission_classes = []  # 无需认证

    def post(self, request):
        """
        处理POST请求，填写订单信息
        """
        # 1. 记录请求开始
        logger.info("=== 客户填写订单信息接口开始 ===")

        # 2. 获取客户端真实IP
        client_ip = IPUtils.get_client_ip(request)
        logger.info(f"客户端IP: {client_ip}")

        # 3. 验证IP合法性
        if not IPUtils.validate_ip(client_ip):
            logger.warning(f"IP格式不合法: {client_ip}")
            return Response({
                'code': 400,
                'msg': '网络连接异常',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 4. 获取请求参数
        data = request.data
        token = data.get('token', '')
        youxi_nicheng = data.get('youxi_nicheng', '').strip()
        zhiding_dashou = data.get('zhiding_dashou', '').strip()  # 可选
        beizhu = data.get('beizhu', '').strip()  # 可选

        # 4.0 临时关闭指定功能（如需开启，请删除或注释掉下面两行）
        if zhiding_dashou:
            return Response({'code': 8, 'msg': '指定功能尚未开启，请不要填写指定打手ID', 'data': None})
        if beizhu:
            return Response({'code': 8, 'msg': '备注功能尚未开启，请不要填写备注', 'data': None})

        logger.info(
            f"请求参数 - Token: {token[:8]}..., 昵称: {youxi_nicheng[:10]}..., 指定打手: {zhiding_dashou}, 备注长度: {len(beizhu)}")

        # 5. 验证必填参数
        if not token:
            logger.warning(f"Token参数缺失 - IP: {client_ip}")
            return Response({
                'code': 400,
                'msg': '缺少链接参数',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        if not youxi_nicheng:
            logger.warning(f"游戏昵称为空 - IP: {client_ip}")
            return Response({
                'code': 400,
                'msg': '游戏昵称不能为空',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 6. 验证游戏昵称格式
        if len(youxi_nicheng) > 50:
            logger.warning(f"游戏昵称过长 - IP: {client_ip}, 长度: {len(youxi_nicheng)}")
            return Response({
                'code': 400,
                'msg': '游戏昵称不能超过50个字符',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 过滤危险字符
        danger_chars = ['<', '>', "'", '"', '&', ';', '|']
        for char in danger_chars:
            if char in youxi_nicheng:
                logger.warning(f"游戏昵称包含危险字符 - IP: {client_ip}, 字符: {char}")
                return Response({
                    'code': 400,
                    'msg': '昵称包含非法字符',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

        # 7. 验证指定打手ID格式（如果提供）
        if zhiding_dashou and len(zhiding_dashou) > 32:
            logger.warning(f"指定打手ID过长 - IP: {client_ip}, 长度: {len(zhiding_dashou)}")
            return Response({
                'code': 400,
                'msg': '指定打手ID不能超过32个字符',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 8. 验证备注长度
        if len(beizhu) > 500:
            logger.warning(f"备注过长 - IP: {client_ip}, 长度: {len(beizhu)}")
            return Response({
                'code': 400,
                'msg': '备注不能超过500个字符',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 9. 查询链接记录
        try:
            lianjie_obj = ShangjiaLianjie.objects.get(lianjie_token=token)
            logger.info(
                f"找到链接记录 - 链接ID: {lianjie_obj.id}, 商家ID: {lianjie_obj.yonghu_id}, 是否已使用: {lianjie_obj.is_used}")
        except ShangjiaLianjie.DoesNotExist:
            logger.warning(f"链接不存在 - Token: {token[:8]}..., IP: {client_ip}")
            return Response({
                'code': 404,
                'msg': '链接不存在或已失效',
                'data': None
            }, status=status.HTTP_404_NOT_FOUND)

        # 10. 检查链接有效期
        now = timezone.now()
        if lianjie_obj.youxiao_shijian and lianjie_obj.youxiao_shijian < now:
            logger.warning(f"链接已过期 - 过期时间: {lianjie_obj.youxiao_shijian}")
            return Response({
                'code': 400,
                'msg': '链接已过期',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 11. 检查链接是否已使用
        if lianjie_obj.is_used:
            logger.warning(f"链接已使用 - 链接ID: {lianjie_obj.id}, IP: {client_ip}")
            return Response({
                'code': 400,
                'msg': '链接已被使用，无法重复提交',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # 12. 验证指定打手（如果需要）
        zhuangtai_xin = 1  # 默认普通订单状态
        if zhiding_dashou:
            # 验证打手是否存在且有效
            try:
                # 先查询用户主表
                dashou_user = UserMain.objects.get(yonghuid=zhiding_dashou)
                logger.info(f"找到打手用户 - 用户ID: {zhiding_dashou}")

                # 通过反向关系获取打手扩展信息
                try:
                    dashou_profile = dashou_user.dashou_profile

                    # 检查打手状态（zhuangtai）
                    if dashou_profile.zhuangtai != 1:
                        logger.warning(f"打手状态异常 - 打手ID: {zhiding_dashou}, 状态: {dashou_profile.zhuangtai}")
                        return Response({
                            'code': 400,
                            'msg': '指定打手当前无法接单',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                    # 检查打手账号状态（zhanghaozhuangtai）
                    if dashou_profile.zhanghaozhuangtai != 1:
                        logger.warning(
                            f"打手账号状态异常 - 打手ID: {zhiding_dashou}, 账号状态: {dashou_profile.zhanghaozhuangtai}")
                        return Response({
                            'code': 400,
                            'msg': '指定打手账号异常',
                            'data': None
                        }, status=status.HTTP_400_BAD_REQUEST)

                    # 验证通过，设置为指定订单状态
                    zhuangtai_xin = 7  # 指定打手订单状态
                    logger.info(f"指定打手验证通过 - 打手ID: {zhiding_dashou}, 昵称: {dashou_profile.nicheng}")

                except UserDashou.DoesNotExist:
                    logger.warning(f"打手扩展信息不存在 - 用户ID: {zhiding_dashou}")
                    return Response({
                        'code': 400,
                        'msg': '指定打手信息不完整',
                        'data': None
                    }, status=status.HTTP_400_BAD_REQUEST)

            except UserMain.DoesNotExist:
                logger.warning(f"指定打手用户不存在 - 用户ID: {zhiding_dashou}")
                return Response({
                    'code': 400,
                    'msg': '指定打手不存在',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

        # 13. 使用事务确保数据一致性
        try:
            with transaction.atomic():
                # 更新链接记录
                lianjie_obj.shiyongzhe_ip = client_ip
                lianjie_obj.is_used = True
                lianjie_obj.save(update_fields=['shiyongzhe_ip', 'is_used'])
                logger.info(f"更新链接记录成功 - 链接ID: {lianjie_obj.id}, 设置为已使用")

                # 查询订单
                if not lianjie_obj.dingdan_id:
                    logger.error(f"链接记录中订单ID为空 - 链接ID: {lianjie_obj.id}")
                    raise ValueError("订单ID为空")

                dingdan_obj = Dingdan.objects.get(dingdan_id=lianjie_obj.dingdan_id)

                # 检查订单状态是否为13（灰色状态）
                if dingdan_obj.zhuangtai != 13:
                    logger.warning(
                        f"订单状态不允许修改 - 订单ID: {dingdan_obj.dingdan_id}, 当前状态: {dingdan_obj.zhuangtai}")
                    # 回滚事务
                    transaction.set_rollback(True)
                    return Response({
                        'code': 400,
                        'msg': '订单状态异常，无法修改',
                        'data': None
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 更新订单信息
                dingdan_obj.nicheng = youxi_nicheng  # 游戏昵称
                dingdan_obj.beizhu = beizhu if beizhu else ''  # 备注
                dingdan_obj.zhuangtai = zhuangtai_xin  # 新状态：1或7

                # 如果是指定打手，设置zhiding_id
                if zhiding_dashou and zhuangtai_xin == 7:
                    dingdan_obj.zhiding_id = zhiding_dashou

                dingdan_obj.save()
                logger.info(
                    f"更新订单成功 - 订单ID: {dingdan_obj.dingdan_id}, 新状态: {zhuangtai_xin}, 指定打手: {zhiding_dashou or '无'}")

                # 🔴【添加】在订单更新成功后，异步发送微信服务号通知（原有）
                # 🟢【7. 新增】支付成功后的广播通知（异步，不阻塞主流程）
                from dingdan.tongzhi_tasks import dingdan_guangbo

                # 构建通知数据（你的 _get_game_type_name 方法保留在视图里直接用）
                order_info = {
                    'dingdan_id': dingdan_obj.dingdan_id,
                    'game_type': self._get_game_type_name(dingdan_obj.leixing_id),
                    'amount': str(dingdan_obj.jine),
                    'order_desc': (dingdan_obj.jieshao or '')[:50],
                }
                dingdan_guangbo.delay(order_info)  # 瞬间返回，不阻塞请求

                # 获取商家信息用于返回
                try:
                    user_main = UserMain.objects.get(yonghuid=lianjie_obj.yonghu_id)
                    shangjia_obj = user_main.shop_profile
                    shangjia_mingcheng = shangjia_obj.nicheng
                except:
                    shangjia_mingcheng = f"商家{lianjie_obj.yonghu_id}"
                    logger.warning(f"获取商家昵称失败，使用默认 - 用户ID: {lianjie_obj.yonghu_id}")

                # ========== 🆕 新增：判断是否需要跨平台同步 ==========
                # 获取商家ID（从链接记录）
                shangjia_user_id = lianjie_obj.yonghu_id
                # 跨平台条件：商家ID在白名单、订单为普通状态（zhuangtai=1）、要求类型为1、会员ID等于配置值
                sync_order_to_partners(dingdan_obj.dingdan_id)
                '''
                if (shangjia_user_id in settings.CROSS_PLATFORM_WHITELIST and
                    dingdan_obj.zhuangtai == 1 and  # 非指定单
                    dingdan_obj.yaoqiuleixing == 1 and 
                    dingdan_obj.huiyuan_id in ['VC7CTU', 'VUXP8D', 'VEX5NJ']
                    ):

                    # 异步发送通知（使用独立模块）
                    import threading  # 若文件顶部已导入可省略，此处保留确保独立运行
                    from dingdan.cross_platform_tasks import send_partner_sync_order
                    thread = threading.Thread(
                        target=send_partner_sync_order,
                        args=(
                            dingdan_obj.dingdan_id,
                            str(dingdan_obj.jine),
                            str(dingdan_obj.dashou_fencheng),
                            dingdan_obj.jieshao,
                            dingdan_obj.beizhu,
                            dingdan_obj.create_time.isoformat(),
                            dingdan_obj.nicheng

                        )
                    )
                    thread.daemon = True
                    thread.start()
                    logger.info(f"跨平台客户填写订单异步任务已启动: {dingdan_obj.dingdan_id}")'''
                # ========== 🆕 新增结束 ==========

                # 14. 返回成功响应
                response_data = {
                    # 'dingdan_id': dingdan_obj.dingdan_id,
                    # 'create_time': dingdan_obj.create_time.strftime('%Y-%m-%d %H:%M:%S'),
                    # 'shangpin_mingcheng': dingdan_obj.jieshao[:50] if dingdan_obj.jieshao else '',  # 截取前50字符
                    # 'jiage': str(dingdan_obj.jine) if dingdan_obj.jine else '0.00',
                    'shangjia_mingcheng': shangjia_mingcheng,
                    'youxi_nicheng': youxi_nicheng,
                    'zhuangtai': zhuangtai_xin,
                    'zhuangtai_text': '普通订单' if zhuangtai_xin == 1 else '指定打手订单'
                }

                logger.info(f"订单提交成功 - 订单ID: {dingdan_obj.dingdan_id}, IP: {client_ip}")

                # ✅ 新方法（行为驱动，更安全）
                from houtai.utils import update_shangjia_daily
                update_shangjia_daily(
                    yonghuid=lianjie_obj.yonghu_id,
                    amount=Decimal(str(dingdan_obj.jine)),
                    action=1  # 1 = 派发
                )

                return Response({
                    'code': 200,
                    'msg': '提交成功',
                    'data': response_data
                }, status=status.HTTP_200_OK)

        except Dingdan.DoesNotExist:
            logger.error(f"订单不存在 - 订单ID: {lianjie_obj.dingdan_id}")
            return Response({
                'code': 500,
                'msg': '订单数据异常',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        except Exception as e:
            logger.error(f"处理订单异常: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '系统错误，请稍后重试',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def _get_game_type_name(self, leixing_id):
        try:
            from shangpin.models import ShangpinLeixing
            gt = ShangpinLeixing.objects.filter(id=leixing_id).first()
            return gt.jieshao if gt else '游戏订单'
        except Exception as e:
            logger.error(f"获取游戏类型名称异常: {e}")
            return '游戏订单'

    def send_order_notification_async(self, dingdan, youxi_nicheng, zhiding_dashou, zhuangtai):
        """异步发送订单通知（客户填写订单后）"""
        try:
            # 避免阻塞主线程，使用线程异步执行
            def notification_task():
                try:
                    # 获取游戏类型名称
                    try:
                        leixing_obj = ShangpinLeixing.objects.get(id=dingdan.leixing_id)
                        game_type = leixing_obj.jieshao
                    except:
                        game_type = '未知游戏'

                    # 构建订单信息
                    order_info = {
                        'dingdan_id': dingdan.dingdan_id,
                        'game_type': game_type,
                        'amount': str(dingdan.jine),
                        'order_desc': dingdan.jieshao[:50] if dingdan.jieshao else '新订单',
                        'youxi_nicheng': youxi_nicheng,
                        'order_type': '指定订单' if zhuangtai == 7 else '普通订单',
                        'zhiding_dashou': zhiding_dashou or ''
                    }

                    # 调用微信广播发送器
                    sender = WeixinBroadcastSender()
                    result = sender.broadcast_order(order_info)

                    if result['success']:
                        logger.info(f"【客户填写订单】微信通知发送成功: {result['msg']}")
                    else:
                        logger.warning(f"【客户填写订单】微信通知发送失败: {result['msg']}")

                except Exception as e:
                    logger.error(f"【客户填写订单】发送通知任务异常: {str(e)}")

            # 启动异步线程
            thread = threading.Thread(target=notification_task)
            thread.daemon = True
            thread.start()

        except Exception as e:
            logger.error(f"【客户填写订单】启动通知线程异常: {str(e)}")






from datetime import timedelta



logger = logging.getLogger(__name__)




class AdminSelfBatchGenerateLinkView(APIView):
    """
    管理员批量生成商家发单链接接口
    POST /dingdan/admin/batch-generate-link
    仅允许白名单中的管理员调用，为指定商家（通过 phone+user_type=lianjie 确定）批量生成链接。
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # ==================== 管理员身份验证（与之前一致）====================
        if not request.user.is_authenticated:
            return Response({
                'code': 401,
                'message': '未认证',
                'data': None
            }, status=status.HTTP_401_UNAUTHORIZED)

        if request.user.user_type != 'admin':
            return Response({
                'code': 403,
                'message': '权限不足',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        admin_profile = getattr(request.user, 'admin_profile', None)
        if not admin_profile:
            return Response({
                'code': 403,
                'message': '管理员信息不完整',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        zhanghao = request.data.get('zhanghao')
        if not zhanghao or request.user.phone != zhanghao:
            return Response({
                'code': 401,
                'message': '账号验证失败',
                'data': None
            }, status=status.HTTP_401_UNAUTHORIZED)

        allowed_phones = getattr(settings, 'ADMIN_BATCH_LINK_ALLOWED_PHONES', [])
        if not allowed_phones:
            logger.error("白名单配置为空，禁止所有管理员操作")
            return Response({
                'code': 500,
                'message': '系统未配置可操作管理员，请联系开发',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if request.user.phone not in allowed_phones:
            logger.warning(f"管理员 {request.user.phone} 不在白名单中")
            return Response({
                'code': 403,
                'message': '您没有权限执行此操作',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)
        # ==================== 管理员验证结束 ====================

        # ========== 获取业务参数 ==========
        data = request.data
        shangpin_type_id = data.get('shangpinTypeId')
        moban_id = data.get('mobanId')
        count = data.get('count')

        if not all([shangpin_type_id, moban_id]):
            return Response({
                'code': 400,
                'msg': '缺少必填参数：商品类型ID和模板ID',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        try:
            shangpin_type_id = int(shangpin_type_id)
            moban_id = int(moban_id)
        except ValueError:
            return Response({
                'code': 400,
                'msg': '参数格式错误，必须是数字',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        default_count = getattr(settings, 'BATCH_LINK_DEFAULT_COUNT', 100)
        max_count = getattr(settings, 'BATCH_LINK_MAX_COUNT', 200)
        if count is None:
            gen_count = default_count
        else:
            try:
                gen_count = int(count)
                if gen_count <= 0:
                    return Response({
                        'code': 400,
                        'msg': '生成数量必须为正整数',
                        'data': None
                    }, status=status.HTTP_400_BAD_REQUEST)
                if gen_count > max_count:
                    return Response({
                        'code': 400,
                        'msg': f'生成数量不能超过{max_count}',
                        'data': None
                    }, status=status.HTTP_400_BAD_REQUEST)
            except ValueError:
                return Response({
                    'code': 400,
                    'msg': '生成数量格式错误',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

        # ========== 🔴 新增：通过 phone 和 user_type 查询真正的用户ID ==========
        phone = request.user.phone  # 当前管理员手机号
        target_user_type = 'lianjie'  # 目标用户类型，根据业务修改

        try:
            target_user = UserMain.objects.get(phone=phone, user_type=target_user_type)
            real_yonghuid = target_user.yonghuid
        except UserMain.DoesNotExist:
            logger.error(f"未找到符合条件的用户: phone={phone}, user_type={target_user_type}")
            return Response({
                'code': 404,
                'msg': '未找到符合条件的商家用户',
                'data': None
            }, status=status.HTTP_404_NOT_FOUND)
        # ========== 新增结束 ==========

        # ========== 使用 real_yonghuid 获取商家扩展信息 ==========
        try:
            # 注意：这里使用 real_yonghuid 查找对应的商家扩展表
            shangjia = UserShangjia.objects.get(user__yonghuid=real_yonghuid)
        except UserShangjia.DoesNotExist:
            logger.error(f"商家扩展信息不存在 - 用户ID: {real_yonghuid}")
            return Response({
                'code': 403,
                'msg': '该用户不是商家或商家信息未完善',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        if shangjia.zhuangtai != 1:
            logger.warning(f"商家状态异常 - 用户ID: {real_yonghuid}, 状态: {shangjia.zhuangtai}")
            return Response({
                'code': 403,
                'msg': '商家账号状态异常，请联系管理员',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # ========== 查询商品类型与模板 ==========
        try:
            shangpin_leixing = ShangpinLeixing.objects.get(id=shangpin_type_id)
        except ShangpinLeixing.DoesNotExist:
            return Response({
                'code': 400,
                'msg': '商品类型不存在',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        yaoqiuleixing = shangpin_leixing.yaoqiuleixing
        huiyuan_id = shangpin_leixing.huiyuan_id or ''

        # 模板必须属于查询出的真实用户
        try:
            moban = ShangjiaMoban.objects.get(
                yonghu_id=real_yonghuid,
                leixing_id=shangpin_type_id,
                id=moban_id
            )
        except ShangjiaMoban.DoesNotExist:
            return Response({
                'code': 400,
                'msg': '订单模板不存在',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        jiage = moban.jiage
        dingdan_jieshao = moban.moban_jieshao

        # ========== 金额与余额 ==========
        try:
            price_decimal = Decimal(str(jiage))
            total_price = price_decimal * gen_count
        except Exception as e:
            logger.error(f"金额计算错误: {e}")
            return Response({
                'code': 500,
                'msg': '金额计算错误',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if total_price > shangjia.yue:
            return Response({
                'code': 400,
                'msg': f'余额不足，当前余额{shangjia.yue}元，需要{total_price}元',
                'data': None
            }, status=status.HTTP_400_BAD_REQUEST)

        # ========== 利率 ==========
        try:
            lilu_obj = Lilubiao.objects.get(fadanpingtai='3')
            lilu = float(lilu_obj.lilu)
            if lilu <= 0:
                lilu = 1
        except Lilubiao.DoesNotExist:
            lilu = 1

        dashou_fencheng = (price_decimal * Decimal(str(lilu))).quantize(Decimal('0.01'))

        # ========== 事务内批量生成 ==========
        try:
            with transaction.atomic():
                # 锁定商家行
                shangjia_locked = UserShangjia.objects.select_for_update().get(id=shangjia.id)

                if total_price > shangjia_locked.yue:
                    return Response({
                        'code': 400,
                        'msg': '余额不足，请刷新后重试',
                        'data': None
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 生成唯一订单ID和Token
                dingdan_ids = set()
                tokens = set()
                while len(dingdan_ids) < gen_count:
                    dingdan_ids.add(self._generate_dingdan_id())
                while len(tokens) < gen_count:
                    tokens.add(self._generate_secure_token())

                dingdan_id_list = list(dingdan_ids)
                token_list = list(tokens)

                # 构建订单对象列表
                orders_to_create = []
                for i in range(gen_count):
                    orders_to_create.append(Dingdan(
                        dingdan_id=dingdan_id_list[i],
                        zhuangtai=13,
                        fadan_pingtai=2,
                        jine=jiage,
                        dashou_fencheng=dashou_fencheng,
                        leixing_id=shangpin_type_id,
                        yaoqiuleixing=yaoqiuleixing or 1,
                        huiyuan_id=huiyuan_id,
                        jieshao=dingdan_jieshao[:2000],
                        tupian=target_user.avatar or '',  # 使用 target_user 的头像
                        user1_id=f"Sj{real_yonghuid}",
                        user2_id='',
                        zhiding_id='',
                        dashou_liuyan='',
                        beizhu='',
                        nicheng='',
                        shangpin_id=0,
                        yongjin=None,
                    ))

                # 批量插入订单（此时 orders 获得主键 id）
                created_orders = Dingdan.objects.bulk_create(orders_to_create)

                # 构建商家扩展对象列表（使用已保存的订单）
                extensions_to_create = []
                # 构建商家扩展对象列表（使用循环 create，绝对安全）
                for order in created_orders:
                    DingdanShangjia.objects.create(
                        dingdan=order,  # 直接使用订单对象，create 能正确处理
                        shangjia_id=real_yonghuid,
                        sjnicheng=shangjia.nicheng or f"商家{real_yonghuid}",
                        shangjia_pingjia='',
                        cfliyou='',
                        sqzhuangtai=0,
                        bhliyou=''
                    )
                '''for order in created_orders:
                    extensions_to_create.append(DingdanShangjia(
                        dingdan=order.id,
                        shangjia_id=real_yonghuid,
                        sjnicheng=shangjia.nicheng or f"商家{real_yonghuid}",
                        shangjia_pingjia='',
                        cfliyou='',
                        sqzhuangtai=0,
                        bhliyou=''
                    ))'''

                DingdanShangjia.objects.bulk_create(extensions_to_create)

                # 构建链接对象列表
                base_url = getattr(settings, 'H5_DOMAIN', 'https://h5.yourdomain.com')
                if not base_url.endswith('/'):
                    base_url += '/'
                links_to_create = []
                for i, order in enumerate(created_orders):
                    token = token_list[i]
                    link_url = f"{base_url}order/{token}"
                    links_to_create.append(ShangjiaLianjie(
                        yonghu_id=real_yonghuid,
                        leixing_id=shangpin_type_id,
                        moban_id=moban_id,
                        lianjie=link_url,
                        lianjie_token=token,
                        dingdan_id=order.dingdan_id,
                        is_used=False,
                        youxiao_shijian=timezone.now() + timedelta(days=1)
                    ))
                ShangjiaLianjie.objects.bulk_create(links_to_create)

                # 更新商家统计信息
                UserShangjia.objects.filter(id=shangjia.id).update(
                    fabu=F('fabu') + gen_count,
                    yue=F('yue') - total_price,
                    jinridingdan=F('jinridingdan') + gen_count,
                    jinriliushui=F('jinriliushui') + total_price,
                    jinyuedingdan=F('jinyuedingdan') + gen_count,
                    jinyueliushui=F('jinyueliushui') + total_price,
                )

                # 更新模板发布数量
                ShangjiaMoban.objects.filter(id=moban_id).update(
                    fabu_shuliang=F('fabu_shuliang') + gen_count,
                )

            # ========== 返回数据 ==========
            shangjia.refresh_from_db()
            link_list = []
            for link_obj in links_to_create:
                link_list.append({
                    'linkUrl': link_obj.lianjie,
                    'token': link_obj.lianjie_token,
                    'dingdanId': link_obj.dingdan_id,
                    'youxiaoShijian': link_obj.youxiao_shijian.strftime('%Y-%m-%d %H:%M:%S') if link_obj.youxiao_shijian else '',
                    'isUsed': link_obj.is_used,
                })

            return Response({
                'code': 200,
                'msg': '批量链接生成成功',
                'data': {
                    'totalCount': gen_count,
                    'links': link_list,
                    'newBalance': str(shangjia.yue),
                    'jinriliushui': str(shangjia.jinriliushui),
                    'jinyueliushui': str(shangjia.jinyueliushui),
                    'jinridingdan': shangjia.jinridingdan,
                    'jinyuedingdan': shangjia.jinyuedingdan
                }
            }, status=status.HTTP_200_OK)

        except Exception as e:
            logger.error(f"批量生成链接失败 - 管理员: {request.user.yonghuid}, 错误: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': '系统错误，生成链接失败',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    # ---------- 辅助函数 ----------
    def _generate_dingdan_id(self):
        import time
        import os
        import random
        timestamp = int(time.time() * 1000)
        process_id = os.getpid() % 1000
        random_num = random.randint(1000, 9999)
        return f"SJ{timestamp}{process_id:03d}{random_num}"

    def _generate_secure_token(self):
        import hashlib
        import secrets
        timestamp = int(time.time() * 1_000_000)
        random_salt = secrets.token_hex(8)
        raw_string = f"{timestamp}:{secrets.token_hex(4)}:{random_salt}"
        hash_obj = hashlib.sha256(raw_string.encode('utf-8'))
        return hash_obj.hexdigest()[:24]









class ShangjiaMobanListViewpl(APIView):
    """
    商家订单模板列表接口
    POST /peizhi/sjddmblb
    权限: JWT认证用户
    功能: 支持普通分页获取和搜索两种模式
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取用户ID和基础参数
            user = request.user
            #yonghu_id = user.yonghuid

            # 2. 获取请求参数
            leixing_id = request.data.get('shangpinTypeId')
            get_type = request.data.get('getType', 2)  # 1=搜索，2=普通获取
            page = int(request.data.get('page', 1))
            page_size = int(request.data.get('pageSize', 50))
            search_keyword = request.data.get('searchKeyword', '').strip()

            # 3. 验证必填字段
            if not leixing_id:
                return Response({
                    'code': 400,
                    'msg': '商品类型ID不能为空',
                    'data': {}
                }, status=400)

            if get_type not in [1, 2]:
                return Response({
                    'code': 400,
                    'msg': '获取类型参数错误',
                    'data': {}
                }, status=400)
            # ========== 新增：通过手机号和用户类型查询真正的用户ID ==========


            phone = request.user.phone  # 当前登录用户的手机号
            target_user_type = 'lianjie'  # 目标用户类型，请根据实际业务确认（例如：'admin'、'shangjia' 或固定值 'lianjie'）

            try:
                target_user = UserMain.objects.get(phone=phone, user_type=target_user_type)
                yonghu_id = target_user.yonghuid
            except UserMain.DoesNotExist:
                return Response({
                    'code': 404,
                    'msg': '未找到符合条件的用户',
                    'data': {}
                }, status=404)
            # ========== 新增结束 ==========

            # 后续代码使用 yonghu_id 进行查询，而不是 request.user.yonghuid

            # 4. 构建查询条件
            query_conditions = Q(
                yonghu_id=yonghu_id,
                leixing_id=leixing_id,
                #zhuangtai=1  # 只查询正常状态的模板
            )

            # 5. 搜索模式处理
            if get_type == 1 and search_keyword:
                # 搜索模式：按模板ID搜索
                try:
                    moban_id = int(search_keyword)
                    query_conditions &= Q(id=moban_id)
                except ValueError:
                    return Response({
                        'code': 400,
                        'msg': '搜索关键词必须是有效的模板ID',
                        'data': {}
                    }, status=400)

            # 6. 执行查询（使用values优化性能）
            queryset = ShangjiaMoban.objects.filter(query_conditions)

            # 7. 计算总数和分页
            total_count = queryset.count()

            # 搜索模式不进行分页，直接返回所有匹配结果
            if get_type == 1:
                moban_list = queryset.values(
                    'id',  # 模板ID
                    'leixing_id',  # 商品类型ID
                    'moban_jieshao',  # 模板介绍
                    'jiage',  # 价格
                    'fabu_shuliang',  # 发布数量
                    'create_time'  # 创建时间
                )

                # 格式化数据（与前端字段对应）
                formatted_list = []
                for item in moban_list:
                    formatted_list.append({
                        'mobanId': item['id'],
                        'shangpinTypeId': item['leixing_id'],
                        'jieshao': item['moban_jieshao'],
                        'jiage': str(item['jiage']),  # 转为字符串
                        'fabushuliang': item['fabu_shuliang'],
                        'createTime': item['create_time'].strftime('%Y-%m-%d %H:%M:%S') if item['create_time'] else ''
                    })

                return Response({
                    'code': 200,
                    'msg': '搜索成功',
                    'data': {
                        'list': formatted_list,
                        'total': len(formatted_list),
                        'hasMore': False,  # 搜索模式不分页，没有更多数据
                        'currentPage': 1,
                        'pageSize': len(formatted_list)
                    }
                })

            # 8. 普通获取模式：分页处理
            else:
                # 使用Django分页器
                paginator = Paginator(queryset, page_size)

                # 检查页码是否有效
                if page < 1 or page > paginator.num_pages:
                    return Response({
                        'code': 400,
                        'msg': '页码超出范围',
                        'data': {}
                    }, status=400)

                # 获取当前页数据
                current_page = paginator.page(page)

                # 获取数据并格式化
                moban_list = current_page.object_list.values(
                    'id',  # 模板ID
                    'leixing_id',  # 商品类型ID
                    'moban_jieshao',  # 模板介绍
                    'jiage',  # 价格
                    'fabu_shuliang',  # 发布数量
                    'create_time'  # 创建时间
                )

                # 格式化数据（与前端字段对应）
                formatted_list = []
                for item in moban_list:
                    formatted_list.append({
                        'mobanId': item['id'],
                        'shangpinTypeId': item['leixing_id'],
                        'jieshao': item['moban_jieshao'],
                        'jiage': str(item['jiage']),  # 转为字符串
                        'fabushuliang': item['fabu_shuliang'],
                        'createTime': item['create_time'].strftime('%Y-%m-%d %H:%M:%S') if item['create_time'] else ''
                    })

                # 判断是否有更多数据
                has_more = current_page.has_next()

                # 特殊逻辑：如果当前页数据量不足page_size，说明没有更多数据
                if len(formatted_list) < page_size:
                    has_more = False

                return Response({
                    'code': 200,
                    'msg': '获取成功',
                    'data': {
                        'list': formatted_list,
                        'total': total_count,
                        'hasMore': has_more,  # 是否允许上拉刷新
                        'currentPage': page,
                        'pageSize': page_size,
                        'totalPages': paginator.num_pages
                    }
                })

        except Exception as e:
            # 记录错误日志
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"获取商家模板列表失败: {str(e)}", exc_info=True)

            return Response({
                'code': 500,
                'msg': '服务器内部错误',
                'data': {}
            }, status=500)







# views.py
import io
import time

from yonghu.models import UserGuanshi
from utils.oss_utils import upload_to_oss, delete_from_oss
from utils.yaoqingma_utils import chuangjianYaoqingma, yanzhengYaoqingmaWeiyixing
import requests

logger = logging.getLogger(__name__)



import urllib.parse


#和手机号绑定冲突接口
'''class GuanshiQRCodeView(APIView):
    """
    管事专属二维码生成接口
    路径：/peizhi/guanshiewm
    方法：POST
    权限：JWT认证，仅限管事身份
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        # 获取当前认证用户
        current_user = request.user

        # 1. 查询管事扩展表
        try:
            guanshi = current_user.guanshi_profile
        except UserGuanshi.DoesNotExist:
            return Response({'code': 403, 'message': '当前用户不是管事身份', 'data': None},
                            status=status.HTTP_403_FORBIDDEN)

        # 2. 验证管事状态
        if guanshi.zhuangtai != 1:
            return Response({'code': 403, 'message': '管事账号状态异常', 'data': None},
                            status=status.HTTP_403_FORBIDDEN)

        # 3. 获取或生成邀请码
        invite_code = guanshi.yaoqingma
        if not invite_code:
            invite_code = chuangjianYaoqingma(str(current_user.yonghuid))
            if not yanzhengYaoqingmaWeiyixing(invite_code):
                logger.error(f"邀请码生成失败，用户: {current_user.yonghuid}")
                return Response({'code': 500, 'message': '邀请码生成失败', 'data': None},
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            guanshi.yaoqingma = invite_code
            guanshi.save(update_fields=['yaoqingma'])

        # 4. 获取 access_token（与原来相同）
        access_token = self.get_cached_access_token()
        if not access_token:
            return Response({'code': 500, 'message': '获取微信access_token失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # ========== 🔥 主要修改：使用 A接口（getwxacode） ==========
        # 对邀请码进行URL编码，防止特殊字符破坏路径
        encoded_invite = urllib.parse.quote(invite_code, safe='')
        # 构造带参数的完整路径
        page_path = f'pages/dashouduan/dashouduan?inviteCode={encoded_invite}'

        wx_url = f'https://api.weixin.qq.com/wxa/getwxacode?access_token={access_token}'
        post_data = {
            'path': page_path,
            'width': 430,
            'auto_color': False,
            'line_color': {'r': 0, 'g': 0, 'b': 0}
        }

        try:
            wx_resp = requests.post(wx_url, json=post_data, timeout=10)
        except Exception as e:
            logger.error(f"请求微信接口失败: {e}")
            return Response({'code': 500, 'message': '调用微信服务失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 处理微信返回结果
        if wx_resp.status_code != 200:
            # 尝试解析微信返回的错误信息
            error_info = wx_resp.json() if wx_resp.headers.get('content-type') == 'application/json' else {}
            errmsg = error_info.get('errmsg', '未知错误')
            logger.error(f"微信接口返回错误 (HTTP {wx_resp.status_code}): {errmsg}")
            return Response({'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 检查返回内容是否为图片
        content_type = wx_resp.headers.get('content-type', '')
        if 'image' not in content_type:
            # 可能是 JSON 错误信息
            try:
                error_info = wx_resp.json()
                errmsg = error_info.get('errmsg', '未知错误')
                logger.error(f"微信接口返回非图片内容: {errmsg}")
                return Response({'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            except:
                logger.error("微信接口返回未知内容")
                return Response({'code': 500, 'message': '生成二维码失败', 'data': None},
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 5. 上传到腾讯云COS（与原来相同）
        timestamp = int(time.time())
        filename = f"{current_user.yonghuid}_{timestamp}.png"
        oss_path = f"erweima/guanshi/{filename}"
        file_obj = io.BytesIO(wx_resp.content)

        try:
            full_url = upload_to_oss(file_obj, oss_path)
        except Exception as e:
            logger.error(f"上传到COS失败: {e}")
            return Response({'code': 500, 'message': '图片上传失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if not full_url:
            return Response({'code': 500, 'message': '图片上传失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 6. 获取相对路径
        cos_domain = getattr(settings, 'COS_DOMAIN', '')
        if cos_domain and full_url.startswith(cos_domain):
            relative_path = full_url[len(cos_domain):].lstrip('/')
        else:
            relative_path = oss_path

        # 7. 删除旧二维码文件
        old_url = guanshi.invite_qrcode_url
        if old_url:
            delete_from_oss(old_url)

        # 8. 更新数据库
        with transaction.atomic():
            guanshi.invite_qrcode_url = relative_path
            guanshi.save(update_fields=['invite_qrcode_url'])

        # 9. 返回相对路径
        return Response({'code': 0, 'message': 'success', 'data': {'url': relative_path}})

    def get_cached_access_token(self):
        """从缓存获取微信 access_token（与原来相同）"""
        cache_key = 'wx_mini_access_token'
        token = cache.get(cache_key)
        if token:
            return token

        appid = getattr(settings, 'WEIXIN_APPID', '')
        secret = getattr(settings, 'WEIXIN_SECRET', '')
        if not appid or not secret:
            logger.error("微信小程序配置缺失")
            return None

        url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}'
        try:
            resp = requests.get(url, timeout=5)
            data = resp.json()
            token = data.get('access_token')
            expires_in = data.get('expires_in', 7200)
            if token:
                cache.set(cache_key, token, 7000)
            return token
        except Exception as e:
            logger.error(f"获取微信access_token失败: {e}")
            return None'''






class GuanshiQRCodeView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        logger.info("===== 开始生成管事二维码 =====")
        current_user = request.user
        logger.info(f"当前用户ID: {current_user.yonghuid}")

        try:
            try:
                guanshi = current_user.guanshi_profile
                logger.info(f"管事扩展表查询成功，状态: {guanshi.zhuangtai}")
            except UserGuanshi.DoesNotExist:
                logger.warning(f"用户 {current_user.yonghuid} 不是管事身份")
                return Response(
                    {'code': 403, 'message': '当前用户不是管事身份', 'data': None},
                    status=status.HTTP_403_FORBIDDEN
                )

            if guanshi.zhuangtai != 1:
                logger.warning(f"用户 {current_user.yonghuid} 管事状态异常: {guanshi.zhuangtai}")
                return Response(
                    {'code': 403, 'message': '管事账号状态异常', 'data': None},
                    status=status.HTTP_403_FORBIDDEN
                )

            invite_code = guanshi.yaoqingma
            if not invite_code:
                logger.info(f"用户 {current_user.yonghuid} 无邀请码，开始生成")
                invite_code = chuangjianYaoqingma(str(current_user.yonghuid))
                if not yanzhengYaoqingmaWeiyixing(invite_code):
                    logger.error(f"邀请码唯一性校验失败，用户: {current_user.yonghuid}")
                    return Response(
                        {'code': 500, 'message': '邀请码生成失败', 'data': None},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR
                    )
                guanshi.yaoqingma = invite_code
                guanshi.save(update_fields=['yaoqingma'])
                logger.info(f"邀请码生成成功: {invite_code}")
            else:
                logger.info(f"使用已有邀请码: {invite_code}")

            access_token = self.get_cached_access_token()
            if not access_token:
                logger.error("获取 access_token 失败")
                return Response(
                    {'code': 500, 'message': '获取微信access_token失败', 'data': None},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )
            logger.info(f"获取到 access_token (前10位): {access_token[:10]}...")

            encoded_invite = urllib.parse.quote(invite_code, safe='')
            page_path = f'pages/dashouduan/dashouduan?inviteCode={encoded_invite}'
            wx_url = f'https://api.weixin.qq.com/wxa/getwxacode?access_token={access_token}'
            post_data = {
                'path': page_path,
                'width': 430,
                'auto_color': False,
                'line_color': {'r': 0, 'g': 0, 'b': 0}
            }
            logger.info(f"调用微信接口生成小程序码，path: {page_path}")

            try:
                wx_resp = requests.post(wx_url, json=post_data, timeout=10)
                logger.info(f"微信接口响应状态码: {wx_resp.status_code}")
            except Exception as e:
                logger.error(f"请求微信接口异常: {str(e)}", exc_info=True)
                return Response(
                    {'code': 500, 'message': '调用微信服务失败', 'data': None},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            if wx_resp.status_code != 200:
                error_info = {}
                if wx_resp.headers.get('content-type') == 'application/json':
                    try:
                        error_info = wx_resp.json()
                    except:
                        pass
                errmsg = error_info.get('errmsg', '未知错误')
                logger.error(f"微信接口返回错误 (HTTP {wx_resp.status_code}): {errmsg}")
                if 'errcode' in error_info:
                    logger.error(f"微信错误码: {error_info.get('errcode')}")
                return Response(
                    {'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            content_type = wx_resp.headers.get('content-type', '')
            if 'image' not in content_type:
                try:
                    error_info = wx_resp.json()
                    errmsg = error_info.get('errmsg', '未知错误')
                    logger.error(f"微信接口返回非图片内容: {errmsg}")
                    return Response(
                        {'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR
                    )
                except:
                    logger.error("微信接口返回未知内容，无法解析为JSON")
                    return Response(
                        {'code': 500, 'message': '生成二维码失败', 'data': None},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR
                    )

            timestamp = int(time.time())
            filename = f"{current_user.yonghuid}_{timestamp}.png"
            oss_path = f"erweima/guanshi/{filename}"
            file_obj = io.BytesIO(wx_resp.content)
            logger.info(f"开始上传图片到COS: {oss_path}")

            try:
                full_url = upload_to_oss(file_obj, oss_path)
                logger.info(f"上传成功，完整URL: {full_url}")
            except Exception as e:
                logger.error(f"上传到COS异常: {str(e)}", exc_info=True)
                return Response(
                    {'code': 500, 'message': '图片上传失败', 'data': None},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            if not full_url:
                logger.error("upload_to_oss 返回了空 URL")
                return Response(
                    {'code': 500, 'message': '图片上传失败', 'data': None},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            cos_domain = getattr(settings, 'COS_DOMAIN', '')
            if cos_domain and full_url.startswith(cos_domain):
                relative_path = full_url[len(cos_domain):].lstrip('/')
            else:
                relative_path = oss_path
            logger.info(f"相对路径: {relative_path}")

            old_url = guanshi.invite_qrcode_url
            if old_url:
                logger.info(f"删除旧二维码: {old_url}")
                try:
                    delete_from_oss(old_url)
                except Exception as e:
                    logger.warning(f"删除旧二维码失败(不影响主流程): {str(e)}")

            with transaction.atomic():
                guanshi.invite_qrcode_url = relative_path
                guanshi.save(update_fields=['invite_qrcode_url'])
                logger.info("数据库更新成功")

            return Response({
                'code': 0,
                'message': 'success',
                'data': {'url': relative_path}
            })

        except Exception as e:
            logger.error(f"生成二维码过程中发生未捕获异常: {str(e)}", exc_info=True)
            return Response(
                {'code': 500, 'message': '服务器内部错误', 'data': None},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    def get_cached_access_token(self):
        """获取微信全局 access_token，使用 cache.add 实现互斥锁"""
        cache_key = 'wechat_access_token'
        lock_key = 'wechat_access_token_lock'
        lock_timeout = 10

        # 先尝试读取缓存
        token = cache.get(cache_key)
        if token:
            logger.info("从缓存中读取到 access_token")
            return token

        # 尝试获取锁（原子操作）
        lock_acquired = cache.add(lock_key, '1', timeout=lock_timeout)
        if not lock_acquired:
            # 未获得锁，等待其他进程刷新
            import time
            time.sleep(0.2)
            token = cache.get(cache_key)
            if token:
                logger.info("其他进程已刷新 token，使用缓存值")
                return token
            # 等待几秒再试
            for _ in range(5):
                time.sleep(0.2)
                token = cache.get(cache_key)
                if token:
                    return token
            logger.warning("获取锁失败且缓存中无 token，返回 None")
            return None

        # 获得锁，执行刷新
        try:
            token = cache.get(cache_key)
            if token:
                logger.info("加锁后再次读取到缓存中的 token")
                return token

            appid = getattr(settings, 'WEIXIN_APPID', '')
            secret = getattr(settings, 'WEIXIN_SECRET', '')
            if not appid or not secret:
                logger.error("微信小程序配置缺失: WEIXIN_APPID 或 WEIXIN_SECRET 未设置")
                return None

            url = 'https://api.weixin.qq.com/cgi-bin/token'
            params = {
                'grant_type': 'client_credential',
                'appid': appid,
                'secret': secret
            }
            try:
                logger.info("正在请求微信接口获取 access_token...")
                resp = requests.get(url, params=params, timeout=5)
                data = resp.json()
                new_token = data.get('access_token')
                if new_token:
                    cache.set(cache_key, new_token, timeout=7100)
                    logger.info("成功获取新 access_token，已缓存")
                    return new_token
                else:
                    errcode = data.get('errcode', '未知')
                    errmsg = data.get('errmsg', '未知错误')
                    logger.error(f"获取 access_token 失败，errcode={errcode}, errmsg={errmsg}")
                    return None
            except Exception as e:
                logger.error(f"请求微信 access_token 接口异常: {str(e)}", exc_info=True)
                return None
        finally:
            cache.delete(lock_key)
    # ==================== 修改结束 ====================








'''class GuanshiQRCodeView(APIView):
    """
    管事专属二维码生成接口
    路径：/peizhi/guanshiewm
    方法：POST
    权限：JWT认证，仅限管事身份
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # 获取当前认证用户
        current_user = request.user

        # 1. 查询管事扩展表
        try:
            guanshi = current_user.guanshi_profile
        except UserGuanshi.DoesNotExist:
            return Response({'code': 403, 'message': '当前用户不是管事身份', 'data': None},
                            status=status.HTTP_403_FORBIDDEN)

        # 2. 验证管事状态
        if guanshi.zhuangtai != 1:
            return Response({'code': 403, 'message': '管事账号状态异常', 'data': None},
                            status=status.HTTP_403_FORBIDDEN)

        # 3. 获取或生成邀请码
        invite_code = guanshi.yaoqingma
        if not invite_code:
            invite_code = chuangjianYaoqingma(str(current_user.yonghuid))
            if not yanzhengYaoqingmaWeiyixing(invite_code):
                logger.error(f"邀请码生成失败，用户: {current_user.yonghuid}")
                return Response({'code': 500, 'message': '邀请码生成失败', 'data': None},
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            guanshi.yaoqingma = invite_code
            guanshi.save(update_fields=['yaoqingma'])

        # 4. 从缓存获取 access_token（全局唯一，不存在则自动获取并缓存）
        access_token = self.get_cached_access_token()
        if not access_token:
            return Response({'code': 500, 'message': '获取微信access_token失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 5. 调用微信接口生成小程序码
        page_path = 'pages/dashouduan/dashouduan'  # 打手注册页
        scene = invite_code
        wx_url = f'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={access_token}'
        post_data = {
            'scene': scene,
            'page': page_path,
            'width': 430,
            'auto_color': False,
            'line_color': {'r': 0, 'g': 0, 'b': 0},
        }

        try:
            wx_resp = requests.post(wx_url, json=post_data, timeout=10)
        except Exception as e:
            logger.error(f"请求微信接口失败: {e}")
            return Response({'code': 500, 'message': '调用微信服务失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if wx_resp.status_code != 200 or 'image' not in wx_resp.headers.get('content-type', ''):
            error_info = wx_resp.json() if wx_resp.headers.get('content-type') == 'application/json' else {}
            errmsg = error_info.get('errmsg', '未知错误')
            logger.error(f"微信接口返回错误: {errmsg}")
            return Response({'code': 500, 'message': f'生成二维码失败: {errmsg}', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 6. 上传到腾讯云COS
        timestamp = int(time.time())
        filename = f"{current_user.yonghuid}_{timestamp}.png"
        oss_path = f"erweima/guanshi/{filename}"
        file_obj = io.BytesIO(wx_resp.content)

        try:
            full_url = upload_to_oss(file_obj, oss_path)
        except Exception as e:
            logger.error(f"上传到COS失败: {e}")
            return Response({'code': 500, 'message': '图片上传失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if not full_url:
            return Response({'code': 500, 'message': '图片上传失败', 'data': None},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 7. 获取相对路径
        cos_domain = getattr(settings, 'COS_DOMAIN', '')
        if cos_domain and full_url.startswith(cos_domain):
            relative_path = full_url[len(cos_domain):].lstrip('/')
        else:
            relative_path = oss_path

        # 8. 删除旧二维码文件
        old_url = guanshi.invite_qrcode_url
        if old_url:
            delete_from_oss(old_url)

        # 9. 更新数据库
        with transaction.atomic():
            guanshi.invite_qrcode_url = relative_path
            guanshi.save(update_fields=['invite_qrcode_url'])

        # 10. 返回相对路径
        return Response({'code': 0, 'message': 'success', 'data': {'url': relative_path}})

    def get_cached_access_token(self):
        """
        从缓存获取微信 access_token，缓存键为 'wx_mini_access_token'
        如果缓存中不存在，则调用微信接口获取并存入缓存，有效期7000秒。
        使用 Django 缓存框架，默认使用内存缓存，不会占用磁盘。
        """
        cache_key = 'wx_mini_access_token'
        token = cache.get(cache_key)
        if token:
            return token

        appid = getattr(settings, 'WEIXIN_APPID', '')
        secret = getattr(settings, 'WEIXIN_SECRET', '')
        if not appid or not secret:
            logger.error("微信小程序配置缺失")
            return None

        url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}'
        try:
            resp = requests.get(url, timeout=5)
            data = resp.json()
            token = data.get('access_token')
            expires_in = data.get('expires_in', 7200)
            if token:
                # 缓存7000秒（小于7200秒，确保不会过期才使用）
                cache.set(cache_key, token, 7000)
            return token
        except Exception as e:
            logger.error(f"获取微信access_token失败: {e}")
            return None'''






from django.db.models import Max, F


class GuanZhuAListView(APIView):
    """
    获取关注阿龙页面图片列表
    POST /peizhi/gzal
    需要JWT认证
    返回：
    {
        "code": 0,
        "msg": "success",
        "data": {
            "images": ["相对URL1", "相对URL2", ...],
            "last_update": "2025-03-16 12:34:56",
            "visit_count": 123
        }
    }
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 查询类型为3的所有记录
            qs = Tupianpeizhi.objects.filter(leixing=3)
            if not qs.exists():
                return Response({
                    'code': 0,
                    'msg': 'success',
                    'data': {
                        'images': [],
                        'last_update': None,
                        'visit_count': 0
                    }
                }, status=status.HTTP_200_OK)

            # 获取所有图片的相对URL
            images = list(qs.values_list('tupian', flat=True))

            # 获取最新更新时间（取所有记录的update_time最大值）
            last_update = qs.aggregate(Max('update_time'))['update_time__max']
            last_update_str = last_update.strftime('%Y-%m-%d %H:%M:%S') if last_update else None

            # 原子性增加访问次数
            # 先更新所有记录，再获取更新后的最大次数
            qs.update(fangwen_cishu=F('fangwen_cishu') + 1)
            visit_count = qs.aggregate(Max('fangwen_cishu'))['fangwen_cishu__max'] or 0

            return Response({
                'code': 0,
                'msg': 'success',
                'data': {
                    'images': images,
                    'last_update': last_update_str,
                    'visit_count': visit_count
                }
            }, status=status.HTTP_200_OK)

        except Exception as e:
            logger.error(f"获取关注阿龙图片列表失败: {e}", exc_info=True)
            return Response({
                'code': 1,
                'msg': '服务器内部错误',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)









from yonghu.models import UserZuzhang



class ZuzhangHaibaoView(APIView):
    """
    组长专属海报二维码生成接口
    路径：/peizhi/zuzhanghb
    方法：POST
    权限：JWT认证，仅限组长身份
    返回：
        {
            "code": 0,
            "message": "success",
            "data": {
                "url": "erweima/zuzhang/1000001_1234567890.png",  # 相对路径
                "yaoqingma": "ABC123XYZ"                          # 组长邀请码
            }
        }
    """
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        current_user = request.user

        # 1. 验证组长身份
        try:
            zuzhang = current_user.zuzhang_profile
        except UserZuzhang.DoesNotExist:
            return Response({
                'code': 403,
                'message': '当前用户不是组长身份',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # 2. 验证组长账号状态（zhuangtai=1正常）
        if zuzhang.zhuangtai != 1:
            return Response({
                'code': 403,
                'message': '组长账号状态异常，无法生成海报',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # 3. 获取或生成邀请码（yaoqingma字段）
        invite_code = zuzhang.yaoqingma
        if not invite_code:
            # 生成唯一邀请码（使用已有工具函数）
            invite_code = chuangjianYaoqingma(str(current_user.yonghuid))
            if not yanzhengYaoqingmaWeiyixing(invite_code):
                logger.error(f"组长邀请码生成失败，用户: {current_user.yonghuid}")
                return Response({
                    'code': 500,
                    'message': '邀请码生成失败',
                    'data': None
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            # 更新到数据库
            zuzhang.yaoqingma = invite_code
            zuzhang.save(update_fields=['yaoqingma'])

        # 4. 获取微信 access_token（使用缓存）
        access_token = self.get_cached_access_token()
        if not access_token:
            return Response({
                'code': 500,
                'message': '获取微信access_token失败',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # ========== 调用微信B接口：getUnlimited ==========
        # 页面路径：打手注册页（组长邀请打手）
        page_path = 'pages/guanshiduan/guanshiduan'
        # scene参数：直接放入邀请码（需确保字符符合微信要求）
        scene = invite_code  # 假设邀请码只包含字母数字

        wx_url = f'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={access_token}'
        post_data = {
            'scene': scene,
            'page': page_path,
            'width': 430,
            'auto_color': False,
            'line_color': {'r': 0, 'g': 0, 'b': 0}
        }

        try:
            wx_resp = requests.post(wx_url, json=post_data, timeout=10)
        except Exception as e:
            logger.error(f"请求微信接口失败: {e}")
            return Response({
                'code': 500,
                'message': '调用微信服务失败',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 处理微信返回结果
        if wx_resp.status_code != 200:
            error_info = wx_resp.json() if wx_resp.headers.get('content-type') == 'application/json' else {}
            errmsg = error_info.get('errmsg', '未知错误')
            logger.error(f"微信接口返回错误 (HTTP {wx_resp.status_code}): {errmsg}")
            return Response({
                'code': 500,
                'message': f'生成二维码失败: {errmsg}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        content_type = wx_resp.headers.get('content-type', '')
        if 'image' not in content_type:
            # 可能是JSON错误
            try:
                error_info = wx_resp.json()
                errmsg = error_info.get('errmsg', '未知错误')
                logger.error(f"微信接口返回非图片内容: {errmsg}")
                return Response({
                    'code': 500,
                    'message': f'生成二维码失败: {errmsg}',
                    'data': None
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            except:
                logger.error("微信接口返回未知内容")
                return Response({
                    'code': 500,
                    'message': '生成二维码失败',
                    'data': None
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 5. 上传二维码到腾讯云COS
        timestamp = int(time.time())
        filename = f"{current_user.yonghuid}_{timestamp}.png"
        oss_path = f"erweima/zuzhang/{filename}"  # 组长专用文件夹
        file_obj = io.BytesIO(wx_resp.content)

        try:
            full_url = upload_to_oss(file_obj, oss_path)
        except Exception as e:
            logger.error(f"上传到COS失败: {e}")
            return Response({
                'code': 500,
                'message': '图片上传失败',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if not full_url:
            return Response({
                'code': 500,
                'message': '图片上传失败',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 6. 获取相对路径（用于前端拼接）
        cos_domain = getattr(settings, 'COS_DOMAIN', '')
        if cos_domain and full_url.startswith(cos_domain):
            relative_path = full_url[len(cos_domain):].lstrip('/')
        else:
            relative_path = oss_path

        # 7. 删除旧的二维码文件（haibao_url字段）
        old_url = zuzhang.haibao_url
        if old_url:
            delete_from_oss(old_url)

        # 8. 更新数据库中的haibao_url字段
        with transaction.atomic():
            zuzhang.haibao_url = relative_path
            zuzhang.save(update_fields=['haibao_url'])

        # 9. 返回邀请码和二维码相对路径
        return Response({
            'code': 0,
            'message': 'success',
            'data': {
                'url': relative_path,
                'yaoqingma': invite_code
            }
        })

    def get_cached_access_token(self):
        """从缓存获取微信access_token（与管事接口共用）"""
        cache_key = 'wx_mini_access_token'
        token = cache.get(cache_key)
        if token:
            return token

        appid = getattr(settings, 'WEIXIN_APPID', '')
        secret = getattr(settings, 'WEIXIN_SECRET', '')
        if not appid or not secret:
            logger.error("微信小程序配置缺失")
            return None

        url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}'
        try:
            resp = requests.get(url, timeout=5)
            data = resp.json()
            token = data.get('access_token')
            expires_in = data.get('expires_in', 7200)
            if token:
                cache.set(cache_key, token, 7000)
            return token
        except Exception as e:
            logger.error(f"获取微信access_token失败: {e}")
            return None


from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from django.utils import timezone
from django.db.models import Q
from .models import PopupPage, PopupConfig
from .serializers import PopupConfigSerializer


class PopupConfigView(APIView):
    """
    获取指定页面的弹窗配置
    请求格式：POST { "pageKey": "dashouzhongxin" }
    返回格式：{ "code": 0, "data": { "serverTime": "...", "popups": [...] } }
    """
    permission_classes = [IsAuthenticated]  # JWT 认证

    def post(self, request):
        # 1. 获取当前用户（已通过JWT认证，可直接使用）
        user = request.user
        if not user or not user.id:
            return Response({
                'code': 401,
                'msg': '用户未登录或认证失败'
            }, status=status.HTTP_401_UNAUTHORIZED)

        # 2. 获取参数 pageKey
        page_key = request.data.get('pageKey')
        if not page_key:
            return Response({
                'code': 400,
                'msg': '缺少参数 pageKey'
            }, status=status.HTTP_400_BAD_REQUEST)

        # 3. 查询页面配置
        try:
            popup_page = PopupPage.objects.get(page_key=page_key, is_active=True)
        except PopupPage.DoesNotExist:
            # 该页面没有配置弹窗，返回空
            return Response({
                'code': 0,
                'data': {
                    'serverTime': timezone.now().isoformat(),
                    'popups': []
                }
            })

        # 4. 查询该页面下所有启用的弹窗（且在有效时间范围内）
        now = timezone.now()
        popups = PopupConfig.objects.filter(
            page=popup_page,
            is_active=True,
            start_time__lte=now,
            end_time__gte=now
        ).prefetch_related('images')  # 预加载图片，避免N+1查询
        # 按 sort_order 排序（model Meta 已定义）

        # 5. 序列化
        serializer = PopupConfigSerializer(popups, many=True)

        # 6. 返回数据
        return Response({
            'code': 0,
            'data': {
                'serverTime': now.isoformat(),
                'ignore_user_mute': popup_page.ignore_user_mute,  # 新增
                'popups': serializer.data
            }
        })





from .models import WithdrawConfig

class GetWithdrawModeView(APIView):
    """
    获取提现模式配置
    前端请求 /peizhi/hqtxzsym
    返回格式: { "code": 0, "data": { "mode": 1 or 2 } }
    """
    permission_classes = [IsAuthenticated]  # JWT 认证

    def post(self, request, *args, **kwargs):
        # 尝试获取 ID=1 的记录
        config = WithdrawConfig.objects.filter(id=1).first()
        # 如果存在则取实际值，否则默认 2（手动提现）
        mode = config.mode if config else 2

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




from shangpin.models import (
    Huiyuangoumai
)


class CheckPhoneAuthView(APIView):
    """
    判断当前登录用户是否需要强制手机号认证

    规则：
      - 已认证过手机号（shoujihao_renzheng=True） → 不需要
      - 未认证时，检查用户角色及其业务指标是否达到阈值：
        打手：有会员购买记录（Huiyuangoumai 中存在记录）
        商家：发布订单总数（fabu）>= 5
        管事：充值分佣总额（chongzhifenrun）>= 15
        组长：分佣总额（fenyong_zonge）>= 2
        老板：消费总额（zonge）>= 20
      - 满足任一角色的阈值 → 需要认证
      - 否则 → 不需要认证
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        user = request.user  # UserMain 实例

        # 已经认证过手机号，直接放行
        if user.shoujihao_renzheng:
            return Response({'need_auth': False})

        # 检查各角色是否满足阈值条件
        if self._dashou_meets_threshold(user):
            return Response({'need_auth': True})
        if self._shangjia_meets_threshold(user):
            return Response({'need_auth': True})
        if self._guanshi_meets_threshold(user):
            return Response({'need_auth': True})
        if self._zuzhang_meets_threshold(user):
            return Response({'need_auth': True})
        if self._boss_meets_threshold(user):
            return Response({'need_auth': True})

        # 都不满足，无需认证
        return Response({'need_auth': False})

    # ==================== 各角色阈值判断方法 ====================

    def _dashou_meets_threshold(self, user):
        """
        打手条件：存在会员购买记录（Huiyuangoumai 表中有该用户的记录）
        """
        try:
            # 先确认用户是打手
            UserDashou.objects.get(user=user)
        except UserDashou.DoesNotExist:
            return False
        # 检查是否有任意会员购买记录
        return Huiyuangoumai.objects.filter(yonghu_id=user.yonghuid).exists()

    def _shangjia_meets_threshold(self, user):
        """商家条件：发布订单总数 fabu >= 5"""
        try:
            shop = UserShangjia.objects.get(user=user)
        except UserShangjia.DoesNotExist:
            return False
        return shop.fabu >= 5

    def _guanshi_meets_threshold(self, user):
        """管事条件：充值分佣总额 chongzhifenrun >= 15"""
        try:
            guanshi = UserGuanshi.objects.get(user=user)
        except UserGuanshi.DoesNotExist:
            return False
        return guanshi.chongzhifenrun >= 15

    def _zuzhang_meets_threshold(self, user):
        """组长条件：分佣总额 fenyong_zonge >= 2"""
        try:
            zuzhang = UserZuzhang.objects.get(user=user)
        except UserZuzhang.DoesNotExist:
            return False
        return zuzhang.fenyong_zonge >= 2

    def _boss_meets_threshold(self, user):
        """老板条件：消费总额 zonge >= 20"""
        try:
            boss = UserBoss.objects.get(user=user)
        except UserBoss.DoesNotExist:
            return False
        return boss.zonge >= 20