# Generated by Django 5.2.15 on 2026-07-21 01:16 import django.db.models.deletion from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ('users', '0005_alter_apikey_created_at_alter_apikey_key_hash_and_more'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='ClientDevice', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('product_code', models.CharField(choices=[('cmshopee', '虾皮圈优化助手')], max_length=32, verbose_name='产品代码')), ('device_id_version', models.CharField(max_length=32, verbose_name='设备标识版本')), ('device_fingerprint', models.CharField(editable=False, max_length=64, verbose_name='设备摘要')), ('public_key_fingerprint', models.CharField(editable=False, max_length=64, verbose_name='安装公钥摘要')), ('platform', models.CharField(choices=[('windows', 'Windows'), ('macos', 'macOS'), ('linux', 'Linux')], max_length=16, verbose_name='平台')), ('client_version', models.CharField(max_length=64, verbose_name='客户端版本')), ('status', models.CharField(choices=[('active', '有效'), ('revoked', '已吊销')], default='active', max_length=20, verbose_name='状态')), ('first_seen_at', models.DateTimeField(auto_now_add=True, verbose_name='首次登记时间')), ('last_seen_at', models.DateTimeField(auto_now_add=True, verbose_name='最后活跃时间')), ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='client_devices', to=settings.AUTH_USER_MODEL, verbose_name='用户')), ], options={ 'verbose_name': '客户端设备', 'verbose_name_plural': '客户端设备', 'db_table': 'client_device', 'ordering': ('-last_seen_at', '-id'), }, ), migrations.CreateModel( name='DeviceBindingAudit', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('action', models.CharField(choices=[('registered', '已登记'), ('session_issued', '会话已签发'), ('heartbeat', '心跳'), ('rejected', '请求被拒绝'), ('revoked', '设备已吊销')], max_length=32, verbose_name='动作')), ('reason', models.CharField(blank=True, max_length=120, verbose_name='原因')), ('client_version', models.CharField(blank=True, max_length=64, verbose_name='客户端版本')), ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), ('api_key', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='device_binding_audits', to='users.apikey', verbose_name='来源 API 密钥')), ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='audits', to='licensing.clientdevice', verbose_name='客户端设备')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='device_binding_audits', to=settings.AUTH_USER_MODEL, verbose_name='用户')), ], options={ 'verbose_name': '设备绑定审计', 'verbose_name_plural': '设备绑定审计', 'db_table': 'device_binding_audit', 'ordering': ('-created_at', '-id'), }, ), migrations.CreateModel( name='DeviceSession', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('token_hash', models.CharField(editable=False, max_length=64, unique=True, verbose_name='会话令牌哈希')), ('expires_at', models.DateTimeField(verbose_name='过期时间')), ('last_used_at', models.DateTimeField(blank=True, null=True, verbose_name='最后使用时间')), ('revoked_at', models.DateTimeField(blank=True, null=True, verbose_name='吊销时间')), ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), ('device', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sessions', to='licensing.clientdevice', verbose_name='客户端设备')), ], options={ 'verbose_name': '设备会话', 'verbose_name_plural': '设备会话', 'db_table': 'device_session', 'ordering': ('-created_at', '-id'), }, ), migrations.AddIndex( model_name='clientdevice', index=models.Index(fields=['user', 'product_code', 'status'], name='client_devi_user_id_bdec20_idx'), ), migrations.AddIndex( model_name='clientdevice', index=models.Index(fields=['product_code', 'last_seen_at'], name='client_devi_product_a3ba66_idx'), ), migrations.AddConstraint( model_name='clientdevice', constraint=models.UniqueConstraint(fields=('user', 'product_code', 'device_fingerprint'), name='client_device_user_product_fingerprint_unique'), ), migrations.AddIndex( model_name='devicebindingaudit', index=models.Index(fields=['user', 'created_at'], name='device_bind_user_id_d2befc_idx'), ), migrations.AddIndex( model_name='devicebindingaudit', index=models.Index(fields=['device', 'created_at'], name='device_bind_device__f566a5_idx'), ), migrations.AddIndex( model_name='devicesession', index=models.Index(fields=['device', 'expires_at'], name='device_sess_device__ab6307_idx'), ), migrations.AddIndex( model_name='devicesession', index=models.Index(fields=['expires_at', 'revoked_at'], name='device_sess_expires_04b16a_idx'), ), ]