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).
This commit is contained in:
2026-04-30 09:22:08 +08:00
parent e67b07a7c8
commit a3800bf09d

View File

@@ -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="单位",
)
plot_area = models.DecimalField(
max_digits=12,
decimal_places=2,
null=True,
blank=True,
verbose_name="小区占地面积",
help_text="单位",
)
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"