Files
nexus/Project/fonrey/DATA_MODEL/DATA_MODEL.md
Shen Wei 712a33fbac docs: 新增系统配置模块PRD及数据模型文档,更新TASK.md
- 新增 PRD/系统配置/系统配置模块PRD.md(v0.1 Draft)
  - MVP 范围:US-SETTING-001-A(Lookup Items)、B(房源字段必填规则)、C(客源录入规则)
- 新增 PRD/系统配置/系统配置数据模型设计说明_for_Atlas.md
- 新增 PRD/系统配置/系统配置参数数据.md(竞品参数数据)
- 删除旧版 PRD/系统配置/系统配置.md(已被新PRD替代)
- 新增 DATA_MODEL/DATA_MODEL_SETTING.md(系统配置数据模型)
- 新增 DATA_MODEL/ENUMS.md(枚举定义与约定)
- 新增 AGENTS.md(AI Agent 开发规范)
- 更新 PRD/TASK.md:US-SETTING-001 拆分为 A/B/C 三个子任务,修正参考文档路径与验收标准
- 新增 VIBE_CODING_开工前缺失清单.md
- 新增 TECH_STACK/房源管理技术方案.md
- 更新 DATA_MODEL/DATA_MODEL.md、DATA_MODEL_CLIENT.md、DATA_MODEL_LOGIN.md
- 更新 PRD/PRD_MVP.md、PRD/权限管理/权限管理模块PRD.md
- 更新 TECH_STACK/TECH_STACK.md、权限管理系统技术方案.md
- 更新 UI_DESIGN/preview.html、UI_SYSTEM/UI_SYSTEM.md
- 新增 prompt/PRD - 为系统设置生成PRD设计文档.md、更新 prompt 模板
2026-04-27 15:33:12 +08:00

45 KiB
Raw Blame History

For AI assistants: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.

Fonrey 房产经纪管理系统 — DATA MODEL 设计文档

作者: Backend Architect
版本: v1.4
日期: 2026-04-24v1.1 修复 S1/S2/S4v1.2 扩展 public schemav1.3 §三 DDL 迁至 DATA_MODEL_PUBLIC.md本文改为索引v1.4 补充 LOGIN/PERMISSION 子文档引用、领域对象、租户 Schema 章节、Redis 缓存策略)
技术栈: Django 4.x + PostgreSQL + django-tenants + Redis
设计目标: 支撑 89,000+ 房源、多租户隔离、sub-100ms 查询、合规审计


一、架构决策总览 (Architecture Decision Records)

1.1 多租户策略Schema-per-Tenant

┌──────────────────────────────────────────────────────────────────────┐
│  PostgreSQL Instance                                                 │
│                                                                      │
│  ┌─────────────────────────┐  ┌──────────────────┐  ┌────────────┐  │
│  │   public schema         │  │   tenant_abc     │  │ tenant_xyz │  │
│  │   (平台运营层)            │  │   schema         │  │   schema   │  │
│  │                         │  │                  │  │            │  │
│  │ - tenants               │  │ - org_units      │  │ (同左)      │  │
│  │ - domains               │  │ - staff          │  │            │  │
│  │ - tenant_status_logs    │  │ - complexes      │  │            │  │
│  │ - platform_admins       │  │ - properties     │  │            │  │
│  │ - admin_mfa_devices     │  │ - clients        │  │            │  │
│  │ - admin_sessions        │  │ - user_accounts  │  │            │  │
│  │ - ip_whitelist          │  │ - login_attempts │  │            │  │
│  │ - platform_audit_logs   │  │ - permission_defs│  │            │  │
│  │ - backup_schedules      │  │ - roles          │  │            │  │
│  │ - backup_records        │  │ - staff_roles    │  │            │  │
│  │ - export_tasks          │  │ - lookup_items   │  │            │  │
│  │ - system_versions       │  │ - ...            │  │            │  │
│  │ - upgrade_events        │  └──────────────────┘  └────────────┘  │
│  │ - enum_labels           │                                        │
│  └─────────────────────────┘                                        │
└──────────────────────────────────────────────────────────────────────┘

选型理由

  • django-tenants 的 Schema 隔离提供最强的数据安全边界
  • 房产经纪公司之间数据绝对不能互通(合规要求)
  • 每个 Schema 独立索引,避免全局锁竞争
  • 支持按租户独立备份/恢复

1.2 核心领域模型关系图

  [区域/商圈]──────────────────────────────┐
       │                                  │
  [学校管理]                               │
       │                                  ▼
  [楼盘/小区] ──── [楼栋] ─────────► [房源] ◄──── [挂牌历史]
       │                                   │
       │                          ┌────────┼────────┐
       │                          │        │        │
       │                     [联系人]  [跟进日志] [维护完成度]
       │                          │        │
       │                    ┌─────┘   ┌────┴──────┐
       │                    │         │           │
       │               [电话查看]  [钥匙]  [委托] [实勘]
       │
  [客源] ──── [配对记录] ──── [带看记录]
       │
  [员工/组织] ──── [权限]

1.3 关键设计原则

原则 决策
主键类型 UUID v4(跨环境安全,避免枚举攻击)
软删除 所有核心表含 deleted_at(历史可追溯)
时间戳 全部使用 TIMESTAMPTZ(含时区)
手机号存储 AES-256-GCM 加密存储,建立 SHA-256 哈希索引
审计字段 created_by, updated_by 全表覆盖
枚举值 业务枚举用 VARCHAR + CHECK系统枚举用 lookup 表
大文本 TEXT 类型不设长度PG 内部优化)
金额 NUMERIC(12,2) 万元精度,避免浮点误差

二、领域概览Domain Overview

本节用业务语言描述系统的核心领域对象及其关系,作为各子模块数据模型的导读。

核心领域对象

Public Schema平台运营层

领域对象 业务说明
Tenant租户 public.tenants 每家房产经纪公司一条记录含状态机creating/active/suspended/pending_delete/deleted、套餐、联系人
Domain域名 public.domains 子域名↔租户映射,支持多域名绑定,子域名创建后不可修改
TenantStatusLog public.tenant_status_logs 租户状态变更不可变审计append-only
PlatformAdmin public.platform_admins 平台管理员账号3 种角色:超级管理员/运营人员/只读审计员
AdminMfaDevice public.admin_mfa_devices 管理员 TOTP 设备(强制启用)
AdminSession public.admin_sessions 登录会话30 分钟超时,支持强制登出)
IpWhitelist public.ip_whitelist 管理控制台 CIDR 白名单
PlatformAuditLog public.platform_audit_logs 所有写操作+高危操作审计append-only建议月度分区
BackupSchedule public.backup_schedules 全局/租户级定时备份计划(频率/保留数/存储目标)
BackupRecord public.backup_records 备份任务执行记录(自动/手动/升级前/恢复前)
ExportTask public.export_tasks 数据导出异步任务CSV/JSON/SQL Dump24h 下载链接)
SystemVersion public.system_versions 平台版本历史,唯一 current 版本约束
UpgradeEvent public.upgrade_events 升级/回滚事件,含灰度租户维度进度快照
EnumLabel public.enum_labels 固定枚举字典(英文 Key → 中文标签),所有租户共享,供前端下拉渲染、导出报表中文标签、日志快照使用

Tenant Schema租户业务层

领域对象 表/子文档 业务说明
OrgUnit组织架构 org_unitsDATA_MODEL_ORG.md 树形组织架构(总部/区域/城市/大区/分公司/门店/团队/虚拟团队),物化路径存储,支持权限继承
Staff员工 staffDATA_MODEL_ORG.md 经纪人/店长/经理,绑定组织节点,手机号加密存储,与账号(登录)分离
District城区 districtsDATA_MODEL_COMPLEX.md 行政区划,如「静安区」,是区域体系的顶层节点
BusinessArea商圈 business_areasDATA_MODEL_COMPLEX.md 商圈/板块,从属于城区,一个楼盘可归属多个商圈
School学校 schoolsDATA_MODEL_COMPLEX.md 对口学校数据库,是买家购房决策的核心参考,与楼盘多对多关联
Complex楼盘/小区) complexesDATA_MODEL_COMPLEX.md 房源录入的基础底座,维护楼盘标准名称/坐标/锁定状态/别名等
Building楼栋/单元) buildingsDATA_MODEL_COMPLEX.md 楼盘下的物理楼栋,区分标准结构与非标结构
RoomUnit房号 room_unitsDATA_MODEL_COMPLEX.md 楼层+房间号,房源定位的最细粒度
Property房源 propertiesDATA_MODEL_PROPERTY.md 系统核心表,每套二手房源的完整档案,支持出售/出租/出售兼出租三态
Client客源 clientsDATA_MODEL_CLIENT.md 买家/租客档案,分私客/公客/成交客,含活跃度评分与自动公客转换机制
Viewing带看 client_viewingsDATA_MODEL_CLIENT.md 经纪人带客户看房的完整记录
Match配对 client_property_matchesDATA_MODEL_CLIENT.md 系统/人工推荐的客源↔房源配对
UserAccount用户账号 user_accountsDATA_MODEL_LOGIN.md 系统登录主体,与员工档案 1:1 绑定,含账号锁定/密码历史/登录审计
PermissionDef权限定义 permission_defsDATA_MODEL_PERMISSION.md 权限目录(约 300 条),驱动 Hybrid RBAC + Override 权限模型
Role业务角色 rolesDATA_MODEL_PERMISSION.md 权限模板,含 4 大类别(置业顾问/店管/总经/运营/自定义)

领域关系快速导航

District (城区)
  └─ BusinessArea (商圈)
       └─ Complex (楼盘) ─── School (对口学校)
            ├─ Building (楼栋)
            │    └─ RoomUnit (房号)
            └─ Property (房源)
                  ├─ PropertyContact (联系人/委托方)
                  ├─ FollowLog (跟进日志)
                  ├─ Viewing (带看记录) ──── Client (客源)
                  └─ Match (配对记录) ──────┘

OrgUnit (组织架构)
  └─ Staff (员工/经纪人) ─── Property / Client / Viewing / Match

子文档索引

子文档 覆盖模块 状态
DATA_MODEL_PUBLIC.md Public schema 平台运营层tenants, domains, platform_admins, admin_sessions, audit_logs, backup, export, upgrade 共 13 张表) 完成
DATA_MODEL_ORG.md 组织人事org_units, staff, 异动/奖惩/教育/家庭等) 完成
DATA_MODEL_COMPLEX.md 楼盘/区域districts, business_areas, complexes, buildings, room_units, schools 等) 完成
DATA_MODEL_CLIENT.md 客源管理clients, requirements, follow_logs, viewings, matches 等) 完成
DATA_MODEL_PROPERTY.md 房源管理properties 及配套 22 张表,含跟进/钥匙/委托/实勘/营销/产证/完成度/标签/收藏/保护/号码方审批等) 完成
DATA_MODEL_LOGIN.md 登录与账号认证user_accounts, login_attempts, password_reset_tokens, password_histories + Redis 登录缓存) 完成
DATA_MODEL_PERMISSION.md 权限管理permission_defs, roles, role_permissions, staff_roles, staff_permission_overrides, staff_data_scopes, permission_change_logs + Redis 权限缓存) 完成
ENUMS.md 枚举字典(public.enum_labels 表设计 + 所有模块枚举定义 + 种子数据 SQL 完成

三、公共 SchemaShared / Public

权威源:完整 DDL 已迁至 DATA_MODEL_PUBLIC.md,本节仅保留摘要索引。
覆盖范围public schema 存储平台运营层数据——租户注册、管理员账号、审计日志、备份/导出任务、版本升级记录(共 13 张表)。
设计依据:系统管理模块 PRDPRD/系统管理/系统管理模块PRD.md)。

表清单(开发以 DATA_MODEL_PUBLIC.md 为准)

表名 说明
public.tenants 租户主表django-tenants 核心,状态机 6 态) §2.1
public.domains 域名↔租户映射(支持多域名,子域名不可修改) §2.1
public.tenant_status_logs 租户状态变更不可变审计日志append-only §2.1
public.platform_admins 平台管理员账号super_admin/ops_operator/read_only_auditor §2.2
public.admin_mfa_devices 管理员 TOTP MFA 设备(强制启用) §2.2
public.admin_sessions 管理员登录会话30 min 滚动超时,支持强制登出) §2.2
public.ip_whitelist 管理控制台 CIDR 白名单 §2.2
public.platform_audit_logs 所有写操作+高危操作审计append-only建议月度分区 §2.3
public.backup_schedules 全局/租户级定时备份计划NULL tenant_id = 全局默认) §2.4
public.backup_records 备份任务执行记录auto/manual/pre_upgrade/pre_restore §2.4
public.export_tasks 数据导出异步任务CSV/JSON/SQL Dump24h 下载链接) §2.4
public.system_versions 平台版本历史,部分唯一索引保证唯一 current §2.5
public.upgrade_events 升级/回滚事件,tenant_progress JSONB 快照各租户状态 §2.5
public.enum_labels 固定枚举字典(英文 Key → 中文标签),所有租户共享 §2.6

关键约束提示

  • tenant_status_logs / platform_audit_logs 无 deleted_at,禁止 UPDATE/DELETEappend-only
  • public.tenants.schema_name 创建后不可修改
  • public.tenants 不再使用 is_active boolean改用 6 态 status 枚举
  • platform_admins 与租户 staff 完全独立,不共享 auth 系统
  • system_versions 通过部分唯一索引确保全局只有一个 status='current'

四、租户 SchemaTenant Schema

以下所有表均在每个租户的独立 Schema 内创建。


3.1 组织人事模块Organization & HR

详细模型 → 见 DATA_MODEL_ORG.md
该文件为权威定义,包含完整字段、枚举、查询模式和禁止操作。

核心表概览(开发时以 DATA_MODEL_ORG.md 为准):

表名 说明
org_units 组织树节点(公司/事业部/大区/区域/片区/门店/店组/职能),物化路径树
staff 员工主表含加密手机号、角色、在职状态、Django auth 绑定
staff_personal_info 员工个人信息扩展证件、学历、婚育等1:1
staff_transfer_logs 人事异动不可变审计日志(入职/调动/离职/复职等)
staff_reward_punish 奖惩记录
staff_work_experiences 工作经历
staff_educations 教育经历
staff_trainings 培训经历
staff_family_members 家庭成员
staff_accounts 第三方平台账号绑定58安居客/中国网络经纪人等)

关键约束提示

  • staff.phone_enc AES-256-GCM 加密,staff.phone_hash SHA-256 用于唯一索引
  • staff_transfer_logs 无 deleted_at,不可删除
  • org_units 路径查询:WHERE path LIKE '/root/{target_id}/%'
  • 员工离职:status = 'resigned' + deleted_at 软删除,记录永久保留

3.2 区域与楼盘模块Region & Complex Management

详细模型 → 见 DATA_MODEL_COMPLEX.md
本节仅作概览,开发时以 DATA_MODEL_COMPLEX.md 为权威定义。

核心表概览(开发时以 DATA_MODEL_COMPLEX.md 为准):

表名 说明 关键字段
districts 城区/行政区 city, name, short_name, sort_order
business_areas 商圈/板块(从属于城区) district_id, name, latitude, longitude
metro_lines 地铁线路 city, name, color
metro_stations 地铁站点 metro_line_id, name, latitude, longitude
schools 学校(对口学区) district_id, name, type, nature, level
complexes 楼盘/小区(房源底座) name, district_id, address, latitude/longitude, lock_*, search_vector
complex_aliases 楼盘别名(含系统别名/用户自定义别名) complex_id, alias, is_system
complex_business_areas 楼盘↔商圈多对多(含主商圈标识) complex_id, business_area_id, is_primary
complex_schools 楼盘↔学校关联(含学区类型) complex_id, school_id, zone_type
complex_metro_stations 楼盘↔地铁站关联(含步行距离) complex_id, station_id, distance_meters
buildings 楼栋/单元 complex_id, name, is_standard, total_floors
room_units 房号/结构单元(楼层+房间号) building_id, floor, room_no, is_standard
complex_photos 楼盘照片(楼盘图/户型图/VR complex_id, category, file_key, is_cover
complex_attachments 楼盘附件 complex_id, file_key, file_name
complex_price_trends 楼盘价格走势(月度) complex_id, record_month, avg_unit_price

3.3 房源模块Property Management

详细模型 → 见 DATA_MODEL_PROPERTY.md
本节仅作概览,开发时以 DATA_MODEL_PROPERTY.md 为权威定义。

核心表概览(开发时以 DATA_MODEL_PROPERTY.md 为准):

表名 说明 关键字段
properties 房源主表系统核心89,000+ 数据量) status, attribute, property_type, complex_id, sale_price, area, grade, completeness_score, search_vector
property_contacts 业主/联系人(手机号 AES 加密+哈希索引) property_id, phone_enc, phone_hash, identity, is_number_holder
listing_histories 挂牌历史快照(不可删除) property_id, listing_type, status, sale_price, seller_agent_snapshot
price_changes 调价记录(不可删除) property_id, old_sale_price, new_sale_price, change_reason, changed_by
follow_logs 跟进日志6种类型最高写入频率 property_id, log_type, content, is_deletable, operator_id
follow_log_attachments 跟进附件(图片) follow_log_id, file_key, file_type
follow_log_recordings 跟进录音 follow_log_id, file_key, duration_seconds
property_keys 钥匙管理(机械钥匙/密码) property_id, key_type, holder_id, is_active
key_attachments 钥匙附件 key_id, file_key
commissions 委托管理(独家/非独家) property_id, commission_type, period_start, status
commission_attachments 委托附件(身份证/产证/委托书) commission_id, category, file_key
field_surveys 实勘管理GPS 打卡) property_id, status, gps_latitude, gps_longitude, created_by
survey_photos 实勘照片(按空间分类) survey_id, category, file_key, is_vr_screenshot
property_photos 房源图片(经纪人管理,封面唯一约束) property_id, category, is_cover, file_key
property_attachments 房源附件 property_id, category, file_key
property_marketing 营销信息1:1卖点/业主心态/介绍) property_id, marketing_title, core_selling_points
property_certificates 产证信息1:1 property_id, cert_no, owner_name, land_nature
property_completeness 维护完成度快照1:1Celery 异步计算) property_id, total_score, score_survey, score_commission, ...
property_tags 标签字典(系统预置+运营自定义) name, color, is_system
property_tag_relations 房源↔标签多对多 property_id, tag_id
property_favorites 经纪人收藏房源 staff_id, property_id
property_protections 保护房设置1:1 property_id, is_protected, start_at, end_at
number_holder_approvals 号码方变更审批 property_id, applicant_id, status

关键约束提示

  • property_contacts.phone_hash 是重复房源检测的主要依据,录入前必须查重
  • listing_histories / price_changes 无 deleted_at,不可删除
  • follow_logsis_deletable=FALSEsensitive_view 类型)不可软删
  • completeness_score 只由 Celery 任务写入Application 层禁止直接更新
  • last_followed_at 由触发器 trg_update_last_followed 自动维护
  • property_photos.is_cover 唯一约束:每套房源仅一张封面

3.4 登录与账号认证Login & Account

详细模型 → 见 DATA_MODEL_LOGIN.md
该文件为权威定义包含完整字段、状态机、Redis 缓存结构和禁止操作。

核心表概览(开发时以 DATA_MODEL_LOGIN.md 为准):

表名 说明
user_accounts 账号主表1:1 绑定 org.Staff),含加密手机号/哈希、状态机active/locked/disabled、初始密码标识
login_attempts 登录审计日志append-only成功/失败均记录,无 FK 冗余存 username 保证历史完整)
password_reset_tokens 密码重置 Token有效期 30 分钟,使用后立即标记 is_used
password_histories 历史密码记录(保留最近 3 条,含初始密码,防止重复使用)

关键约束提示

  • user_accounts 主键用 BIGSERIAL(非 UUID登录审计场景 BigInt 更高效
  • user_accounts.phone_enc AES-256-GCM 加密,phone_hash SHA-256 用于唯一索引
  • 禁止物理删除 user_accounts,离职员工只能 status=disabled
  • 账号锁定5 次密码错误)→ status=lockedlocked_until=NOW()+30minRedis 仅计数,实际锁定以 DB 为准
  • Tenant Admin 的 staff_id 可为空(可无员工档案);普通员工 staff_id 必填且关联 active Staff
  • 员工离职(org.Staff.status→resigned)→ 应用层 Service 调用触发账号 status→disabled禁止循环 FK
  • password_reset_tokens / login_attempts 无 deleted_at,不可修改/删除

Redis 辅助状态(非持久化):

Key 格式 TTL 说明
captcha_token:{uuid} 3 分钟 滑块验证会话 Token
captcha_pass:{uuid} 3 分钟 一次性通过凭证(验证后立即删除)
login_fail:{tenant_id}:{username} 30 分钟 连续密码错误计数≥5 触发锁定
recover_email:{email} 1 小时 找回邮件发送次数上限 3 次
tenant_verify_ip:{ip} 1 分钟 Tenant 验证接口 IP 限流≥10 次拒绝

3.5 权限管理Permission & RBAC

详细模型 → 见 DATA_MODEL_PERMISSION.md
该文件为权威定义,包含完整字段、权限解析算法、ScopeQueryBuilder 实现和禁止操作。

权限模型概述Hybrid RBAC + Individual Override支持 BOOLEAN / SCOPE / INTEGER 三类权限值,多角色合并规则 OR / MAX。

核心表概览(开发时以 DATA_MODEL_PERMISSION.md 为准):

表名 说明
permission_defs 权限目录(约 300 条,PUBLIC Schemashared_apps 存储,所有租户共享),含模块/分组/值类型/默认值/上限类别
roles 业务角色每租户独立5 种类别:agent/store_manager/director/operator/custom,含系统内置标识
role_permissions 角色↔权限值(稀疏存储,仅存与 default_value 不同的项)
staff_roles 员工↔角色分配N:M含主角色标识 is_primary、有效期)
staff_permission_overrides 员工个人权限覆盖稀疏存储仅存与角色合并值不同的项3 种 override_modeREPLACE / RESTRICT / GRANT
staff_data_scopes 员工数据范围扩展(补充 SCOPE 权限之外的额外可读范围,如特殊跨门店授权)
permission_change_logs 权限变更不可变审计日志append-only禁止 UPDATE/DELETE

关键约束提示

  • permission_defs 位于 Public Schemashared_apps),所有租户共享;roles 及其余表属租户 Schema
  • 禁止硬删除 permission_defs,改用 is_active=FALSE 下线;code 字段不可修改
  • 禁止直接构造 Q 对象绕过 ScopeQueryBuilder,会导致越权漏洞
  • permission_change_logs 无 deleted_at,禁止 UPDATE/DELETE
  • 员工权限解析:is_system_admin=TRUE → 短路返回全权限;否则多角色 OR/MAX 合并后叠加 Override
  • StaffPermissionOverride 保存前必须做差异对比,禁止存与角色合并值相同的冗余记录(稀疏存储)
  • staff_roles.is_primary 唯一约束通过 Signal 维护,禁止绕过

权限解析缓存

Cache Key TTL 失效触发
perm:v{VER}:{schema}:{staff_id} 3600s Override / StaffRole 变更
perm:v{VER}:{schema}:role:{role_id}:staff_ids 3600s 角色权限变更 → Pipeline 批量失效
perm:inconsistent:{schema}:{staff_id} 300s 同上
perm:defs:{schema} 86400s PermissionDef 变更(低频)
perm:role_applied_count:{schema}:{role_id} 600s StaffRole 变更

版本号机制CACHE_VERSION 在 Django settings 中,升级 PermissionDef 结构时 bump一键全局失效。


3.17 客源管理Client Management

详细模型 → 见 DATA_MODEL_CLIENT.md
该文件为权威定义,包含完整字段、枚举、状态机、查询模式和禁止操作。

核心表概览(开发时以 DATA_MODEL_CLIENT.md 为准):

表名 说明
clients 客源主表(私客/公客/成交客),含加密手机号哈希、活跃度、归属人
client_contacts 联系人1:N手机号加密+哈希,支持多联系人
client_requirements 需求信息(可多类型:二手/新房/租房),含预算/面积/商圈/朝向等偏好
client_follow_logs 跟进日志高写入频率5种类型敏感查看类型不可删
client_follow_log_attachments 跟进附件(图片/录音最大20MB
client_viewings 带看/预约记录1:N含陪看人/合作带看人)
client_property_matches 智能配房结果(录客配房/系统配房,匹配度评分)
client_status_logs 状态变更不可变审计日志(改状态/改等级/转公/转成交/转无效等)
client_favorite_folders 私客收藏夹(经纪人自定义分组)
client_folder_items 收藏夹与客源的多对多关联
client_school_preferences 意向学校(拆表,支持精确查询)

关键约束提示

  • client_contacts.phone_hash 是重复客源检测的唯一依据,录入前必须查重
  • client_status_logs 无 deleted_at,不可删除
  • 私客超时(配置天数内无跟进)→ Celery 自动转公(transfer_to_public_type = 'auto'
  • 活跃度 activity_level 由 Celery 每日凌晨批量计算,不实时更新

3.18 系统设置System Settings

归属说明

  • lookup_categories / lookup_items / saved_filters跨模块系统表,权威定义在本节。
  • property_tags / property_tag_relations / property_favorites / property_protections / number_holder_approvals 属房源模块配套表,权威定义已迁至 DATA_MODEL_PROPERTY.md §4.19-§4.22,本节不再重复 DDL修复 S1/S2
-- ============================================================
-- 枚举/选项管理:跟进目的、标签、来源渠道 等运营维护的枚举值
-- ============================================================

CREATE TABLE lookup_categories (
    id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code    VARCHAR(50) UNIQUE NOT NULL,       -- 如follow_purpose, property_source
    name    VARCHAR(100) NOT NULL,
    module  VARCHAR(30) NOT NULL              -- property/client/system
);

CREATE TABLE lookup_items (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    category_id     UUID NOT NULL REFERENCES lookup_categories(id) ON DELETE CASCADE,
    value           VARCHAR(100) NOT NULL,
    label           VARCHAR(100) NOT NULL,     -- 显示文本
    sort_order      INTEGER NOT NULL DEFAULT 0,
    is_active       BOOLEAN NOT NULL DEFAULT TRUE,
    metadata        JSONB NOT NULL DEFAULT '{}',  -- 扩展属性
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_lookup_items_category ON lookup_items(category_id)
    WHERE is_active = TRUE;
CREATE UNIQUE INDEX idx_lookup_items_value ON lookup_items(category_id, value);

-- 筛选方案(保存的搜索条件,跨模块通用)
CREATE TABLE saved_filters (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    staff_id        UUID NOT NULL REFERENCES staff(id) ON DELETE CASCADE,
    name            VARCHAR(100) NOT NULL,
    module          VARCHAR(20) NOT NULL DEFAULT 'property',
    filter_params   JSONB NOT NULL,            -- 完整筛选参数 JSON
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_saved_filters_staff ON saved_filters(staff_id, module);

已迁出本节的表(权威源见子文档):

表名 权威定义位置
property_tags DATA_MODEL_PROPERTY.md §4.19
property_tag_relations DATA_MODEL_PROPERTY.md §4.19
property_favorites DATA_MODEL_PROPERTY.md §4.20
property_protections DATA_MODEL_PROPERTY.md §4.21
number_holder_approvals DATA_MODEL_PROPERTY.md §4.22

3.19 枚举字典Enum Labels

权威定义 → 见 DATA_MODEL/ENUMS.md
本节为概览,开发时以 ENUMS.md 为准。

表归属

enum_labels 位于 Public Schemashared_apps),所有租户共享,不属于任何租户 Schema

核心表设计

CREATE TABLE enum_labels (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    domain      VARCHAR(60)  NOT NULL,  -- 枚举域,格式:{模块}.{字段},如 client.status
    value       VARCHAR(60)  NOT NULL,  -- 英文 Key与数据库 CHECK 约束一致)
    label_zh    VARCHAR(60)  NOT NULL,  -- 中文标签(前端展示用)
    sort_order  SMALLINT     NOT NULL DEFAULT 0,
    is_active   BOOLEAN      NOT NULL DEFAULT TRUE,
    remark      TEXT,
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX idx_enum_labels_domain_value ON enum_labels(domain, value);
CREATE INDEX idx_enum_labels_domain ON enum_labels(domain, sort_order);

覆盖的枚举域domain 清单)

domain 说明 对应表字段
client.status 客源状态7 态) clients.status
client.grade 客源等级5 档 + E clients.grade
client.purpose_type 需求类型 client_requirements.purpose_type
client.usage 房源用途偏好 client_requirements.usage
client.orientation 朝向偏好 client_requirements.orientation
client.payment_method 付款方式 clients.payment_method
property.status 房源状态 properties.status
property.attribute 房源属性(公/私/保护) properties.attribute
property.usage 房源用途 properties.usage
property.grade 房源等级5 档) properties.grade
property.listing_type 挂牌类型 properties.listing_type
property.decoration 装修程度 properties.decoration
property.orientation 朝向 properties.orientation
commission.type 委托类型 commissions.commission_type
field_survey.status 实勘状态 field_surveys.status
follow_log.log_type 跟进日志类型 follow_logs.log_type

重要约定

  • enum_labels.value 必须与对应表的 CHECK 约束完全一致,两者必须同步修改
  • 新增枚举值流程:① 修改 DDL CHECK 约束 → ② 插入 enum_labels 种子数据 → ③ 更新 ENUMS.md
  • is_active = FALSE 仅停用前端展示,不得修改或删除已有 value(历史数据引用不可破坏)
  • 前端下拉渲染统一从 enum_labels 读取,禁止在前端代码中硬编码中文标签

lookup_items 的区别

对比维度 enum_labels lookup_items
用途 固定枚举的中文标签映射 运营可配置的动态选项(如跟进目的、来源渠道)
修改权限 仅开发/DBA 运营人员后台配置
Schema 位置 Public Schema共享 Tenant Schema每租户独立
典型示例 客源状态、房源等级 跟进目的、客户来源渠道

五、关键索引汇总与查询优化策略

4.1 房源列表页核心查询分析

-- 典型查询:出售状态 + 公盘 + 特定区域 + 价格区间 + 户型筛选 + 按挂牌日期排序
-- 优化方案:复合索引覆盖最高频维度组合

-- 高频组合索引status + attribute覆盖 90% 的列表查询)
CREATE INDEX idx_properties_list_composite ON properties
    (status, attribute, complex_id, sale_price DESC NULLS LAST)
    WHERE deleted_at IS NULL;

-- 与我相关查询(经纪人个人仪表板)
CREATE INDEX idx_properties_my_properties ON properties
    (seller_agent_id, status, listed_at DESC NULLS LAST)
    WHERE deleted_at IS NULL;

4.2 全文搜索触发器(自动维护 search_vector

-- 房源全文检索向量更新触发器
CREATE OR REPLACE FUNCTION update_property_search_vector()
RETURNS TRIGGER AS $$
BEGIN
    NEW.search_vector :=
        setweight(to_tsvector('simple', COALESCE(NEW.block_no, '') ||
                              ' ' || COALESCE(NEW.unit_no, '') ||
                              ' ' || COALESCE(NEW.room_no, '')), 'A') ||
        setweight(to_tsvector('simple', COALESCE(NEW.remarks, '')), 'C');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_property_search_vector
    BEFORE INSERT OR UPDATE OF block_no, unit_no, room_no, remarks
    ON properties
    FOR EACH ROW EXECUTE FUNCTION update_property_search_vector();

-- 楼盘全文检索向量(含别名,提升模糊搜索精度)
CREATE OR REPLACE FUNCTION update_complex_search_vector()
RETURNS TRIGGER AS $$
BEGIN
    NEW.search_vector :=
        setweight(to_tsvector('simple', COALESCE(NEW.name, '')), 'A') ||
        setweight(to_tsvector('simple', COALESCE(NEW.alias, '')), 'B') ||
        setweight(to_tsvector('simple', COALESCE(NEW.address, '')), 'C');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_complex_search_vector
    BEFORE INSERT OR UPDATE OF name, alias, address
    ON complexes
    FOR EACH ROW EXECUTE FUNCTION update_complex_search_vector();

4.3 last_followed_at 自动维护触发器

-- 每次写入跟进日志时,自动更新 properties.last_followed_at
CREATE OR REPLACE FUNCTION update_property_last_followed()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.log_type = 'written' THEN
        UPDATE properties
        SET last_followed_at = NEW.created_at,
            updated_at = NOW()
        WHERE id = NEW.property_id;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_update_last_followed
    AFTER INSERT ON follow_logs
    FOR EACH ROW EXECUTE FUNCTION update_property_last_followed();

六、Redis 缓存策略

5.1 缓存 Key 规范

# 格式:{tenant_schema}:{module}:{entity}:{id}:{field}
# TTL 单位:秒

# 房源详情(高频读取)
{schema}:prop:detail:{property_id}              TTL: 300  (5分钟)

# 房源联系人含解密号码敏感TTL 短)
{schema}:prop:contacts:{property_id}            TTL: 60   (1分钟)

# 楼盘基础信息(低变更频率)
{schema}:complex:base:{complex_id}              TTL: 3600 (1小时)

# 楼盘名称自动补全候选列表(联想搜索)
{schema}:complex:autocomplete:{prefix}          TTL: 600  (10分钟)

# 员工信息(用于日志快照)
{schema}:staff:base:{staff_id}                  TTL: 1800 (30分钟)

# 枚举值/lookup几乎不变
{schema}:lookup:{category_code}                 TTL: 86400 (24小时)

# 登录模块(详见 DATA_MODEL_LOGIN.md §四)
captcha_token:{uuid}                            TTL: 180   (3分钟)
captcha_pass:{uuid}                             TTL: 180   (3分钟)
login_fail:{tenant_id}:{username}               TTL: 1800  (30分钟连续失败计数)
recover_email:{email}                           TTL: 3600  (1小时发送次数限流)
recover_reset:{account_id}                      TTL: 3600  (1小时Token 生成次数限流)
tenant_verify_ip:{ip}                           TTL: 60    (1分钟IP 限流)

# 权限模块(详见 DATA_MODEL_PERMISSION.md §六)
perm:v{VER}:{schema}:{staff_id}                 TTL: 3600  (员工完整权限快照)
perm:v{VER}:{schema}:role:{role_id}:staff_ids   TTL: 3600  (角色→员工 ID 列表,批量失效用)
perm:inconsistent:{schema}:{staff_id}           TTL: 300   (权限不一致标记)
perm:defs:{schema}                              TTL: 86400 (权限定义全量缓存)
perm:role_applied_count:{schema}:{role_id}      TTL: 600   (角色应用人数)

# 标签列表
{schema}:tags:property                          TTL: 3600

# 维护完成度Celery 计算后写入,详情页直接读 Redis
{schema}:prop:completeness:{property_id}        TTL: 600

# 房源列表计数(筛选后总条数,避免 COUNT(*) 全扫)
{schema}:prop:count:{filter_hash}               TTL: 30   (短TTL保证准确性)

5.2 缓存失效策略

# Django Signal 驱动的缓存失效(在 models.py 中注册)

# 房源更新 → 失效详情缓存 + 完成度缓存
# 跟进日志新增 → 失效 last_followed_at 缓存
# 联系人更新 → 失效联系人缓存(立即)
# 楼盘更新 → 失效楼盘缓存 + 相关房源缓存(批量)
# 枚举更新 → 失效对应 lookup 缓存

七、Django Model 层设计要点

6.1 抽象基类

# models/base.py

import uuid
from django.db import models

class UUIDPrimaryKeyModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    class Meta:
        abstract = True

class TimeStampedModel(UUIDPrimaryKeyModel):
    created_at = models.DateTimeField(auto_now_add=True, db_index=False)
    updated_at = models.DateTimeField(auto_now=True)
    class Meta:
        abstract = True

class SoftDeleteModel(TimeStampedModel):
    deleted_at = models.DateTimeField(null=True, blank=True, db_index=False)

    class Meta:
        abstract = True

    def soft_delete(self, deleted_by=None):
        from django.utils import timezone
        self.deleted_at = timezone.now()
        self.save(update_fields=['deleted_at', 'updated_at'])

class AuditedModel(SoftDeleteModel):
    created_by = models.ForeignKey(
        'staff.Staff', null=True, on_delete=models.SET_NULL,
        related_name='+', db_column='created_by'
    )
    updated_by = models.ForeignKey(
        'staff.Staff', null=True, on_delete=models.SET_NULL,
        related_name='+', db_column='updated_by'
    )
    class Meta:
        abstract = True

6.2 加密字段 Mixin

# utils/encryption.py
# 手机号加密AES-256-GCM + SHA-256 哈希索引

class EncryptedPhoneField:
    """
    存储时phone → AES加密 → phone_enc (BYTEA)
              phone → SHA256  → phone_hash (VARCHAR 64)
    查询时phone_hash 走索引phone_enc 解密展示
    打码展示前3位明文 + ******* + 后3位
    """
    pass

6.3 Manager 过滤软删除

class ActiveManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)

class PropertyManager(ActiveManager):
    def public(self):
        return self.get_queryset().filter(attribute='public')

    def mine(self, staff_id):
        return self.get_queryset().filter(seller_agent_id=staff_id)

八、数据量与性能预测

表名 预估行数 增长速度 分区策略
properties 89,000+ 中速 暂不分区,建议 500k 后按 created_at RANGE 分区
follow_logs 200万+ 高速(最高频写入) created_at 月度 RANGE 分区
property_photos 500万+ 高速 property_id HASH 分区16分区
price_changes 50万 中速 无需分区
listing_histories 20万 低速 无需分区
clients 10万+ 中速 暂不分区
viewings 100万 中速 无需分区

九、必须在开发启动前明确的数据架构决策

决策项 推荐方案 风险
小区数据来源 预导入基础数据(安居客/链家 API+ 支持手动新增兜底 高:影响录入体验
私盘可见范围 录入人所在门店可见(综合业务需求) 需与权限模块约定
号码查看权限 角色级控制:经纪人限查自己相关房源,店长无限制 需合规确认
重复房源主键 主键:手机号 hash辅助小区+楼栋+单元+房号)组合 需双重校验
跟进目的枚举 存 lookup_items 表,运营可维护 初始化数据需提前收集
手机号加密算法 AES-256-GCM密钥存 Django settings生产用 Vault 密钥管理需单独规划

本文档为 Fonrey 系统 DATA MODEL v1.0,随 PRD 迭代同步更新。 下一步建议API 接口规范URL 设计 + Request/Response Schema