Files
nexus/Project/fonrey/TECH_STACK/楼盘管理技术方案.md
2026-04-30 20:33:51 +08:00

363 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> **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 APIP0
| 端点 | 方法 | 权限 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` 对应条目