363 lines
12 KiB
Markdown
363 lines
12 KiB
Markdown
> **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` 对应条目
|