Sync: update data model docs
This commit is contained in:
@@ -87,7 +87,7 @@
|
||||
| **Complex(楼盘/小区)** | `complexes` → [DATA_MODEL_COMPLEX.md](./DATA_MODEL_COMPLEX.md) | 房源录入的基础底座,维护楼盘标准名称/坐标/锁定状态/别名等 |
|
||||
| **Building(楼栋/单元)** | `buildings` → [DATA_MODEL_COMPLEX.md](./DATA_MODEL_COMPLEX.md) | 楼盘下的物理楼栋,区分标准结构与非标结构 |
|
||||
| **RoomUnit(房号)** | `room_units` → [DATA_MODEL_COMPLEX.md](./DATA_MODEL_COMPLEX.md) | 楼层+房间号,房源定位的最细粒度 |
|
||||
| **Property(房源)** | `properties` → §3.3 | 系统核心表,每套二手房源的完整档案,支持出售/出租/出售兼出租三态 |
|
||||
| **Property(房源)** | `properties` → [DATA_MODEL_PROPERTY.md](./DATA_MODEL_PROPERTY.md) | 系统核心表,每套二手房源的完整档案,支持出售/出租/出售兼出租三态 |
|
||||
| **Client(客源)** | `clients` → [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 买家/租客档案,分私客/公客/成交客,含活跃度评分与自动公客转换机制 |
|
||||
| **Viewing(带看)** | `client_viewings` → [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 经纪人带客户看房的完整记录 |
|
||||
| **Match(配对)** | `client_property_matches` → [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 系统/人工推荐的客源↔房源配对 |
|
||||
@@ -117,7 +117,7 @@ OrgUnit (组织架构)
|
||||
| [DATA_MODEL_ORG.md](./DATA_MODEL_ORG.md) | 组织人事(org_units, staff, 异动/奖惩/教育/家庭等) | ✅ 完成 |
|
||||
| [DATA_MODEL_COMPLEX.md](./DATA_MODEL_COMPLEX.md) | 楼盘/区域(districts, business_areas, complexes, buildings, room_units, schools 等) | ✅ 完成 |
|
||||
| [DATA_MODEL_CLIENT.md](./DATA_MODEL_CLIENT.md) | 客源管理(clients, requirements, follow_logs, viewings, matches 等) | ✅ 完成 |
|
||||
| 本文档 §3.3–§3.16 | 房源核心(properties 及配套 12 张表)、系统设置 | ✅ 完成 |
|
||||
| [DATA_MODEL_PROPERTY.md](./DATA_MODEL_PROPERTY.md) | 房源管理(properties 及配套 22 张表,含跟进/钥匙/委托/实勘/营销/产证/完成度等) | ✅ 完成 |
|
||||
|
||||
---
|
||||
|
||||
@@ -220,765 +220,46 @@ CREATE INDEX idx_domains_primary ON public.domains(tenant_id) WHERE is_primary =
|
||||
|
||||
---
|
||||
|
||||
### 3.3 房源核心模块(Property Core)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 房源主表:系统最核心的表,全部筛选/排序/搜索围绕此表展开
|
||||
-- 设计重点:89,000+ 数据量,复合索引策略,分区预留
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE properties (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- ── 基础分类 ──
|
||||
property_type VARCHAR(20) NOT NULL
|
||||
CHECK (property_type IN ('residential','villa','commercial_residential',
|
||||
'shop','office','other')),
|
||||
-- residential=住宅, villa=别墅, commercial_residential=商住,
|
||||
-- shop=商铺, office=写字楼, other=其他
|
||||
|
||||
-- ── 交易状态 ──
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'for_sale'
|
||||
CHECK (status IN ('for_sale','for_rent','for_sale_rent',
|
||||
'suspended','sold_elsewhere','rented_elsewhere',
|
||||
'sold','unlisted')),
|
||||
-- for_sale=出售, for_rent=出租, for_sale_rent=租售,
|
||||
-- suspended=暂缓, sold_elsewhere=他售, rented_elsewhere=他租,
|
||||
-- sold=成交, unlisted=未挂牌
|
||||
|
||||
-- ── 流通属性 ──
|
||||
attribute VARCHAR(20) NOT NULL DEFAULT 'public'
|
||||
CHECK (attribute IN ('public','private','special','sealed')),
|
||||
-- public=公盘, private=私盘, special=特盘, sealed=封盘
|
||||
private_reason TEXT, -- 私盘/封盘必填说明
|
||||
|
||||
-- ── 位置信息 ──
|
||||
complex_id UUID NOT NULL REFERENCES complexes(id) ON DELETE RESTRICT,
|
||||
building_id UUID REFERENCES buildings(id) ON DELETE SET NULL,
|
||||
block_no VARCHAR(30), -- 栋/幢/弄号
|
||||
unit_no VARCHAR(30), -- 单元号
|
||||
room_no VARCHAR(30), -- 房号/门牌号
|
||||
floor SMALLINT NOT NULL, -- 所在楼层
|
||||
total_floors SMALLINT NOT NULL, -- 总楼层
|
||||
CONSTRAINT chk_floor CHECK (floor > 0 AND floor <= total_floors),
|
||||
|
||||
-- ── 户型 ──
|
||||
bedroom_count SMALLINT NOT NULL DEFAULT 0, -- 室
|
||||
living_room_count SMALLINT NOT NULL DEFAULT 0, -- 厅
|
||||
bathroom_count SMALLINT NOT NULL DEFAULT 0, -- 卫
|
||||
kitchen_count SMALLINT NOT NULL DEFAULT 0, -- 厨
|
||||
balcony_count SMALLINT NOT NULL DEFAULT 0, -- 阳台数
|
||||
|
||||
-- ── 面积 ──
|
||||
area NUMERIC(8,2) NOT NULL, -- 建筑面积 m²
|
||||
inner_area NUMERIC(8,2), -- 套内面积 m²(编辑时填写)
|
||||
|
||||
-- ── 价格 ──
|
||||
sale_price NUMERIC(12,2), -- 挂牌售价(万元)
|
||||
sale_bottom_price NUMERIC(12,2), -- 售底价(万元,内部可见)
|
||||
sale_record_price NUMERIC(12,2), -- 备案/核验价(万元)
|
||||
rent_price NUMERIC(10,2), -- 挂牌租价(元/月)
|
||||
|
||||
-- ── 基础物理属性 ──
|
||||
orientation VARCHAR(10)
|
||||
CHECK (orientation IN ('east','south','west','north',
|
||||
'southeast','northeast','east_west',
|
||||
'south_north','northwest','southwest')),
|
||||
decoration VARCHAR(10)
|
||||
CHECK (decoration IN ('rough','plain','simple','medium',
|
||||
'fine','luxury')),
|
||||
-- rough=毛坯, plain=清水, simple=简装, medium=中装, fine=精装, luxury=豪装
|
||||
|
||||
has_elevator BOOLEAN,
|
||||
built_year SMALLINT,
|
||||
|
||||
-- ── 用途 ──
|
||||
usage_type VARCHAR(30), -- 住宅/商住/商业/普通住宅/花园洋房 等
|
||||
usage_subtype VARCHAR(30), -- 细分用途
|
||||
|
||||
-- ── 商铺专属 ──
|
||||
shop_frontage NUMERIC(6,2), -- 开间(米)
|
||||
shop_depth NUMERIC(6,2), -- 进深(米)
|
||||
shop_height NUMERIC(6,2), -- 层高(米)
|
||||
shop_location VARCHAR(20)
|
||||
CHECK (shop_location IS NULL OR
|
||||
shop_location IN ('street','mall','residential',
|
||||
'ground_floor','complex')),
|
||||
|
||||
-- ── 房屋状态 ──
|
||||
house_status VARCHAR(20)
|
||||
CHECK (house_status IN ('owner_occupied','vacant',
|
||||
'tenant_occupied','unknown')),
|
||||
viewing_time VARCHAR(20)
|
||||
CHECK (viewing_time IN ('anytime','by_appointment','inconvenient')),
|
||||
|
||||
-- ── 等级与标签 ──
|
||||
grade VARCHAR(5)
|
||||
CHECK (grade IN ('A_urgent','A','B','C','D')),
|
||||
-- A_urgent=A(急迫), A=A, B=B(较强), C=C(一般), D=D
|
||||
|
||||
-- ── 交易属性 ──
|
||||
ownership_years VARCHAR(30), -- 房本年限:不满2年/满2年/满5年 等
|
||||
ownership_years_detail VARCHAR(20), -- 满五/不满五
|
||||
ownership_nature VARCHAR(20)
|
||||
CHECK (ownership_nature IS NULL OR
|
||||
ownership_nature IN ('commercial','reform_housing',
|
||||
'collective','economic')),
|
||||
-- commercial=商品房, reform_housing=房改房, collective=集资房, economic=经济适用房
|
||||
is_only_house BOOLEAN, -- 唯一住房
|
||||
payment_method VARCHAR(30)
|
||||
CHECK (payment_method IS NULL OR
|
||||
payment_method IN ('full','mortgage','installment','advance')),
|
||||
tax_included VARCHAR(10)
|
||||
CHECK (tax_included IS NULL OR
|
||||
tax_included IN ('each_party','net','inclusive')),
|
||||
has_mortgage BOOLEAN,
|
||||
has_loan BOOLEAN,
|
||||
has_seal BOOLEAN,
|
||||
has_restriction BOOLEAN,
|
||||
original_price NUMERIC(12,2), -- 原购价(万元)
|
||||
sale_reason TEXT, -- 售房原因(最多200字)
|
||||
|
||||
-- ── 营销备注 ──
|
||||
remarks TEXT, -- 房源备注(最多500字)
|
||||
|
||||
-- ── 相关方(冗余存储 UUID,完整信息查 staff 表)──
|
||||
first_recorder_id UUID REFERENCES staff(id) ON DELETE SET NULL, -- 首录方
|
||||
number_holder_id UUID REFERENCES staff(id) ON DELETE SET NULL, -- 号码方
|
||||
seller_agent_id UUID REFERENCES staff(id) ON DELETE SET NULL, -- 出售方
|
||||
buyer_agent_id UUID REFERENCES staff(id) ON DELETE SET NULL, -- 实买方
|
||||
|
||||
-- ── 来源 ──
|
||||
source VARCHAR(50), -- 房源来源渠道(由运营维护枚举)
|
||||
|
||||
-- ── 维护完成度(冗余缓存,定期重算)──
|
||||
completeness_score SMALLINT NOT NULL DEFAULT 0, -- 0-100 分
|
||||
|
||||
-- ── 时间轨迹 ──
|
||||
listed_at TIMESTAMPTZ, -- 最近一次挂牌时间
|
||||
last_followed_at TIMESTAMPTZ, -- 最后跟进时间(冗余,加速排序)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_by UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
updated_by UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
|
||||
-- ── 全文检索向量 ──
|
||||
search_vector TSVECTOR
|
||||
);
|
||||
|
||||
-- ── 索引策略(针对高频查询路径设计)──
|
||||
|
||||
-- 1. 最核心的列表页:按状态 + 属性 + 类型过滤
|
||||
CREATE INDEX idx_properties_status_attr ON properties(status, attribute, property_type)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 2. 区域筛选(通过 complex 表 JOIN 优化)
|
||||
CREATE INDEX idx_properties_complex ON properties(complex_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 3. 价格排序(出售最常用)
|
||||
CREATE INDEX idx_properties_sale_price ON properties(sale_price DESC NULLS LAST)
|
||||
WHERE deleted_at IS NULL AND status IN ('for_sale','for_sale_rent');
|
||||
|
||||
-- 4. 面积区间筛选
|
||||
CREATE INDEX idx_properties_area ON properties(area)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 5. 挂牌日期倒序(最新挂牌)
|
||||
CREATE INDEX idx_properties_listed_at ON properties(listed_at DESC NULLS LAST)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 6. 最后跟进日期(超时未跟进功能)
|
||||
CREATE INDEX idx_properties_last_followed ON properties(last_followed_at DESC NULLS LAST)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 7. 户型筛选
|
||||
CREATE INDEX idx_properties_bedroom ON properties(bedroom_count)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 8. 等级筛选
|
||||
CREATE INDEX idx_properties_grade ON properties(grade)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 9. 完成度排序(引导补全信息)
|
||||
CREATE INDEX idx_properties_completeness ON properties(completeness_score)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 10. 全文搜索
|
||||
CREATE INDEX idx_properties_search ON properties USING gin(search_vector);
|
||||
|
||||
-- 11. 与我相关(相关方快速定位)
|
||||
CREATE INDEX idx_properties_seller_agent ON properties(seller_agent_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
CREATE INDEX idx_properties_number_holder ON properties(number_holder_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 12. 复合索引:列表默认排序(状态 + 挂牌时间)
|
||||
CREATE INDEX idx_properties_list_default ON properties(status, listed_at DESC NULLS LAST)
|
||||
WHERE deleted_at IS NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 房源联系人(Property Contacts)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 业主/联系人:手机号加密存储,哈希值支持重复检测
|
||||
-- 安全要点:任何查看明文号码的行为均触发审计日志
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE property_contacts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
name VARCHAR(50) NOT NULL,
|
||||
gender VARCHAR(10) NOT NULL DEFAULT 'male'
|
||||
CHECK (gender IN ('male','female')),
|
||||
identity VARCHAR(20) NOT NULL DEFAULT 'contact'
|
||||
CHECK (identity IN ('owner','contact','subletter',
|
||||
'tenant','agent','corporate')),
|
||||
-- owner=业主, contact=联系人, subletter=二房东, tenant=租客,
|
||||
-- agent=代理人, corporate=企业法人
|
||||
|
||||
-- 手机号:加密存储 + 哈希索引(重复检测用)
|
||||
phone_enc BYTEA NOT NULL, -- AES-256-GCM 加密
|
||||
phone_hash VARCHAR(64) NOT NULL, -- SHA-256(phone) 用于去重查询
|
||||
phone2_enc BYTEA,
|
||||
phone2_hash VARCHAR(64),
|
||||
|
||||
wechat VARCHAR(100), -- 微信号(相对不敏感,可明文)
|
||||
qq VARCHAR(20),
|
||||
remarks TEXT,
|
||||
|
||||
-- 是否为号码方(关联审批流)
|
||||
is_number_holder BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
number_holder_approved_at TIMESTAMPTZ, -- 审批通过时间
|
||||
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_by UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
updated_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_contacts_property ON property_contacts(property_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 关键:手机号哈希全局索引(用于重复房源检测)
|
||||
CREATE INDEX idx_contacts_phone_hash ON property_contacts(phone_hash)
|
||||
WHERE deleted_at IS NULL;
|
||||
CREATE INDEX idx_contacts_phone2_hash ON property_contacts(phone2_hash)
|
||||
WHERE phone2_hash IS NOT NULL AND deleted_at IS NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 挂牌历史(Listing History)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 挂牌历史:记录房源每次上架的完整快照
|
||||
-- 设计重点:不可删除(合规),仅追加
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE listing_histories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE RESTRICT,
|
||||
|
||||
listing_type VARCHAR(20) NOT NULL
|
||||
CHECK (listing_type IN ('for_sale','for_rent')),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active'
|
||||
CHECK (status IN ('active','ended')),
|
||||
|
||||
-- 价格快照
|
||||
sale_price NUMERIC(12,2),
|
||||
rent_price NUMERIC(10,2),
|
||||
sale_unit_price NUMERIC(10,2), -- 元/m²,计算字段
|
||||
|
||||
-- 交易信息快照
|
||||
ownership_years VARCHAR(30),
|
||||
is_only_house BOOLEAN,
|
||||
tax_included VARCHAR(10),
|
||||
sale_reason TEXT,
|
||||
|
||||
-- 经纪人快照
|
||||
seller_agent_id UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
seller_agent_snapshot JSONB, -- 存储经纪人姓名+门店(防止变更后丢失)
|
||||
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ended_at TIMESTAMPTZ,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
-- 注:无 deleted_at,此表记录不可删除
|
||||
);
|
||||
|
||||
CREATE INDEX idx_listing_histories_property ON listing_histories(property_id);
|
||||
CREATE INDEX idx_listing_histories_active ON listing_histories(property_id)
|
||||
WHERE status = 'active';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.6 调价记录(Price Change Log)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 调价记录:支持折线图展示,不可删除
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE price_changes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE RESTRICT,
|
||||
|
||||
old_sale_price NUMERIC(12,2),
|
||||
new_sale_price NUMERIC(12,2),
|
||||
old_bottom_price NUMERIC(12,2),
|
||||
new_bottom_price NUMERIC(12,2),
|
||||
old_record_price NUMERIC(12,2),
|
||||
new_record_price NUMERIC(12,2),
|
||||
old_rent_price NUMERIC(10,2),
|
||||
new_rent_price NUMERIC(10,2),
|
||||
|
||||
change_reason TEXT NOT NULL, -- 最多200字
|
||||
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
changed_by UUID NOT NULL REFERENCES staff(id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_price_changes_property ON price_changes(property_id);
|
||||
CREATE INDEX idx_price_changes_time ON price_changes(property_id, changed_at DESC);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.7 跟进日志(Follow-up Logs)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 跟进日志:系统最高写入频率的表,按 property_id 分区预留
|
||||
-- 6 种类型:写入跟进/修改跟进/敏感信息跟进/敏感信息查看/其他跟进/系统日志
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE follow_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
log_type VARCHAR(30) NOT NULL
|
||||
CHECK (log_type IN ('written','modified','sensitive_op',
|
||||
'sensitive_view','other','system')),
|
||||
-- written=写入跟进(经纪人主动写)
|
||||
-- modified=修改跟进(字段变更自动生成)
|
||||
-- sensitive_op=敏感信息跟进(相关方保护变更)
|
||||
-- sensitive_view=敏感信息查看(查看号码等)
|
||||
-- other=其他跟进(钥匙/新增联系人等)
|
||||
-- system=系统日志
|
||||
|
||||
-- 写入跟进专用字段
|
||||
purpose VARCHAR(50), -- 跟进目的(由运营维护枚举值)
|
||||
content TEXT, -- 跟进内容,最少6字最多500字
|
||||
ai_tag VARCHAR(20)
|
||||
CHECK (ai_tag IS NULL OR ai_tag IN ('ai_for_sale','ai_not_for_sale')),
|
||||
|
||||
-- 修改跟进专用字段
|
||||
change_detail JSONB,
|
||||
-- 格式:{"field": "sale_price", "old": 850, "new": 800, "label": "售价"}
|
||||
-- 支持多字段同时变更
|
||||
|
||||
-- 系统标签(显示在日志时间线上的 tag)
|
||||
log_tag VARCHAR(50),
|
||||
-- 如:查看号码/图片下载/改状态/改价格/改等级/修改相关方 等
|
||||
|
||||
-- 可见性控制
|
||||
is_public BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
-- FALSE = 仅本人及上级可见
|
||||
|
||||
-- 操作人
|
||||
operator_id UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
operator_snapshot JSONB, -- {name, role, org_unit_name, store_group}
|
||||
|
||||
-- 是否可删除(敏感信息查看类型 = FALSE)
|
||||
is_deletable BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ -- 仅 is_deletable=TRUE 时才能软删
|
||||
);
|
||||
|
||||
-- 核心索引:时间线展示
|
||||
CREATE INDEX idx_follow_logs_property_time ON follow_logs(property_id, created_at DESC)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 按类型过滤(6个 Tab 查询)
|
||||
CREATE INDEX idx_follow_logs_type ON follow_logs(property_id, log_type, created_at DESC)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 操作员过滤(跟进日志搜索功能)
|
||||
CREATE INDEX idx_follow_logs_operator ON follow_logs(operator_id, created_at DESC)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 不可删除类型专用索引(合规审计)
|
||||
CREATE INDEX idx_follow_logs_sensitive ON follow_logs(property_id, created_at DESC)
|
||||
WHERE log_type IN ('sensitive_view','sensitive_op');
|
||||
|
||||
-- 跟进日志附件(一条跟进可附多张图)
|
||||
CREATE TABLE follow_log_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
follow_log_id UUID NOT NULL REFERENCES follow_logs(id) ON DELETE CASCADE,
|
||||
file_key TEXT NOT NULL, -- R2/S3 存储路径
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_size INTEGER NOT NULL, -- bytes
|
||||
file_type VARCHAR(10)
|
||||
CHECK (file_type IN ('bmp','jpg','png','svg','gif')),
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_follow_attachments_log ON follow_log_attachments(follow_log_id);
|
||||
|
||||
-- 跟进录音(独立存储,支持音频文件)
|
||||
CREATE TABLE follow_log_recordings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
follow_log_id UUID NOT NULL REFERENCES follow_logs(id) ON DELETE CASCADE,
|
||||
file_key TEXT NOT NULL,
|
||||
duration_seconds INTEGER,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.8 钥匙管理(Key Management)
|
||||
|
||||
```sql
|
||||
CREATE TABLE property_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
key_type VARCHAR(20) NOT NULL
|
||||
CHECK (key_type IN ('mechanical','password')),
|
||||
|
||||
-- 钥匙持有方
|
||||
holder_id UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
holder_snapshot JSONB, -- {name, store_group}(防人员变动丢失)
|
||||
storage_unit_id UUID REFERENCES org_units(id) ON DELETE SET NULL, -- 保管部门
|
||||
|
||||
-- 他司钥匙标记
|
||||
is_other_agency BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
other_agency_info VARCHAR(30), -- 他司信息,最多30字
|
||||
|
||||
remarks TEXT, -- 备注,最多200字
|
||||
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- FALSE = 钥匙已归还/失效
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_property_keys_property ON property_keys(property_id)
|
||||
WHERE is_active = TRUE;
|
||||
|
||||
-- 钥匙附件
|
||||
CREATE TABLE key_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
key_id UUID NOT NULL REFERENCES property_keys(id) ON DELETE CASCADE,
|
||||
file_key TEXT NOT NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.9 委托管理(Commission Management)
|
||||
|
||||
```sql
|
||||
CREATE TABLE commissions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
commission_type VARCHAR(50) NOT NULL, -- 独家委托/非独家委托(运营维护枚举)
|
||||
period_start DATE NOT NULL,
|
||||
period_end DATE,
|
||||
is_open_ended BOOLEAN NOT NULL DEFAULT FALSE, -- 无固定结束日期
|
||||
|
||||
-- 委托方(负责经纪人)
|
||||
agent_id UUID REFERENCES staff(id) ON DELETE SET NULL,
|
||||
agent_snapshot JSONB,
|
||||
|
||||
-- 签约方式(选择后动态展示委托书模板)
|
||||
signing_method VARCHAR(50),
|
||||
|
||||
-- 委托人(产权人)信息
|
||||
owner_type VARCHAR(20) NOT NULL DEFAULT 'owner'
|
||||
CHECK (owner_type IN ('owner','authorized_third')),
|
||||
-- 从 property_contacts 中选择
|
||||
property_owner_contact_id UUID REFERENCES property_contacts(id) ON DELETE SET NULL,
|
||||
owner_name VARCHAR(50), -- 产权人姓名
|
||||
owner_id_type VARCHAR(20), -- 证件类型:身份证/护照 等
|
||||
owner_id_number VARCHAR(50), -- 证件号码(加密存储)
|
||||
owner_id_number_enc BYTEA,
|
||||
|
||||
remarks TEXT, -- 备注,最多200字
|
||||
|
||||
-- 状态
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active'
|
||||
CHECK (status IN ('active','expired','cancelled')),
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_commissions_property ON commissions(property_id);
|
||||
CREATE INDEX idx_commissions_active ON commissions(property_id)
|
||||
WHERE status = 'active';
|
||||
|
||||
-- 委托附件(身份证/房产证/委托书 等)
|
||||
CREATE TABLE commission_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
commission_id UUID NOT NULL REFERENCES commissions(id) ON DELETE CASCADE,
|
||||
category VARCHAR(20) NOT NULL
|
||||
CHECK (category IN ('id_card','property_cert',
|
||||
'commission_letter','other')),
|
||||
file_key TEXT NOT NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_size INTEGER,
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_commission_attachments_commission ON commission_attachments(commission_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.10 实勘管理(Field Survey)
|
||||
|
||||
```sql
|
||||
CREATE TABLE field_surveys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
status VARCHAR(10) NOT NULL DEFAULT 'draft'
|
||||
CHECK (status IN ('draft','submitted')),
|
||||
|
||||
-- GPS 定位
|
||||
gps_latitude NUMERIC(10,7),
|
||||
gps_longitude NUMERIC(10,7),
|
||||
gps_accuracy NUMERIC(6,2), -- 精度(米)
|
||||
|
||||
description TEXT, -- 实勘说明,最多200字
|
||||
|
||||
submitted_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID NOT NULL REFERENCES staff(id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_field_surveys_property ON field_surveys(property_id);
|
||||
CREATE INDEX idx_field_surveys_submitted ON field_surveys(property_id)
|
||||
WHERE status = 'submitted';
|
||||
|
||||
-- 实勘照片(按空间分类)
|
||||
CREATE TABLE survey_photos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
survey_id UUID NOT NULL REFERENCES field_surveys(id) ON DELETE CASCADE,
|
||||
category VARCHAR(20) NOT NULL
|
||||
CHECK (category IN ('layout','living_room','dining_room',
|
||||
'bedroom','bathroom','kitchen',
|
||||
'entrance','balcony','study',
|
||||
'indoor_other','outdoor')),
|
||||
file_key TEXT NOT NULL, -- R2/S3 路径
|
||||
thumbnail_key TEXT, -- 缩略图路径
|
||||
file_size INTEGER,
|
||||
width INTEGER,
|
||||
height INTEGER,
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
is_vr_screenshot BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_survey_photos_survey ON survey_photos(survey_id);
|
||||
CREATE INDEX idx_survey_photos_category ON survey_photos(survey_id, category);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.11 房源图片管理(Property Photos)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 房源图片:与实勘照片分离存储,经纪人自主上传和管理
|
||||
-- 封面限1张,全景类型单独处理
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE property_photos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
category VARCHAR(20) NOT NULL
|
||||
CHECK (category IN ('cover','entrance','living_room',
|
||||
'dining_room','bedroom','bathroom',
|
||||
'kitchen','balcony','study',
|
||||
'indoor_other','outdoor','panorama')),
|
||||
|
||||
file_key TEXT NOT NULL, -- R2/S3 原图路径
|
||||
thumbnail_key TEXT, -- 缩略图路径(Cloudflare Images 生成)
|
||||
file_name VARCHAR(255),
|
||||
file_size INTEGER,
|
||||
width INTEGER,
|
||||
height INTEGER,
|
||||
|
||||
is_cover BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_property_photos_property ON property_photos(property_id);
|
||||
CREATE INDEX idx_property_photos_cover ON property_photos(property_id)
|
||||
WHERE is_cover = TRUE;
|
||||
CREATE INDEX idx_property_photos_category ON property_photos(property_id, category);
|
||||
|
||||
-- 唯一约束:每个房源只能有一张封面
|
||||
CREATE UNIQUE INDEX idx_property_photos_unique_cover
|
||||
ON property_photos(property_id)
|
||||
WHERE is_cover = TRUE;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.12 房源附件(Property Attachments)
|
||||
|
||||
```sql
|
||||
CREATE TABLE property_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
category VARCHAR(20) NOT NULL DEFAULT 'other'
|
||||
CHECK (category IN ('id_card','property_cert',
|
||||
'commission_letter','other')),
|
||||
|
||||
file_key TEXT NOT NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_size INTEGER NOT NULL,
|
||||
file_type VARCHAR(50), -- MIME type
|
||||
|
||||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_property_attachments_property ON property_attachments(property_id);
|
||||
CREATE INDEX idx_property_attachments_category ON property_attachments(property_id, category);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.13 房源营销信息(Property Marketing)
|
||||
|
||||
```sql
|
||||
CREATE TABLE property_marketing (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL UNIQUE REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
marketing_title VARCHAR(30), -- 营销标题 0-30字
|
||||
core_selling_points TEXT, -- 核心卖点,最多200字
|
||||
owner_attitude TEXT, -- 业主心态,最多200字
|
||||
layout_description TEXT, -- 户型介绍,最多200字
|
||||
complex_description TEXT, -- 小区介绍,最多200字
|
||||
|
||||
-- AI 生成标记
|
||||
ai_generated_points BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ai_generated_attitude BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.14 产证信息(Property Certificate)
|
||||
|
||||
```sql
|
||||
CREATE TABLE property_certificates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL UNIQUE REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
owner_name VARCHAR(100),
|
||||
owner_id_number VARCHAR(50), -- 身份证号/统一社会信用代码
|
||||
owner_cert_type VARCHAR(20), -- 身份证/护照/营业执照
|
||||
property_location VARCHAR(500), -- 房屋坐落(产权证书上的地址),最多50字
|
||||
|
||||
-- 产证状态
|
||||
cert_status VARCHAR(30),
|
||||
cert_no VARCHAR(100), -- 产证号
|
||||
first_registered_at DATE, -- 首次登记时间
|
||||
ownership_nature VARCHAR(30),
|
||||
land_nature VARCHAR(30), -- 土地性质
|
||||
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_by UUID REFERENCES staff(id) ON DELETE SET NULL
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.15 楼盘基本信息扩展(Complex Property Info)
|
||||
|
||||
```sql
|
||||
-- 补充:楼盘与房源通过 complex_id 关联,楼盘信息首次填写后修改需走楼盘管理系统
|
||||
-- 楼盘价格走势(用于楼盘详情页展示)
|
||||
CREATE TABLE complex_price_trends (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
complex_id UUID NOT NULL REFERENCES complexes(id) ON DELETE CASCADE,
|
||||
record_month DATE NOT NULL, -- 月份(取该月1日存储)
|
||||
avg_sale_price NUMERIC(10,2), -- 月均售价(万元/套)
|
||||
avg_unit_price NUMERIC(10,2), -- 月均单价(元/m²)
|
||||
transaction_count INTEGER, -- 成交套数
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_complex_price_trend_month
|
||||
ON complex_price_trends(complex_id, record_month);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.16 维护完成度评分(Completeness Scoring)
|
||||
|
||||
```sql
|
||||
-- ============================================================
|
||||
-- 维护完成度:不直接存完整计算明细(减少宽表),
|
||||
-- 以触发器/Celery 任务异步更新 properties.completeness_score
|
||||
-- 此表存储各维度的得分快照,供详情页展示
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE property_completeness (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL UNIQUE REFERENCES properties(id) ON DELETE CASCADE,
|
||||
|
||||
-- 各维度得分(满分见 PRD 8.2)
|
||||
score_core_info SMALLINT NOT NULL DEFAULT 0, -- 重点信息 满分8
|
||||
score_attachment SMALLINT NOT NULL DEFAULT 0, -- 附件 满分8
|
||||
score_survey SMALLINT NOT NULL DEFAULT 0, -- 实勘 满分16
|
||||
score_vr SMALLINT NOT NULL DEFAULT 0, -- VR 满分8
|
||||
score_key SMALLINT NOT NULL DEFAULT 0, -- 钥匙 满分10
|
||||
score_commission SMALLINT NOT NULL DEFAULT 0, -- 委托 满分10
|
||||
score_verification SMALLINT NOT NULL DEFAULT 0, -- 验证 满分7
|
||||
score_follow_up SMALLINT NOT NULL DEFAULT 0, -- 跟进 满分8
|
||||
score_viewing SMALLINT NOT NULL DEFAULT 0, -- 带看 满分8
|
||||
score_other SMALLINT NOT NULL DEFAULT 0, -- 其他 满分7
|
||||
total_score SMALLINT NOT NULL DEFAULT 0, -- 总分 0-100
|
||||
|
||||
calculated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
### 3.3 房源模块(Property Management)
|
||||
|
||||
> **详细模型** → 见 [`DATA_MODEL_PROPERTY.md`](./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:1,Celery 异步计算) | `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_logs` 中 `is_deletable=FALSE`(`sensitive_view` 类型)不可软删
|
||||
- `completeness_score` 只由 Celery 任务写入,Application 层禁止直接更新
|
||||
- `last_followed_at` 由触发器 `trg_update_last_followed` 自动维护
|
||||
- `property_photos.is_cover` 唯一约束:每套房源仅一张封面
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user