feat(property): add Chinese verbose_name and help_text to all property fields (Phase 4.1)

Sync DATA_MODEL_PROPERTY.md field-level Chinese annotations to Django
models across 23 property tables. Adds verbose_name= and help_text= to
every field in core.py, follow_keys.py, listings.py, media.py.

Pre-existing partitioned-table docstrings on FollowLog/PropertyPhoto
retained (signal Django ORM treats parent as unmanaged, RunSQL managed).
This commit is contained in:
2026-04-30 09:15:43 +08:00
parent 79c3cf2924
commit 3638fc0302
4 changed files with 1328 additions and 243 deletions

View File

@@ -15,32 +15,93 @@ class FollowLog(models.Model):
Managed via RunSQL; Django ORM treats parent as unmanaged.
"""
id = models.UUIDField(primary_key=True)
created_at = models.DateTimeField()
id = models.UUIDField(primary_key=True, verbose_name="主键")
created_at = models.DateTimeField(
verbose_name="创建时间",
help_text="分区键,必须在最前声明;系统自动",
)
property = models.ForeignKey(
"fonrey_property.Property", on_delete=models.CASCADE, related_name="follow_logs"
"fonrey_property.Property",
on_delete=models.CASCADE,
related_name="follow_logs",
verbose_name="所属房源",
)
log_type = models.CharField(max_length=30, choices=PropertyFollowLogType.choices)
purpose = models.CharField(max_length=50, blank=True, default="")
content = models.TextField(blank=True, default="")
log_type = models.CharField(
max_length=30,
choices=PropertyFollowLogType.choices,
verbose_name="跟进日志类型",
help_text="written=经纪人主动写入/modified=字段变更自动生成/sensitive_op=敏感操作跟进/sensitive_view=敏感信息查看(不可删)/other=其他/system=系统日志",
)
purpose = models.CharField(
max_length=50,
blank=True,
default="",
verbose_name="跟进目的",
help_text="枚举值由 lookup_items 维护,如:电话/业主跟进/议价/带看;仅 written 类型使用",
)
content = models.TextField(
blank=True,
default="",
verbose_name="跟进内容",
help_text="最少 6 字,最多 500 字;仅 written 类型必填",
)
ai_tag = models.CharField(
max_length=20, blank=True, default="", choices=PropertyFollowAiTag.choices
max_length=20,
blank=True,
default="",
choices=PropertyFollowAiTag.choices,
verbose_name="AI 辅助标签",
help_text="ai_for_sale=AI判断业主在售/ai_not_for_sale=AI判断业主不售由系统智能分析后打标",
)
change_detail = models.JSONField(null=True, blank=True)
log_tag = models.CharField(max_length=50, blank=True, default="")
change_detail = models.JSONField(
null=True,
blank=True,
verbose_name="字段变更明细",
help_text='格式:{"field": "sale_price", "old": 850, "new": 800, "label": "售价"}modified 类型使用',
)
log_tag = models.CharField(
max_length=50,
blank=True,
default="",
verbose_name="前端展示标签",
help_text="如:查看号码/图片下载/改状态/改价格/改等级/修改相关方;对应跟进时间线显示的方括号标签",
)
is_public = models.BooleanField(default=True)
is_public = models.BooleanField(
default=True,
verbose_name="是否公开",
help_text="true=全员可见/false=仅本人及上级可见",
)
operator = models.ForeignKey(
"org.Staff", null=True, blank=True, on_delete=models.SET_NULL
"org.Staff",
null=True,
blank=True,
on_delete=models.SET_NULL,
verbose_name="操作人",
help_text="人员离职后置 NULL但 snapshot 保留",
)
operator_snapshot = models.JSONField(
null=True,
blank=True,
verbose_name="操作人快照",
help_text="{name, role, org_unit_name, store_group};防止人员离职后丢失显示信息",
)
operator_snapshot = models.JSONField(null=True, blank=True)
is_deletable = models.BooleanField(default=True)
deleted_at = models.DateTimeField(null=True, blank=True)
is_deletable = models.BooleanField(
default=True,
verbose_name="是否可软删除",
help_text="false=敏感信息查看类型,合规要求不可删除",
)
deleted_at = models.DateTimeField(
null=True,
blank=True,
verbose_name="软删除时间戳",
help_text="仅 is_deletable=TRUE 时可软删NULL=未删除",
)
class Meta:
db_table = "follow_logs"
@@ -51,15 +112,37 @@ class FollowLog(models.Model):
class FollowLogAttachment(UUIDPrimaryKeyModel):
follow_log_id = models.UUIDField() # cross-partitioned FK; not enforced via Django FK
file_key = models.TextField()
file_name = models.CharField(max_length=255)
file_size = models.IntegerField()
file_type = models.CharField(
max_length=10, blank=True, default="", choices=PropertyFollowAttachmentFileType.choices
follow_log_id = models.UUIDField(
verbose_name="所属跟进日志ID",
help_text="跨分区外键,未通过 Django FK 强约束;日志删除时联级删除",
)
sort_order = models.SmallIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
file_key = models.TextField(
verbose_name="图片存储路径",
help_text="Cloudflare R2 对象路径",
)
file_name = models.CharField(
max_length=255,
verbose_name="原始文件名",
help_text="用户上传时的文件名",
)
file_size = models.IntegerField(
verbose_name="文件大小",
help_text="bytes最大 20MB = 20971520",
)
file_type = models.CharField(
max_length=10,
blank=True,
default="",
choices=PropertyFollowAttachmentFileType.choices,
verbose_name="文件格式",
help_text="bmp/jpg/png/svg/gifPRD 限定格式)",
)
sort_order = models.SmallIntegerField(
default=0,
verbose_name="排序权重",
help_text="控制同一跟进附件的显示顺序",
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="上传时间")
class Meta:
db_table = "follow_log_attachments"
@@ -69,10 +152,21 @@ class FollowLogAttachment(UUIDPrimaryKeyModel):
class FollowLogRecording(UUIDPrimaryKeyModel):
follow_log_id = models.UUIDField()
file_key = models.TextField()
duration_seconds = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
follow_log_id = models.UUIDField(
verbose_name="所属跟进日志ID",
help_text="跨分区外键,未通过 Django FK 强约束;日志删除时联级删除",
)
file_key = models.TextField(
verbose_name="录音文件存储路径",
help_text="Cloudflare R2 对象路径",
)
duration_seconds = models.IntegerField(
null=True,
blank=True,
verbose_name="录音时长",
help_text="秒;可空,上传时若能解析则填写",
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="上传时间")
class Meta:
db_table = "follow_log_recordings"
@@ -83,9 +177,17 @@ class FollowLogRecording(UUIDPrimaryKeyModel):
class PropertyKey(UUIDPrimaryKeyModel):
property = models.ForeignKey(
"fonrey_property.Property", on_delete=models.CASCADE, related_name="keys"
"fonrey_property.Property",
on_delete=models.CASCADE,
related_name="keys",
verbose_name="所属房源",
)
key_type = models.CharField(
max_length=20,
choices=PropertyKeyType.choices,
verbose_name="钥匙类型",
help_text="mechanical=机械钥匙/password=密码(如密码门锁)",
)
key_type = models.CharField(max_length=20, choices=PropertyKeyType.choices)
holder = models.ForeignKey(
"org.Staff",
@@ -93,29 +195,58 @@ class PropertyKey(UUIDPrimaryKeyModel):
blank=True,
on_delete=models.SET_NULL,
related_name="held_keys",
verbose_name="持有人",
help_text="人员离职后置 NULL",
)
holder_snapshot = models.JSONField(
null=True,
blank=True,
verbose_name="持有人快照",
help_text="{name, store_group};防止人员离职后丢失显示信息",
)
holder_snapshot = models.JSONField(null=True, blank=True)
storage_unit = models.ForeignKey(
"org.OrgUnit",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="stored_keys",
verbose_name="保管部门",
help_text="钥匙存放在哪个部门",
)
is_other_agency = models.BooleanField(default=False)
other_agency_info = models.CharField(max_length=30, blank=True, default="")
remarks = models.TextField(blank=True, default="")
is_other_agency = models.BooleanField(
default=False,
verbose_name="是否他司钥匙",
help_text="true=是他中介公司的钥匙/false=本司钥匙",
)
other_agency_info = models.CharField(
max_length=30,
blank=True,
default="",
verbose_name="他司中介信息",
help_text='最多 30 字is_other_agency=true 时填写,如"链家"',
)
remarks = models.TextField(
blank=True,
default="",
verbose_name="备注",
help_text="最多 200 字;如密码内容等补充说明",
)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(
default=True,
verbose_name="是否有效",
help_text="true=在管中/false=已归还或失效",
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="最后更新时间")
created_by = models.ForeignKey(
"org.Staff",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="created_property_keys",
verbose_name="创建人",
)
class Meta:
@@ -127,11 +258,18 @@ class PropertyKey(UUIDPrimaryKeyModel):
class KeyAttachment(UUIDPrimaryKeyModel):
key = models.ForeignKey(
PropertyKey, on_delete=models.CASCADE, related_name="attachments"
PropertyKey,
on_delete=models.CASCADE,
related_name="attachments",
verbose_name="所属钥匙记录",
help_text="钥匙删除时联级删除",
)
file_key = models.TextField()
file_name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
file_key = models.TextField(
verbose_name="附件存储路径",
help_text="Cloudflare R2 对象路径",
)
file_name = models.CharField(max_length=255, verbose_name="原始文件名")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="上传时间")
class Meta:
db_table = "key_attachments"