<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="title" value="多租户隔离机制" style="text;fontSize=18;fontStyle=1;align=center;fillColor=none;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="200" y="10" width="300" height="30" as="geometry"/></mxCell><mxCell id="req" value="浏览器请求" style="rounded=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=12;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="290" y="50" width="120" height="36" as="geometry"/></mxCell><mxCell id="headers" value="Header: X-Hermes-Session-Token + X-Company-ID" style="text;fontSize=10;fontStyle=2;color=#666;" vertex="1" parent="1"><mxGeometry x="200" y="90" width="300" height="16" as="geometry"/></mxCell><mxCell id="arr-1" value="" style="endArrow=classic;html=1;" edge="1" parent="1"><mxGeometry><mxPoint x="350" y="106" as="sourcePoint"/><mxPoint x="350" y="130" as="targetPoint"/></mxGeometry></mxCell><mxCell id="layer1" value="第1层: API 依赖注入 (api/deps.py)" style="rounded=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;fontStyle=1;align=center;verticalAlign=top;" vertex="1" parent="1"><mxGeometry x="40" y="130" width="620" height="100" as="geometry"/></mxCell><mxCell id="l1-1" value="require_session() — secrets.compare_digest 防时序攻击 → HTTP 401" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="158" width="590" height="18" as="geometry"/></mxCell><mxCell id="l1-2" value="require_company() — 解析 company_id → DB 查询验证存在 + is_active → HTTP 401 / 403" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="180" width="590" height="18" as="geometry"/></mxCell><mxCell id="l1-3" value="返回 validated company_id 注入业务层" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="202" width="590" height="18" as="geometry"/></mxCell><mxCell id="arr-2" value="" style="endArrow=classic;html=1;" edge="1" parent="1"><mxGeometry><mxPoint x="350" y="230" as="sourcePoint"/><mxPoint x="350" y="260" as="targetPoint"/></mxGeometry></mxCell><mxCell id="layer2" value="第2层: 业务逻辑 (customer.py / library.py / order.py)" style="rounded=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;fontStyle=1;align=center;verticalAlign=top;" vertex="1" parent="1"><mxGeometry x="40" y="260" width="620" height="110" as="geometry"/></mxCell><mxCell id="l2-1" value="所有 CRUD 强制带 company_id 条件: WHERE id=? AND company_id=?" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="288" width="590" height="18" as="geometry"/></mxCell><mxCell id="l2-2" value="关联操作双重验证: customer.company_id==cid AND library.company_id==cid 才允许 link" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="310" width="590" height="18" as="geometry"/></mxCell><mxCell id="l2-3" value="写操作显式事务: BEGIN → 操作 → COMMIT / ROLLBACK" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="332" width="590" height="18" as="geometry"/></mxCell><mxCell id="l2-4" value="JSON spare column 读取→合并→写回，不整体覆盖" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="354" width="590" height="18" as="geometry"/></mxCell><mxCell id="arr-3" value="" style="endArrow=classic;html=1;" edge="1" parent="1"><mxGeometry><mxPoint x="350" y="370" as="sourcePoint"/><mxPoint x="350" y="400" as="targetPoint"/></mxGeometry></mxCell><mxCell id="layer3" value="第3层: 数据库约束 (SQLite)" style="rounded=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=13;fontStyle=1;align=center;verticalAlign=top;" vertex="1" parent="1"><mxGeometry x="40" y="400" width="620" height="95" as="geometry"/></mxCell><mxCell id="l3-1" value='FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE' style="text;fontSize=11;align=left;fontFamily=monospace;" vertex="1" parent="1"><mxGeometry x="55" y="428" width="590" height="18" as="geometry"/></mxCell><mxCell id="l3-2" value="删除公司→级联删除: trade_companies · libraries · customers · conversations · orders" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="450" width="590" height="18" as="geometry"/></mxCell><mxCell id="l3-3" value="WAL 模式 + sqlite3.Row 工厂 + spare columns (extra1/2/3 TEXT)" style="text;fontSize=11;align=left;" vertex="1" parent="1"><mxGeometry x="55" y="472" width="590" height="18" as="geometry"/></mxCell></root></mxGraphModel>