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` 唯一约束:每套房源仅一张封面
|
||||
|
||||
---
|
||||
|
||||
|
||||
1168
Project/fonrey/DATA_MODEL/DATA_MODEL_PROPERTY.md
Normal file
1168
Project/fonrey/DATA_MODEL/DATA_MODEL_PROPERTY.md
Normal file
File diff suppressed because it is too large
Load Diff
860
Project/fonrey/DATA_MODEL/diagram/fonrey-er.drawio
Normal file
860
Project/fonrey/DATA_MODEL/diagram/fonrey-er.drawio
Normal file
@@ -0,0 +1,860 @@
|
||||
<mxfile host="app.diagrams.net" modified="2026-04-24" agent="OpenCode" version="21.0.0">
|
||||
<diagram name="Fonrey ER Diagram" id="fonrey-er-v1">
|
||||
<mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="3300" pageHeight="2340" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- SWIM LANE BACKGROUNDS -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- ORG / HR region -->
|
||||
<mxCell id="region-org" value="ORG / HR" style="swimlane;startSize=30;fillColor=#0d3349;strokeColor=#22d3ee;fontColor=#22d3ee;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="60" width="340" height="760" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- REGION & COMPLEX region -->
|
||||
<mxCell id="region-complex" value="REGION & COMPLEX" style="swimlane;startSize=30;fillColor=#063b2f;strokeColor=#34d399;fontColor=#34d399;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="420" y="60" width="820" height="1380" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- PROPERTY region -->
|
||||
<mxCell id="region-property" value="PROPERTY" style="swimlane;startSize=30;fillColor=#2d1a5e;strokeColor=#a78bfa;fontColor=#a78bfa;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="60" width="900" height="1700" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- CLIENT region -->
|
||||
<mxCell id="region-client" value="CLIENT" style="swimlane;startSize=30;fillColor=#3d1f06;strokeColor=#fbbf24;fontColor=#fbbf24;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="2220" y="60" width="860" height="1380" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- ORG MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- org_units -->
|
||||
<mxCell id="org-units" value="<b>org_units</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
parent_id: uuid (FK → self)
|
||||
type: varchar(20)
|
||||
name: varchar(100)
|
||||
path: varchar(500) [物化路径]
|
||||
depth: smallint
|
||||
sort_order: int
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#22d3ee;fillColor=#0d3349;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-org">
|
||||
<mxGeometry x="30" y="60" width="280" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- staff -->
|
||||
<mxCell id="staff" value="<b>staff</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
FK org_unit_id → org_units
|
||||
name: varchar(50)
|
||||
phone_enc: text [AES-256-GCM]
|
||||
phone_hash: varchar(64) [SHA-256]
|
||||
id_no_enc: text [AES]
|
||||
user_id: uuid [FK → auth_user]
|
||||
entry_date: date
|
||||
status: active/resigned/...
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#22d3ee;fillColor=#0d3349;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-org">
|
||||
<mxGeometry x="30" y="310" width="280" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- REGION & COMPLEX MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- districts -->
|
||||
<mxCell id="districts" value="<b>districts</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
city: varchar(50)
|
||||
name: varchar(50)
|
||||
short_name: varchar(20)
|
||||
sort_order: int
|
||||
is_active: bool
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="60" width="280" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- business_areas -->
|
||||
<mxCell id="business-areas" value="<b>business_areas</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK district_id → districts
|
||||
name: varchar(100)
|
||||
latitude: numeric(10,7)
|
||||
longitude: numeric(10,7)
|
||||
sort_order: int
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="310" width="280" height="155" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- schools -->
|
||||
<mxCell id="schools" value="<b>schools</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK district_id → districts
|
||||
name: varchar(100)
|
||||
type: primary/middle/high/k9/k12
|
||||
nature: public/private/international
|
||||
level: normal/key/top
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="60" width="290" height="155" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complexes -->
|
||||
<mxCell id="complexes" value="<b>complexes</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK district_id → districts
|
||||
🔗 FK created_by → staff
|
||||
name: varchar(200) [⚠ 不可直接修改]
|
||||
address: varchar(500) [只读]
|
||||
address_summary: varchar(100)
|
||||
latitude: numeric(10,7)
|
||||
longitude: numeric(10,7)
|
||||
property_usage_types: varchar[]
|
||||
building_structure: varchar(30)
|
||||
building_type: slab/tower/slab_tower
|
||||
land_use_years: varchar(30)
|
||||
built_years: smallint[]
|
||||
total_units: int
|
||||
total_households: int
|
||||
total_floor_area: numeric(12,2)
|
||||
plot_area: numeric(12,2)
|
||||
plot_ratio: numeric(5,2)
|
||||
green_rate: numeric(5,2)
|
||||
developer: varchar(200)
|
||||
property_company: varchar(200)
|
||||
property_fee: numeric(8,2)
|
||||
property_phone: varchar(30)
|
||||
parking_total: int
|
||||
parking_underground: int
|
||||
parking_ratio: varchar(20)
|
||||
water_type: civil/commercial
|
||||
electricity_type: civil/commercial
|
||||
has_central_heating: bool
|
||||
has_gas: bool
|
||||
lock_building: bool
|
||||
lock_room: bool
|
||||
lock_info: bool
|
||||
lock_standard_room: bool
|
||||
search_vector: tsvector
|
||||
remarks: text
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
updated_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="570" width="340" height="570" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complex_aliases -->
|
||||
<mxCell id="complex-aliases" value="<b>complex_aliases</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
alias: varchar(200)
|
||||
is_system: bool [系统别名只读]
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="570" width="290" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complex_business_areas (join) -->
|
||||
<mxCell id="complex-biz-areas" value="<b>complex_business_areas</b> [N:M join]
|
||||
<hr/>
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK business_area_id → business_areas
|
||||
is_primary: bool [UNIQUE where TRUE]" style="text;html=1;strokeColor=#34d399;fillColor=#0a2e22;strokeWidth=1;dashed=1;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#6ee7b7;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="490" width="370" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complex_schools (join) -->
|
||||
<mxCell id="complex-schools" value="<b>complex_schools</b> [N:M join]
|
||||
<hr/>
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK school_id → schools
|
||||
zone_type: guaranteed/reference/lottery" style="text;html=1;strokeColor=#34d399;fillColor=#0a2e22;strokeWidth=1;dashed=1;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#6ee7b7;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="250" width="300" height="75" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- buildings -->
|
||||
<mxCell id="buildings" value="<b>buildings</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK school_id → schools [楼栋级学区]
|
||||
name: varchar(50)
|
||||
is_standard: bool
|
||||
property_usage_type: varchar(20)
|
||||
built_year: smallint
|
||||
total_floors: smallint
|
||||
land_use_years: varchar(30)
|
||||
has_elevator: bool
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="1000" width="310" height="225" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- room_units -->
|
||||
<mxCell id="room-units" value="<b>room_units</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK building_id → buildings
|
||||
floor: smallint
|
||||
floor_name: varchar(20)
|
||||
room_no: varchar(30)
|
||||
display_no: varchar(50)
|
||||
is_standard: bool
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
updated_at: timestamptz
|
||||
UNIQUE(building_id, floor, room_no)" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="1260" width="310" height="200" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complex_price_trends -->
|
||||
<mxCell id="complex-price-trends" value="<b>complex_price_trends</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
record_month: date [存月份1日]
|
||||
avg_sale_price: numeric(12,2)
|
||||
avg_unit_price: numeric(10,2)
|
||||
transaction_count: int
|
||||
listing_count: int
|
||||
created_at: timestamptz
|
||||
UNIQUE(complex_id, record_month)" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="400" y="1000" width="380" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- metro_lines -->
|
||||
<mxCell id="metro-lines" value="<b>metro_lines</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
city: varchar(50)
|
||||
name: varchar(50)
|
||||
color: varchar(7) [HEX]
|
||||
sort_order: int
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="1520" width="260" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- metro_stations -->
|
||||
<mxCell id="metro-stations" value="<b>metro_stations</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK metro_line_id → metro_lines
|
||||
name: varchar(50)
|
||||
latitude: numeric(10,7)
|
||||
longitude: numeric(10,7)
|
||||
sort_order: int
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="320" y="1520" width="280" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complex_metro_stations (join) -->
|
||||
<mxCell id="complex-metro-stations" value="<b>complex_metro_stations</b> [N:M join]
|
||||
<hr/>
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK station_id → metro_stations
|
||||
distance_meters: int [步行距离]" style="text;html=1;strokeColor=#34d399;fillColor=#0a2e22;strokeWidth=1;dashed=1;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#6ee7b7;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="320" y="1700" width="320" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- complex_photos -->
|
||||
<mxCell id="complex-photos" value="<b>complex_photos</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
category: complex/layout/vr/other
|
||||
file_key: text [R2/S3]
|
||||
thumbnail_key: text
|
||||
file_name: varchar(255)
|
||||
file_size: int
|
||||
width, height: int
|
||||
is_cover: bool [UNIQUE where TRUE]
|
||||
sort_order: smallint
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="770" width="300" height="205" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- PROPERTY MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- properties -->
|
||||
<mxCell id="properties" value="<b>properties</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK building_id → buildings
|
||||
🔗 FK room_unit_id → room_units
|
||||
🔗 FK agent_id → staff
|
||||
listing_type: sale/rent/both
|
||||
status: varchar(20)
|
||||
sale_price: numeric(12,2) [万元]
|
||||
rent_price: numeric(10,2) [元/月]
|
||||
area: numeric(8,2) [m²]
|
||||
floor: smallint
|
||||
total_floors: smallint
|
||||
bedroom: smallint
|
||||
living_room: smallint
|
||||
bathroom: smallint
|
||||
orientation: varchar(30)
|
||||
decoration: varchar(20)
|
||||
has_elevator: bool
|
||||
built_year: smallint
|
||||
ownership_years: varchar(20)
|
||||
is_exclusive: bool [独家委托]
|
||||
completeness_score: int
|
||||
search_vector: tsvector
|
||||
source: varchar(30)
|
||||
remarks: text
|
||||
created_at: timestamptz
|
||||
updated_at: timestamptz
|
||||
deleted_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
🔗 FK updated_by → staff
|
||||
<i>[89,000+ rows · 复合索引 · 分区预留]</i>" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="60" width="380" height="560" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_contacts -->
|
||||
<mxCell id="property-contacts" value="<b>property_contacts</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
name: varchar(50)
|
||||
phone_enc: text [AES-256-GCM]
|
||||
phone_hash: varchar(64) [SHA-256]
|
||||
role: owner/agent/tenant
|
||||
is_primary: bool
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="670" width="310" height="170" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_follow_logs -->
|
||||
<mxCell id="property-follow-logs" value="<b>property_follow_logs</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK staff_id → staff
|
||||
log_type: call/visit/price_change/note/...
|
||||
content: text
|
||||
phone_no_viewed: bool [敏感操作]
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
⚠ NO DELETE — append-only audit log" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="60" width="380" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- listing_histories -->
|
||||
<mxCell id="listing-histories" value="<b>listing_histories</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
listed_at: timestamptz
|
||||
delisted_at: timestamptz
|
||||
list_price: numeric(12,2)
|
||||
reason: varchar(50)
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="300" width="310" height="155" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_photos -->
|
||||
<mxCell id="property-photos" value="<b>property_photos</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
category: listing/vr/layout/other
|
||||
file_key: text [R2/S3]
|
||||
thumbnail_key: text
|
||||
is_cover: bool
|
||||
sort_order: smallint
|
||||
width, height: int
|
||||
file_size: int
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="900" width="310" height="205" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_keys -->
|
||||
<mxCell id="property-keys" value="<b>property_keys</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK holder_id → staff
|
||||
key_no: varchar(50)
|
||||
status: held/returned
|
||||
taken_at: timestamptz
|
||||
returned_at: timestamptz
|
||||
notes: text" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="510" width="310" height="165" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_commissions -->
|
||||
<mxCell id="property-commissions" value="<b>property_commissions</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
commission_type: exclusive/open
|
||||
rate: numeric(5,4)
|
||||
amount: numeric(12,2)
|
||||
start_date: date
|
||||
end_date: date
|
||||
signed_at: timestamptz
|
||||
document_key: text
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="740" width="330" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_inspections -->
|
||||
<mxCell id="property-inspections" value="<b>property_inspections</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK staff_id → staff
|
||||
inspected_at: timestamptz
|
||||
status: pending/done/cancelled
|
||||
notes: text
|
||||
attachments: jsonb
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="1160" width="320" height="165" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_marketing -->
|
||||
<mxCell id="property-marketing" value="<b>property_marketing</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties [UNIQUE 1:1]
|
||||
title: varchar(200)
|
||||
highlights: text[]
|
||||
description: text
|
||||
tags: varchar[]
|
||||
platforms: jsonb
|
||||
published_at: timestamptz
|
||||
updated_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="990" width="340" height="175" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- property_certificates -->
|
||||
<mxCell id="property-certificates" value="<b>property_certificates</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties [UNIQUE 1:1]
|
||||
cert_no: varchar(50)
|
||||
owner_name: varchar(100)
|
||||
ownership_type: varchar(30)
|
||||
area_registered: numeric(8,2)
|
||||
issue_date: date
|
||||
document_key: text" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="1390" width="330" height="165" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- completeness_scores -->
|
||||
<mxCell id="completeness-scores" value="<b>completeness_scores</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties [UNIQUE 1:1]
|
||||
score: int [0-100]
|
||||
missing_fields: text[]
|
||||
calculated_at: timestamptz
|
||||
version: int" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="1230" width="310" height="135" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- CLIENT MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- clients -->
|
||||
<mxCell id="clients" value="<b>clients</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK agent_id → staff
|
||||
client_type: private/public/closed
|
||||
status: active/inactive/converted
|
||||
name: varchar(50)
|
||||
phone_enc: text [AES-256-GCM]
|
||||
phone_hash: varchar(64) [SHA-256]
|
||||
budget_min/max: numeric
|
||||
activity_level: 1-5 [Celery每日计算]
|
||||
is_protected: bool [防自动转公客]
|
||||
transfer_to_public_type: auto/manual
|
||||
last_follow_at: timestamptz
|
||||
source: varchar(30)
|
||||
remarks: text
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
<i>[私客/公客/成交客 三态状态机]</i>" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="60" width="370" height="360" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_requirements -->
|
||||
<mxCell id="client-requirements" value="<b>client_requirements</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
req_type: second_hand/new/rent
|
||||
district_ids: uuid[]
|
||||
business_area_ids: uuid[]
|
||||
price_min: numeric
|
||||
price_max: numeric
|
||||
area_min: numeric
|
||||
area_max: numeric
|
||||
bedrooms: int[]
|
||||
school_ids: uuid[]
|
||||
has_elevator: bool
|
||||
is_active: bool
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="480" width="350" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_follow_logs -->
|
||||
<mxCell id="client-follow-logs" value="<b>client_follow_logs</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
🔗 FK staff_id → staff
|
||||
log_type: call/visit/match/note/status_change
|
||||
content: text
|
||||
next_follow_date: date
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
⚠ NO DELETE — append-only audit log" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="430" y="60" width="380" height="200" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_viewings -->
|
||||
<mxCell id="client-viewings" value="<b>client_viewings</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK agent_id → staff
|
||||
viewed_at: timestamptz
|
||||
feedback: text
|
||||
rating: smallint [1-5]
|
||||
status: planned/done/cancelled
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="430" y="310" width="360" height="195" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_property_matches -->
|
||||
<mxCell id="client-matches" value="<b>client_property_matches</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK agent_id → staff
|
||||
match_type: system/manual
|
||||
score: numeric(5,2)
|
||||
status: pending/sent/viewed/dismissed
|
||||
sent_at: timestamptz
|
||||
viewed_at: timestamptz
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="800" width="380" height="205" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_status_logs -->
|
||||
<mxCell id="client-status-logs" value="<b>client_status_logs</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
from_status: varchar(20)
|
||||
to_status: varchar(20)
|
||||
transfer_type: auto/manual
|
||||
reason: text
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
⚠ NO DELETE — append-only audit log" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="430" y="560" width="370" height="195" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_favorite_folders -->
|
||||
<mxCell id="client-fav-folders" value="<b>client_favorite_folders</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
name: varchar(100)
|
||||
sort_order: int
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="1070" width="300" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- client_folder_items -->
|
||||
<mxCell id="client-folder-items" value="<b>client_folder_items</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK folder_id → client_favorite_folders
|
||||
🔗 FK property_id → properties
|
||||
sort_order: int
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="370" y="1070" width="320" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- EDGES / RELATIONSHIPS -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- OrgUnit self-ref -->
|
||||
<mxCell id="e-org-self" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.3;entryDx=0;entryDy=0;strokeColor=#22d3ee;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="org-units" target="org-units" parent="region-org">
|
||||
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="340" y="153"/><mxPoint x="340" y="108"/></Array></mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e-org-self-lbl" value="自引用 parent_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-org-self"><mxGeometry x="0.1" relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- OrgUnit → Staff -->
|
||||
<mxCell id="e-org-staff" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#22d3ee;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="org-units" target="staff" parent="region-org">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-org-staff-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-org-staff"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- District → BusinessArea -->
|
||||
<mxCell id="e-dist-biz" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="districts" target="business-areas" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-dist-biz-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-dist-biz"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- District → Schools -->
|
||||
<mxCell id="e-dist-school" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="districts" target="schools" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-dist-school-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-dist-school"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- District → Complexes -->
|
||||
<mxCell id="e-dist-complex" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="districts" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-dist-complex-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-dist-complex"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- BusinessArea ↔ Complexes via join -->
|
||||
<mxCell id="e-biz-join" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="business-areas" target="complex-biz-areas" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-join-complex" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="complex-biz-areas" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- Schools ↔ Complexes via join -->
|
||||
<mxCell id="e-school-join" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="schools" target="complex-schools" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-school-join2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="complex-schools" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- Complexes → complex_aliases -->
|
||||
<mxCell id="e-complex-alias" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="complex-aliases" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-alias-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-alias"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Complexes → complex_photos -->
|
||||
<mxCell id="e-complex-photos" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="complex-photos" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-photos-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-photos"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Complexes → complex_price_trends -->
|
||||
<mxCell id="e-complex-trend" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="complex-price-trends" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-trend-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-trend"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Complexes → Buildings -->
|
||||
<mxCell id="e-complex-bldg" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="buildings" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-bldg-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-bldg"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Buildings → RoomUnits -->
|
||||
<mxCell id="e-bldg-room" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="buildings" target="room-units" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-bldg-room-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-bldg-room"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- MetroLine → MetroStation -->
|
||||
<mxCell id="e-metro-line-station" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="metro-lines" target="metro-stations" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-metro-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-metro-line-station"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- MetroStation ↔ Complexes via join -->
|
||||
<mxCell id="e-metro-join1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="metro-stations" target="complex-metro-stations" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-metro-join2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="complex-metro-stations" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
|
||||
<!-- Properties → PropertyContacts -->
|
||||
<mxCell id="e-prop-contact" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-contacts" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-contact-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-contact"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → FollowLogs -->
|
||||
<mxCell id="e-prop-follow" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-follow-logs" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-follow-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-follow"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → ListingHistories -->
|
||||
<mxCell id="e-prop-listing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="listing-histories" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-listing-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-listing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Photos -->
|
||||
<mxCell id="e-prop-photos" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-photos" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-photos-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-photos"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Keys -->
|
||||
<mxCell id="e-prop-keys" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-keys" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-keys-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-keys"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Commissions -->
|
||||
<mxCell id="e-prop-comm" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-commissions" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-comm-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-comm"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Inspections -->
|
||||
<mxCell id="e-prop-insp" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-inspections" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-insp-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-insp"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Marketing (1:1) -->
|
||||
<mxCell id="e-prop-marketing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-marketing" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-marketing-lbl" value="1:1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-marketing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Certificates (1:1) -->
|
||||
<mxCell id="e-prop-cert" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-certificates" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-cert-lbl" value="1:1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-cert"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Completeness (1:1) -->
|
||||
<mxCell id="e-prop-score" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="completeness-scores" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-score-lbl" value="1:1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-score"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Clients → ClientRequirements -->
|
||||
<mxCell id="e-client-req" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-requirements" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-req-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-req"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Clients → FollowLogs -->
|
||||
<mxCell id="e-client-follow" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-follow-logs" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-follow-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-follow"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Clients → Viewings -->
|
||||
<mxCell id="e-client-viewing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-viewings" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-viewing-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-viewing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Clients → Matches -->
|
||||
<mxCell id="e-client-match" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-matches" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-match-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-match"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Clients → StatusLogs -->
|
||||
<mxCell id="e-client-statuslog" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-status-logs" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-statuslog-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-statuslog"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Clients → FavFolders -->
|
||||
<mxCell id="e-client-fav" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-fav-folders" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-fav-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-fav"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- FavFolders → FolderItems -->
|
||||
<mxCell id="e-fav-items" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="client-fav-folders" target="client-folder-items" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-fav-items-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-fav-items"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- CROSS-REGION EDGES (parent=1) -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Complexes → Properties -->
|
||||
<mxCell id="e-complex-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=0;endArrow=ERmany;startArrow=ERone;fontSize=9;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" source="complexes" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-prop-lbl" value="1:N complex_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-complex-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Buildings → Properties -->
|
||||
<mxCell id="e-bldg-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="buildings" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-bldg-prop-lbl" value="1:N building_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-bldg-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- RoomUnits → Properties -->
|
||||
<mxCell id="e-room-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="room-units" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-room-prop-lbl" value="1:N room_unit_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-room-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Staff → Properties (agent_id) -->
|
||||
<mxCell id="e-staff-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#22d3ee;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="staff" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-staff-prop-lbl" value="agent_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-staff-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Staff → Clients (agent_id) -->
|
||||
<mxCell id="e-staff-client" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#22d3ee;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="staff" target="clients" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-staff-client-lbl" value="agent_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-staff-client"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Viewings (cross-region) -->
|
||||
<mxCell id="e-prop-viewing" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="client-viewings" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-viewing-lbl" value="property_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" vertex="1" connectable="0" parent="e-prop-viewing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → Matches (cross-region) -->
|
||||
<mxCell id="e-prop-match" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="client-matches" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-match-lbl" value="property_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" vertex="1" connectable="0" parent="e-prop-match"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
<!-- Properties → FolderItems (cross-region) -->
|
||||
<mxCell id="e-prop-folder" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="client-folder-items" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-folder-lbl" value="property_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" vertex="1" connectable="0" parent="e-prop-folder"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
File diff suppressed because it is too large
Load Diff
774
Project/fonrey/DATA_MODEL/fonrey-data-model.xml.bak
Normal file
774
Project/fonrey/DATA_MODEL/fonrey-data-model.xml.bak
Normal file
@@ -0,0 +1,774 @@
|
||||
<mxfile host="app.diagrams.net" modified="2026-04-24" agent="OpenCode" version="21.0.0">
|
||||
<diagram name="Fonrey ER Diagram" id="fonrey-er-v1">
|
||||
<mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="3300" pageHeight="2340" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- SWIM LANE BACKGROUNDS -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- ORG / HR region -->
|
||||
<mxCell id="region-org" value="ORG / HR" style="swimlane;startSize=30;fillColor=#0d3349;strokeColor=#22d3ee;fontColor=#22d3ee;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="60" width="340" height="760" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- REGION & COMPLEX region -->
|
||||
<mxCell id="region-complex" value="REGION & COMPLEX" style="swimlane;startSize=30;fillColor=#063b2f;strokeColor=#34d399;fontColor=#34d399;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="420" y="60" width="820" height="1380" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- PROPERTY region -->
|
||||
<mxCell id="region-property" value="PROPERTY" style="swimlane;startSize=30;fillColor=#2d1a5e;strokeColor=#a78bfa;fontColor=#a78bfa;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="1280" y="60" width="900" height="1700" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- CLIENT region -->
|
||||
<mxCell id="region-client" value="CLIENT" style="swimlane;startSize=30;fillColor=#3d1f06;strokeColor=#fbbf24;fontColor=#fbbf24;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" vertex="1" parent="1">
|
||||
<mxGeometry x="2220" y="60" width="860" height="1380" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- ORG MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- org_units -->
|
||||
<mxCell id="org-units" value="<b>org_units</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
parent_id: uuid (FK → self)
|
||||
type: varchar(20)
|
||||
name: varchar(100)
|
||||
path: varchar(500) [物化路径]
|
||||
depth: smallint
|
||||
sort_order: int
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#22d3ee;fillColor=#0d3349;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-org">
|
||||
<mxGeometry x="30" y="60" width="280" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- staff -->
|
||||
<mxCell id="staff" value="<b>staff</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
FK org_unit_id → org_units
|
||||
name: varchar(50)
|
||||
phone_enc: text [AES-256-GCM]
|
||||
phone_hash: varchar(64) [SHA-256]
|
||||
id_no_enc: text [AES]
|
||||
user_id: uuid [FK → auth_user]
|
||||
entry_date: date
|
||||
status: active/resigned/...
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#22d3ee;fillColor=#0d3349;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-org">
|
||||
<mxGeometry x="30" y="310" width="280" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- REGION & COMPLEX MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- districts -->
|
||||
<mxCell id="districts" value="<b>districts</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
city: varchar(50)
|
||||
name: varchar(50)
|
||||
short_name: varchar(20)
|
||||
sort_order: int
|
||||
is_active: bool
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="60" width="280" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- business_areas -->
|
||||
<mxCell id="business-areas" value="<b>business_areas</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK district_id → districts
|
||||
name: varchar(100)
|
||||
latitude: numeric(10,7)
|
||||
longitude: numeric(10,7)
|
||||
sort_order: int
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="310" width="280" height="155" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- schools -->
|
||||
<mxCell id="schools" value="<b>schools</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK district_id → districts
|
||||
name: varchar(100)
|
||||
type: primary/middle/high/k9/k12
|
||||
nature: public/private/international
|
||||
level: normal/key/top
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="60" width="290" height="155" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complexes -->
|
||||
<mxCell id="complexes" value="<b>complexes</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK district_id → districts
|
||||
🔗 FK created_by → staff
|
||||
name: varchar(200) [⚠ 不可直接修改]
|
||||
address: varchar(500) [只读]
|
||||
address_summary: varchar(100)
|
||||
latitude: numeric(10,7)
|
||||
longitude: numeric(10,7)
|
||||
property_usage_types: varchar[]
|
||||
building_structure: varchar(30)
|
||||
building_type: slab/tower/slab_tower
|
||||
land_use_years: varchar(30)
|
||||
built_years: smallint[]
|
||||
total_units: int
|
||||
total_households: int
|
||||
total_floor_area: numeric(12,2)
|
||||
plot_area: numeric(12,2)
|
||||
plot_ratio: numeric(5,2)
|
||||
green_rate: numeric(5,2)
|
||||
developer: varchar(200)
|
||||
property_company: varchar(200)
|
||||
property_fee: numeric(8,2)
|
||||
property_phone: varchar(30)
|
||||
parking_total: int
|
||||
parking_underground: int
|
||||
parking_ratio: varchar(20)
|
||||
water_type: civil/commercial
|
||||
electricity_type: civil/commercial
|
||||
has_central_heating: bool
|
||||
has_gas: bool
|
||||
lock_building: bool
|
||||
lock_room: bool
|
||||
lock_info: bool
|
||||
lock_standard_room: bool
|
||||
search_vector: tsvector
|
||||
remarks: text
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
updated_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="570" width="340" height="570" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complex_aliases -->
|
||||
<mxCell id="complex-aliases" value="<b>complex_aliases</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
alias: varchar(200)
|
||||
is_system: bool [系统别名只读]
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="570" width="290" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complex_business_areas (join) -->
|
||||
<mxCell id="complex-biz-areas" value="<b>complex_business_areas</b> [N:M join]
|
||||
<hr/>
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK business_area_id → business_areas
|
||||
is_primary: bool [UNIQUE where TRUE]" style="text;html=1;strokeColor=#34d399;fillColor=#0a2e22;strokeWidth=1;dashed=1;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#6ee7b7;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="490" width="370" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complex_schools (join) -->
|
||||
<mxCell id="complex-schools" value="<b>complex_schools</b> [N:M join]
|
||||
<hr/>
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK school_id → schools
|
||||
zone_type: guaranteed/reference/lottery" style="text;html=1;strokeColor=#34d399;fillColor=#0a2e22;strokeWidth=1;dashed=1;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#6ee7b7;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="250" width="300" height="75" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- buildings -->
|
||||
<mxCell id="buildings" value="<b>buildings</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK school_id → schools [楼栋级学区]
|
||||
name: varchar(50)
|
||||
is_standard: bool
|
||||
property_usage_type: varchar(20)
|
||||
built_year: smallint
|
||||
total_floors: smallint
|
||||
land_use_years: varchar(30)
|
||||
has_elevator: bool
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="1000" width="310" height="225" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- room_units -->
|
||||
<mxCell id="room-units" value="<b>room_units</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK building_id → buildings
|
||||
floor: smallint
|
||||
floor_name: varchar(20)
|
||||
room_no: varchar(30)
|
||||
display_no: varchar(50)
|
||||
is_standard: bool
|
||||
is_active: bool
|
||||
created_at: timestamptz
|
||||
updated_at: timestamptz
|
||||
UNIQUE(building_id, floor, room_no)" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="1260" width="310" height="200" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complex_price_trends -->
|
||||
<mxCell id="complex-price-trends" value="<b>complex_price_trends</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
record_month: date [存月份1日]
|
||||
avg_sale_price: numeric(12,2)
|
||||
avg_unit_price: numeric(10,2)
|
||||
transaction_count: int
|
||||
listing_count: int
|
||||
created_at: timestamptz
|
||||
UNIQUE(complex_id, record_month)" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="400" y="1000" width="380" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- metro_lines -->
|
||||
<mxCell id="metro-lines" value="<b>metro_lines</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
city: varchar(50)
|
||||
name: varchar(50)
|
||||
color: varchar(7) [HEX]
|
||||
sort_order: int
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="30" y="1520" width="260" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- metro_stations -->
|
||||
<mxCell id="metro-stations" value="<b>metro_stations</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK metro_line_id → metro_lines
|
||||
name: varchar(50)
|
||||
latitude: numeric(10,7)
|
||||
longitude: numeric(10,7)
|
||||
sort_order: int
|
||||
is_active: bool" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="320" y="1520" width="280" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complex_metro_stations (join) -->
|
||||
<mxCell id="complex-metro-stations" value="<b>complex_metro_stations</b> [N:M join]
|
||||
<hr/>
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK station_id → metro_stations
|
||||
distance_meters: int [步行距离]" style="text;html=1;strokeColor=#34d399;fillColor=#0a2e22;strokeWidth=1;dashed=1;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#6ee7b7;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="320" y="1700" width="320" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- complex_photos -->
|
||||
<mxCell id="complex-photos" value="<b>complex_photos</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
category: complex/layout/vr/other
|
||||
file_key: text [R2/S3]
|
||||
thumbnail_key: text
|
||||
file_name: varchar(255)
|
||||
file_size: int
|
||||
width, height: int
|
||||
is_cover: bool [UNIQUE where TRUE]
|
||||
sort_order: smallint
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#34d399;fillColor=#063b2f;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-complex">
|
||||
<mxGeometry x="490" y="770" width="300" height="205" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- PROPERTY MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- properties -->
|
||||
<mxCell id="properties" value="<b>properties</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK complex_id → complexes
|
||||
🔗 FK building_id → buildings
|
||||
🔗 FK room_unit_id → room_units
|
||||
🔗 FK agent_id → staff
|
||||
listing_type: sale/rent/both
|
||||
status: varchar(20)
|
||||
sale_price: numeric(12,2) [万元]
|
||||
rent_price: numeric(10,2) [元/月]
|
||||
area: numeric(8,2) [m²]
|
||||
floor: smallint
|
||||
total_floors: smallint
|
||||
bedroom: smallint
|
||||
living_room: smallint
|
||||
bathroom: smallint
|
||||
orientation: varchar(30)
|
||||
decoration: varchar(20)
|
||||
has_elevator: bool
|
||||
built_year: smallint
|
||||
ownership_years: varchar(20)
|
||||
is_exclusive: bool [独家委托]
|
||||
completeness_score: int
|
||||
search_vector: tsvector
|
||||
source: varchar(30)
|
||||
remarks: text
|
||||
created_at: timestamptz
|
||||
updated_at: timestamptz
|
||||
deleted_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
🔗 FK updated_by → staff
|
||||
<i>[89,000+ rows · 复合索引 · 分区预留]</i>" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="60" width="380" height="560" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_contacts -->
|
||||
<mxCell id="property-contacts" value="<b>property_contacts</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
name: varchar(50)
|
||||
phone_enc: text [AES-256-GCM]
|
||||
phone_hash: varchar(64) [SHA-256]
|
||||
role: owner/agent/tenant
|
||||
is_primary: bool
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="670" width="310" height="170" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_follow_logs -->
|
||||
<mxCell id="property-follow-logs" value="<b>property_follow_logs</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK staff_id → staff
|
||||
log_type: call/visit/price_change/note/...
|
||||
content: text
|
||||
phone_no_viewed: bool [敏感操作]
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
⚠ NO DELETE — append-only audit log" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="60" width="380" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- listing_histories -->
|
||||
<mxCell id="listing-histories" value="<b>listing_histories</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
listed_at: timestamptz
|
||||
delisted_at: timestamptz
|
||||
list_price: numeric(12,2)
|
||||
reason: varchar(50)
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="300" width="310" height="155" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_photos -->
|
||||
<mxCell id="property-photos" value="<b>property_photos</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
category: listing/vr/layout/other
|
||||
file_key: text [R2/S3]
|
||||
thumbnail_key: text
|
||||
is_cover: bool
|
||||
sort_order: smallint
|
||||
width, height: int
|
||||
file_size: int
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="900" width="310" height="205" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_keys -->
|
||||
<mxCell id="property-keys" value="<b>property_keys</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK holder_id → staff
|
||||
key_no: varchar(50)
|
||||
status: held/returned
|
||||
taken_at: timestamptz
|
||||
returned_at: timestamptz
|
||||
notes: text" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="510" width="310" height="165" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_commissions -->
|
||||
<mxCell id="property-commissions" value="<b>property_commissions</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
commission_type: exclusive/open
|
||||
rate: numeric(5,4)
|
||||
amount: numeric(12,2)
|
||||
start_date: date
|
||||
end_date: date
|
||||
signed_at: timestamptz
|
||||
document_key: text
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="740" width="330" height="185" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_inspections -->
|
||||
<mxCell id="property-inspections" value="<b>property_inspections</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK staff_id → staff
|
||||
inspected_at: timestamptz
|
||||
status: pending/done/cancelled
|
||||
notes: text
|
||||
attachments: jsonb
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="1160" width="320" height="165" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_marketing -->
|
||||
<mxCell id="property-marketing" value="<b>property_marketing</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties [UNIQUE 1:1]
|
||||
title: varchar(200)
|
||||
highlights: text[]
|
||||
description: text
|
||||
tags: varchar[]
|
||||
platforms: jsonb
|
||||
published_at: timestamptz
|
||||
updated_at: timestamptz" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="990" width="340" height="175" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- property_certificates -->
|
||||
<mxCell id="property-certificates" value="<b>property_certificates</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties [UNIQUE 1:1]
|
||||
cert_no: varchar(50)
|
||||
owner_name: varchar(100)
|
||||
ownership_type: varchar(30)
|
||||
area_registered: numeric(8,2)
|
||||
issue_date: date
|
||||
document_key: text" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="30" y="1390" width="330" height="165" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- completeness_scores -->
|
||||
<mxCell id="completeness-scores" value="<b>completeness_scores</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK property_id → properties [UNIQUE 1:1]
|
||||
score: int [0-100]
|
||||
missing_fields: text[]
|
||||
calculated_at: timestamptz
|
||||
version: int" style="text;html=1;strokeColor=#a78bfa;fillColor=#2d1a5e;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-property">
|
||||
<mxGeometry x="470" y="1230" width="310" height="135" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- CLIENT MODULE -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- clients -->
|
||||
<mxCell id="clients" value="<b>clients</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK agent_id → staff
|
||||
client_type: private/public/closed
|
||||
status: active/inactive/converted
|
||||
name: varchar(50)
|
||||
phone_enc: text [AES-256-GCM]
|
||||
phone_hash: varchar(64) [SHA-256]
|
||||
budget_min/max: numeric
|
||||
activity_level: 1-5 [Celery每日计算]
|
||||
is_protected: bool [防自动转公客]
|
||||
transfer_to_public_type: auto/manual
|
||||
last_follow_at: timestamptz
|
||||
source: varchar(30)
|
||||
remarks: text
|
||||
created_at: timestamptz
|
||||
deleted_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
<i>[私客/公客/成交客 三态状态机]</i>" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="60" width="370" height="360" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_requirements -->
|
||||
<mxCell id="client-requirements" value="<b>client_requirements</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
req_type: second_hand/new/rent
|
||||
district_ids: uuid[]
|
||||
business_area_ids: uuid[]
|
||||
price_min: numeric
|
||||
price_max: numeric
|
||||
area_min: numeric
|
||||
area_max: numeric
|
||||
bedrooms: int[]
|
||||
school_ids: uuid[]
|
||||
has_elevator: bool
|
||||
is_active: bool
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="480" width="350" height="260" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_follow_logs -->
|
||||
<mxCell id="client-follow-logs" value="<b>client_follow_logs</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
🔗 FK staff_id → staff
|
||||
log_type: call/visit/match/note/status_change
|
||||
content: text
|
||||
next_follow_date: date
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
⚠ NO DELETE — append-only audit log" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="430" y="60" width="380" height="200" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_viewings -->
|
||||
<mxCell id="client-viewings" value="<b>client_viewings</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK agent_id → staff
|
||||
viewed_at: timestamptz
|
||||
feedback: text
|
||||
rating: smallint [1-5]
|
||||
status: planned/done/cancelled
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="430" y="310" width="360" height="195" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_property_matches -->
|
||||
<mxCell id="client-matches" value="<b>client_property_matches</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
🔗 FK property_id → properties
|
||||
🔗 FK agent_id → staff
|
||||
match_type: system/manual
|
||||
score: numeric(5,2)
|
||||
status: pending/sent/viewed/dismissed
|
||||
sent_at: timestamptz
|
||||
viewed_at: timestamptz
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="800" width="380" height="205" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_status_logs -->
|
||||
<mxCell id="client-status-logs" value="<b>client_status_logs</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
from_status: varchar(20)
|
||||
to_status: varchar(20)
|
||||
transfer_type: auto/manual
|
||||
reason: text
|
||||
created_at: timestamptz
|
||||
🔗 FK created_by → staff
|
||||
⚠ NO DELETE — append-only audit log" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="430" y="560" width="370" height="195" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_favorite_folders -->
|
||||
<mxCell id="client-fav-folders" value="<b>client_favorite_folders</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK client_id → clients
|
||||
name: varchar(100)
|
||||
sort_order: int
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="30" y="1070" width="300" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- client_folder_items -->
|
||||
<mxCell id="client-folder-items" value="<b>client_folder_items</b>
|
||||
<hr/>
|
||||
🔑 PK id: uuid
|
||||
🔗 FK folder_id → client_favorite_folders
|
||||
🔗 FK property_id → properties
|
||||
sort_order: int
|
||||
created_at: timestamptz" style="text;html=1;strokeColor=#fbbf24;fillColor=#3d1f06;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4;overflow=hidden;rotatable=0;fontSize=11;fontFamily=monospace;fontColor=#e2e8f0;whiteSpace=pre;" vertex="1" parent="region-client">
|
||||
<mxGeometry x="370" y="1070" width="320" height="130" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- EDGES / RELATIONSHIPS -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- OrgUnit self-ref -->
|
||||
<mxCell id="e-org-self" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.3;entryDx=0;entryDy=0;strokeColor=#22d3ee;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="org-units" target="org-units" parent="region-org">
|
||||
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="340" y="153"/><mxPoint x="340" y="108"/></Array></mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e-org-self-lbl" value="自引用 parent_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-org-self"><mxGeometry x="0.1" relative="1" as="geometry"/></mxCell>
|
||||
<!-- OrgUnit → Staff -->
|
||||
<mxCell id="e-org-staff" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#22d3ee;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="org-units" target="staff" parent="region-org">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-org-staff-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-org-staff"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- District → BusinessArea -->
|
||||
<mxCell id="e-dist-biz" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="districts" target="business-areas" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-dist-biz-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-dist-biz"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- District → Schools -->
|
||||
<mxCell id="e-dist-school" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="districts" target="schools" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-dist-school-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-dist-school"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- District → Complexes -->
|
||||
<mxCell id="e-dist-complex" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="districts" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-dist-complex-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-dist-complex"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- BusinessArea ↔ Complexes via join -->
|
||||
<mxCell id="e-biz-join" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="business-areas" target="complex-biz-areas" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-join-complex" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="complex-biz-areas" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- Schools ↔ Complexes via join -->
|
||||
<mxCell id="e-school-join" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="schools" target="complex-schools" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-school-join2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="complex-schools" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- Complexes → complex_aliases -->
|
||||
<mxCell id="e-complex-alias" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="complex-aliases" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-alias-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-alias"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Complexes → complex_photos -->
|
||||
<mxCell id="e-complex-photos" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="complex-photos" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-photos-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-photos"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Complexes → complex_price_trends -->
|
||||
<mxCell id="e-complex-trend" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="complex-price-trends" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-trend-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-trend"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Complexes → Buildings -->
|
||||
<mxCell id="e-complex-bldg" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="complexes" target="buildings" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-bldg-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-complex-bldg"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Buildings → RoomUnits -->
|
||||
<mxCell id="e-bldg-room" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="buildings" target="room-units" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-bldg-room-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-bldg-room"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- MetroLine → MetroStation -->
|
||||
<mxCell id="e-metro-line-station" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="metro-lines" target="metro-stations" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-metro-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" vertex="1" connectable="0" parent="e-metro-line-station"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- MetroStation ↔ Complexes via join -->
|
||||
<mxCell id="e-metro-join1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="metro-stations" target="complex-metro-stations" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-metro-join2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" edge="1" source="complex-metro-stations" target="complexes" parent="region-complex">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<!-- Properties → PropertyContacts -->
|
||||
<mxCell id="e-prop-contact" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-contacts" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-contact-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-contact"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → FollowLogs -->
|
||||
<mxCell id="e-prop-follow" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-follow-logs" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-follow-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-follow"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → ListingHistories -->
|
||||
<mxCell id="e-prop-listing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="listing-histories" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-listing-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-listing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Photos -->
|
||||
<mxCell id="e-prop-photos" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-photos" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-photos-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-photos"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Keys -->
|
||||
<mxCell id="e-prop-keys" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-keys" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-keys-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-keys"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Commissions -->
|
||||
<mxCell id="e-prop-comm" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-commissions" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-comm-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-comm"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Inspections -->
|
||||
<mxCell id="e-prop-insp" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-inspections" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-insp-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-insp"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Marketing (1:1) -->
|
||||
<mxCell id="e-prop-marketing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-marketing" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-marketing-lbl" value="1:1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-marketing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Certificates (1:1) -->
|
||||
<mxCell id="e-prop-cert" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="property-certificates" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-cert-lbl" value="1:1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-cert"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Completeness (1:1) -->
|
||||
<mxCell id="e-prop-score" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="completeness-scores" parent="region-property">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-score-lbl" value="1:1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-prop-score"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Clients → ClientRequirements -->
|
||||
<mxCell id="e-client-req" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-requirements" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-req-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-req"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Clients → FollowLogs -->
|
||||
<mxCell id="e-client-follow" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-follow-logs" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-follow-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-follow"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Clients → Viewings -->
|
||||
<mxCell id="e-client-viewing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-viewings" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-viewing-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-viewing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Clients → Matches -->
|
||||
<mxCell id="e-client-match" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-matches" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-match-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-match"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Clients → StatusLogs -->
|
||||
<mxCell id="e-client-statuslog" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-status-logs" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-statuslog-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-statuslog"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Clients → FavFolders -->
|
||||
<mxCell id="e-client-fav" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="clients" target="client-fav-folders" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-client-fav-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-client-fav"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- FavFolders → FolderItems -->
|
||||
<mxCell id="e-fav-items" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="client-fav-folders" target="client-folder-items" parent="region-client">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-fav-items-lbl" value="1:N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" vertex="1" connectable="0" parent="e-fav-items"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- CROSS-REGION EDGES (parent=1) -->
|
||||
<!-- ═══════════════════════════════════════════════════ -->
|
||||
<!-- Complexes → Properties -->
|
||||
<mxCell id="e-complex-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=0;endArrow=ERmany;startArrow=ERone;fontSize=9;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" source="complexes" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-complex-prop-lbl" value="1:N complex_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-complex-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Buildings → Properties -->
|
||||
<mxCell id="e-bldg-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="buildings" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-bldg-prop-lbl" value="1:N building_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-bldg-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- RoomUnits → Properties -->
|
||||
<mxCell id="e-room-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="room-units" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-room-prop-lbl" value="1:N room_unit_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" vertex="1" connectable="0" parent="e-room-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Staff → Properties (agent_id) -->
|
||||
<mxCell id="e-staff-prop" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#22d3ee;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="staff" target="properties" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-staff-prop-lbl" value="agent_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-staff-prop"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Staff → Clients (agent_id) -->
|
||||
<mxCell id="e-staff-client" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#22d3ee;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="staff" target="clients" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-staff-client-lbl" value="agent_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" vertex="1" connectable="0" parent="e-staff-client"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Viewings (cross-region) -->
|
||||
<mxCell id="e-prop-viewing" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="client-viewings" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-viewing-lbl" value="property_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" vertex="1" connectable="0" parent="e-prop-viewing"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → Matches (cross-region) -->
|
||||
<mxCell id="e-prop-match" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="client-matches" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-match-lbl" value="property_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" vertex="1" connectable="0" parent="e-prop-match"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
<!-- Properties → FolderItems (cross-region) -->
|
||||
<mxCell id="e-prop-folder" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" edge="1" source="properties" target="client-folder-items" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e-prop-folder-lbl" value="property_id" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" vertex="1" connectable="0" parent="e-prop-folder"><mxGeometry relative="1" as="geometry"/></mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
Reference in New Issue
Block a user