From a3800bf09da70874576b5a5f7d219ecf0601ed01 Mon Sep 17 00:00:00 2001 From: ishenwei Date: Thu, 30 Apr 2026 09:22:08 +0800 Subject: [PATCH] feat(complex): add Chinese verbose_name and help_text to all complex fields (Phase 4.1 part 3/9) Sync DATA_MODEL_COMPLEX.md field-level Chinese annotations to Django models across 10 complex tables (Complex, ComplexAlias, ComplexBusinessArea, ComplexSchool, ComplexMetroStation, Building, RoomUnit, ComplexPhoto, ComplexAttachment, ComplexPriceTrend). --- apps/complex/models/complex.py | 504 ++++++++++++++++++++++++++++----- 1 file changed, 434 insertions(+), 70 deletions(-) diff --git a/apps/complex/models/complex.py b/apps/complex/models/complex.py index 50a8e40..b85a3c9 100644 --- a/apps/complex/models/complex.py +++ b/apps/complex/models/complex.py @@ -16,95 +16,258 @@ from core.models.base import SoftDeleteModel, TimeStampedModel, UUIDPrimaryKeyMo class Complex(SoftDeleteModel): - name = models.CharField(max_length=200, help_text="标准楼盘名称,不可在编辑页直接修改") + name = models.CharField( + max_length=200, + verbose_name="楼盘名称", + help_text="标准楼盘名称,不可在编辑页直接修改(需走合并/申请流程)", + ) district = models.ForeignKey( "region.District", null=True, blank=True, on_delete=models.SET_NULL, related_name="complexes", + verbose_name="所属城区", + ) + address = models.CharField( + max_length=500, + blank=True, + default="", + verbose_name="详细地址", + help_text="不可在编辑页修改,需走纠错流程", + ) + address_summary = models.CharField( + max_length=100, + blank=True, + default="", + verbose_name="概要地址", + help_text='如「海波路1000弄」,可编辑', + ) + latitude = models.DecimalField( + max_digits=10, + decimal_places=7, + null=True, + blank=True, + verbose_name="纬度", + help_text="WGS84,完整度目标 ≥ 90%", + ) + longitude = models.DecimalField( + max_digits=10, + decimal_places=7, + null=True, + blank=True, + verbose_name="经度", + help_text="WGS84", ) - address = models.CharField(max_length=500, blank=True, default="") - address_summary = models.CharField(max_length=100, blank=True, default="") - latitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) - longitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) property_usage_types = ArrayField( models.CharField(max_length=30, choices=ComplexPropertyUsageType.choices), default=list, blank=True, + verbose_name="物业类型", + help_text="多选:residential / villa / commercial_residential / commercial / office / other", ) building_structure = models.CharField( max_length=30, blank=True, default="", choices=ComplexBuildingStructure.choices, + verbose_name="楼栋结构", + help_text="unit_room=单元-房号 / other=其他", ) building_type = models.CharField( max_length=20, blank=True, default="", choices=ComplexBuildingType.choices, + verbose_name="建筑类型", + help_text="slab=板楼 / tower=塔楼 / slab_tower=板塔结合", + ) + land_use_years = models.CharField( + max_length=30, + blank=True, + default="", + verbose_name="土地使用年限", + help_text='如「70年」', + ) + built_year = models.SmallIntegerField( + null=True, + blank=True, + verbose_name="竣工年份", + help_text="可多选时存最早竣工年", ) - land_use_years = models.CharField(max_length=30, blank=True, default="") - built_year = models.SmallIntegerField(null=True, blank=True) built_years = ArrayField( models.SmallIntegerField(), default=list, blank=True, + verbose_name="竣工年份多值", + help_text="楼盘分期竣工", ) ownership_category = ArrayField( models.CharField(max_length=30), default=list, blank=True, + verbose_name="权属类别", + help_text="多选(运营维护枚举)", + ) + total_units = models.IntegerField( + null=True, + blank=True, + verbose_name="单元总数", + ) + total_households = models.IntegerField( + null=True, + blank=True, + verbose_name="总户数", ) - total_units = models.IntegerField(null=True, blank=True) - total_households = models.IntegerField(null=True, blank=True) - total_floor_area = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) - plot_area = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) - plot_ratio = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) - green_rate = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) - developer = models.CharField(max_length=200, blank=True, default="") + total_floor_area = models.DecimalField( + max_digits=12, + decimal_places=2, + null=True, + blank=True, + verbose_name="小区总建筑面积", + help_text="单位:m²", + ) + plot_area = models.DecimalField( + max_digits=12, + decimal_places=2, + null=True, + blank=True, + verbose_name="小区占地面积", + help_text="单位:m²", + ) + plot_ratio = models.DecimalField( + max_digits=5, + decimal_places=2, + null=True, + blank=True, + verbose_name="容积率", + ) + green_rate = models.DecimalField( + max_digits=5, + decimal_places=2, + null=True, + blank=True, + verbose_name="绿化率", + help_text="单位:%", + ) + developer = models.CharField( + max_length=200, + blank=True, + default="", + verbose_name="开发商", + ) - property_company = models.CharField(max_length=200, blank=True, default="") - property_fee = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True) - property_phone = models.CharField(max_length=30, blank=True, default="") + property_company = models.CharField( + max_length=200, + blank=True, + default="", + verbose_name="物业公司", + ) + property_fee = models.DecimalField( + max_digits=8, + decimal_places=2, + null=True, + blank=True, + verbose_name="物业费", + help_text="单位:元/m²/月", + ) + property_phone = models.CharField( + max_length=30, + blank=True, + default="", + verbose_name="物业电话", + ) - parking_total = models.IntegerField(null=True, blank=True) - parking_underground = models.IntegerField(null=True, blank=True) - parking_ratio = models.CharField(max_length=20, blank=True, default="") + parking_total = models.IntegerField( + null=True, + blank=True, + verbose_name="车位总数", + ) + parking_underground = models.IntegerField( + null=True, + blank=True, + verbose_name="地下车位数", + ) + parking_ratio = models.CharField( + max_length=20, + blank=True, + default="", + verbose_name="车位配比", + help_text='如「100:63」', + ) water_type = models.CharField( max_length=10, blank=True, default="", choices=ComplexWaterType.choices, + verbose_name="水费类型", + help_text="civil=民水 / commercial=商水", ) electricity_type = models.CharField( max_length=10, blank=True, default="", choices=ComplexElectricityType.choices, + verbose_name="电费类型", + help_text="civil=民电 / commercial=商电", + ) + has_central_heating = models.BooleanField( + null=True, + blank=True, + verbose_name="是否统一供暖", + ) + has_gas = models.BooleanField( + null=True, + blank=True, + verbose_name="是否有燃气", + ) + remarks = models.TextField( + blank=True, + default="", + verbose_name="备注", ) - has_central_heating = models.BooleanField(null=True, blank=True) - has_gas = models.BooleanField(null=True, blank=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) + lock_building = models.BooleanField( + default=False, + verbose_name="楼栋锁", + help_text="锁定后不可增删楼栋", + ) + lock_room = models.BooleanField( + default=False, + verbose_name="房号锁", + ) + lock_info = models.BooleanField( + default=False, + verbose_name="信息锁", + help_text="锁定后基本信息只读", + ) + lock_standard_room = models.BooleanField( + default=False, + verbose_name="标准房号锁", + ) - search_vector = SearchVectorField(null=True, blank=True) + search_vector = SearchVectorField( + null=True, + blank=True, + verbose_name="全文检索向量", + help_text="由触发器自动维护(name + alias + address)", + ) - is_active = models.BooleanField(default=True) + is_active = models.BooleanField( + default=True, + verbose_name="是否启用", + help_text="FALSE=已停用楼盘", + ) created_by = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, related_name="created_complexes", + verbose_name="创建人", ) updated_by = models.ForeignKey( "org.Staff", @@ -112,23 +275,31 @@ class Complex(SoftDeleteModel): blank=True, on_delete=models.SET_NULL, related_name="updated_complexes", + verbose_name="最后更新人", + ) + version = models.IntegerField( + default=1, + verbose_name="版本号", + help_text="乐观锁;UPDATE 时 +1;应用层检测 0 行受影响时抛 ConflictError", ) - version = models.IntegerField(default=1, help_text="乐观锁版本号;UPDATE 时 +1") business_areas = models.ManyToManyField( "region.BusinessArea", through="fonrey_complex.ComplexBusinessArea", related_name="complexes", + verbose_name="关联商圈", ) schools = models.ManyToManyField( "region.School", through="fonrey_complex.ComplexSchool", related_name="complexes", + verbose_name="对口学校", ) metro_stations = models.ManyToManyField( "region.MetroStation", through="fonrey_complex.ComplexMetroStation", related_name="complexes", + verbose_name="周边地铁站", ) class Meta: @@ -164,16 +335,30 @@ class ComplexAlias(UUIDPrimaryKeyModel): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="aliases", + verbose_name="所属楼盘", + help_text="别名随楼盘级联删除", + ) + alias = models.CharField( + max_length=200, + verbose_name="别名", + help_text="最多20字/条,多别名多行存储", + ) + is_system = models.BooleanField( + default=False, + verbose_name="是否系统别名", + help_text="TRUE=系统/标准别名(只读),FALSE=用户自定义", + ) + created_at = models.DateTimeField( + auto_now_add=True, + verbose_name="创建时间", ) - alias = models.CharField(max_length=200) - is_system = models.BooleanField(default=False, help_text="TRUE=系统/标准别名(只读)") - created_at = models.DateTimeField(auto_now_add=True) created_by = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, related_name="created_complex_aliases", + verbose_name="创建人", ) class Meta: @@ -194,13 +379,19 @@ class ComplexBusinessArea(models.Model): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="complex_business_areas", + verbose_name="所属楼盘", ) business_area = models.ForeignKey( "region.BusinessArea", on_delete=models.CASCADE, related_name="complex_links", + verbose_name="关联商圈", + ) + is_primary = models.BooleanField( + default=False, + verbose_name="是否主商圈", + help_text="主商圈唯一,用于列表显示", ) - is_primary = models.BooleanField(default=False) class Meta: db_table = "complex_business_areas" @@ -224,17 +415,21 @@ class ComplexSchool(models.Model): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="complex_schools", + verbose_name="所属楼盘", ) school = models.ForeignKey( "region.School", on_delete=models.CASCADE, related_name="complex_links", + verbose_name="对口学校", ) zone_type = models.CharField( max_length=30, blank=True, default="", choices=SchoolZoneType.choices, + verbose_name="学区类型", + help_text="guaranteed=对口(直升) / reference=参考(可能入读) / lottery=摇号", ) class Meta: @@ -257,13 +452,20 @@ class ComplexMetroStation(models.Model): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="complex_metro_stations", + verbose_name="所属楼盘", ) station = models.ForeignKey( "region.MetroStation", on_delete=models.CASCADE, related_name="complex_links", + verbose_name="关联地铁站", + ) + distance_meters = models.IntegerField( + null=True, + blank=True, + verbose_name="步行距离", + help_text="单位:米", ) - distance_meters = models.IntegerField(null=True, blank=True) class Meta: db_table = "complex_metro_stations" @@ -286,33 +488,68 @@ class Building(TimeStampedModel): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="buildings", + verbose_name="所属楼盘", + ) + name = models.CharField( + max_length=50, + verbose_name="楼栋名称", + help_text='如「1号楼」「A栋2单元」', + ) + is_standard = models.BooleanField( + default=False, + verbose_name="是否标准结构", + help_text="TRUE=已经运营核准", ) - name = models.CharField(max_length=50, help_text="楼栋名,如「1号楼」「A栋2单元」") - is_standard = models.BooleanField(default=False, help_text="TRUE=标准结构(经运营核准)") property_usage_type = models.CharField( max_length=30, blank=True, default="", choices=ComplexPropertyUsageType.choices, + verbose_name="物业类型", + help_text="可与楼盘不同,如商住楼盘内有纯商铺楼栋", + ) + built_year = models.SmallIntegerField( + null=True, + blank=True, + verbose_name="竣工年份", + ) + total_floors = models.SmallIntegerField( + null=True, + blank=True, + verbose_name="总层数", + ) + land_use_years = models.CharField( + max_length=30, + blank=True, + default="", + verbose_name="土地使用年限", + ) + has_elevator = models.BooleanField( + null=True, + blank=True, + verbose_name="是否有电梯", ) - built_year = models.SmallIntegerField(null=True, blank=True) - total_floors = models.SmallIntegerField(null=True, blank=True) - land_use_years = models.CharField(max_length=30, blank=True, default="") - has_elevator = models.BooleanField(null=True, blank=True) school = models.ForeignKey( "region.School", null=True, blank=True, on_delete=models.SET_NULL, related_name="buildings", + verbose_name="对口学校", + help_text="楼栋级别的学区差异", + ) + is_active = models.BooleanField( + default=True, + verbose_name="是否启用", + help_text="FALSE=已停用(楼栋被删除或合并)", ) - is_active = models.BooleanField(default=True) created_by = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, related_name="created_buildings", + verbose_name="创建人", ) class Meta: @@ -344,13 +581,41 @@ class RoomUnit(TimeStampedModel): "fonrey_complex.Building", on_delete=models.CASCADE, related_name="room_units", + verbose_name="所属楼栋", + ) + floor = models.SmallIntegerField( + verbose_name="楼层", + help_text="实际层数,地下为负数", + ) + floor_name = models.CharField( + max_length=20, + blank=True, + default="", + verbose_name="楼层名称", + help_text='如「1层」「B1层」', + ) + room_no = models.CharField( + max_length=30, + verbose_name="房号", + help_text='如「01」「101」', + ) + display_no = models.CharField( + max_length=50, + blank=True, + default="", + verbose_name="展示房号", + help_text='展示用完整房号,如「3-1-101」', + ) + is_standard = models.BooleanField( + default=False, + verbose_name="是否标准化", + help_text="TRUE=已归一化为标准结构", + ) + is_active = models.BooleanField( + default=True, + verbose_name="是否启用", + help_text="FALSE=已拆除/不存在", ) - floor = models.SmallIntegerField(help_text="楼层(实际层数,地下为负数)") - floor_name = models.CharField(max_length=20, blank=True, default="") - room_no = models.CharField(max_length=30) - display_no = models.CharField(max_length=50, blank=True, default="") - is_standard = models.BooleanField(default=False) - is_active = models.BooleanField(default=True) class Meta: db_table = "room_units" @@ -381,23 +646,68 @@ class ComplexPhoto(UUIDPrimaryKeyModel): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="photos", + verbose_name="所属楼盘", + ) + category = models.CharField( + max_length=20, + choices=ComplexPhotoCategory.choices, + verbose_name="照片类别", + help_text="complex=楼盘图 / layout=户型图 / vr=VR全景 / other=其他", + ) + file_key = models.TextField( + verbose_name="文件存储路径", + help_text="R2/S3 路径", + ) + thumbnail_key = models.TextField( + blank=True, + default="", + verbose_name="缩略图路径", + ) + file_name = models.CharField( + max_length=255, + blank=True, + default="", + verbose_name="原始文件名", + ) + file_size = models.IntegerField( + null=True, + blank=True, + verbose_name="文件大小", + help_text="单位:bytes", + ) + width = models.IntegerField( + null=True, + blank=True, + verbose_name="图片宽度", + help_text="单位:px", + ) + height = models.IntegerField( + null=True, + blank=True, + verbose_name="图片高度", + help_text="单位:px", + ) + is_cover = models.BooleanField( + default=False, + verbose_name="是否封面图", + help_text="楼盘封面图(每楼盘唯一)", + ) + sort_order = models.SmallIntegerField( + default=0, + verbose_name="排序顺序", + help_text="同类别内的排序顺序", + ) + created_at = models.DateTimeField( + auto_now_add=True, + verbose_name="创建时间", ) - category = models.CharField(max_length=20, choices=ComplexPhotoCategory.choices) - file_key = models.TextField() - thumbnail_key = models.TextField(blank=True, default="") - file_name = models.CharField(max_length=255, blank=True, default="") - file_size = models.IntegerField(null=True, blank=True, help_text="bytes") - width = models.IntegerField(null=True, blank=True) - height = models.IntegerField(null=True, blank=True) - is_cover = models.BooleanField(default=False) - sort_order = models.SmallIntegerField(default=0) - created_at = models.DateTimeField(auto_now_add=True) created_by = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, related_name="created_complex_photos", + verbose_name="上传人", ) class Meta: @@ -423,19 +733,44 @@ class ComplexAttachment(UUIDPrimaryKeyModel): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="attachments", + verbose_name="所属楼盘", + ) + file_key = models.TextField( + verbose_name="文件存储路径", + help_text="R2/S3 存储路径", + ) + file_name = models.CharField( + max_length=255, + verbose_name="原始文件名", + ) + file_size = models.IntegerField( + null=True, + blank=True, + verbose_name="文件大小", + help_text="单位:bytes", + ) + file_type = models.CharField( + max_length=50, + blank=True, + default="", + verbose_name="文件类型", + help_text="MIME type", + ) + sort_order = models.SmallIntegerField( + default=0, + verbose_name="排序顺序", + ) + created_at = models.DateTimeField( + auto_now_add=True, + verbose_name="创建时间", ) - file_key = models.TextField() - file_name = models.CharField(max_length=255) - file_size = models.IntegerField(null=True, blank=True) - file_type = models.CharField(max_length=50, blank=True, default="", help_text="MIME type") - sort_order = models.SmallIntegerField(default=0) - created_at = models.DateTimeField(auto_now_add=True) created_by = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, related_name="created_complex_attachments", + verbose_name="上传人", ) class Meta: @@ -450,13 +785,42 @@ class ComplexPriceTrend(UUIDPrimaryKeyModel): "fonrey_complex.Complex", on_delete=models.CASCADE, related_name="price_trends", + verbose_name="所属楼盘", + ) + record_month = models.DateField( + verbose_name="月份", + help_text="统一存为该月1日,如 2026-04-01", + ) + avg_sale_price = models.DecimalField( + max_digits=12, + decimal_places=2, + null=True, + blank=True, + verbose_name="月均售价", + help_text="单位:万元/套", + ) + avg_unit_price = models.DecimalField( + max_digits=10, + decimal_places=2, + null=True, + blank=True, + verbose_name="月均单价", + help_text="单位:元/m²", + ) + transaction_count = models.IntegerField( + null=True, + blank=True, + verbose_name="成交套数", + ) + listing_count = models.IntegerField( + null=True, + blank=True, + verbose_name="当月挂牌套数", + ) + created_at = models.DateTimeField( + auto_now_add=True, + verbose_name="创建时间", ) - record_month = models.DateField(help_text="月份(统一存为该月1日)") - avg_sale_price = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) - avg_unit_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - transaction_count = models.IntegerField(null=True, blank=True) - listing_count = models.IntegerField(null=True, blank=True) - created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = "complex_price_trends"