> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked. # Fonrey 楼盘管理技术方案 **版本**: 1.0 **项目**: Fonrey 房产经纪管理系统 **技术栈**: Django 4.x + HTMX + Alpine.js + PostgreSQL 16 + Redis + Celery + Cloudflare R2 **关联 PRD**: `PRD/房源管理/楼盘管理模块PRD.md`(v1.0) **关联数据模型**: `DATA_MODEL/DATA_MODEL_COMPLEX.md`(本方案不重复 DDL) **关联契约规范**: `TECH_STACK/API_CONTRACT.md`(全局 API 契约权威) **关联枚举字典**: `DATA_MODEL/ENUMS.md` **最后更新**: 2026-04-27 --- ## 变更历史 | 日期 | 变更人 | 变更内容 | |---|---|---| | 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) | ## 一、文档定位与边界 本文件只定义楼盘模块的: 1. 服务边界与模块协作 2. API 端点设计(重点) 3. HTMX 局刷协议 4. 权限接入、异步任务、缓存与性能约束 5. 测试与验收映射 > **不在本文件展开**:`complexes/buildings/room_units/districts/business_areas/schools` 表结构、索引、触发器。以 `DATA_MODEL_COMPLEX.md` 为唯一权威来源。 --- ## 二、范围定义(以 TASK.md 为准) ### 2.1 P0 必须覆盖 - US-COMPLEX-001:管理员录入与维护楼盘基础信息 - US-COMPLEX-002:经纪人查看楼盘列表与详情 - US-COMPLEX-003:管理员维护区域管理(城区/商圈) ### 2.2 P1/P2 预留(端点可预留,不在当前强制交付) - US-COMPLEX-010:楼盘照片管理 - US-COMPLEX-011:楼盘价格走势维护 - US-COMPLEX-012:周边配套/学校管理 - US-COMPLEX-020:应用数据标准 --- ## 三、模块架构边界 ## 3.1 模块职责(`apps/complex`) - 维护楼盘基础档案(楼盘、别名、商圈、学校、地铁关联) - 维护楼栋与房号结构(供房源录入精准归位) - 提供楼盘搜索、联想、详情聚合与区域筛选能力 - 承担区域与学校主数据管理(P0 侧重城区/商圈) ## 3.2 外部依赖 | 依赖模块 | 依赖内容 | 依赖方式 | |---|---|---| | `apps/property` | 房源数量统计、楼盘详情联动跳转 | 只读聚合查询 | | `apps/client` | 客源区域筛选项复用(城区/商圈) | 读取共享主数据 | | `apps/org` | 操作人信息审计 | staff_id 写审计 | | `apps/permission` | 楼盘查看/编辑/区域维护权限校验 | PermissionChecker | | `core/cache.py` | 楼盘联想缓存、区域字典缓存 | Redis key 带 schema 前缀 | | `Celery` | 完整度重算、价格趋势重算、批量任务 | 异步任务 | | 地图服务(后续) | 周边配套查询、坐标纠偏 | 适配器层(P1) | ## 3.3 分层约束 - `views.py` 仅做参数校验、鉴权、响应拼装 - 业务规则(锁校验、命名约束、合并规则)全部下沉 `services/` - 任何写操作必须记录审计日志(操作人、前后值、时间) - 耗时 >500ms 的批量操作与重算流程必须异步化 --- ## 四、API 设计原则 1. 页面路由与数据 API 分离: - 页面:`/complex/...` - 数据:`/api/complex/...` 2. 列表筛选、Tab 切换、矩阵加载优先 HTMX 局刷。 3. 楼盘名称/地址修改遵循数据治理规则: - `name` 不允许普通编辑页直接改 - `address` 走纠错流程 4. 锁定状态前置校验:`lock_info/lock_building/lock_room/lock_standard_room`。 5. 统一错误协议: - JSON: `{"error":"...","code":"..."}` - HTMX: 片段 + `HX-Trigger` toast --- ## 五、端点清单(核心) ## 5.1 页面路由(SSR + HTMX 容器) | 路径 | 方法 | 鉴权 | 说明 | |---|---|---|---| | `/complex/list/` | GET | 是 | 楼盘列表主页面(含完整度面板与筛选容器) | | `/complex/create/` | GET | 是 | 新增楼盘页面 | | `/complex/{complex_id}/` | GET | 是 | 楼盘详情主页面(多 Tab 容器) | | `/complex/{complex_id}/edit/` | GET | 是 | 编辑楼盘页面 | | `/complex/region/` | GET | 是 | 区域管理(城区/商圈)页面 | ## 5.2 HTMX 片段端点 | 路径 | 方法 | 用途 | 返回 | |---|---|---|---| | `/complex/fragments/list-table/` | POST | 列表筛选/排序/分页局刷 | HTML | | `/complex/fragments/completeness-panel/` | GET | 顶部完整度指标面板局刷 | HTML | | `/complex/{id}/fragments/tab/{tab}/` | GET | 详情 Tab 懒加载 | HTML | | `/complex/{id}/fragments/building-list/` | POST | 楼栋管理列表局刷 | HTML | | `/complex/{id}/fragments/structure-matrix/` | POST | 结构矩阵局刷 | HTML | | `/complex/region/fragments/district-table/` | POST | 城区列表局刷 | HTML | | `/complex/region/fragments/business-area-table/` | POST | 商圈列表局刷 | HTML | > fragment 端点必须校验 `HX-Request=true`,否则返回 400。 ## 5.3 JSON API(P0) | 端点 | 方法 | 权限 code(建议) | 说明 | |---|---|---|---| | `/api/complex/` | POST | `complex.base.create.allow` | 新增楼盘(US-001) | | `/api/complex/list/query/` | POST | `complex.base.view.scope` | 楼盘列表查询(US-002) | | `/api/complex/{id}/detail/` | GET | `complex.base.view.scope` | 楼盘详情聚合(US-002) | | `/api/complex/{id}/base-info/` | PATCH | `complex.base.edit.allow` | 编辑楼盘基础信息(US-001) | | `/api/complex/{id}/lock/release/` | POST | `complex.lock.release.allow` | 解锁楼盘(锁权限) | | `/api/complex/{id}/buildings/query/` | POST | `complex.base.view.scope` | 楼栋列表查询 | | `/api/complex/{id}/buildings/` | POST | `complex.building.edit.allow` | 新增楼栋 | | `/api/complex/{id}/room-units/query/` | POST | `complex.base.view.scope` | 结构矩阵查询 | | `/api/complex/regions/districts/query/` | POST | `complex.region.manage.allow` | 城区列表查询(US-003) | | `/api/complex/regions/districts/` | POST | `complex.region.manage.allow` | 新增城区(US-003) | | `/api/complex/regions/districts/{id}/` | PATCH | `complex.region.manage.allow` | 编辑城区 | | `/api/complex/regions/business-areas/query/` | POST | `complex.region.manage.allow` | 商圈列表查询(US-003) | | `/api/complex/regions/business-areas/` | POST | `complex.region.manage.allow` | 新增商圈(US-003) | | `/api/complex/regions/business-areas/{id}/` | PATCH | `complex.region.manage.allow` | 编辑商圈 | | `/api/complex/regions/business-areas/merge/` | POST | `complex.region.manage.allow` | 合并商圈 | | `/api/complex/regions/districts/merge/` | POST | `complex.region.manage.allow` | 合并城区 | --- ## 六、关键 API 规范(请求/响应) ## 6.1 新增楼盘 `POST /api/complex/` ```json { "name": "万科城市花园", "district_id": "uuid", "address": "上海市闵行区XX路XX号", "address_summary": "XX路XX弄", "property_usage_types": ["residential"], "building_structure": "unit_room", "business_area_ids": ["uuid1", "uuid2"], "school_ids": ["uuid3"] } ``` 成功 `201`: ```json { "id": "uuid", "message": "保存成功", "redirect_url": "/complex/uuid/" } ``` ## 6.2 楼盘列表查询 `POST /api/complex/list/query/` ```json { "keyword": "万科", "filters": { "district_ids": ["uuid"], "usage_types": ["residential"], "has_coordinate": true }, "sort": {"field": "updated_at", "order": "desc"}, "pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20} } ``` ## 6.3 编辑楼盘基础信息 `PATCH /api/complex/{id}/base-info/` ```json { "address_summary": "海波路1000弄", "built_years": [2005, 2008], "ownership_category": ["commodity_residential"], "remarks": "已校验最新物业信息" } ``` 规则: - 若 `lock_info=true` 且无解锁权限,返回 403 - `name` 字段不允许通过该接口修改,返回 400 ## 6.4 新增商圈 `POST /api/complex/regions/business-areas/` ```json { "district_id": "uuid", "name": "江桥新城" } ``` 规则: - `district_id + name` 唯一 - 商圈必须归属城区 --- ## 七、HTMX 交互约定 ## 7.1 Header 约定 - 请求:`HX-Request: true` - 成功:`HX-Trigger: {"toast":{"level":"success","message":"操作成功"}}` - 失败:`HX-Trigger: {"toast":{"level":"error","message":"操作失败"}}` - 跳转:`HX-Redirect: /complex/{id}/` ## 7.2 模板分片命名 | 场景 | 模板 | |---|---| | 楼盘列表 | `templates/complex/fragments/list_table.html` | | 完整度面板 | `templates/complex/fragments/completeness_panel.html` | | 楼栋列表 | `templates/complex/fragments/building_list.html` | | 结构矩阵 | `templates/complex/fragments/structure_matrix.html` | | 城区列表 | `templates/complex/fragments/district_table.html` | | 商圈列表 | `templates/complex/fragments/business_area_table.html` | --- ## 八、权限与数据范围 ## 8.1 最小权限矩阵(P0 建议) | 能力 | 权限 code | |---|---| | 查看楼盘列表/详情范围 | `complex.base.view.scope` | | 新增/编辑楼盘基础信息 | `complex.base.create.allow` / `complex.base.edit.allow` | | 解锁楼盘 | `complex.lock.release.allow` | | 楼栋与结构维护 | `complex.building.edit.allow` | | 区域管理(城区/商圈) | `complex.region.manage.allow` | ## 8.2 数据域规则 最终可见数据 = `scope 过滤` ∩ `业务过滤`(启用状态、软删过滤) - 所有查询必须 `deleted_at IS NULL` - 区域管理默认只展示 `is_active=true`,可选查看停用项 --- ## 九、异步任务与缓存策略 ## 9.1 Celery 任务 | 任务 | 触发时机 | 说明 | |---|---|---| | `complex_completeness_recalc_task` | 数据管理员点击“重新计算” | 重算完整度指标面板 | | `complex_price_trend_refresh_task` | 夜间定时 | 价格走势数据聚合(T+1) | | `complex_merge_cleanup_task` | 合并楼盘后 | 处理关联表与索引重建 | > 任务参数必须包含 `tenant_schema_name`,任务开头显式切 schema。 ## 9.2 Redis Key 规范 | Key | TTL | 说明 | |---|---|---| | `{schema}:complex:list:query:{hash}` | 60s | 热门筛选缓存 | | `{schema}:complex:detail:{id}` | 120s | 详情聚合缓存 | | `{schema}:complex:completeness:panel` | 300s | 完整度指标缓存 | | `{schema}:complex:region:districts` | 300s | 城区字典缓存 | | `{schema}:complex:region:business_areas:{district_id}` | 300s | 商圈字典缓存 | --- ## 十、性能与可靠性约束 1. 列表查询强制 Keyset 分页,禁止 OFFSET 大页深翻。 2. 楼盘检索优先使用 `search_vector` + `trgm` 索引。 3. 详情页按 Tab 懒加载,避免一次拉全量聚合。 4. 合并与批量改动使用事务;失败必须回滚。 5. 区域与楼盘维护接口需限流,防止误操作风暴。 --- ## 十一、安全与合规 - 楼盘锁定状态必须在服务层强校验,不允许绕过。 - 所有写操作记录审计:操作人、IP、前后值。 - 地图类外部 API 调用不得暴露密钥到前端。 - 删除有关联数据(楼盘、区域)必须返回阻断型错误提示。 --- ## 十二、错误码建议 | code | HTTP | 场景 | |---|---|---| | `COMPLEX_NOT_FOUND` | 404 | 楼盘不存在或无权限 | | `COMPLEX_LOCKED_INFO` | 403 | 楼盘信息锁定不可编辑 | | `COMPLEX_NAME_EDIT_FORBIDDEN` | 400 | 禁止直接修改楼盘名称 | | `COMPLEX_REGION_REQUIRED` | 400 | 商圈未归属城区 | | `COMPLEX_REGION_DUPLICATED` | 409 | 城区/商圈重名冲突 | | `COMPLEX_PERMISSION_DENIED` | 403 | 权限不足 | --- ## 十三、测试映射 ### 13.1 P0 User Story 映射 | User Story | 最低覆盖 | |---|---| | US-COMPLEX-001 | 新增/编辑楼盘成功、必填校验、锁定校验 | | US-COMPLEX-002 | 列表关键词筛选、分页、详情 Tab 懒加载 | | US-COMPLEX-003 | 城区商圈新增编辑、归属约束、冲突校验 | 测试文件:`tests/integration/complex/test_us_complex.py` ### 13.2 强制测试约束 - 集成测试使用 `TenantClient` - HTMX 请求携带 `HTTP_HX_REQUEST=true` - 权限三态覆盖:200 / 403 / 302 - 外部服务(地图/R2/Redis)全部 mock --- ## 十四、落地顺序建议 1. 先打通楼盘列表查询 + 详情聚合(US-COMPLEX-002) 2. 再完成新增/编辑与锁校验(US-COMPLEX-001) 3. 最后完成区域管理(US-COMPLEX-003) 4. P1 再接照片、价格走势、学校管理 --- ## 十五、文档同步规则 - 枚举变更:同步 `DATA_MODEL/ENUMS.md` - 权限 code 变更:同步 `DATA_MODEL/DATA_MODEL_PERMISSION.md` - 数据结构变更:同步 `DATA_MODEL/DATA_MODEL_COMPLEX.md` - API 变更:同步本文件与 `PRD/TASK.md` 对应条目