图片更新修改

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

@@ -0,0 +1,505 @@
<mxfile host="drawio.ishenwei.online" agent="OpenCode">
<diagram name="Fonrey ER Diagram" id="fonrey-er-v1">
<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="380" x="-860" y="60" as="geometry">
<mxRectangle height="30" width="100" x="40" y="60" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="org-units" parent="region-org" 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;" value="&lt;b&gt;org_units&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;parent_id: uuid (FK → self)&#xa;type: varchar(20)&#xa;name: varchar(100)&#xa;path: varchar(500) [物化路径]&#xa;depth: smallint&#xa;sort_order: int&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;deleted_at: timestamptz" vertex="1">
<mxGeometry height="185" width="280" x="30" y="60" as="geometry" />
</mxCell>
<mxCell id="staff" parent="region-org" 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;" value="&lt;b&gt;staff&lt;/b&gt;&#xa;&lt;hr/&gt;&#xa;🔑 PK id: uuid&#xa;FK org_unit_id → org_units&#xa;name: varchar(50)&#xa;phone_enc: text [AES-256-GCM]&#xa;phone_hash: varchar(64) [SHA-256]&#xa;id_no_enc: text [AES]&#xa;user_id: uuid [FK → auth_user]&#xa;entry_date: date&#xa;status: active/resigned/...&#xa;is_active: bool&#xa;created_at: timestamptz&#xa;deleted_at: timestamptz" vertex="1">
<mxGeometry height="215" width="280" x="30" y="280" as="geometry" />
</mxCell>
<mxCell id="e-org-self" edge="1" parent="region-org" source="org-units" 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;" target="org-units">
<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" connectable="0" parent="e-org-self" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" value="自引用 parent_id" vertex="1">
<mxGeometry relative="1" x="0.1" as="geometry" />
</mxCell>
<mxCell id="e-org-staff" edge="1" parent="region-org" source="org-units" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#22d3ee;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="staff">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-org-staff-lbl" connectable="0" parent="e-org-staff" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#22d3ee;" value="1:N" vertex="1">
<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="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="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="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="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="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="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="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="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="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" />
</mxCell>
<mxCell id="e-dist-biz-lbl" connectable="0" parent="e-dist-biz" 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-dist-school" edge="1" parent="region-complex" source="districts" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="schools">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-dist-school-lbl" connectable="0" parent="e-dist-school" 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-dist-complex" edge="1" parent="region-complex" source="districts" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="complexes">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-dist-complex-lbl" connectable="0" parent="e-dist-complex" 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-biz-join" edge="1" parent="region-complex" source="business-areas" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" target="complex-biz-areas">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-join-complex" edge="1" parent="region-complex" source="complex-biz-areas" 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="e-school-join" edge="1" parent="region-complex" source="schools" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;dashed=1;endArrow=open;startArrow=open;fontSize=9;" target="complex-schools">
<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">
<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">
<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" />
</mxCell>
<mxCell id="e-complex-photos" edge="1" parent="region-complex" source="complexes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#34d399;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="complex-photos">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-complex-photos-lbl" connectable="0" parent="e-complex-photos" 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-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">
<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="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="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="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" />
</mxCell>
<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="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="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="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="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="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="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="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="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="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="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="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">
<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">
<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">
<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">
<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">
<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">
<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">
<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">
<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">
<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">
<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="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="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="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="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="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="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="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">
<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">
<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">
<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">
<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">
<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">
<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" />
</mxCell>
<mxCell id="e-fav-items" edge="1" parent="region-client" source="client-fav-folders" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#fbbf24;endArrow=ERmany;startArrow=ERone;fontSize=9;" target="client-folder-items">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-fav-items-lbl" connectable="0" parent="e-fav-items" 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-complex-prop" edge="1" parent="1" source="complexes" 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;" target="properties">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e-complex-prop-lbl" connectable="0" parent="e-complex-prop" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=9;fontColor=#a78bfa;" value="1:N complex_id" vertex="1">
<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">
<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">
<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">
<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">
<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">
<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">
<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">
<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" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View 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 &amp; COMPLEX region -->
<mxCell id="region-complex" value="REGION &amp; 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 &amp; 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>

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