图片更新修改

This commit is contained in:
2026-04-24 16:28:04 +08:00
parent 6bd1759da8
commit 0d6f30a55a
6 changed files with 386 additions and 714 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 KiB

View File

@@ -1,11 +1,11 @@
<mxfile host="drawio.ishenwei.online" agent="OpenCode">
<diagram name="Fonrey ER Diagram" id="fonrey-er-v1">
<mxGraphModel dx="815" dy="389" 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">
<mxGraphModel dx="5097" dy="959" 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" />
<mxCell id="region-org" parent="1" style="swimlane;startSize=30;fillColor=#0d3349;strokeColor=#22d3ee;fontColor=#22d3ee;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" value="ORG / HR" vertex="1">
<mxGeometry height="760" width="340" x="40" y="60" as="geometry">
<mxGeometry height="760" width="380" x="-860" y="60" as="geometry">
<mxRectangle height="30" width="100" x="40" y="60" as="alternateBounds" />
</mxGeometry>
</mxCell>
@@ -33,49 +33,34 @@
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="region-complex" parent="1" style="swimlane;startSize=30;fillColor=#063b2f;strokeColor=#34d399;fontColor=#34d399;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" value="REGION &amp; COMPLEX" vertex="1">
<mxGeometry height="1380" width="820" x="420" y="60" as="geometry" />
<mxGeometry height="1830" width="1200" x="-440" y="60" as="geometry" />
</mxCell>
<mxCell id="districts" parent="region-complex" 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;" value="&lt;b&gt;districts&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;city: varchar(50)&#xa;name: varchar(50)&#xa;short_name: varchar(20)&#xa;sort_order: int&#xa;is_active: bool&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="150" width="280" x="30" y="60" as="geometry" />
<mxGeometry height="150" width="280" x="50" y="60" as="geometry" />
</mxCell>
<mxCell id="business-areas" parent="region-complex" 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;" value="&lt;b&gt;business_areas&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK district_id → districts&#xa;name: varchar(100)&#xa;latitude: numeric(10,7)&#xa;longitude: numeric(10,7)&#xa;sort_order: int&#xa;is_active: bool" vertex="1">
<mxGeometry height="155" width="280" x="10" y="300" as="geometry" />
<mxGeometry height="155" width="280" x="50" y="265" as="geometry" />
</mxCell>
<mxCell id="schools" parent="region-complex" 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;" value="&lt;b&gt;schools&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK district_id → districts&#xa;name: varchar(100)&#xa;type: primary/middle/high/k9/k12&#xa;nature: public/private/international&#xa;level: normal/key/top&#xa;is_active: bool" vertex="1">
<mxGeometry height="155" width="290" x="490" y="60" as="geometry" />
</mxCell>
<mxCell id="complexes" parent="region-complex" 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;" value="&lt;b&gt;complexes&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK district_id → districts&#xa;🔗 FK created_by → staff&#xa;name: varchar(200) [⚠ 不可直接修改]&#xa;address: varchar(500) [只读]&#xa;address_summary: varchar(100)&#xa;latitude: numeric(10,7)&#xa;longitude: numeric(10,7)&#xa;property_usage_types: varchar[]&#xa;building_structure: varchar(30)&#xa;building_type: slab/tower/slab_tower&#xa;land_use_years: varchar(30)&#xa;built_years: smallint[]&#xa;total_units: int&#xa;total_households: int&#xa;total_floor_area: numeric(12,2)&#xa;plot_area: numeric(12,2)&#xa;plot_ratio: numeric(5,2)&#xa;green_rate: numeric(5,2)&#xa;developer: varchar(200)&#xa;property_company: varchar(200)&#xa;property_fee: numeric(8,2)&#xa;property_phone: varchar(30)&#xa;parking_total: int&#xa;parking_underground: int&#xa;parking_ratio: varchar(20)&#xa;water_type: civil/commercial&#xa;electricity_type: civil/commercial&#xa;has_central_heating: bool&#xa;has_gas: bool&#xa;lock_building: bool&#xa;lock_room: bool&#xa;lock_info: bool&#xa;lock_standard_room: bool&#xa;search_vector: tsvector&#xa;remarks: text&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;updated_at: timestamptz&#xa;deleted_at: timestamptz" vertex="1">
<mxGeometry height="570" width="340" x="20" y="615" as="geometry" />
<mxGeometry height="570" width="340" x="30" y="660" as="geometry" />
</mxCell>
<mxCell id="complex-aliases" parent="region-complex" 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;" value="&lt;b&gt;complex_aliases&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK complex_id → complexes&#xa;alias: varchar(200)&#xa;is_system: bool [系统别名只读]&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff" vertex="1">
<mxGeometry height="130" width="290" x="490" y="570" as="geometry" />
<mxGeometry height="130" width="290" x="475" y="450" as="geometry" />
</mxCell>
<mxCell id="complex-biz-areas" parent="region-complex" 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;" value="&lt;b&gt;complex_business_areas&lt;/b&gt; [N:M join]&#xa;&lt;hr/&gt;&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK business_area_id → business_areas&#xa;is_primary: bool [UNIQUE where TRUE]" vertex="1">
<mxGeometry height="100" width="380" x="20" y="490" as="geometry" />
<mxGeometry height="110" width="380" x="30" y="480" as="geometry" />
</mxCell>
<mxCell id="complex-schools" parent="region-complex" 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;" value="&lt;b&gt;complex_schools&lt;/b&gt; [N:M join]&#xa;&lt;hr/&gt;&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK school_id → schools&#xa;zone_type: guaranteed/reference/lottery" vertex="1">
<mxGeometry height="75" width="300" x="490" y="250" as="geometry" />
</mxCell>
<mxCell id="buildings" parent="region-complex" 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;" value="&lt;b&gt;buildings&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK school_id → schools [楼栋级学区]&#xa;name: varchar(50)&#xa;is_standard: bool&#xa;property_usage_type: varchar(20)&#xa;built_year: smallint&#xa;total_floors: smallint&#xa;land_use_years: varchar(30)&#xa;has_elevator: bool&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff" vertex="1">
<mxGeometry height="225" width="310" x="30" y="1000" as="geometry" />
</mxCell>
<mxCell id="room-units" parent="region-complex" 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;" value="&lt;b&gt;room_units&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK building_id → buildings&#xa;floor: smallint&#xa;floor_name: varchar(20)&#xa;room_no: varchar(30)&#xa;display_no: varchar(50)&#xa;is_standard: bool&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;updated_at: timestamptz&#xa;UNIQUE(building_id, floor, room_no)" vertex="1">
<mxGeometry height="200" width="310" x="30" y="1260" as="geometry" />
<mxGeometry height="110" width="310" x="480" y="270" as="geometry" />
</mxCell>
<mxCell id="complex-price-trends" parent="region-complex" 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;" value="&lt;b&gt;complex_price_trends&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK complex_id → complexes&#xa;record_month: date [存月份1日]&#xa;avg_sale_price: numeric(12,2)&#xa;avg_unit_price: numeric(10,2)&#xa;transaction_count: int&#xa;listing_count: int&#xa;created_at: timestamptz&#xa;UNIQUE(complex_id, record_month)" vertex="1">
<mxGeometry height="185" width="380" x="400" y="1000" as="geometry" />
</mxCell>
<mxCell id="metro-lines" parent="region-complex" 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;" value="&lt;b&gt;metro_lines&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;city: varchar(50)&#xa;name: varchar(50)&#xa;color: varchar(7) [HEX]&#xa;sort_order: int&#xa;is_active: bool" vertex="1">
<mxGeometry height="130" width="260" x="30" y="1520" as="geometry" />
</mxCell>
<mxCell id="metro-stations" parent="region-complex" 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;" value="&lt;b&gt;metro_stations&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK metro_line_id → metro_lines&#xa;name: varchar(50)&#xa;latitude: numeric(10,7)&#xa;longitude: numeric(10,7)&#xa;sort_order: int&#xa;is_active: bool" vertex="1">
<mxGeometry height="150" width="280" x="320" y="1520" as="geometry" />
</mxCell>
<mxCell id="complex-metro-stations" parent="region-complex" 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;" value="&lt;b&gt;complex_metro_stations&lt;/b&gt; [N:M join]&#xa;&lt;hr/&gt;&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK station_id → metro_stations&#xa;distance_meters: int [步行距离]" vertex="1">
<mxGeometry height="70" width="320" x="320" y="1700" as="geometry" />
<mxGeometry height="185" width="380" x="500" y="980" as="geometry" />
</mxCell>
<mxCell id="complex-photos" parent="region-complex" 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;" value="&lt;b&gt;complex_photos&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK complex_id → complexes&#xa;category: complex/layout/vr/other&#xa;file_key: text [R2/S3]&#xa;thumbnail_key: text&#xa;file_name: varchar(255)&#xa;file_size: int&#xa;width, height: int&#xa;is_cover: bool [UNIQUE where TRUE]&#xa;sort_order: smallint&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff" vertex="1">
<mxGeometry height="205" width="300" x="490" y="770" as="geometry" />
<mxGeometry height="205" width="300" x="470" y="650" as="geometry" />
</mxCell>
<mxCell id="e-dist-biz" edge="1" parent="region-complex" source="districts" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="business-areas">
<mxGeometry relative="1" as="geometry" />
@@ -105,10 +90,20 @@
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-school-join2" edge="1" parent="region-complex" source="complex-schools" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" target="complexes">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="420" y="325" />
<mxPoint x="420" y="690" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-complex-alias" edge="1" parent="region-complex" source="complexes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="complex-aliases">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="820" y="945" />
<mxPoint x="820" y="515" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-complex-alias-lbl" connectable="0" parent="e-complex-alias" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
@@ -120,22 +115,24 @@
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-complex-trend" edge="1" parent="region-complex" source="complexes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="complex-price-trends">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="400" y="1072" />
<mxPoint x="400" y="1072" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-complex-trend-lbl" connectable="0" parent="e-complex-trend" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-complex-bldg" edge="1" parent="region-complex" source="complexes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="buildings">
<mxGeometry relative="1" as="geometry" />
<mxCell id="room-units" parent="region-complex" 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;" value="&lt;b&gt;room_units&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK building_id → buildings&#xa;floor: smallint&#xa;floor_name: varchar(20)&#xa;room_no: varchar(30)&#xa;display_no: varchar(50)&#xa;is_standard: bool&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;updated_at: timestamptz&#xa;UNIQUE(building_id, floor, room_no)" vertex="1">
<mxGeometry height="200" width="310" x="860" y="1402.5" as="geometry" />
</mxCell>
<mxCell id="e-complex-bldg-lbl" connectable="0" parent="e-complex-bldg" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="metro-lines" parent="region-complex" 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;" value="&lt;b&gt;metro_lines&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;city: varchar(50)&#xa;name: varchar(50)&#xa;color: varchar(7) [HEX]&#xa;sort_order: int&#xa;is_active: bool" vertex="1">
<mxGeometry height="130" width="260" x="10" y="1680" as="geometry" />
</mxCell>
<mxCell id="e-bldg-room" edge="1" parent="region-complex" source="buildings" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="room-units">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-bldg-room-lbl" connectable="0" parent="e-bldg-room" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="metro-stations" parent="region-complex" 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;" value="&lt;b&gt;metro_stations&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK metro_line_id → metro_lines&#xa;name: varchar(50)&#xa;latitude: numeric(10,7)&#xa;longitude: numeric(10,7)&#xa;sort_order: int&#xa;is_active: bool" vertex="1">
<mxGeometry height="150" width="280" x="300" y="1670" as="geometry" />
</mxCell>
<mxCell id="e-metro-line-station" edge="1" parent="region-complex" source="metro-lines" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="metro-stations">
<mxGeometry relative="1" as="geometry" />
@@ -143,167 +140,270 @@
<mxCell id="e-metro-lbl" connectable="0" parent="e-metro-line-station" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="complex-metro-stations" parent="region-complex" 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;" value="&lt;b&gt;complex_metro_stations&lt;/b&gt; [N:M join]&#xa;&lt;hr/&gt;&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK station_id → metro_stations&#xa;distance_meters: int [步行距离]" vertex="1">
<mxGeometry height="130" width="330" x="10" y="1520" as="geometry" />
</mxCell>
<mxCell id="e-metro-join1" edge="1" parent="region-complex" source="metro-stations" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" target="complex-metro-stations">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-metro-join2" edge="1" parent="region-complex" source="complex-metro-stations" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" target="complexes">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="buildings" parent="region-complex" 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;" value="&lt;b&gt;buildings&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK school_id → schools [楼栋级学区]&#xa;name: varchar(50)&#xa;is_standard: bool&#xa;property_usage_type: varchar(20)&#xa;built_year: smallint&#xa;total_floors: smallint&#xa;land_use_years: varchar(30)&#xa;has_elevator: bool&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff" vertex="1">
<mxGeometry height="225" width="310" x="500" y="1390" as="geometry" />
</mxCell>
<mxCell id="e-bldg-room" edge="1" parent="region-complex" source="buildings" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="room-units">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-bldg-room-lbl" connectable="0" parent="e-bldg-room" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-complex-bldg" edge="1" parent="region-complex" source="complexes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="buildings">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="270" y="1502" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-complex-bldg-lbl" connectable="0" parent="e-complex-bldg" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#34d399;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="region-property" parent="1" style="swimlane;startSize=30;fillColor=#2d1a5e;strokeColor=#a78bfa;fontColor=#a78bfa;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" value="PROPERTY" vertex="1">
<mxGeometry height="1700" width="900" x="1280" y="60" as="geometry" />
<mxGeometry height="1700" width="1380" x="800" y="60" as="geometry" />
</mxCell>
<mxCell id="properties" parent="region-property" 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;" value="&lt;b&gt;properties&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK complex_id → complexes&#xa;🔗 FK building_id → buildings&#xa;🔗 FK room_unit_id → room_units&#xa;🔗 FK agent_id → staff&#xa;listing_type: sale/rent/both&#xa;status: varchar(20)&#xa;sale_price: numeric(12,2) [万元]&#xa;rent_price: numeric(10,2) [元/月]&#xa;area: numeric(8,2) [m²]&#xa;floor: smallint&#xa;total_floors: smallint&#xa;bedroom: smallint&#xa;living_room: smallint&#xa;bathroom: smallint&#xa;orientation: varchar(30)&#xa;decoration: varchar(20)&#xa;has_elevator: bool&#xa;built_year: smallint&#xa;ownership_years: varchar(20)&#xa;is_exclusive: bool [独家委托]&#xa;completeness_score: int&#xa;search_vector: tsvector&#xa;source: varchar(30)&#xa;remarks: text&#xa;created_at: timestamptz&#xa;updated_at: timestamptz&#xa;deleted_at: timestamptz&#xa;🔗 FK created_by → staff&#xa;🔗 FK updated_by → staff&#xa;&lt;i&gt;[89,000+ rows · 复合索引 · 分区预留]&lt;/i&gt;" vertex="1">
<mxGeometry height="560" width="380" x="30" y="60" as="geometry" />
</mxCell>
<mxCell id="property-contacts" parent="region-property" 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;" value="&lt;b&gt;property_contacts&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;name: varchar(50)&#xa;phone_enc: text [AES-256-GCM]&#xa;phone_hash: varchar(64) [SHA-256]&#xa;role: owner/agent/tenant&#xa;is_primary: bool&#xa;created_at: timestamptz&#xa;deleted_at: timestamptz" vertex="1">
<mxGeometry height="170" width="310" x="30" y="670" as="geometry" />
<mxGeometry height="170" width="310" x="460" y="350" as="geometry" />
</mxCell>
<mxCell id="property-follow-logs" parent="region-property" 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;" value="&lt;b&gt;property_follow_logs&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;🔗 FK staff_id → staff&#xa;log_type: call/visit/price_change/note/...&#xa;content: text&#xa;phone_no_viewed: bool [敏感操作]&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff&#xa;⚠ NO DELETE — append-only audit log" vertex="1">
<mxGeometry height="185" width="380" x="470" y="60" as="geometry" />
<mxGeometry height="185" width="380" x="980" y="50" as="geometry" />
</mxCell>
<mxCell id="listing-histories" parent="region-property" 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;" value="&lt;b&gt;listing_histories&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;listed_at: timestamptz&#xa;delisted_at: timestamptz&#xa;list_price: numeric(12,2)&#xa;reason: varchar(50)&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="155" width="310" x="470" y="300" as="geometry" />
<mxGeometry height="155" width="310" x="980" y="250" as="geometry" />
</mxCell>
<mxCell id="property-photos" parent="region-property" 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;" value="&lt;b&gt;property_photos&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;category: listing/vr/layout/other&#xa;file_key: text [R2/S3]&#xa;thumbnail_key: text&#xa;is_cover: bool&#xa;sort_order: smallint&#xa;width, height: int&#xa;file_size: int&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff" vertex="1">
<mxGeometry height="205" width="310" x="30" y="900" as="geometry" />
<mxGeometry height="205" width="310" x="30" y="740" as="geometry" />
</mxCell>
<mxCell id="property-keys" parent="region-property" 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;" value="&lt;b&gt;property_keys&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;🔗 FK holder_id → staff&#xa;key_no: varchar(50)&#xa;status: held/returned&#xa;taken_at: timestamptz&#xa;returned_at: timestamptz&#xa;notes: text" vertex="1">
<mxGeometry height="165" width="310" x="470" y="510" as="geometry" />
<mxGeometry height="165" width="310" x="980" y="430" as="geometry" />
</mxCell>
<mxCell id="property-commissions" parent="region-property" 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;" value="&lt;b&gt;property_commissions&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;commission_type: exclusive/open&#xa;rate: numeric(5,4)&#xa;amount: numeric(12,2)&#xa;start_date: date&#xa;end_date: date&#xa;signed_at: timestamptz&#xa;document_key: text&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="185" width="330" x="470" y="740" as="geometry" />
<mxGeometry height="185" width="330" x="980" y="620" as="geometry" />
</mxCell>
<mxCell id="property-inspections" parent="region-property" 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;" value="&lt;b&gt;property_inspections&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties&#xa;🔗 FK staff_id → staff&#xa;inspected_at: timestamptz&#xa;status: pending/done/cancelled&#xa;notes: text&#xa;attachments: jsonb&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="165" width="320" x="30" y="1160" as="geometry" />
<mxGeometry height="165" width="320" x="450" y="575" as="geometry" />
</mxCell>
<mxCell id="property-marketing" parent="region-property" 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;" value="&lt;b&gt;property_marketing&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties [UNIQUE 1:1]&#xa;title: varchar(200)&#xa;highlights: text[]&#xa;description: text&#xa;tags: varchar[]&#xa;platforms: jsonb&#xa;published_at: timestamptz&#xa;updated_at: timestamptz" vertex="1">
<mxGeometry height="175" width="340" x="470" y="990" as="geometry" />
<mxGeometry height="175" width="340" x="980" y="870" as="geometry" />
</mxCell>
<mxCell id="property-certificates" parent="region-property" 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;" value="&lt;b&gt;property_certificates&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties [UNIQUE 1:1]&#xa;cert_no: varchar(50)&#xa;owner_name: varchar(100)&#xa;ownership_type: varchar(30)&#xa;area_registered: numeric(8,2)&#xa;issue_date: date&#xa;document_key: text" vertex="1">
<mxGeometry height="165" width="330" x="30" y="1390" as="geometry" />
<mxGeometry height="165" width="330" x="450" y="790" as="geometry" />
</mxCell>
<mxCell id="completeness-scores" parent="region-property" 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;" value="&lt;b&gt;completeness_scores&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK property_id → properties [UNIQUE 1:1]&#xa;score: int [0-100]&#xa;missing_fields: text[]&#xa;calculated_at: timestamptz&#xa;version: int" vertex="1">
<mxGeometry height="135" width="310" x="470" y="1230" as="geometry" />
<mxGeometry height="135" width="310" x="980" y="1090" as="geometry" />
</mxCell>
<mxCell id="e-prop-contact" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="property-contacts">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="435" y="280" />
<mxPoint x="435" y="435" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-contact-lbl" connectable="0" parent="e-prop-contact" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-follow" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="property-follow-logs">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="840" y="80" />
<mxPoint x="840" y="80" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-follow-lbl" connectable="0" parent="e-prop-follow" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-listing" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="listing-histories">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="960" y="110" />
<mxPoint x="960" y="328" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-listing-lbl" connectable="0" parent="e-prop-listing" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-photos" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="property-photos">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="50" y="672" />
<mxPoint x="50" y="672" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-photos-lbl" connectable="0" parent="e-prop-photos" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-keys" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="property-keys">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="920" y="140" />
<mxPoint x="920" y="512" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-keys-lbl" connectable="0" parent="e-prop-keys" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-comm" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="property-commissions">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="880" y="180" />
<mxPoint x="880" y="712" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-comm-lbl" connectable="0" parent="e-prop-comm" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-insp" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="property-inspections">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="440" y="500" />
<mxPoint x="440" y="680" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-insp-lbl" connectable="0" parent="e-prop-insp" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-marketing" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" target="property-marketing">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="830" y="220" />
<mxPoint x="830" y="958" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-marketing-lbl" connectable="0" parent="e-prop-marketing" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:1" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-cert" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" target="property-certificates">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="290" y="670" />
<mxPoint x="410" y="670" />
<mxPoint x="410" y="872" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-cert-lbl" connectable="0" parent="e-prop-cert" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:1" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-score" edge="1" parent="region-property" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#a78bfa;endArrow=ERone;startArrow=ERone;fontSize=9;" target="completeness-scores">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="790" y="260" />
<mxPoint x="790" y="1158" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-score-lbl" connectable="0" parent="e-prop-score" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:1" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="region-client" parent="1" style="swimlane;startSize=30;fillColor=#3d1f06;strokeColor=#fbbf24;fontColor=#fbbf24;fontSize=12;fontStyle=1;swimlaneLine=1;rounded=1;arcSize=3;" value="CLIENT" vertex="1">
<mxGeometry height="1380" width="860" x="2220" y="60" as="geometry" />
<mxGeometry height="1380" width="1060" x="2220" y="60" as="geometry" />
</mxCell>
<mxCell id="clients" parent="region-client" 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;" value="&lt;b&gt;clients&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK agent_id → staff&#xa;client_type: private/public/closed&#xa;status: active/inactive/converted&#xa;name: varchar(50)&#xa;phone_enc: text [AES-256-GCM]&#xa;phone_hash: varchar(64) [SHA-256]&#xa;budget_min/max: numeric&#xa;activity_level: 1-5 [Celery每日计算]&#xa;is_protected: bool [防自动转公客]&#xa;transfer_to_public_type: auto/manual&#xa;last_follow_at: timestamptz&#xa;source: varchar(30)&#xa;remarks: text&#xa;created_at: timestamptz&#xa;deleted_at: timestamptz&#xa;🔗 FK created_by → staff&#xa;&lt;i&gt;[私客/公客/成交客 三态状态机]&lt;/i&gt;" vertex="1">
<mxGeometry height="360" width="370" x="30" y="60" as="geometry" />
</mxCell>
<mxCell id="client-requirements" parent="region-client" 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;" value="&lt;b&gt;client_requirements&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK client_id → clients&#xa;req_type: second_hand/new/rent&#xa;district_ids: uuid[]&#xa;business_area_ids: uuid[]&#xa;price_min: numeric&#xa;price_max: numeric&#xa;area_min: numeric&#xa;area_max: numeric&#xa;bedrooms: int[]&#xa;school_ids: uuid[]&#xa;has_elevator: bool&#xa;is_active: bool&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="260" width="350" x="30" y="480" as="geometry" />
<mxGeometry height="260" width="350" x="40" y="507.5" as="geometry" />
</mxCell>
<mxCell id="client-follow-logs" parent="region-client" 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;" value="&lt;b&gt;client_follow_logs&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK client_id → clients&#xa;🔗 FK staff_id → staff&#xa;log_type: call/visit/match/note/status_change&#xa;content: text&#xa;next_follow_date: date&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff&#xa;⚠ NO DELETE — append-only audit log" vertex="1">
<mxGeometry height="200" width="380" x="430" y="70" as="geometry" />
<mxGeometry height="200" width="380" x="660" y="50" as="geometry" />
</mxCell>
<mxCell id="client-viewings" parent="region-client" 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;" value="&lt;b&gt;client_viewings&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK client_id → clients&#xa;🔗 FK property_id → properties&#xa;🔗 FK agent_id → staff&#xa;viewed_at: timestamptz&#xa;feedback: text&#xa;rating: smallint [1-5]&#xa;status: planned/done/cancelled&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="195" width="360" x="430" y="310" as="geometry" />
<mxGeometry height="195" width="360" x="660" y="310" as="geometry" />
</mxCell>
<mxCell id="client-matches" parent="region-client" 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;" value="&lt;b&gt;client_property_matches&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK client_id → clients&#xa;🔗 FK property_id → properties&#xa;🔗 FK agent_id → staff&#xa;match_type: system/manual&#xa;score: numeric(5,2)&#xa;status: pending/sent/viewed/dismissed&#xa;sent_at: timestamptz&#xa;viewed_at: timestamptz&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="205" width="380" x="30" y="800" as="geometry" />
</mxCell>
<mxCell id="client-status-logs" parent="region-client" 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;" value="&lt;b&gt;client_status_logs&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK client_id → clients&#xa;from_status: varchar(20)&#xa;to_status: varchar(20)&#xa;transfer_type: auto/manual&#xa;reason: text&#xa;created_at: timestamptz&#xa;🔗 FK created_by → staff&#xa;⚠ NO DELETE — append-only audit log" vertex="1">
<mxGeometry height="195" width="370" x="430" y="560" as="geometry" />
<mxGeometry height="195" width="370" x="655" y="540" as="geometry" />
</mxCell>
<mxCell id="client-fav-folders" parent="region-client" 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;" value="&lt;b&gt;client_favorite_folders&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK client_id → clients&#xa;name: varchar(100)&#xa;sort_order: int&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="130" width="300" x="30" y="1070" as="geometry" />
<mxGeometry height="130" width="300" x="655" y="980" as="geometry" />
</mxCell>
<mxCell id="client-folder-items" parent="region-client" 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;" value="&lt;b&gt;client_folder_items&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;🔗 FK folder_id → client_favorite_folders&#xa;🔗 FK property_id → properties&#xa;sort_order: int&#xa;created_at: timestamptz" vertex="1">
<mxGeometry height="130" width="320" x="370" y="1070" as="geometry" />
<mxGeometry height="130" width="320" x="655" y="780" as="geometry" />
</mxCell>
<mxCell id="e-client-req" edge="1" parent="region-client" source="clients" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-requirements">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="290" y="460" />
<mxPoint x="290" y="460" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-client-req-lbl" connectable="0" parent="e-client-req" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-client-follow" edge="1" parent="region-client" source="clients" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-follow-logs">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="600" y="80" />
<mxPoint x="600" y="80" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-client-follow-lbl" connectable="0" parent="e-client-follow" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-client-viewing" edge="1" parent="region-client" source="clients" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-viewings">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="110" />
<mxPoint x="640" y="408" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-client-viewing-lbl" connectable="0" parent="e-client-viewing" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-client-match" edge="1" parent="region-client" source="clients" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-matches">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="440" y="300" />
<mxPoint x="440" y="902" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-client-match-lbl" connectable="0" parent="e-client-match" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-client-statuslog" edge="1" parent="region-client" source="clients" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-status-logs">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="620" y="140" />
<mxPoint x="620" y="638" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-client-statuslog-lbl" connectable="0" parent="e-client-statuslog" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-client-fav" edge="1" parent="region-client" source="clients" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-fav-folders">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="530" y="170" />
<mxPoint x="530" y="1045" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-client-fav-lbl" connectable="0" parent="e-client-fav" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fbbf24;" value="1:N" vertex="1">
<mxGeometry relative="1" as="geometry" />
@@ -321,43 +421,80 @@
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-bldg-prop" edge="1" parent="1" source="buildings" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="properties">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="215" y="1390" />
<mxPoint x="1020" y="1390" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-bldg-prop-lbl" connectable="0" parent="e-bldg-prop" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N building_id" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-room-prop" edge="1" parent="1" source="room-units" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#a78bfa;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="properties">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="575" y="1710" />
<mxPoint x="1060" y="1710" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-room-prop-lbl" connectable="0" parent="e-room-prop" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N room_unit_id" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-staff-prop" edge="1" parent="1" source="staff" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#22d3ee;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="properties">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-530" y="448" />
<mxPoint x="-530" y="300" />
<mxPoint x="380" y="300" />
<mxPoint x="380" y="400" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-staff-prop-lbl" connectable="0" parent="e-staff-prop" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" value="agent_id" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-staff-client" edge="1" parent="1" source="staff" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#22d3ee;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="clients">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="700" y="490" />
<mxPoint x="700" y="300" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-staff-client-lbl" connectable="0" parent="e-staff-client" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" value="agent_id" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-viewing" edge="1" parent="1" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-viewings">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="2340" y="530" />
<mxPoint x="2340" y="530" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-viewing-lbl" connectable="0" parent="e-prop-viewing" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" value="property_id" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-match" edge="1" parent="1" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-matches">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="2690" y="400" />
<mxPoint x="2690" y="990" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-match-lbl" connectable="0" parent="e-prop-match" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" value="property_id" vertex="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-prop-folder" edge="1" parent="1" source="properties" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;strokeColor=#fb923c;dashed=1;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-folder-items">
<mxGeometry relative="1" as="geometry" />
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="2800" y="310" />
<mxPoint x="2800" y="905" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e-prop-folder-lbl" connectable="0" parent="e-prop-folder" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#fb923c;" value="property_id" vertex="1">
<mxGeometry relative="1" as="geometry" />

View File

@@ -1,574 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2560 1980">
<defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&amp;display=swap');
text { font-family: 'JetBrains Mono', 'Noto Sans SC', 'PingFang SC', 'SF Mono', monospace; }
</style>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
</pattern>
<!-- Arrow markers per color -->
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#22d3ee"/>
</marker>
<marker id="arrow-emerald" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#34d399"/>
</marker>
<marker id="arrow-violet" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#a78bfa"/>
</marker>
<marker id="arrow-amber" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#fbbf24"/>
</marker>
<marker id="arrow-slate" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#94a3b8"/>
</marker>
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#fb923c"/>
</marker>
</defs>
<!-- Background -->
<rect width="2560" height="1980" fill="#0f172a"/>
<rect width="2560" height="1980" fill="url(#grid)"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- MODULE BOUNDARIES -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- Org Module boundary (cyan) -->
<rect x="30" y="60" width="310" height="680" rx="12" fill="none" stroke="#22d3ee" stroke-width="1" stroke-dasharray="8,4" opacity="0.6"/>
<text x="44" y="80" fill="#22d3ee" font-size="10" font-weight="600">ORG / HR</text>
<!-- Region+Complex Module boundary (emerald) -->
<rect x="360" y="60" width="680" height="1200" rx="12" fill="none" stroke="#34d399" stroke-width="1" stroke-dasharray="8,4" opacity="0.6"/>
<text x="374" y="80" fill="#34d399" font-size="10" font-weight="600">REGION &amp; COMPLEX</text>
<!-- Property Module boundary (violet) -->
<rect x="1060" y="60" width="720" height="1560" rx="12" fill="none" stroke="#a78bfa" stroke-width="1" stroke-dasharray="8,4" opacity="0.6"/>
<text x="1074" y="80" fill="#a78bfa" font-size="10" font-weight="600">PROPERTY</text>
<!-- Client Module boundary (amber) -->
<rect x="1800" y="60" width="730" height="1200" rx="12" fill="none" stroke="#fbbf24" stroke-width="1" stroke-dasharray="8,4" opacity="0.6"/>
<text x="1814" y="80" fill="#fbbf24" font-size="10" font-weight="600">CLIENT</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- CONNECTION LINES (drawn before boxes) -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- OrgUnit → Staff (1:N) -->
<line x1="185" y1="220" x2="185" y2="320" stroke="#22d3ee" stroke-width="1.2" marker-end="url(#arrow-cyan)"/>
<text x="193" y="275" fill="#22d3ee" font-size="8">1:N</text>
<!-- Staff → Property (1:N, via created_by) -->
<line x1="335" y1="370" x2="1060" y2="370" stroke="#22d3ee" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-cyan)"/>
<text x="690" y="362" fill="#22d3ee" font-size="8">created_by</text>
<!-- Staff → Client (1:N, via agent) -->
<line x1="335" y1="410" x2="1800" y2="410" stroke="#22d3ee" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-cyan)"/>
<text x="1060" y="402" fill="#22d3ee" font-size="8">agent_id</text>
<!-- District → BusinessArea (1:N) -->
<line x1="560" y1="225" x2="560" y2="320" stroke="#34d399" stroke-width="1.2" marker-end="url(#arrow-emerald)"/>
<text x="568" y="277" fill="#34d399" font-size="8">1:N</text>
<!-- District → School (1:N) -->
<line x1="700" y1="175" x2="870" y2="175" stroke="#34d399" stroke-width="1.2" marker-end="url(#arrow-emerald)"/>
<text x="775" y="167" fill="#34d399" font-size="8">1:N</text>
<!-- BusinessArea ↔ Complex (N:M via complex_business_areas) -->
<line x1="560" y1="420" x2="560" y2="500" stroke="#34d399" stroke-width="1.2" marker-end="url(#arrow-emerald)"/>
<text x="568" y="464" fill="#34d399" font-size="8">N:M</text>
<!-- Complex → Complex_schools join label -->
<line x1="700" y1="570" x2="870" y2="400" stroke="#34d399" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-emerald)"/>
<text x="780" y="495" fill="#34d399" font-size="8">N:M</text>
<!-- Complex → Building (1:N) -->
<line x1="560" y1="700" x2="560" y2="790" stroke="#34d399" stroke-width="1.2" marker-end="url(#arrow-emerald)"/>
<text x="568" y="749" fill="#34d399" font-size="8">1:N</text>
<!-- Building → RoomUnit (1:N) -->
<line x1="560" y1="980" x2="560" y2="1060" stroke="#34d399" stroke-width="1.2" marker-end="url(#arrow-emerald)"/>
<text x="568" y="1024" fill="#34d399" font-size="8">1:N</text>
<!-- Complex → Property (1:N) -->
<line x1="720" y1="600" x2="1060" y2="300" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="885" y="445" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → PropertyContact (1:N) -->
<line x1="1300" y1="390" x2="1300" y2="490" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1308" y="444" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → FollowLog (1:N) -->
<line x1="1420" y1="300" x2="1570" y2="300" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1482" y="292" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → PropertyPhoto (1:N) -->
<line x1="1300" y1="670" x2="1300" y2="760" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1308" y="718" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → KeyManagement (1:N) -->
<line x1="1420" y1="550" x2="1570" y2="550" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1482" y="542" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → Commission (1:N) -->
<line x1="1420" y1="650" x2="1570" y2="750" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1490" y="695" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → Inspection (1:N) -->
<line x1="1300" y1="940" x2="1300" y2="1020" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1308" y="984" fill="#a78bfa" font-size="8">1:N</text>
<!-- Property → Marketing (1:1) -->
<line x1="1420" y1="870" x2="1570" y2="960" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1490" y="910" fill="#a78bfa" font-size="8">1:1</text>
<!-- Property → ListingHistory (1:N) -->
<line x1="1180" y1="390" x2="1080" y2="490" stroke="#a78bfa" stroke-width="1.2" marker-end="url(#arrow-violet)"/>
<text x="1100" y="435" fill="#a78bfa" font-size="8">1:N</text>
<!-- Client → ClientRequirement (1:N) -->
<line x1="2050" y1="220" x2="2050" y2="310" stroke="#fbbf24" stroke-width="1.2" marker-end="url(#arrow-amber)"/>
<text x="2058" y="269" fill="#fbbf24" font-size="8">1:N</text>
<!-- Client → ClientFollowLog (1:N) -->
<line x1="1930" y1="220" x2="1830" y2="310" stroke="#fbbf24" stroke-width="1.2" marker-end="url(#arrow-amber)"/>
<text x="1850" y="260" fill="#fbbf24" font-size="8">1:N</text>
<!-- Client → Viewing (1:N) -->
<line x1="2170" y1="220" x2="2270" y2="310" stroke="#fbbf24" stroke-width="1.2" marker-end="url(#arrow-amber)"/>
<text x="2210" y="260" fill="#fbbf24" font-size="8">1:N</text>
<!-- Client → Match (1:N) -->
<line x1="2050" y1="490" x2="2050" y2="580" stroke="#fbbf24" stroke-width="1.2" marker-end="url(#arrow-amber)"/>
<text x="2058" y="538" fill="#fbbf24" font-size="8">1:N</text>
<!-- Property → Viewing (1:N) -->
<line x1="1780" y1="340" x2="2270" y2="340" stroke="#fb923c" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-orange)"/>
<text x="2020" y="332" fill="#fb923c" font-size="8">1:N</text>
<!-- Property → Match (1:N) -->
<line x1="1780" y1="620" x2="1950" y2="620" stroke="#fb923c" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-orange)"/>
<text x="1845" y="612" fill="#fb923c" font-size="8">1:N</text>
<!-- MetroStation → Complex (N:M) -->
<line x1="620" y1="1280" x2="620" y2="1200" stroke="#34d399" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-emerald)"/>
<text x="628" y="1244" fill="#34d399" font-size="8">N:M</text>
<!-- MetroLine → MetroStation (1:N) -->
<line x1="430" y1="1280" x2="500" y2="1280" stroke="#34d399" stroke-width="1.2" marker-end="url(#arrow-emerald)"/>
<text x="452" y="1272" fill="#34d399" font-size="8">1:N</text>
<!-- ComplexPriceTrend → Complex -->
<line x1="870" y1="900" x2="720" y2="660" stroke="#34d399" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-emerald)"/>
<text x="790" y="800" fill="#34d399" font-size="8">1:N</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ORG MODULE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- OrgUnit -->
<rect x="80" y="100" width="210" height="120" rx="6" fill="#0f172a"/>
<rect x="80" y="100" width="210" height="120" rx="6" fill="rgba(8,51,68,0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<line x1="80" y1="128" x2="290" y2="128" stroke="#22d3ee" stroke-width="0.5" opacity="0.6"/>
<text x="185" y="120" fill="white" font-size="11" font-weight="700" text-anchor="middle">org_units</text>
<text x="90" y="145" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="90" y="158" fill="#94a3b8" font-size="8">parent_id: uuid (FK→self)</text>
<text x="90" y="171" fill="#94a3b8" font-size="8">type: varchar(20)</text>
<text x="90" y="184" fill="#94a3b8" font-size="8">name, path, depth</text>
<text x="90" y="197" fill="#94a3b8" font-size="8">is_active: bool</text>
<!-- Staff -->
<rect x="80" y="320" width="210" height="150" rx="6" fill="#0f172a"/>
<rect x="80" y="320" width="210" height="150" rx="6" fill="rgba(8,51,68,0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<line x1="80" y1="348" x2="290" y2="348" stroke="#22d3ee" stroke-width="0.5" opacity="0.6"/>
<text x="185" y="340" fill="white" font-size="11" font-weight="700" text-anchor="middle">staff</text>
<text x="90" y="365" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="90" y="378" fill="#94a3b8" font-size="8">FK org_unit_id</text>
<text x="90" y="391" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="90" y="404" fill="#94a3b8" font-size="8">phone_enc: text (AES)</text>
<text x="90" y="417" fill="#94a3b8" font-size="8">phone_hash: varchar(64)</text>
<text x="90" y="430" fill="#94a3b8" font-size="8">user_id: uuid (FK→auth)</text>
<text x="90" y="443" fill="#94a3b8" font-size="8">is_active, deleted_at</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- REGION + COMPLEX MODULE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- District -->
<rect x="440" y="100" width="220" height="125" rx="6" fill="#0f172a"/>
<rect x="440" y="100" width="220" height="125" rx="6" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<line x1="440" y1="128" x2="660" y2="128" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="550" y="120" fill="white" font-size="11" font-weight="700" text-anchor="middle">districts</text>
<text x="450" y="145" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="450" y="158" fill="#94a3b8" font-size="8">city: varchar(50)</text>
<text x="450" y="171" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="450" y="184" fill="#94a3b8" font-size="8">short_name: varchar(20)</text>
<text x="450" y="197" fill="#94a3b8" font-size="8">sort_order, is_active</text>
<!-- BusinessArea -->
<rect x="440" y="320" width="240" height="135" rx="6" fill="#0f172a"/>
<rect x="440" y="320" width="240" height="135" rx="6" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<line x1="440" y1="348" x2="680" y2="348" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="560" y="340" fill="white" font-size="11" font-weight="700" text-anchor="middle">business_areas</text>
<text x="450" y="365" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="450" y="378" fill="#94a3b8" font-size="8">FK district_id</text>
<text x="450" y="391" fill="#94a3b8" font-size="8">name: varchar(100)</text>
<text x="450" y="404" fill="#94a3b8" font-size="8">latitude, longitude</text>
<text x="450" y="417" fill="#94a3b8" font-size="8">sort_order, is_active</text>
<!-- School -->
<rect x="700" y="100" width="220" height="135" rx="6" fill="#0f172a"/>
<rect x="700" y="100" width="220" height="135" rx="6" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<line x1="700" y1="128" x2="920" y2="128" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="810" y="120" fill="white" font-size="11" font-weight="700" text-anchor="middle">schools</text>
<text x="710" y="145" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="710" y="158" fill="#94a3b8" font-size="8">FK district_id</text>
<text x="710" y="171" fill="#94a3b8" font-size="8">name: varchar(100)</text>
<text x="710" y="184" fill="#94a3b8" font-size="8">type: primary/middle/high</text>
<text x="710" y="197" fill="#94a3b8" font-size="8">nature: public/private</text>
<text x="710" y="210" fill="#94a3b8" font-size="8">level: normal/key/top</text>
<!-- Complex -->
<rect x="440" y="500" width="300" height="200" rx="6" fill="#0f172a"/>
<rect x="440" y="500" width="300" height="200" rx="6" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<line x1="440" y1="528" x2="740" y2="528" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="590" y="520" fill="white" font-size="11" font-weight="700" text-anchor="middle">complexes</text>
<text x="450" y="545" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="450" y="558" fill="#94a3b8" font-size="8">name: varchar(200) [不可直接修改]</text>
<text x="450" y="571" fill="#94a3b8" font-size="8">FK district_id</text>
<text x="450" y="584" fill="#94a3b8" font-size="8">address, address_summary</text>
<text x="450" y="597" fill="#94a3b8" font-size="8">latitude, longitude</text>
<text x="450" y="610" fill="#94a3b8" font-size="8">lock_building/room/info: bool</text>
<text x="450" y="623" fill="#94a3b8" font-size="8">property_usage_types: varchar[]</text>
<text x="450" y="636" fill="#94a3b8" font-size="8">search_vector: tsvector</text>
<text x="450" y="649" fill="#94a3b8" font-size="8">developer, property_company</text>
<text x="450" y="662" fill="#94a3b8" font-size="8">deleted_at, created_by</text>
<text x="450" y="675" fill="#94a3b8" font-size="8">...</text>
<!-- complex_business_areas join table (small) -->
<rect x="440" y="465" width="220" height="30" rx="4" fill="#0f172a"/>
<rect x="440" y="465" width="220" height="30" rx="4" fill="rgba(6,78,59,0.2)" stroke="#34d399" stroke-width="1" stroke-dasharray="3,2"/>
<text x="550" y="484" fill="#34d399" font-size="8" text-anchor="middle">complex_business_areas (N:M) · is_primary</text>
<!-- complex_schools join table (small) -->
<rect x="700" y="310" width="210" height="30" rx="4" fill="#0f172a"/>
<rect x="700" y="310" width="210" height="30" rx="4" fill="rgba(6,78,59,0.2)" stroke="#34d399" stroke-width="1" stroke-dasharray="3,2"/>
<text x="805" y="329" fill="#34d399" font-size="8" text-anchor="middle">complex_schools · zone_type</text>
<!-- Building -->
<rect x="440" y="790" width="300" height="185" rx="6" fill="#0f172a"/>
<rect x="440" y="790" width="300" height="185" rx="6" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<line x1="440" y1="818" x2="740" y2="818" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="590" y="810" fill="white" font-size="11" font-weight="700" text-anchor="middle">buildings</text>
<text x="450" y="835" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="450" y="848" fill="#94a3b8" font-size="8">FK complex_id</text>
<text x="450" y="861" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="450" y="874" fill="#94a3b8" font-size="8">is_standard: bool</text>
<text x="450" y="887" fill="#94a3b8" font-size="8">total_floors: smallint</text>
<text x="450" y="900" fill="#94a3b8" font-size="8">has_elevator: bool</text>
<text x="450" y="913" fill="#94a3b8" font-size="8">built_year: smallint</text>
<text x="450" y="926" fill="#94a3b8" font-size="8">property_usage_type</text>
<text x="450" y="939" fill="#94a3b8" font-size="8">is_active, created_at</text>
<text x="450" y="952" fill="#94a3b8" font-size="8">FK school_id</text>
<!-- RoomUnit -->
<rect x="440" y="1060" width="300" height="155" rx="6" fill="#0f172a"/>
<rect x="440" y="1060" width="300" height="155" rx="6" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<line x1="440" y1="1088" x2="740" y2="1088" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="590" y="1080" fill="white" font-size="11" font-weight="700" text-anchor="middle">room_units</text>
<text x="450" y="1105" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="450" y="1118" fill="#94a3b8" font-size="8">FK building_id</text>
<text x="450" y="1131" fill="#94a3b8" font-size="8">floor: smallint</text>
<text x="450" y="1144" fill="#94a3b8" font-size="8">floor_name: varchar(20)</text>
<text x="450" y="1157" fill="#94a3b8" font-size="8">room_no: varchar(30)</text>
<text x="450" y="1170" fill="#94a3b8" font-size="8">display_no: varchar(50)</text>
<text x="450" y="1183" fill="#94a3b8" font-size="8">is_standard: bool</text>
<text x="450" y="1196" fill="#94a3b8" font-size="8">is_active</text>
<!-- ComplexPriceTrend -->
<rect x="770" y="800" width="250" height="140" rx="6" fill="#0f172a"/>
<rect x="770" y="800" width="250" height="140" rx="6" fill="rgba(6,78,59,0.3)" stroke="#34d399" stroke-width="1.5"/>
<line x1="770" y1="828" x2="1020" y2="828" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="895" y="820" fill="white" font-size="11" font-weight="700" text-anchor="middle">complex_price_trends</text>
<text x="780" y="845" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="780" y="858" fill="#94a3b8" font-size="8">FK complex_id</text>
<text x="780" y="871" fill="#94a3b8" font-size="8">record_month: date</text>
<text x="780" y="884" fill="#94a3b8" font-size="8">avg_unit_price: numeric(10,2)</text>
<text x="780" y="897" fill="#94a3b8" font-size="8">avg_sale_price: numeric(12,2)</text>
<text x="780" y="910" fill="#94a3b8" font-size="8">transaction_count: int</text>
<text x="780" y="923" fill="#94a3b8" font-size="8">listing_count: int</text>
<!-- MetroLine -->
<rect x="370" y="1270" width="200" height="105" rx="6" fill="#0f172a"/>
<rect x="370" y="1270" width="200" height="105" rx="6" fill="rgba(6,78,59,0.3)" stroke="#34d399" stroke-width="1.5"/>
<line x1="370" y1="1298" x2="570" y2="1298" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="470" y="1290" fill="white" font-size="11" font-weight="700" text-anchor="middle">metro_lines</text>
<text x="380" y="1315" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="380" y="1328" fill="#94a3b8" font-size="8">city: varchar(50)</text>
<text x="380" y="1341" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="380" y="1354" fill="#94a3b8" font-size="8">color: varchar(7) [HEX]</text>
<!-- MetroStation -->
<rect x="580" y="1270" width="240" height="120" rx="6" fill="#0f172a"/>
<rect x="580" y="1270" width="240" height="120" rx="6" fill="rgba(6,78,59,0.3)" stroke="#34d399" stroke-width="1.5"/>
<line x1="580" y1="1298" x2="820" y2="1298" stroke="#34d399" stroke-width="0.5" opacity="0.6"/>
<text x="700" y="1290" fill="white" font-size="11" font-weight="700" text-anchor="middle">metro_stations</text>
<text x="590" y="1315" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="590" y="1328" fill="#94a3b8" font-size="8">FK metro_line_id</text>
<text x="590" y="1341" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="590" y="1354" fill="#94a3b8" font-size="8">latitude, longitude</text>
<text x="590" y="1367" fill="#94a3b8" font-size="8">sort_order</text>
<!-- complex_metro_stations join (small) -->
<rect x="580" y="1200" width="240" height="28" rx="4" fill="#0f172a"/>
<rect x="580" y="1200" width="240" height="28" rx="4" fill="rgba(6,78,59,0.2)" stroke="#34d399" stroke-width="1" stroke-dasharray="3,2"/>
<text x="700" y="1218" fill="#34d399" font-size="8" text-anchor="middle">complex_metro_stations · distance_meters</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- PROPERTY MODULE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- Property -->
<rect x="1080" y="100" width="340" height="290" rx="6" fill="#0f172a"/>
<rect x="1080" y="100" width="340" height="290" rx="6" fill="rgba(76,29,149,0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1080" y1="128" x2="1420" y2="128" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1250" y="120" fill="white" font-size="11" font-weight="700" text-anchor="middle">properties</text>
<text x="1090" y="145" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1090" y="158" fill="#94a3b8" font-size="8">FK complex_id FK building_id</text>
<text x="1090" y="171" fill="#94a3b8" font-size="8">FK room_unit_id FK agent_id</text>
<text x="1090" y="184" fill="#94a3b8" font-size="8">listing_type: sale/rent/both</text>
<text x="1090" y="197" fill="#94a3b8" font-size="8">status: varchar(20)</text>
<text x="1090" y="210" fill="#94a3b8" font-size="8">sale_price: numeric(12,2)</text>
<text x="1090" y="223" fill="#94a3b8" font-size="8">rent_price: numeric(10,2)</text>
<text x="1090" y="236" fill="#94a3b8" font-size="8">floor, total_floors</text>
<text x="1090" y="249" fill="#94a3b8" font-size="8">area: numeric(8,2) [m²]</text>
<text x="1090" y="262" fill="#94a3b8" font-size="8">bedroom, living, bathroom</text>
<text x="1090" y="275" fill="#94a3b8" font-size="8">orientation, decoration</text>
<text x="1090" y="288" fill="#94a3b8" font-size="8">search_vector: tsvector</text>
<text x="1090" y="301" fill="#94a3b8" font-size="8">is_exclusive: bool</text>
<text x="1090" y="314" fill="#94a3b8" font-size="8">completeness_score: int</text>
<text x="1090" y="327" fill="#94a3b8" font-size="8">deleted_at, created_by</text>
<text x="1090" y="340" fill="#94a3b8" font-size="8">...</text>
<text x="1090" y="353" fill="#94a3b8" font-size="7">[89,000+ rows · partitioned by status]</text>
<text x="1090" y="370" fill="#94a3b8" font-size="7">UNIQUE (complex_id, building_id, floor, room_no)</text>
<!-- PropertyContact -->
<rect x="1080" y="490" width="280" height="155" rx="6" fill="#0f172a"/>
<rect x="1080" y="490" width="280" height="155" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1080" y1="518" x2="1360" y2="518" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1220" y="510" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_contacts</text>
<text x="1090" y="535" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1090" y="548" fill="#94a3b8" font-size="8">FK property_id</text>
<text x="1090" y="561" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="1090" y="574" fill="#94a3b8" font-size="8">phone_enc: text (AES)</text>
<text x="1090" y="587" fill="#94a3b8" font-size="8">phone_hash: varchar(64)</text>
<text x="1090" y="600" fill="#94a3b8" font-size="8">role: owner/agent/tenant</text>
<text x="1090" y="613" fill="#94a3b8" font-size="8">is_primary: bool</text>
<!-- PropertyPhoto -->
<rect x="1080" y="760" width="280" height="155" rx="6" fill="#0f172a"/>
<rect x="1080" y="760" width="280" height="155" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1080" y1="788" x2="1360" y2="788" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1220" y="780" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_photos</text>
<text x="1090" y="805" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1090" y="818" fill="#94a3b8" font-size="8">FK property_id</text>
<text x="1090" y="831" fill="#94a3b8" font-size="8">category: listing/vr/layout</text>
<text x="1090" y="844" fill="#94a3b8" font-size="8">file_key: text (R2/S3)</text>
<text x="1090" y="857" fill="#94a3b8" font-size="8">is_cover: bool</text>
<text x="1090" y="870" fill="#94a3b8" font-size="8">sort_order: smallint</text>
<text x="1090" y="883" fill="#94a3b8" font-size="8">width, height, file_size</text>
<!-- Inspection -->
<rect x="1080" y="1020" width="280" height="135" rx="6" fill="#0f172a"/>
<rect x="1080" y="1020" width="280" height="135" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1080" y1="1048" x2="1360" y2="1048" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1220" y="1040" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_inspections</text>
<text x="1090" y="1065" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1090" y="1078" fill="#94a3b8" font-size="8">FK property_id FK staff_id</text>
<text x="1090" y="1091" fill="#94a3b8" font-size="8">inspected_at: timestamptz</text>
<text x="1090" y="1104" fill="#94a3b8" font-size="8">status: pending/done</text>
<text x="1090" y="1117" fill="#94a3b8" font-size="8">notes: text</text>
<text x="1090" y="1130" fill="#94a3b8" font-size="8">attachments: jsonb</text>
<!-- FollowLog (property) -->
<rect x="1440" y="100" width="300" height="155" rx="6" fill="#0f172a"/>
<rect x="1440" y="100" width="300" height="155" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1440" y1="128" x2="1740" y2="128" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1590" y="120" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_follow_logs</text>
<text x="1450" y="145" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1450" y="158" fill="#94a3b8" font-size="8">FK property_id FK staff_id</text>
<text x="1450" y="171" fill="#94a3b8" font-size="8">log_type: call/visit/note...</text>
<text x="1450" y="184" fill="#94a3b8" font-size="8">content: text</text>
<text x="1450" y="197" fill="#94a3b8" font-size="8">sensitive_view: bool [不可删]</text>
<text x="1450" y="210" fill="#94a3b8" font-size="8">created_at, created_by</text>
<text x="1450" y="237" fill="#fb7185" font-size="7">⚠ NO DELETE (audit log)</text>
<!-- KeyManagement -->
<rect x="1440" y="490" width="300" height="120" rx="6" fill="#0f172a"/>
<rect x="1440" y="490" width="300" height="120" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1440" y1="518" x2="1740" y2="518" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1590" y="510" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_keys</text>
<text x="1450" y="535" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1450" y="548" fill="#94a3b8" font-size="8">FK property_id FK holder_id</text>
<text x="1450" y="561" fill="#94a3b8" font-size="8">key_no: varchar(50)</text>
<text x="1450" y="574" fill="#94a3b8" font-size="8">status: held/returned</text>
<text x="1450" y="587" fill="#94a3b8" font-size="8">taken_at, returned_at</text>
<!-- Commission -->
<rect x="1440" y="720" width="300" height="135" rx="6" fill="#0f172a"/>
<rect x="1440" y="720" width="300" height="135" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1440" y1="748" x2="1740" y2="748" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1590" y="740" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_commissions</text>
<text x="1450" y="765" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1450" y="778" fill="#94a3b8" font-size="8">FK property_id</text>
<text x="1450" y="791" fill="#94a3b8" font-size="8">commission_type: exclusive/open</text>
<text x="1450" y="804" fill="#94a3b8" font-size="8">rate: numeric(5,4)</text>
<text x="1450" y="817" fill="#94a3b8" font-size="8">start_date, end_date</text>
<text x="1450" y="830" fill="#94a3b8" font-size="8">signed_at, document_key</text>
<!-- Marketing -->
<rect x="1440" y="940" width="300" height="120" rx="6" fill="#0f172a"/>
<rect x="1440" y="940" width="300" height="120" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1440" y1="968" x2="1740" y2="968" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1590" y="960" fill="white" font-size="11" font-weight="700" text-anchor="middle">property_marketing</text>
<text x="1450" y="985" fill="#fbbf24" font-size="8">PK id: uuid [1:1 property]</text>
<text x="1450" y="998" fill="#94a3b8" font-size="8">FK property_id (UNIQUE)</text>
<text x="1450" y="1011" fill="#94a3b8" font-size="8">title: varchar(200)</text>
<text x="1450" y="1024" fill="#94a3b8" font-size="8">highlights: text[]</text>
<text x="1450" y="1037" fill="#94a3b8" font-size="8">published_at, platforms: jsonb</text>
<!-- ListingHistory -->
<rect x="1080" y="1250" width="300" height="120" rx="6" fill="#0f172a"/>
<rect x="1080" y="1250" width="300" height="120" rx="6" fill="rgba(76,29,149,0.35)" stroke="#a78bfa" stroke-width="1.5"/>
<line x1="1080" y1="1278" x2="1380" y2="1278" stroke="#a78bfa" stroke-width="0.5" opacity="0.6"/>
<text x="1230" y="1270" fill="white" font-size="11" font-weight="700" text-anchor="middle">listing_histories</text>
<text x="1090" y="1295" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1090" y="1308" fill="#94a3b8" font-size="8">FK property_id</text>
<text x="1090" y="1321" fill="#94a3b8" font-size="8">listed_at, delisted_at</text>
<text x="1090" y="1334" fill="#94a3b8" font-size="8">list_price: numeric(12,2)</text>
<text x="1090" y="1347" fill="#94a3b8" font-size="8">reason: varchar(50)</text>
<!-- Arrow from Property to ListingHistory -->
<line x1="1230" y1="390" x2="1230" y2="1250" stroke="#a78bfa" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-violet)"/>
<text x="1238" y="820" fill="#a78bfa" font-size="8">1:N</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- CLIENT MODULE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- Client -->
<rect x="1870" y="100" width="320" height="250" rx="6" fill="#0f172a"/>
<rect x="1870" y="100" width="320" height="250" rx="6" fill="rgba(120,53,15,0.4)" stroke="#fbbf24" stroke-width="1.5"/>
<line x1="1870" y1="128" x2="2190" y2="128" stroke="#fbbf24" stroke-width="0.5" opacity="0.6"/>
<text x="2030" y="120" fill="white" font-size="11" font-weight="700" text-anchor="middle">clients</text>
<text x="1880" y="145" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1880" y="158" fill="#94a3b8" font-size="8">FK agent_id (staff)</text>
<text x="1880" y="171" fill="#94a3b8" font-size="8">client_type: private/public/closed</text>
<text x="1880" y="184" fill="#94a3b8" font-size="8">status: active/inactive/converted</text>
<text x="1880" y="197" fill="#94a3b8" font-size="8">name: varchar(50)</text>
<text x="1880" y="210" fill="#94a3b8" font-size="8">phone_enc: text (AES)</text>
<text x="1880" y="223" fill="#94a3b8" font-size="8">phone_hash: varchar(64)</text>
<text x="1880" y="236" fill="#94a3b8" font-size="8">activity_level: 1-5 (Celery daily)</text>
<text x="1880" y="249" fill="#94a3b8" font-size="8">is_protected: bool [防止转公客]</text>
<text x="1880" y="262" fill="#94a3b8" font-size="8">transfer_to_public_type: auto/manual</text>
<text x="1880" y="275" fill="#94a3b8" font-size="8">source: varchar(30)</text>
<text x="1880" y="288" fill="#94a3b8" font-size="8">deleted_at, created_by</text>
<text x="1880" y="301" fill="#94a3b8" font-size="8">...</text>
<text x="1880" y="325" fill="#94a3b8" font-size="7">[私客/公客/成交客 三态状态机]</text>
<!-- ClientRequirement -->
<rect x="1960" y="490" width="280" height="155" rx="6" fill="#0f172a"/>
<rect x="1960" y="490" width="280" height="155" rx="6" fill="rgba(120,53,15,0.35)" stroke="#fbbf24" stroke-width="1.5"/>
<line x1="1960" y1="518" x2="2240" y2="518" stroke="#fbbf24" stroke-width="0.5" opacity="0.6"/>
<text x="2100" y="510" fill="white" font-size="11" font-weight="700" text-anchor="middle">client_requirements</text>
<text x="1970" y="535" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1970" y="548" fill="#94a3b8" font-size="8">FK client_id</text>
<text x="1970" y="561" fill="#94a3b8" font-size="8">req_type: second_hand/new/rent</text>
<text x="1970" y="574" fill="#94a3b8" font-size="8">district_ids: uuid[]</text>
<text x="1970" y="587" fill="#94a3b8" font-size="8">price_min/max: numeric</text>
<text x="1970" y="600" fill="#94a3b8" font-size="8">area_min/max, bedrooms</text>
<text x="1970" y="613" fill="#94a3b8" font-size="8">school_ids: uuid[]</text>
<!-- ClientFollowLog -->
<rect x="1820" y="490" width="130" height="155" rx="6" fill="#0f172a"/>
<rect x="1820" y="490" width="130" height="155" rx="6" fill="rgba(120,53,15,0.35)" stroke="#fbbf24" stroke-width="1.5"/>
<line x1="1820" y1="518" x2="1950" y2="518" stroke="#fbbf24" stroke-width="0.5" opacity="0.6"/>
<text x="1885" y="510" fill="white" font-size="10" font-weight="700" text-anchor="middle">client_follow_logs</text>
<text x="1828" y="535" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1828" y="548" fill="#94a3b8" font-size="8">FK client_id</text>
<text x="1828" y="561" fill="#94a3b8" font-size="8">log_type</text>
<text x="1828" y="574" fill="#94a3b8" font-size="8">content: text</text>
<text x="1828" y="587" fill="#94a3b8" font-size="8">created_at</text>
<text x="1828" y="617" fill="#fb7185" font-size="7">⚠ NO DELETE</text>
<!-- Viewing -->
<rect x="2250" y="290" width="250" height="165" rx="6" fill="#0f172a"/>
<rect x="2250" y="290" width="250" height="165" rx="6" fill="rgba(120,53,15,0.35)" stroke="#fbbf24" stroke-width="1.5"/>
<line x1="2250" y1="318" x2="2500" y2="318" stroke="#fbbf24" stroke-width="0.5" opacity="0.6"/>
<text x="2375" y="310" fill="white" font-size="11" font-weight="700" text-anchor="middle">client_viewings</text>
<text x="2260" y="335" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="2260" y="348" fill="#94a3b8" font-size="8">FK client_id</text>
<text x="2260" y="361" fill="#94a3b8" font-size="8">FK property_id</text>
<text x="2260" y="374" fill="#94a3b8" font-size="8">FK agent_id (staff)</text>
<text x="2260" y="387" fill="#94a3b8" font-size="8">viewed_at: timestamptz</text>
<text x="2260" y="400" fill="#94a3b8" font-size="8">feedback: text</text>
<text x="2260" y="413" fill="#94a3b8" font-size="8">rating: smallint</text>
<text x="2260" y="426" fill="#94a3b8" font-size="8">status: planned/done/cancelled</text>
<!-- Match -->
<rect x="1960" y="700" width="280" height="150" rx="6" fill="#0f172a"/>
<rect x="1960" y="700" width="280" height="150" rx="6" fill="rgba(120,53,15,0.35)" stroke="#fbbf24" stroke-width="1.5"/>
<line x1="1960" y1="728" x2="2240" y2="728" stroke="#fbbf24" stroke-width="0.5" opacity="0.6"/>
<text x="2100" y="720" fill="white" font-size="11" font-weight="700" text-anchor="middle">client_property_matches</text>
<text x="1970" y="745" fill="#fbbf24" font-size="8">PK id: uuid</text>
<text x="1970" y="758" fill="#94a3b8" font-size="8">FK client_id</text>
<text x="1970" y="771" fill="#94a3b8" font-size="8">FK property_id</text>
<text x="1970" y="784" fill="#94a3b8" font-size="8">match_type: system/manual</text>
<text x="1970" y="797" fill="#94a3b8" font-size="8">score: numeric(5,2)</text>
<text x="1970" y="810" fill="#94a3b8" font-size="8">status: pending/sent/viewed</text>
<text x="1970" y="823" fill="#94a3b8" font-size="8">created_at</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- LEGEND -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="30" y="1850" width="900" height="100" rx="8" fill="rgba(15,23,42,0.8)" stroke="#334155" stroke-width="1"/>
<text x="50" y="1873" fill="#94a3b8" font-size="9" font-weight="600">LEGEND</text>
<!-- Org -->
<rect x="50" y="1885" width="14" height="14" rx="2" fill="rgba(8,51,68,0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="70" y="1896" fill="#22d3ee" font-size="8">ORG / HR</text>
<!-- Complex -->
<rect x="155" y="1885" width="14" height="14" rx="2" fill="rgba(6,78,59,0.4)" stroke="#34d399" stroke-width="1.5"/>
<text x="175" y="1896" fill="#34d399" font-size="8">REGION &amp; COMPLEX</text>
<!-- Property -->
<rect x="310" y="1885" width="14" height="14" rx="2" fill="rgba(76,29,149,0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="330" y="1896" fill="#a78bfa" font-size="8">PROPERTY</text>
<!-- Client -->
<rect x="420" y="1885" width="14" height="14" rx="2" fill="rgba(120,53,15,0.4)" stroke="#fbbf24" stroke-width="1.5"/>
<text x="440" y="1896" fill="#fbbf24" font-size="8">CLIENT</text>
<!-- Relationship lines -->
<line x1="50" y1="1920" x2="90" y2="1920" stroke="#94a3b8" stroke-width="1.5" marker-end="url(#arrow-slate)"/>
<text x="96" y="1924" fill="#94a3b8" font-size="8">Foreign Key (FK)</text>
<line x1="210" y1="1920" x2="250" y2="1920" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-slate)"/>
<text x="256" y="1924" fill="#94a3b8" font-size="8">Soft reference / optional FK</text>
<rect x="410" y="1913" width="14" height="14" rx="2" fill="rgba(15,23,42,0.5)" stroke="#34d399" stroke-width="1" stroke-dasharray="3,2"/>
<text x="430" y="1924" fill="#34d399" font-size="8">Join table (N:M)</text>
<text x="550" y="1924" fill="#fbbf24" font-size="8">PK Primary Key</text>
<text x="640" y="1924" fill="#fb7185" font-size="8">⚠ NO DELETE = append-only audit log</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- TITLE BLOCK -->
<!-- ═══════════════════════════════════════════════════════════ -->
<text x="1080" y="1930" fill="#475569" font-size="10" text-anchor="middle">Fonrey 房产经纪管理系统 — Entity Relationship Diagram · v1.0 · 2026-04-24 · Schema-per-Tenant (django-tenants)</text>
</svg>

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 KiB