from django.db import models from core.enums import ClientFollowLogType from core.models.base import UUIDPrimaryKeyModel class ClientFollowLog(models.Model): """Partitioned table (PARTITION BY RANGE created_at). Managed via RunSQL; Django ORM treats parent as unmanaged. """ id = models.UUIDField( primary_key=True, verbose_name="主键", ) created_at = models.DateTimeField( verbose_name="创建时间", help_text="分区键", ) client = models.ForeignKey( "fonrey_client.Client", on_delete=models.CASCADE, related_name="follow_logs", verbose_name="所属客源", help_text="跟进日志随客源级联删除", ) log_type = models.CharField( max_length=30, choices=ClientFollowLogType.choices, verbose_name="跟进类型", help_text="written=写入跟进 / modified=修改跟进 / sensitive_view=敏感信息查看(不可删) / other=其他跟进 / system=系统日志", ) purpose = models.CharField( max_length=50, blank=True, default="", verbose_name="跟进目的", help_text="lookup_items 维护,23项", ) content = models.TextField( blank=True, default="", verbose_name="跟进内容", help_text="最少6字,最多500字", ) log_tag = models.CharField( max_length=50, blank=True, default="", verbose_name="跟进标签", help_text="has_recording=有录音 / has_photo=有图片 / not_satisfied=对房源不满意 / still_considering=还在考虑 / ready_to_deposit=可交定金", ) change_detail = models.JSONField( null=True, blank=True, verbose_name="字段变更明细", help_text='修改跟进专用,格式:{"field": "grade", "old": "C", "new": "B", "label": "等级"}', ) is_public = models.BooleanField( default=True, verbose_name="是否公开", help_text="FALSE=仅本人及上级可见", ) is_deletable = models.BooleanField( default=True, verbose_name="是否可删除", help_text="敏感信息查看类型为 FALSE,不可删除", ) operator = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="操作人", ) operator_snapshot = models.JSONField( null=True, blank=True, verbose_name="操作人快照", help_text="{name, store_group, role}(防止人员调动后显示异常)", ) deleted_at = models.DateTimeField( null=True, blank=True, verbose_name="删除时间", help_text="仅 is_deletable=TRUE 时可软删", ) class Meta: db_table = "client_follow_logs" verbose_name = "客源跟进日志" verbose_name_plural = "客源跟进日志" managed = False unique_together = (("id", "created_at"),) class ClientFollowLogAttachment(UUIDPrimaryKeyModel): follow_log_id = models.UUIDField( verbose_name="所属跟进日志ID", help_text="跨分区 FK;不通过 Django FK 强制约束", ) file_key = models.TextField( verbose_name="文件存储路径", help_text="R2/S3 存储路径", ) file_name = models.CharField( max_length=255, verbose_name="文件名", help_text="原始文件名(用于展示和下载)", ) file_size = models.IntegerField( verbose_name="文件大小", help_text="单位:bytes,最大 20MB", ) file_type = models.CharField( max_length=10, blank=True, default="", verbose_name="文件类型", help_text="bmp / jpg / png / gif", ) has_location = models.BooleanField( default=False, verbose_name="是否含位置信息", help_text="是否含 GPS 位置信息", ) sort_order = models.SmallIntegerField( default=0, verbose_name="排序顺序", ) created_at = models.DateTimeField( auto_now_add=True, verbose_name="创建时间", ) class Meta: db_table = "client_follow_log_attachments" verbose_name = "客源跟进附件" verbose_name_plural = "客源跟进附件" indexes = [models.Index(fields=["follow_log_id"], name="idx_cfla_log")]