# Generated by Django 4.2.16 on 2026-04-29 09:12 import django.contrib.postgres.fields import django.contrib.postgres.indexes import django.contrib.postgres.search from django.db import migrations, models import django.db.models.deletion import uuid class Migration(migrations.Migration): initial = True dependencies = [ ('region', '0001_initial'), ('org', '0001_initial'), ] operations = [ migrations.CreateModel( name='Complex', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('deleted_at', models.DateTimeField(blank=True, db_index=True, null=True)), ('name', models.CharField(help_text='标准楼盘名称,不可在编辑页直接修改', max_length=200)), ('address', models.CharField(blank=True, default='', max_length=500)), ('address_summary', models.CharField(blank=True, default='', max_length=100)), ('latitude', models.DecimalField(blank=True, decimal_places=7, max_digits=10, null=True)), ('longitude', models.DecimalField(blank=True, decimal_places=7, max_digits=10, null=True)), ('property_usage_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('residential', '住宅'), ('villa', '别墅'), ('commercial_residential', '商住'), ('commercial', '商业'), ('office', '写字楼'), ('other', '其他')], max_length=30), blank=True, default=list, size=None)), ('building_structure', models.CharField(blank=True, choices=[('unit_room', '单元-房号'), ('other', '其他')], default='', max_length=30)), ('building_type', models.CharField(blank=True, choices=[('slab', '板楼'), ('tower', '塔楼'), ('slab_tower', '板塔结合')], default='', max_length=20)), ('land_use_years', models.CharField(blank=True, default='', max_length=30)), ('built_year', models.SmallIntegerField(blank=True, null=True)), ('built_years', django.contrib.postgres.fields.ArrayField(base_field=models.SmallIntegerField(), blank=True, default=list, size=None)), ('ownership_category', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=30), blank=True, default=list, size=None)), ('total_units', models.IntegerField(blank=True, null=True)), ('total_households', models.IntegerField(blank=True, null=True)), ('total_floor_area', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), ('plot_area', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), ('plot_ratio', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)), ('green_rate', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)), ('developer', models.CharField(blank=True, default='', max_length=200)), ('property_company', models.CharField(blank=True, default='', max_length=200)), ('property_fee', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)), ('property_phone', models.CharField(blank=True, default='', max_length=30)), ('parking_total', models.IntegerField(blank=True, null=True)), ('parking_underground', models.IntegerField(blank=True, null=True)), ('parking_ratio', models.CharField(blank=True, default='', max_length=20)), ('water_type', models.CharField(blank=True, choices=[('civil', '民水'), ('commercial', '商水')], default='', max_length=10)), ('electricity_type', models.CharField(blank=True, choices=[('civil', '民电'), ('commercial', '商电')], default='', max_length=10)), ('has_central_heating', models.BooleanField(blank=True, null=True)), ('has_gas', models.BooleanField(blank=True, null=True)), ('remarks', models.TextField(blank=True, default='')), ('lock_building', models.BooleanField(default=False)), ('lock_room', models.BooleanField(default=False)), ('lock_info', models.BooleanField(default=False)), ('lock_standard_room', models.BooleanField(default=False)), ('search_vector', django.contrib.postgres.search.SearchVectorField(blank=True, null=True)), ('is_active', models.BooleanField(default=True)), ('version', models.IntegerField(default=1, help_text='乐观锁版本号;UPDATE 时 +1')), ], options={ 'db_table': 'complexes', 'ordering': ['name'], }, ), migrations.CreateModel( name='ComplexSchool', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('zone_type', models.CharField(blank=True, choices=[('guaranteed', '对口'), ('reference', '参考'), ('lottery', '摇号')], default='', max_length=30)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complex_schools', to='fonrey_complex.complex')), ('school', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complex_links', to='region.school')), ], options={ 'db_table': 'complex_schools', }, ), migrations.CreateModel( name='ComplexPriceTrend', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('record_month', models.DateField(help_text='月份(统一存为该月1日)')), ('avg_sale_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), ('avg_unit_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), ('transaction_count', models.IntegerField(blank=True, null=True)), ('listing_count', models.IntegerField(blank=True, null=True)), ('created_at', models.DateTimeField(auto_now_add=True)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='price_trends', to='fonrey_complex.complex')), ], options={ 'db_table': 'complex_price_trends', 'ordering': ['complex_id', '-record_month'], }, ), migrations.CreateModel( name='ComplexPhoto', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('category', models.CharField(choices=[('complex', '楼盘图'), ('layout', '户型图'), ('vr', 'VR图'), ('other', '其他')], max_length=20)), ('file_key', models.TextField()), ('thumbnail_key', models.TextField(blank=True, default='')), ('file_name', models.CharField(blank=True, default='', max_length=255)), ('file_size', models.IntegerField(blank=True, help_text='bytes', null=True)), ('width', models.IntegerField(blank=True, null=True)), ('height', models.IntegerField(blank=True, null=True)), ('is_cover', models.BooleanField(default=False)), ('sort_order', models.SmallIntegerField(default=0)), ('created_at', models.DateTimeField(auto_now_add=True)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='fonrey_complex.complex')), ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_complex_photos', to='org.staff')), ], options={ 'db_table': 'complex_photos', 'ordering': ['complex_id', 'sort_order'], }, ), migrations.CreateModel( name='ComplexMetroStation', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('distance_meters', models.IntegerField(blank=True, null=True)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complex_metro_stations', to='fonrey_complex.complex')), ('station', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complex_links', to='region.metrostation')), ], options={ 'db_table': 'complex_metro_stations', }, ), migrations.CreateModel( name='ComplexBusinessArea', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('is_primary', models.BooleanField(default=False)), ('business_area', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complex_links', to='region.businessarea')), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complex_business_areas', to='fonrey_complex.complex')), ], options={ 'db_table': 'complex_business_areas', }, ), migrations.CreateModel( name='ComplexAttachment', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('file_key', models.TextField()), ('file_name', models.CharField(max_length=255)), ('file_size', models.IntegerField(blank=True, null=True)), ('file_type', models.CharField(blank=True, default='', help_text='MIME type', max_length=50)), ('sort_order', models.SmallIntegerField(default=0)), ('created_at', models.DateTimeField(auto_now_add=True)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='fonrey_complex.complex')), ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_complex_attachments', to='org.staff')), ], options={ 'db_table': 'complex_attachments', 'ordering': ['complex_id', 'sort_order'], }, ), migrations.CreateModel( name='ComplexAlias', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('alias', models.CharField(max_length=200)), ('is_system', models.BooleanField(default=False, help_text='TRUE=系统/标准别名(只读)')), ('created_at', models.DateTimeField(auto_now_add=True)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='aliases', to='fonrey_complex.complex')), ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_complex_aliases', to='org.staff')), ], options={ 'db_table': 'complex_aliases', 'ordering': ['complex_id', 'alias'], }, ), migrations.AddField( model_name='complex', name='business_areas', field=models.ManyToManyField(related_name='complexes', through='fonrey_complex.ComplexBusinessArea', to='region.businessarea'), ), migrations.AddField( model_name='complex', name='created_by', field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_complexes', to='org.staff'), ), migrations.AddField( model_name='complex', name='district', field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='complexes', to='region.district'), ), migrations.AddField( model_name='complex', name='metro_stations', field=models.ManyToManyField(related_name='complexes', through='fonrey_complex.ComplexMetroStation', to='region.metrostation'), ), migrations.AddField( model_name='complex', name='schools', field=models.ManyToManyField(related_name='complexes', through='fonrey_complex.ComplexSchool', to='region.school'), ), migrations.AddField( model_name='complex', name='updated_by', field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_complexes', to='org.staff'), ), migrations.CreateModel( name='Building', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('name', models.CharField(help_text='楼栋名,如「1号楼」「A栋2单元」', max_length=50)), ('is_standard', models.BooleanField(default=False, help_text='TRUE=标准结构(经运营核准)')), ('property_usage_type', models.CharField(blank=True, choices=[('residential', '住宅'), ('villa', '别墅'), ('commercial_residential', '商住'), ('commercial', '商业'), ('office', '写字楼'), ('other', '其他')], default='', max_length=30)), ('built_year', models.SmallIntegerField(blank=True, null=True)), ('total_floors', models.SmallIntegerField(blank=True, null=True)), ('land_use_years', models.CharField(blank=True, default='', max_length=30)), ('has_elevator', models.BooleanField(blank=True, null=True)), ('is_active', models.BooleanField(default=True)), ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='buildings', to='fonrey_complex.complex')), ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_buildings', to='org.staff')), ('school', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='buildings', to='region.school')), ], options={ 'db_table': 'buildings', 'ordering': ['complex_id', 'name'], }, ), migrations.CreateModel( name='RoomUnit', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('floor', models.SmallIntegerField(help_text='楼层(实际层数,地下为负数)')), ('floor_name', models.CharField(blank=True, default='', max_length=20)), ('room_no', models.CharField(max_length=30)), ('display_no', models.CharField(blank=True, default='', max_length=50)), ('is_standard', models.BooleanField(default=False)), ('is_active', models.BooleanField(default=True)), ('building', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='room_units', to='fonrey_complex.building')), ], options={ 'db_table': 'room_units', 'ordering': ['building_id', '-floor', 'room_no'], 'indexes': [models.Index(condition=models.Q(('is_active', True)), fields=['building'], name='idx_room_units_building')], }, ), migrations.AddConstraint( model_name='roomunit', constraint=models.UniqueConstraint(condition=models.Q(('is_active', True)), fields=('building', 'floor', 'room_no'), name='uq_room_units_unique'), ), migrations.AddIndex( model_name='complexschool', index=models.Index(fields=['school'], name='idx_complex_schools_school'), ), migrations.AddConstraint( model_name='complexschool', constraint=models.UniqueConstraint(fields=('complex', 'school'), name='pk_complex_schools'), ), migrations.AddIndex( model_name='complexpricetrend', index=models.Index(fields=['complex', '-record_month'], name='idx_cpx_price_trend_complex'), ), migrations.AddConstraint( model_name='complexpricetrend', constraint=models.UniqueConstraint(fields=('complex', 'record_month'), name='uq_complex_price_trend_month'), ), migrations.AddIndex( model_name='complexphoto', index=models.Index(fields=['complex'], name='idx_complex_photos_complex'), ), migrations.AddIndex( model_name='complexphoto', index=models.Index(fields=['complex', 'category'], name='idx_complex_photos_category'), ), migrations.AddConstraint( model_name='complexphoto', constraint=models.UniqueConstraint(condition=models.Q(('is_cover', True)), fields=('complex',), name='uq_complex_photos_cover'), ), migrations.AddIndex( model_name='complexmetrostation', index=models.Index(fields=['complex'], name='idx_complex_metro_complex'), ), migrations.AddIndex( model_name='complexmetrostation', index=models.Index(fields=['station'], name='idx_complex_metro_station'), ), migrations.AddConstraint( model_name='complexmetrostation', constraint=models.UniqueConstraint(fields=('complex', 'station'), name='pk_complex_metro_stations'), ), migrations.AddConstraint( model_name='complexbusinessarea', constraint=models.UniqueConstraint(fields=('complex', 'business_area'), name='pk_complex_business_areas'), ), migrations.AddConstraint( model_name='complexbusinessarea', constraint=models.UniqueConstraint(condition=models.Q(('is_primary', True)), fields=('complex',), name='uq_complex_biz_area_primary'), ), migrations.AddIndex( model_name='complexalias', index=models.Index(fields=['complex'], name='idx_complex_aliases_complex'), ), migrations.AddIndex( model_name='complex', index=models.Index(condition=models.Q(('deleted_at__isnull', True)), fields=['district'], name='idx_complexes_district'), ), migrations.AddIndex( model_name='complex', index=django.contrib.postgres.indexes.GinIndex(fields=['search_vector'], name='idx_complexes_search'), ), migrations.AddIndex( model_name='complex', index=models.Index(condition=models.Q(('deleted_at__isnull', True), ('latitude__isnull', False)), fields=['latitude', 'longitude'], name='idx_complexes_geo'), ), migrations.AddIndex( model_name='complex', index=models.Index(condition=models.Q(('deleted_at__isnull', True)), fields=['is_active'], name='idx_complexes_active'), ), migrations.AddIndex( model_name='building', index=models.Index(condition=models.Q(('is_active', True)), fields=['complex'], name='idx_buildings_complex'), ), migrations.AddConstraint( model_name='building', constraint=models.UniqueConstraint(condition=models.Q(('is_active', True)), fields=('complex', 'name'), name='uq_buildings_complex_name'), ), ]