# Generated by Django 5.2.12 on 2026-04-30 10:08

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('device_interaction', '0003_userdevice_affinity_level_userdevice_favorability_and_more'),
        ('userapp', '0004_affinitylevel_affinityrule'),
    ]

    operations = [
        migrations.CreateModel(
            name='AffinitySetting',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('initial_affinity', models.IntegerField(default=10, help_text='新建 UserDevice 绑定时的初始好感度', verbose_name='初始好感度')),
                ('max_affinity', models.IntegerField(default=100, help_text='好感度上限（管理员手动调整也不能突破）', verbose_name='最大好感度')),
                ('daily_cap', models.IntegerField(default=20, help_text='每台设备每日好感度净增长上限（跨规则汇总，仅限正向）', verbose_name='每日全局增长上限')),
                ('decay_rate', models.IntegerField(default=2, verbose_name='全局衰减速率(点/天)')),
                ('decay_threshold', models.IntegerField(default=3, help_text='设备多少天不互动后开始衰减', verbose_name='衰减开始天数')),
                ('decay_min_decay', models.IntegerField(default=1, verbose_name='单日衰减最小值')),
                ('decay_max_decay', models.IntegerField(default=3, verbose_name='单日衰减最大值')),
                ('decay_cap', models.IntegerField(default=5, verbose_name='单日衰减上限')),
                ('decay_min_floor', models.IntegerField(default=0, help_text='好感度不会低于此值', verbose_name='衰减下限')),
                ('enable_notify', models.BooleanField(default=True, verbose_name='启用变化通知')),
                ('enable_rewards', models.BooleanField(default=True, verbose_name='启用等级奖励')),
                ('notify_decay', models.BooleanField(default=True, verbose_name='衰减时通知')),
                ('timezone', models.CharField(default='Asia/Shanghai', help_text='自然日 0 点重置基准（全用户统一）', max_length=50, verbose_name='时区')),
                ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
                ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
            ],
            options={
                'verbose_name': '好感度系统设置',
                'verbose_name_plural': '好感度系统设置',
            },
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='is_deleted',
            field=models.BooleanField(default=False, verbose_name='已删除（软删除）'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='is_enabled',
            field=models.BooleanField(default=True, verbose_name='已启用'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='max_affinity',
            field=models.IntegerField(default=0, help_text='等级好感度区间上限（闭区间）', verbose_name='最大好感度'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='min_affinity',
            field=models.IntegerField(default=0, help_text='等级好感度区间下限（闭区间）', verbose_name='最小好感度'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='reward_currency',
            field=models.IntegerField(default=0, verbose_name='虚拟货币奖励'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='reward_items',
            field=models.JSONField(default=list, help_text='道具列表，如 [{"item_id": 1, "qty": 2}]', verbose_name='道具奖励'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='reward_type',
            field=models.CharField(choices=[('unlock', '内容解锁'), ('item', '道具奖励'), ('currency', '虚拟货币'), ('mixed', '混合奖励'), ('none', '无奖励')], default='unlock', max_length=10, verbose_name='奖励类型'),
        ),
        migrations.AddField(
            model_name='affinitylevel',
            name='unlock_content',
            field=models.TextField(blank=True, help_text='展示文案，描述该等级解锁的功能/外观/剧情等', verbose_name='解锁内容'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='cooldown_seconds',
            field=models.IntegerField(default=0, help_text='同一设备同一规则的最小触发间隔，0 表示无冷却', verbose_name='冷却时间(秒)'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='daily_cap',
            field=models.IntegerField(default=20, help_text='本规则在每台设备每日累计变化上限（绝对值）', verbose_name='每日上限'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='is_deleted',
            field=models.BooleanField(default=False, verbose_name='已删除（软删除）'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='is_enabled',
            field=models.BooleanField(default=True, verbose_name='已启用'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='is_negative',
            field=models.BooleanField(default=False, verbose_name='是否负向规则'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='max_change',
            field=models.IntegerField(default=1, help_text='单次变化最大值（含），实际值在 [min, max] 之间随机取整数', verbose_name='最大变化值'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='max_count_per_day',
            field=models.IntegerField(blank=True, help_text='trigger_type=companion_time 时使用：每台设备每日最多触发次数', null=True, verbose_name='每日最多次数'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='min_change',
            field=models.IntegerField(default=1, help_text='单次变化最小值，负数表示扣分', verbose_name='最小变化值'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='min_continuous_minutes',
            field=models.IntegerField(blank=True, help_text='trigger_type=companion_time 时使用：满 X 分钟触发 1 次', null=True, verbose_name='最小连续分钟数'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='rule_key',
            field=models.CharField(blank=True, help_text='代码标识，客户端事件通过此 key 匹配规则（如 chat/sing/dance/touch...）', max_length=50, null=True, unique=True, verbose_name='规则代码'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='single_cap',
            field=models.IntegerField(default=10, help_text='单次变化绝对值上限（保护性钳位）', verbose_name='单次上限'),
        ),
        migrations.AddField(
            model_name='affinityrule',
            name='trigger_type',
            field=models.CharField(choices=[('action', '动作触发'), ('companion_time', '陪伴时长'), ('decay', '衰减')], default='action', max_length=20, verbose_name='触发类型'),
        ),
        migrations.AlterField(
            model_name='affinitylevel',
            name='required_points',
            field=models.IntegerField(default=0, help_text='已弃用，使用 min_affinity/max_affinity', verbose_name='所需积分(已弃用)'),
        ),
        migrations.AlterField(
            model_name='affinitylevel',
            name='rewards',
            field=models.JSONField(default=list, help_text='已弃用，使用 reward_currency/reward_items', verbose_name='奖励(已弃用)'),
        ),
        migrations.AlterField(
            model_name='affinityrule',
            name='daily_limit',
            field=models.IntegerField(blank=True, help_text='已弃用，使用 daily_cap', null=True, verbose_name='每日上限(已弃用)'),
        ),
        migrations.AlterField(
            model_name='affinityrule',
            name='is_active',
            field=models.BooleanField(default=True, help_text='已弃用，使用 is_enabled', verbose_name='已启用(已弃用)'),
        ),
        migrations.AlterField(
            model_name='affinityrule',
            name='points',
            field=models.IntegerField(default=0, help_text='已弃用，使用 min_change/max_change', verbose_name='积分(已弃用)'),
        ),
        migrations.CreateModel(
            name='AffinityLog',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('rule_key', models.CharField(blank=True, help_text='冗余 rule_key 文本，规则被删除后仍可读', max_length=50, verbose_name='规则代码(冗余)')),
                ('change_value', models.IntegerField(help_text='正负整数，含符号', verbose_name='变化值')),
                ('before_value', models.IntegerField(verbose_name='变化前值')),
                ('after_value', models.IntegerField(verbose_name='变化后值')),
                ('source', models.CharField(choices=[('device_event', '设备端事件'), ('mobile_event', '手机端事件'), ('system_decay', '系统衰减'), ('admin_adjust_single', '管理员单次调整'), ('admin_adjust_batch', '管理员批量调整')], max_length=30, verbose_name='来源')),
                ('event_id', models.CharField(blank=True, db_index=True, help_text='客户端事件 UUID，用于幂等去重', max_length=64, verbose_name='事件ID')),
                ('operator_admin_id', models.IntegerField(blank=True, help_text='source=admin_adjust_* 时记录操作管理员', null=True, verbose_name='操作管理员ID')),
                ('reason', models.TextField(blank=True, help_text='管理员调整时填写的原因/备注', verbose_name='操作原因')),
                ('metadata', models.JSONField(blank=True, default=dict, help_text='扩展字段，如对话 message_id、设备状态等', verbose_name='元数据')),
                ('created_at', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间')),
                ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='affinity_logs', to='device_interaction.userdevice', verbose_name='用户设备绑定')),
                ('rule', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='logs', to='userapp.affinityrule', verbose_name='规则')),
                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='affinity_logs', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
            ],
            options={
                'verbose_name': '好感度变化日志',
                'verbose_name_plural': '好感度变化日志',
                'ordering': ['-created_at'],
                'indexes': [models.Index(fields=['device', '-created_at'], name='userapp_aff_device__42aa0b_idx'), models.Index(fields=['user', '-created_at'], name='userapp_aff_user_id_ab2869_idx'), models.Index(fields=['rule_key', '-created_at'], name='userapp_aff_rule_ke_6572b7_idx'), models.Index(fields=['source', '-created_at'], name='userapp_aff_source_4f0798_idx')],
                'constraints': [models.UniqueConstraint(condition=models.Q(('event_id__gt', '')), fields=('event_id',), name='unique_affinity_event_id')],
            },
        ),
        migrations.CreateModel(
            name='UserAffinityDailyCounter',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('date', models.DateField(db_index=True, help_text='Asia/Shanghai 时区的自然日', verbose_name='日期')),
                ('accumulated_change', models.IntegerField(default=0, verbose_name='累计变化（含符号）')),
                ('trigger_count', models.IntegerField(default=0, verbose_name='触发次数')),
                ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
                ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_counters', to='device_interaction.userdevice', verbose_name='用户设备绑定')),
                ('rule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_counters', to='userapp.affinityrule', verbose_name='规则')),
            ],
            options={
                'verbose_name': '好感度每日计数器',
                'verbose_name_plural': '好感度每日计数器',
                'indexes': [models.Index(fields=['date'], name='userapp_use_date_c2efd8_idx')],
                'unique_together': {('device', 'rule', 'date')},
            },
        ),
        migrations.CreateModel(
            name='UserLevelRewardGrant',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('level', models.IntegerField(verbose_name='等级')),
                ('granted_at', models.DateTimeField(auto_now_add=True, verbose_name='发放时间')),
                ('reward_snapshot', models.JSONField(default=dict, help_text='发放时的奖励内容快照（防 AffinityLevel 后续修改）', verbose_name='奖励快照')),
                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='level_reward_grants', to='device_interaction.userdevice', verbose_name='用户设备绑定')),
            ],
            options={
                'verbose_name': '等级奖励发放记录',
                'verbose_name_plural': '等级奖励发放记录',
                'ordering': ['-granted_at'],
                'unique_together': {('device', 'level')},
            },
        ),
    ]
