"""集团多俱乐部数据模型（新建表，不修改 gvsdsdk）。"""
import uuid
from decimal import Decimal

from django.db import models
from gvsdsdk.model_base import QModel


class Club(QModel):
    """子公司 / 俱乐部主表。"""
    club_id = models.CharField(max_length=16, primary_key=True, verbose_name='俱乐部ID')
    name = models.CharField(max_length=64, verbose_name='展示名')
    status = models.IntegerField(default=1, verbose_name='1启用0停用')
    wx_appid = models.CharField(max_length=32, blank=True, default='')
    wx_secret = models.CharField(max_length=128, blank=True, default='')
    # —— 点单/会员等收款商户（V2 JSAPI；退款也应对齐这套）——
    mch_id = models.CharField(max_length=32, blank=True, default='', verbose_name='收款商户号')
    pay_app_id = models.CharField(max_length=32, blank=True, default='', verbose_name='收款AppID')
    mch_key = models.CharField(max_length=128, blank=True, default='', verbose_name='收款APIv2密钥')
    pay_cert_path = models.CharField(
        max_length=512, blank=True, default='',
        verbose_name='收款商户证书路径',
        help_text='apiclient_cert.pem，退款/V2 证书用',
    )
    pay_key_path = models.CharField(
        max_length=512, blank=True, default='',
        verbose_name='收款商户私钥路径',
        help_text='apiclient_key.pem，退款/V2 证书用',
    )
    # —— 提现/转账商户（V3；可与收款商户号不同）——
    withdraw_mch_id = models.CharField(
        max_length=32, blank=True, default='',
        verbose_name='提现商户号',
        help_text='空则回落收款商户号 mch_id',
    )
    api_v3_key = models.CharField(max_length=128, blank=True, default='', verbose_name='提现APIv3密钥')
    cert_serial_no = models.CharField(max_length=64, blank=True, default='', verbose_name='提现证书序列号')
    private_key_path = models.CharField(
        max_length=512, blank=True, default='', verbose_name='提现商户私钥路径',
    )
    platform_cert_dir = models.CharField(
        max_length=512, blank=True, default='', verbose_name='提现平台证书目录',
    )
    official_appid = models.CharField(max_length=32, blank=True, default='')
    official_secret = models.CharField(max_length=128, blank=True, default='')
    official_token = models.CharField(max_length=64, blank=True, default='')
    encoding_aes_key = models.CharField(max_length=64, blank=True, default='')
    template_id = models.CharField(max_length=64, blank=True, default='')
    template_max_per_minute = models.IntegerField(default=950)
    h5_domain = models.CharField(max_length=128, blank=True, default='')
    oss_prefix = models.CharField(max_length=128, blank=True, default='')
    goeasy_appkey = models.CharField(max_length=64, blank=True, default='')
    goeasy_secret = models.CharField(max_length=128, blank=True, default='')
    config_json = models.JSONField(default=dict, blank=True)
    # 小程序「微信支付」入口实际走哪条通道：wechat=直连商户号；fubei=付呗（前端仍传 wechat）
    mini_pay_channel = models.CharField(
        max_length=16,
        blank=True,
        default='wechat',
        verbose_name='小程序收款通道',
        help_text='wechat=微信直连；fubei=付呗；默认 wechat，不影响现网',
    )
    company_uuid = models.BinaryField(max_length=16, null=True, blank=True)
    sort_order = models.IntegerField(default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club'
        verbose_name = '俱乐部'


class ClubPaymentChannel(QModel):
    """俱乐部支付通道配置（可多条；同 channel 用 is_default 选主配置）。"""
    CHANNEL_CHOICES = (
        ('wechat_jsapi', '微信直连-小程序JSAPI'),
        ('wechat_v3', '微信直连-V3转账/退款'),
        ('alipay_wap', '支付宝-手机网站'),
        ('fubei_wx_mini', '付呗-微信小程序'),
    )

    club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部ID')
    channel = models.CharField(max_length=32, choices=CHANNEL_CHOICES, db_index=True)
    name = models.CharField(max_length=64, verbose_name='配置名称', help_text='如：主付呗、备用微信')
    enabled = models.IntegerField(default=1, verbose_name='1启用0停用')
    is_default = models.BooleanField(default=True, verbose_name='同通道默认配置')
    sort_order = models.IntegerField(default=0, verbose_name='前端展示顺序')
    sandbox = models.BooleanField(default=False, verbose_name='沙箱/联调')
    app_id = models.CharField(max_length=64, blank=True, default='', verbose_name='AppID/付呗ID')
    mch_id = models.CharField(max_length=64, blank=True, default='', verbose_name='商户号')
    api_key_enc = models.TextField(blank=True, default='', verbose_name='API密钥/付呗Secret(加密)')
    private_key_enc = models.TextField(blank=True, default='', verbose_name='应用私钥(加密)')
    public_key_enc = models.TextField(blank=True, default='', verbose_name='平台公钥(加密或明文)')
    cert_serial_no = models.CharField(max_length=64, blank=True, default='')
    private_key_path = models.CharField(max_length=255, blank=True, default='')
    platform_cert_dir = models.CharField(max_length=255, blank=True, default='')
    notify_url = models.CharField(max_length=512, blank=True, default='', verbose_name='异步回调URL')
    return_url = models.CharField(max_length=512, blank=True, default='', verbose_name='同步返回URL')
    gateway_url = models.CharField(max_length=255, blank=True, default='', verbose_name='API网关')
    mini_app_id = models.CharField(
        max_length=32, blank=True, default='',
        verbose_name='支付用小程序AppID',
        help_text='付呗/微信JSAPI；空则回落 club.wx_appid',
    )
    extra_json = models.JSONField(blank=True, default=dict, verbose_name='扩展配置')
    remark = models.CharField(max_length=255, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_payment_channel'
        verbose_name = '俱乐部支付通道'
        unique_together = [['club_id', 'channel', 'name']]
        indexes = [
            models.Index(fields=['club_id', 'channel', 'enabled'], name='idx_cpc_club_ch_en'),
            models.Index(fields=['club_id', 'is_default'], name='idx_cpc_club_default'),
        ]


class PayChannelLedger(QModel):
    """下单时记录实际支付通道，供回调路由与退款选用（前端无感知）。"""
    out_trade_no = models.CharField(max_length=64, unique=True, db_index=True, verbose_name='商户订单号')
    club_id = models.CharField(max_length=16, db_index=True, default='')
    channel = models.CharField(max_length=32, db_index=True, verbose_name='通道')
    channel_config_id = models.IntegerField(null=True, blank=True)
    biz_type = models.CharField(max_length=16, blank=True, default='', verbose_name='order/czjilu')
    provider_order_sn = models.CharField(max_length=64, blank=True, default='', verbose_name='通道侧订单号')
    amount_yuan = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'pay_channel_ledger'
        verbose_name = '支付通道流水'


class UserWxOpenid(QModel):
    """微信 openid 与俱乐部用户绑定（club_id + openid 唯一）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    openid = models.CharField(max_length=64, db_index=True)
    yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
    unionid = models.CharField(max_length=64, null=True, blank=True)
    # 小程序与服务号 openid 不同；同用户同俱乐部可各绑一条，支付时按 AppID 选取
    wx_appid = models.CharField(
        max_length=32, blank=True, default='', db_index=True,
        help_text='产生该 openid 的 AppID：小程序或服务号；空=历史小程序绑定',
    )
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'user_wx_openid'
        unique_together = [['club_id', 'openid']]


class AdminAssignment(QModel):
    """管理任职：集团或子公司后台权限范围。"""
    yonghuid = models.CharField(max_length=7, db_index=True)
    club_id = models.CharField(max_length=16, null=True, blank=True, db_index=True,
                                verbose_name='NULL=集团任职')
    role_code = models.CharField(max_length=50, default='CLUB_ADMIN')
    data_scope = models.CharField(max_length=20, default='SINGLE_CLUB')
    is_primary = models.BooleanField(default=False)
    granted_by = models.CharField(max_length=7, blank=True, default='')
    status = models.IntegerField(default=1, verbose_name='1有效0停用')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'admin_assignment'
        unique_together = [['yonghuid', 'club_id', 'role_code']]


class AdminAuditLog(QModel):
    """后台敏感操作审计（改余额等）。"""
    club_id = models.CharField(max_length=16, null=True, blank=True, db_index=True)
    operator_yonghuid = models.CharField(max_length=16, db_index=True)
    operator_role_code = models.CharField(max_length=50, blank=True, default='')
    target_yonghuid = models.CharField(max_length=7, null=True, blank=True)
    action = models.CharField(max_length=64, db_index=True)
    resource_type = models.CharField(max_length=32, blank=True, default='')
    resource_id = models.CharField(max_length=64, blank=True, default='')
    field_name = models.CharField(max_length=64, blank=True, default='')
    value_before = models.TextField(null=True, blank=True)
    value_after = models.TextField(null=True, blank=True)
    request_ip = models.CharField(max_length=64, blank=True, default='')
    remark = models.CharField(max_length=500, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'admin_audit_log'
        indexes = [models.Index(fields=['club_id', '-CreateTime'])]


class ClubHuiyuanPrice(QModel):
    """各俱乐部会员售价/分成（全局 huiyuan_id 定义不变）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    huiyuan_id = models.CharField(max_length=6, db_index=True)
    jiage = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))
    guanshifc = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))
    zuzhangfc = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))
    formal_days = models.PositiveIntegerField(default=30, verbose_name='正式会员有效天数')
    trial_enabled = models.BooleanField(default=False, verbose_name='是否开启体验会员')
    trial_price = models.DecimalField(
        max_digits=10, decimal_places=2, default=Decimal('0.00'), verbose_name='体验会员价格',
    )
    trial_days = models.PositiveIntegerField(default=0, verbose_name='体验会员有效天数')
    trial_guanshifc = models.DecimalField(
        max_digits=10, decimal_places=2, default=Decimal('0.00'), verbose_name='体验管事分成',
    )
    trial_zuzhangfc = models.DecimalField(
        max_digits=10, decimal_places=2, default=Decimal('0.00'), verbose_name='体验组长分成',
    )
    card_image = models.CharField(
        max_length=500, blank=True, default='', verbose_name='充值页会员卡片装饰图(相对URL)',
    )
    card_bg = models.CharField(
        max_length=500, blank=True, default='', verbose_name='充值页会员卡片背景图(相对URL,可选)',
    )
    is_enabled = models.BooleanField(default=True)
    bankuai_id = models.IntegerField(null=True, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_huiyuan_price'
        unique_together = [['club_id', 'huiyuan_id']]


class ClubHuiyuanBundleInclude(QModel):
    """俱乐部总会员包含的子会员类型（仅 is_bundle 会员使用）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    bundle_huiyuan_id = models.CharField(max_length=6, db_index=True, verbose_name='总会员ID')
    included_huiyuan_id = models.CharField(max_length=6, db_index=True, verbose_name='包含的子会员ID')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_huiyuan_bundle_include'
        unique_together = [['club_id', 'bundle_huiyuan_id', 'included_huiyuan_id']]


class ClubShangpinLeixingConfig(QModel):
    """各俱乐部小程序商品类型上架与图标（全局 ShangpinLeixing 由集团定义）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    leixing_id = models.IntegerField(db_index=True, verbose_name='全局商品类型ID')
    is_enabled = models.BooleanField(default=True, verbose_name='是否上架')
    tupian_url = models.CharField(max_length=500, blank=True, default='', verbose_name='俱乐部专属图标')
    paixu = models.IntegerField(default=0, verbose_name='排序权重')
    # 新字段：旧小程序忽略；开启后该类型下真单强制展示价格（不改 hasRequiredMember 抢单逻辑）
    show_price = models.BooleanField(
        default=False,
        verbose_name='强制展示订单价格',
        help_text='开启后小程序该类型订单显示分成价格，即使未开通对应会员',
    )
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_shangpin_leixing_config'
        unique_together = [['club_id', 'leixing_id']]


class ClubKaoheChenghaoConfig(QModel):
    """各俱乐部抢单池标签筛选区上架配置（全局 Chenghao 由集团定义）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    chenghao_id = models.IntegerField(db_index=True, verbose_name='全局称号ID')
    is_enabled = models.BooleanField(default=True, verbose_name='是否在抢单池筛选区展示')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_kaohe_chenghao_config'
        unique_together = [['club_id', 'chenghao_id']]


class ClubLilubiao(QModel):
    """各俱乐部分红/手续费配置（对应 lilubiao.fadanpingtai）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    config_type = models.IntegerField(db_index=True, verbose_name='同 lilubiao.fadanpingtai')
    lilv = models.DecimalField(max_digits=10, decimal_places=4, default=Decimal('0.0000'))
    remark = models.CharField(max_length=255, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_lilubiao'
        unique_together = [['club_id', 'config_type']]


class ClubWithdrawConfig(QModel):
    club_id = models.CharField(max_length=16, primary_key=True)
    mode = models.IntegerField(default=1, verbose_name='1自动2手动')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_withdraw_config'


class ClubTixianQuota(QModel):
    club_id = models.CharField(max_length=16, db_index=True)
    leixing = models.IntegerField(db_index=True)
    daily_limit = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_tixian_quota'
        unique_together = [['club_id', 'leixing']]


class ClubFadanFenhongLilv(QModel):
    club_id = models.CharField(max_length=16, db_index=True)
    shenfen = models.IntegerField(db_index=True)
    lilv = models.DecimalField(max_digits=10, decimal_places=4, default=Decimal('0.0000'))
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_fadan_fenhong_lilv'
        unique_together = [['club_id', 'shenfen']]


class IdentityTag(QModel):
    """身份装饰标签定义（与考核称号、订单需求标签分离）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    name = models.CharField(max_length=64, verbose_name='标签名')
    color = models.CharField(max_length=32, default='#000000', verbose_name='主色')
    flash_enabled = models.BooleanField(default=False, verbose_name='闪光特效')
    texiao_json = models.TextField(blank=True, default='', verbose_name='特效JSON')
    sort_order = models.IntegerField(default=0)
    status = models.IntegerField(default=1, verbose_name='1启用0停用')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'identity_tag'
        ordering = ['sort_order', 'id']


class IdentityTagBind(QModel):
    """用户身份与装饰标签绑定。"""
    club_id = models.CharField(max_length=16, db_index=True)
    yonghuid = models.CharField(max_length=7, db_index=True)
    shenfen = models.IntegerField(verbose_name='1打手2管事3商家4组长')
    tag = models.ForeignKey(IdentityTag, on_delete=models.CASCADE, related_name='binds')
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'identity_tag_bind'
        unique_together = [['club_id', 'yonghuid', 'shenfen', 'tag']]
        indexes = [
            models.Index(fields=['club_id', 'yonghuid', 'shenfen']),
        ]


class ClubDashouExamConfig(QModel):
    """各俱乐部打手抢单前考试配置（互不影响）。"""
    club_id = models.CharField(max_length=16, primary_key=True)
    is_enabled = models.BooleanField(default=False, verbose_name='是否开启考试')
    draw_count = models.PositiveIntegerField(default=10, verbose_name='每次抽题数量')
    pass_count = models.PositiveIntegerField(default=10, verbose_name='至少答对题数')
    require_member = models.BooleanField(default=True, verbose_name='须开通会员方可考试')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_dashou_exam_config'


class ClubDashouExamQuestion(QModel):
    """俱乐部考试题库（题干+最多6选项）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    stem = models.TextField(verbose_name='题干文字')
    question_type = models.IntegerField(default=1, verbose_name='1单选2多选')
    option_1 = models.CharField(max_length=500, blank=True, default='')
    option_2 = models.CharField(max_length=500, blank=True, default='')
    option_3 = models.CharField(max_length=500, blank=True, default='')
    option_4 = models.CharField(max_length=500, blank=True, default='')
    option_5 = models.CharField(max_length=500, blank=True, default='')
    option_6 = models.CharField(max_length=500, blank=True, default='')
    correct_1 = models.BooleanField(default=False)
    correct_2 = models.BooleanField(default=False)
    correct_3 = models.BooleanField(default=False)
    correct_4 = models.BooleanField(default=False)
    correct_5 = models.BooleanField(default=False)
    correct_6 = models.BooleanField(default=False)
    explanation = models.TextField(blank=True, default='', verbose_name='错题解析')
    is_enabled = models.BooleanField(default=True, verbose_name='是否上架')
    sort_order = models.IntegerField(default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_dashou_exam_question'
        indexes = [models.Index(fields=['club_id', 'is_enabled'])]


class ClubDashouExamQuestionImage(QModel):
    """题目配图（题干下方，可多张）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    question_id = models.IntegerField(db_index=True)
    image_url = models.CharField(max_length=500)
    sort_order = models.IntegerField(default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'club_dashou_exam_question_image'
        indexes = [models.Index(fields=['club_id', 'question_id'])]


class ClubDashouExamPass(QModel):
    """打手在本俱乐部是否已通过考试。"""
    club_id = models.CharField(max_length=16, db_index=True)
    yonghuid = models.CharField(max_length=7, db_index=True)
    passed_at = models.DateTimeField(auto_now_add=True)
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'club_dashou_exam_pass'
        unique_together = [['club_id', 'yonghuid']]


class ClubRechargePageCopy(QModel):
    """会员充值页 / 履约金页按俱乐部可配置的文案与图标。"""
    club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部ID')
    page_key = models.CharField(max_length=16, db_index=True, verbose_name='huiyuan|yajin')
    region_key = models.CharField(max_length=32, db_index=True, verbose_name='rules|benefits|advantages')
    title = models.CharField(max_length=64, blank=True, default='', verbose_name='区域标题')
    items_json = models.JSONField(default=list, blank=True, verbose_name='条目列表JSON')
    enabled = models.BooleanField(default=True, verbose_name='是否启用该区域')
    sort_order = models.IntegerField(default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_recharge_page_copy'
        unique_together = [['club_id', 'page_key', 'region_key']]
        verbose_name = '充值页文案配置'


class KefuMenuPage(QModel):
    """客服后台左侧菜单页（固定 page_id，与前端路由对应）。"""
    page_id = models.CharField(max_length=64, primary_key=True, verbose_name='页面ID')
    name = models.CharField(max_length=64, verbose_name='展示名')
    path = models.CharField(max_length=128, blank=True, default='', verbose_name='路由路径，分组为空')
    parent_id = models.CharField(max_length=64, blank=True, default='', db_index=True)
    sort_order = models.IntegerField(default=0)
    perm_codes = models.JSONField(default=list, blank=True, verbose_name='拥有任一权限即可见')
    require_super = models.BooleanField(default=False, verbose_name='仅超级管理000001')
    require_group_manage = models.BooleanField(default=False, verbose_name='仅集团超管任职')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'kefu_menu_page'
        ordering = ['sort_order', 'page_id']


class ClubOrderGrabLink(QModel):
    """
    俱乐部订单单向互通抢单。
    grabber_club_id 的打手可抢 order_club_id 的待接单；反向需另建一条。
    关停：删除记录即可（两字段，无开关列）。
    """
    grabber_club_id = models.CharField(max_length=16, db_index=True, verbose_name='抢单方俱乐部')
    order_club_id = models.CharField(max_length=16, db_index=True, verbose_name='被抢订单所属俱乐部')
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'club_order_grab_link'
        verbose_name = '俱乐部订单互通抢单'
        unique_together = [['grabber_club_id', 'order_club_id']]
        indexes = [
            models.Index(fields=['grabber_club_id'], name='idx_cogl_grabber'),
            models.Index(fields=['order_club_id'], name='idx_cogl_order'),
        ]


# ==================== 资金冻结（默认关闭，按俱乐部×角色开启）====================

class ClubFundFreezeConfig(QModel):
    """俱乐部×角色冻结规则。enabled=False 时入账行为与现网完全一致。"""
    ROLE_CHOICES = (
        ('dashou', '打手'),
        ('guanshi', '管事'),
        ('zuzhang', '组长'),
        ('shenheguan', '考核官'),
    )
    FREEZE_MODE_CHOICES = (
        (1, '按比例'),
        (2, '固定额'),
        (3, '比例+单笔上限'),
    )
    UNFREEZE_TRIGGER_CHOICES = (
        ('time_only', '仅到期'),
        ('condition_only', '仅条件达标'),
        ('time_and_condition', '到期且达标'),
        ('time_or_condition', '到期或达标'),
        ('manual_only', '仅手动'),
    )
    SCHEDULE_MODE_CHOICES = (
        ('all_at_once', '一次全解'),
        ('installment', '分期解冻'),
    )

    club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部')
    role = models.CharField(max_length=16, choices=ROLE_CHOICES, db_index=True, verbose_name='角色')
    enabled = models.BooleanField(default=False, verbose_name='是否启用冻结')
    freeze_mode = models.SmallIntegerField(default=1, choices=FREEZE_MODE_CHOICES)
    freeze_rate = models.DecimalField(
        max_digits=8, decimal_places=6, default=Decimal('0'),
        verbose_name='冻结比例', help_text='0.2=20%',
    )
    freeze_fixed = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0'))
    freeze_cap = models.DecimalField(
        max_digits=12, decimal_places=2, default=Decimal('0'),
        verbose_name='单笔冻结上限(0=不限)',
    )
    min_credit_to_freeze = models.DecimalField(
        max_digits=12, decimal_places=2, default=Decimal('0'),
        verbose_name='低于此入账不冻',
    )
    source_scope = models.JSONField(
        default=list, blank=True,
        verbose_name='入账来源白名单',
        help_text='空=全部来源；如 ["order_settle"]',
    )
    unfreeze_trigger = models.CharField(
        max_length=32, default='time_only', choices=UNFREEZE_TRIGGER_CHOICES,
    )
    schedule_mode = models.CharField(
        max_length=16, default='all_at_once', choices=SCHEDULE_MODE_CHOICES,
    )
    installment_json = models.JSONField(
        default=dict, blank=True,
        verbose_name='分期计划',
        help_text='{"steps":[{"seq":1,"after_days":7,"type":"ratio","value":0.3,"need_condition":false}]}',
    )
    condition_json = models.JSONField(default=dict, blank=True, verbose_name='解冻条件')
    freeze_min_days = models.IntegerField(default=0, verbose_name='最短冻结天数')
    remark = models.CharField(max_length=255, blank=True, default='')
    updated_by = models.CharField(max_length=32, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_fund_freeze_config'
        verbose_name = '资金冻结规则'
        unique_together = [['club_id', 'role']]


class FundFreezeLedger(QModel):
    """单笔入账冻结主单。"""
    STATUS_FROZEN = 1
    STATUS_PARTIAL = 2
    STATUS_DONE = 3
    STATUS_VOID = 4

    club_id = models.CharField(max_length=16, db_index=True)
    user_id = models.CharField(max_length=16, db_index=True, verbose_name='UserUID')
    role = models.CharField(max_length=16, db_index=True)
    amount_total = models.DecimalField(max_digits=12, decimal_places=2)
    amount_unfrozen = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0'))
    status = models.SmallIntegerField(default=1, db_index=True)
    frozen_at = models.DateTimeField(auto_now_add=True, db_index=True)
    source_type = models.CharField(max_length=32, db_index=True)
    order_id = models.CharField(max_length=64, blank=True, default='', db_index=True)
    czjilu_id = models.CharField(max_length=64, blank=True, default='', db_index=True)
    biz_id = models.CharField(max_length=64, verbose_name='幂等业务键')
    freeze_reason = models.CharField(max_length=255, blank=True, default='')
    config_snapshot = models.JSONField(default=dict, blank=True)
    current_step = models.IntegerField(default=1)
    next_eligible_at = models.DateTimeField(null=True, blank=True, db_index=True)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'fund_freeze_ledger'
        verbose_name = '资金冻结主单'
        unique_together = [['club_id', 'role', 'source_type', 'biz_id']]
        indexes = [
            models.Index(fields=['status', 'next_eligible_at'], name='idx_ffl_status_next'),
            models.Index(fields=['user_id', 'role', 'status'], name='idx_ffl_user_role'),
            models.Index(fields=['club_id', 'status', 'frozen_at'], name='idx_ffl_club_st'),
        ]


class FundUnfreezeEvent(QModel):
    """每一次解冻执行记录。"""
    ledger_id = models.BigIntegerField(db_index=True)
    club_id = models.CharField(max_length=16, db_index=True)
    user_id = models.CharField(max_length=16, db_index=True)
    role = models.CharField(max_length=16)
    step_seq = models.IntegerField(default=1)
    amount = models.DecimalField(max_digits=12, decimal_places=2)
    unfrozen_at = models.DateTimeField(auto_now_add=True, db_index=True)
    trigger = models.CharField(max_length=16, default='schedule')
    reason = models.CharField(max_length=255, blank=True, default='')
    operator_id = models.CharField(max_length=32, blank=True, default='')
    condition_snapshot = models.JSONField(default=dict, blank=True)

    class Meta:
        db_table = 'fund_unfreeze_event'
        verbose_name = '资金解冻事件'


class FundRiskStatDaily(QModel):
    """日汇总：罚款/投诉/完成单等，供解冻条件与后台展示。"""
    club_id = models.CharField(max_length=16, db_index=True)
    user_id = models.CharField(max_length=16, db_index=True)
    role = models.CharField(max_length=16, db_index=True)
    day = models.DateField(db_index=True)
    completed_orders = models.IntegerField(default=0)
    fine_count = models.IntegerField(default=0)
    complaint_count = models.IntegerField(default=0)
    valid_invite_count = models.IntegerField(default=0)
    meta_json = models.JSONField(default=dict, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'fund_risk_stat_daily'
        verbose_name = '资金风险日汇总'
        unique_together = [['club_id', 'user_id', 'role', 'day']]
        indexes = [
            models.Index(fields=['club_id', 'role', 'day'], name='idx_frsd_club_role_day'),
        ]


class ClubRoleAgreement(QModel):
    """俱乐部×身份用户协议（当前生效配置，发布时 version+1）。"""
    ROLE_CHOICES = (
        ('dashou', '打手'),
        ('guanshi', '管事'),
        ('zuzhang', '组长'),
    )

    club_id = models.CharField(max_length=16, db_index=True)
    role = models.CharField(max_length=16, choices=ROLE_CHOICES, db_index=True)
    enabled = models.BooleanField(default=False, verbose_name='是否要求签署')
    title = models.CharField(max_length=128, blank=True, default='', verbose_name='协议标题')
    content_text = models.TextField(blank=True, default='', verbose_name='协议正文')
    images_json = models.JSONField(default=list, blank=True, verbose_name='说明图片URL列表')
    # 勾选前强制停留秒数，0=可立即勾选
    read_seconds = models.PositiveIntegerField(default=2, verbose_name='阅读秒数')
    # 卡点开关：qiangdan/register/tixian/invite，按角色默认见服务层
    gate_config = models.JSONField(default=dict, blank=True, verbose_name='签署卡点配置')
    version = models.PositiveIntegerField(default=0, verbose_name='已发布版本号')
    published_at = models.DateTimeField(null=True, blank=True)
    updated_by = models.CharField(max_length=32, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_role_agreement'
        verbose_name = '角色协议'
        unique_together = [['club_id', 'role']]


class ClubRoleAgreementSign(QModel):
    """用户签署某俱乐部某身份某版本协议的留痕（勾选确认，无手写图）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    user_id = models.CharField(max_length=16, db_index=True, verbose_name='UserUID')
    role = models.CharField(max_length=16, db_index=True)
    agreement_id = models.BigIntegerField(db_index=True, default=0)
    version = models.PositiveIntegerField(db_index=True)
    title_snapshot = models.CharField(max_length=128, blank=True, default='')
    content_snapshot = models.TextField(blank=True, default='')
    images_snapshot = models.JSONField(default=list, blank=True)
    # 兼容旧字段，新签署固定留空
    sign_image_url = models.CharField(max_length=512, blank=True, default='')
    client_meta = models.JSONField(default=dict, blank=True)
    signed_at = models.DateTimeField(auto_now_add=True, db_index=True)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_role_agreement_sign'
        verbose_name = '角色协议签署记录'
        unique_together = [['club_id', 'user_id', 'role', 'version']]
        indexes = [
            models.Index(fields=['user_id', 'role', 'version'], name='idx_cras_user_role_ver'),
            models.Index(fields=['club_id', 'role', 'signed_at'], name='idx_cras_club_role_t'),
        ]


class ClubOrderDealConfig(QModel):
    """俱乐部：成交指标统计开关 + 可控自动结算开关（默认全关）。"""
    club_id = models.CharField(max_length=16, unique=True, db_index=True, verbose_name='俱乐部')
    auto_settle_enabled = models.BooleanField(default=False, verbose_name='自动结算开关')
    auto_settle_enabled_at = models.DateTimeField(null=True, blank=True, verbose_name='本次打开自动结算时间')
    hours_platform = models.PositiveIntegerField(default=48, verbose_name='平台单超时小时')
    hours_merchant = models.PositiveIntegerField(default=48, verbose_name='商家单超时小时')
    stats_enabled = models.BooleanField(default=False, verbose_name='成交指标统计开关')
    stats_since = models.DateTimeField(null=True, blank=True, verbose_name='指标统计起点')
    updated_by = models.CharField(max_length=32, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_order_deal_config'
        verbose_name = '成交与自动结算配置'


class MerchantDealStat(QModel):
    """商家成交/罚款/退款指标快照（仅统计起点之后事件累加）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    merchant_uid = models.CharField(max_length=16, db_index=True, verbose_name='商家UserUID')
    order_count = models.PositiveIntegerField(default=0)
    completed_count = models.PositiveIntegerField(default=0)
    refund_count = models.PositiveIntegerField(default=0)
    fine_paid_count = models.PositiveIntegerField(default=0)
    settle_duration_sum_seconds = models.BigIntegerField(default=0)
    settle_duration_sample_count = models.PositiveIntegerField(default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'merchant_deal_stat'
        verbose_name = '商家成交指标'
        unique_together = [['club_id', 'merchant_uid']]


class DashouDealStat(QModel):
    """打手成交/被罚款指标快照。"""
    club_id = models.CharField(max_length=16, db_index=True)
    dashou_uid = models.CharField(max_length=16, db_index=True, verbose_name='打手UserUID')
    order_count = models.PositiveIntegerField(default=0)
    completed_count = models.PositiveIntegerField(default=0)
    fine_paid_count = models.PositiveIntegerField(default=0)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'dashou_deal_stat'
        verbose_name = '打手成交指标'
        unique_together = [['club_id', 'dashou_uid']]


class DealStatEvent(QModel):
    """指标幂等事件（防重复记账）。"""
    club_id = models.CharField(max_length=16, db_index=True)
    event_key = models.CharField(max_length=96, unique=True, db_index=True)
    event_type = models.CharField(max_length=32, db_index=True)
    order_id = models.CharField(max_length=32, blank=True, default='', db_index=True)
    subject_uid = models.CharField(max_length=16, blank=True, default='', db_index=True)
    meta_json = models.JSONField(default=dict, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'deal_stat_event'
        verbose_name = '成交指标事件'


# ==================== 跨俱乐部资产迁移（独立模块，默认关闭不影响现网）====================

class ClubMigratePlan(QModel):
    """迁移计划：from_club → to_club，未启用时小程序完全无感。"""
    plan_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='计划ID')
    name = models.CharField(max_length=64, blank=True, default='', verbose_name='计划名称')
    from_club_id = models.CharField(max_length=16, db_index=True, verbose_name='源俱乐部')
    to_club_id = models.CharField(max_length=16, db_index=True, verbose_name='目标俱乐部')
    enabled = models.BooleanField(default=False, db_index=True, verbose_name='是否启用')
    # null=永久；有值则必须在区间内
    start_at = models.DateTimeField(null=True, blank=True, verbose_name='开始时间')
    end_at = models.DateTimeField(null=True, blank=True, verbose_name='结束时间')
    # ["dashou.yue","dashou.yajin","dashou.jifen","dashou.dongjie_yue","dashou.huiyuan", ...]
    asset_whitelist = models.JSONField(default=list, blank=True, verbose_name='资产白名单')
    # {"require_no_unpaid_penalty":true,"require_orders_closed":false,"require_not_banned":true}
    condition_json = models.JSONField(default=dict, blank=True, verbose_name='门槛条件')
    force_intercept = models.BooleanField(default=True, verbose_name='源店强制迁移页')
    # ["dashou"] 首期默认打手
    force_roles = models.JSONField(default=list, blank=True, verbose_name='强制角色列表')
    default_once_per_user = models.BooleanField(default=True, verbose_name='默认每人成功一次')
    remark = models.CharField(max_length=255, blank=True, default='')
    updated_by = models.CharField(max_length=32, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_migrate_plan'
        verbose_name = '跨店资产迁移计划'
        indexes = [
            models.Index(fields=['from_club_id', 'enabled'], name='idx_cmp_from_en'),
            models.Index(fields=['to_club_id', 'enabled'], name='idx_cmp_to_en'),
        ]


class ClubMigrateToken(QModel):
    """一次性迁移令牌（设密后签发，确认后作废）。"""
    STATUS_PENDING_PASSWORD = 0
    STATUS_ISSUED = 1
    STATUS_CONSUMED = 2
    STATUS_EXPIRED = 3
    STATUS_CANCELLED = 4

    token = models.CharField(max_length=64, unique=True, db_index=True, verbose_name='令牌')
    plan_id = models.CharField(max_length=32, db_index=True)
    from_club_id = models.CharField(max_length=16, db_index=True)
    to_club_id = models.CharField(max_length=16, db_index=True)
    from_uid = models.CharField(max_length=16, db_index=True, verbose_name='源UserUID')
    password_hash = models.CharField(max_length=128, blank=True, default='', verbose_name='转移密码哈希')
    password_salt = models.CharField(max_length=32, blank=True, default='')
    status = models.SmallIntegerField(default=0, db_index=True)
    fail_count = models.PositiveIntegerField(default=0, verbose_name='验密失败次数')
    asset_snapshot = models.JSONField(default=dict, blank=True, verbose_name='发码时资产快照')
    qrcode_url = models.CharField(max_length=512, blank=True, default='', verbose_name='目标店小程序码URL')
    expire_at = models.DateTimeField(null=True, blank=True, db_index=True)
    consumed_at = models.DateTimeField(null=True, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_migrate_token'
        verbose_name = '跨店迁移令牌'
        indexes = [
            models.Index(fields=['plan_id', 'from_uid', 'status'], name='idx_cmt_plan_uid_st'),
        ]


class ClubMigrateConsumption(QModel):
    """计划×源用户成功占用（防并发双迁；默认一次）。"""
    plan_id = models.CharField(max_length=32, db_index=True)
    from_uid = models.CharField(max_length=16, db_index=True)
    success_count = models.PositiveIntegerField(default=0)
    last_ledger_id = models.BigIntegerField(null=True, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_migrate_consumption'
        verbose_name = '跨店迁移占用'
        unique_together = [['plan_id', 'from_uid']]


class ClubMigrateUserQuota(QModel):
    """例外：指定源用户在某计划下可额外成功次数。"""
    plan_id = models.CharField(max_length=32, db_index=True)
    from_uid = models.CharField(max_length=16, db_index=True)
    max_extra_times = models.PositiveIntegerField(default=1, verbose_name='额外成功次数')
    reason = models.CharField(max_length=255, blank=True, default='')
    updated_by = models.CharField(max_length=32, blank=True, default='')
    CreateTime = models.DateTimeField(auto_now_add=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_migrate_user_quota'
        verbose_name = '跨店迁移二次白名单'
        unique_together = [['plan_id', 'from_uid']]


class ClubMigrateLedger(QModel):
    """一次成功过户主单（P0 溯源）。"""
    STATUS_SUCCESS = 1
    STATUS_REVERSED = 2

    plan_id = models.CharField(max_length=32, db_index=True)
    token = models.CharField(max_length=64, blank=True, default='', db_index=True)
    from_club_id = models.CharField(max_length=16, db_index=True)
    to_club_id = models.CharField(max_length=16, db_index=True)
    from_uid = models.CharField(max_length=16, db_index=True)
    to_uid = models.CharField(max_length=16, db_index=True)
    migrate_seq = models.PositiveIntegerField(default=1, verbose_name='本计划第几次')
    status = models.SmallIntegerField(default=1, db_index=True)
    summary_json = models.JSONField(default=dict, blank=True, verbose_name='摘要')
    client_meta = models.JSONField(default=dict, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True, db_index=True)
    UpdateTime = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'club_migrate_ledger'
        verbose_name = '跨店迁移流水主单'
        indexes = [
            models.Index(fields=['from_uid', 'plan_id'], name='idx_cml_from_plan'),
            models.Index(fields=['to_uid', 'plan_id'], name='idx_cml_to_plan'),
            models.Index(fields=['from_club_id', 'to_club_id', 'CreateTime'], name='idx_cml_clubs_t'),
        ]


class ClubMigrateLedgerItem(QModel):
    """流水明细：每个角色字段一行（金额前后值必填，便于对账纠错）。"""
    ITEM_BALANCE = 'balance'
    ITEM_HUIYUAN = 'huiyuan'

    ledger_id = models.BigIntegerField(db_index=True)
    plan_id = models.CharField(max_length=32, db_index=True)
    item_type = models.CharField(max_length=16, default='balance', db_index=True)
    role = models.CharField(max_length=16, db_index=True)
    field = models.CharField(max_length=32, blank=True, default='', verbose_name='字段名')
    amount = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0'))
    before_src = models.CharField(max_length=64, blank=True, default='')
    after_src = models.CharField(max_length=64, blank=True, default='')
    before_dst = models.CharField(max_length=64, blank=True, default='')
    after_dst = models.CharField(max_length=64, blank=True, default='')
    # 会员专用
    huiyuan_id = models.CharField(max_length=16, blank=True, default='', db_index=True)
    meta_json = models.JSONField(default=dict, blank=True)
    CreateTime = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'club_migrate_ledger_item'
        verbose_name = '跨店迁移流水明细'
        indexes = [
            models.Index(fields=['ledger_id', 'role'], name='idx_cmli_led_role'),
        ]
