- 新增 PRD/系统配置/系统配置模块PRD.md(v0.1 Draft) - MVP 范围:US-SETTING-001-A(Lookup Items)、B(房源字段必填规则)、C(客源录入规则) - 新增 PRD/系统配置/系统配置数据模型设计说明_for_Atlas.md - 新增 PRD/系统配置/系统配置参数数据.md(竞品参数数据) - 删除旧版 PRD/系统配置/系统配置.md(已被新PRD替代) - 新增 DATA_MODEL/DATA_MODEL_SETTING.md(系统配置数据模型) - 新增 DATA_MODEL/ENUMS.md(枚举定义与约定) - 新增 AGENTS.md(AI Agent 开发规范) - 更新 PRD/TASK.md:US-SETTING-001 拆分为 A/B/C 三个子任务,修正参考文档路径与验收标准 - 新增 VIBE_CODING_开工前缺失清单.md - 新增 TECH_STACK/房源管理技术方案.md - 更新 DATA_MODEL/DATA_MODEL.md、DATA_MODEL_CLIENT.md、DATA_MODEL_LOGIN.md - 更新 PRD/PRD_MVP.md、PRD/权限管理/权限管理模块PRD.md - 更新 TECH_STACK/TECH_STACK.md、权限管理系统技术方案.md - 更新 UI_DESIGN/preview.html、UI_SYSTEM/UI_SYSTEM.md - 新增 prompt/PRD - 为系统设置生成PRD设计文档.md、更新 prompt 模板
16 KiB
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(v2.1)
关联数据模型: DATA_MODEL/DATA_MODEL_PROPERTY.md(本方案不重复 DDL)
关联枚举字典: DATA_MODEL/ENUMS.md
最后更新: 2026-04-27
一、文档定位与边界
本文件只定义房源模块的:
- 服务边界与模块协作
- API 端点设计(重点)
- HTMX 局刷协议
- 权限接入、异步任务、缓存与性能约束
- 测试与验收映射
不在本文件展开:表结构字段、索引、DDL、触发器。请以
DATA_MODEL_PROPERTY.md为唯一权威来源。
二、P0 范围(与 Task Board 对齐)
本方案覆盖 PRD/TASK.md 中房源 P0 User Story:
- US-PROPERTY-001:录入二手住宅(出售/出租)
- US-PROPERTY-002:查看与筛选房源列表
- US-PROPERTY-003:查看房源详情
- US-PROPERTY-004:写入与查看跟进记录
- US-PROPERTY-005:管理房源图片(上传/分类/排序)
- US-PROPERTY-006:管理业主联系人
- US-PROPERTY-007:调整房源价格
- US-PROPERTY-008:变更房源状态
三、模块架构边界
3.1 模块职责
apps/property 负责:
- 房源主流程:新增、列表检索、详情展示、状态与价格维护
- 房源协作数据:联系人、跟进、图片
- 房源审计轨迹:挂牌历史、调价记录、敏感查看记录(通过服务层写入)
3.2 外部依赖
| 依赖模块 | 依赖内容 | 依赖方式 |
|---|---|---|
apps/complex |
小区/楼盘基础信息、联想搜索 | Service 调用 + 外键引用 |
apps/org |
员工组织信息(相关方、操作人) | 外键 + Service 查询 |
apps/permission |
RBAC/Scope 权限判定 | PermissionChecker + ScopeQueryBuilder |
apps/setting |
可配置枚举(来源、跟进目的等) | lookup_items 读取缓存 |
core/encryption.py |
手机号加密/脱敏/哈希 | 统一工具调用,禁止明文 |
core/cache.py |
筛选缓存、任务进度、短期详情缓存 | Redis Key 带租户前缀 |
Celery |
导出、图片处理、完成度重算 | 异步任务 |
Cloudflare R2 |
房源图片与附件对象存储 | 预签名上传 + 回写元数据 |
3.3 分层约束(必须遵守)
views.py仅做:参数校验、权限门禁、调用 service、组织响应- 业务规则全部下沉到
services/ - 所有写操作必须落审计(至少 follow_logs 或 change_logs)
- 耗时 >500ms 的流程必须异步化
四、API 设计总原则(P0)
- 页面端点与数据端点分离:
- 页面(SSR + HTMX 容器):
/property/... - 数据 API(JSON):
/api/property/...
- 页面(SSR + HTMX 容器):
- HTMX 局刷优先:列表筛选、Tab 内容、弹窗提交走局部刷新。
- 列表性能目标:89k 房源规模下,
p95 < 2s(索引 + Keyset 分页 + 预加载)。 - 统一错误协议:
- JSON:
{"error":"...","code":"SNAKE_CASE"} - HTMX:返回片段 +
HX-TriggerToast
- JSON:
- 权限前置:所有 API 在 service 执行前完成权限与 scope 过滤。
- 敏感信息最小暴露:默认打码;明文查看必须具备权限并记录审计。
五、端点清单(核心)
5.1 页面路由(SSR + HTMX 容器)
| 路径 | 方法 | 鉴权 | 说明 |
|---|---|---|---|
/property/list/ |
GET | 是 | 房源列表主页面(包含筛选栏与列表容器) |
/property/create/ |
GET | 是 | 新增房源页面(P0: 住宅) |
/property/{property_id}/ |
GET | 是 | 房源详情主页面(多 Tab 容器) |
/property/{property_id}/edit/ |
GET | 是 | 编辑房源页面(非弹窗) |
5.2 HTMX 片段端点
| 路径 | 方法 | 用途 | 返回 |
|---|---|---|---|
/property/fragments/list-table/ |
GET | 列表筛选/排序/翻页局刷 | HTML 片段 |
/property/fragments/filter-panel/ |
GET | 筛选项联动刷新(区域→商圈等) | HTML 片段 |
/property/{id}/fragments/tab/{tab_name}/ |
GET | 详情页 Tab 懒加载(跟进/相册/附件等) | HTML 片段 |
/property/{id}/fragments/contact-panel/ |
GET | 联系人侧栏局刷 | HTML 片段 |
/property/{id}/fragments/follow-timeline/ |
GET | 跟进时间线局刷(筛选后) | HTML 片段 |
/property/{id}/fragments/photo-grid/ |
GET | 相册宫格局刷 | HTML 片段 |
所有 fragment 端点必须校验
HX-Request=true,非 HTMX 请求返回 400。
5.3 JSON API(P0)
| 端点 | 方法 | 鉴权 | 权限码(建议) | 说明 |
|---|---|---|---|---|
/api/property/ |
POST | 是 | property.create.allow |
新增房源(US-001) |
/api/property/list/query/ |
POST | 是 | property.list.view.scope |
列表查询(US-002) |
/api/property/{id}/detail/ |
GET | 是 | property.list.view.scope |
详情聚合数据(US-003) |
/api/property/{id}/status/change/ |
POST | 是 | property.status.change.allow |
改状态(US-008) |
/api/property/{id}/price/change/ |
POST | 是 | property.price.change.allow |
调价(US-007) |
/api/property/{id}/follow-logs/ |
POST | 是 | property.follow.create.allow |
新增跟进(US-004) |
/api/property/{id}/follow-logs/query/ |
POST | 是 | property.follow.view.scope |
跟进查询(US-004) |
/api/property/{id}/contacts/ |
POST | 是 | property.contact.create.allow |
新增联系人(US-006) |
/api/property/{id}/contacts/{contact_id}/ |
PATCH | 是 | property.contact.edit.allow |
编辑联系人(US-006) |
/api/property/{id}/contacts/same-owner/ |
GET | 是 | property.list.view.scope |
同业主其他房源(US-006) |
/api/property/{id}/owner-phone/view/ |
POST | 是 | property.owner_phone.view.daily_limit |
查看号码(审计+额度) |
/api/property/{id}/photos/upload-token/ |
POST | 是 | property.photo.upload.allow |
获取 R2 预签名(US-005) |
/api/property/{id}/photos/commit/ |
POST | 是 | property.photo.upload.allow |
上传回执写库(US-005) |
/api/property/{id}/photos/reorder/ |
POST | 是 | property.photo.edit.allow |
图片排序(US-005) |
/api/property/{id}/photos/category/batch/ |
POST | 是 | property.photo.edit.allow |
批量改分类(US-005) |
/api/property/{id}/photos/{photo_id}/set-cover/ |
POST | 是 | property.photo.edit.allow |
设封面(US-005) |
/api/property/export/jobs/ |
POST | 是 | property.list.export.scope |
创建导出任务(US-002) |
/api/property/export/jobs/{job_id}/ |
GET | 是 | property.list.export.scope |
查询导出任务状态 |
/api/property/export/jobs/{job_id}/download/ |
GET | 是 | property.list.export.scope |
下载导出结果 |
六、关键 API 规范(请求/响应)
6.1 新增房源
POST /api/property/
{
"property_type": "residential",
"trade_type": "sale_rent",
"complex_id": "uuid",
"building_no": "5",
"unit_no": "2",
"room_no": "1102",
"floor_current": 11,
"floor_total": 33,
"area": "89.50",
"layout": {"bedroom": 3, "living": 2, "bathroom": 2, "kitchen": 1, "balcony": 1},
"sale_price": "368.00",
"rent_price": null,
"owner_contacts": [
{
"name": "张三",
"identity": "owner",
"phone": "13800000000",
"gender": "male"
}
],
"related_staff": {
"first_agent_id": "uuid",
"number_agent_id": "uuid",
"sale_agent_id": "uuid"
}
}
成功:201
{
"id": "uuid",
"code": "FY202604270001",
"redirect_url": "/property/uuid/",
"message": "保存成功"
}
6.2 列表查询
POST /api/property/list/query/
{
"keyword": "阳光花园",
"filters": {
"district_ids": ["uuid"],
"status": ["sale", "rent"],
"attribute": ["public"],
"price_sale": {"min": 200, "max": 500}
},
"sort": {"field": "updated_at", "order": "desc"},
"pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
}
成功:200
{
"items": [{"id": "uuid", "title": "阳光花园 5-2-1102", "status": "sale"}],
"next_cursor": "opaque_cursor_2",
"total_estimate": 89432
}
6.3 改状态
POST /api/property/{id}/status/change/
{
"from_status": "sale",
"to_status": "paused",
"reason": "业主暂不出售"
}
校验规则:
- 必须符合状态机(例如 sale -> paused/other_sale/deal)
- reason 必填,<= 50 字
6.4 调价
POST /api/property/{id}/price/change/
{
"sale_price": "350.00",
"bottom_price": "338.00",
"reason": "业主急售"
}
成功后联动:
- 更新当前挂牌价
- 追加
price_changes记录 - 触发详情页价格区域局刷
6.5 写跟进
POST /api/property/{id}/follow-logs/
{
"follow_type": "write_follow",
"purpose": "owner_follow",
"content": "今日电话沟通,业主接受350万附近报价。",
"visibility": "team",
"attachments": ["object_key_1", "object_key_2"]
}
校验规则:
- content: 6~500 字
- 附件图片最多 10 张,单张 <=20MB
6.6 导出任务
- 创建:
POST /api/property/export/jobs/
{"query_snapshot": {...}, "columns": ["code", "title", "status", "sale_price"]}
- 查询:
GET /api/property/export/jobs/{job_id}/
{"status":"running", "progress":65}
- 下载:
GET /api/property/export/jobs/{job_id}/download/
- 任务完成后返回一次性下载 URL
七、HTMX 交互约定(房源模块)
7.1 请求头与响应头
- 请求必须带:
HX-Request: true - 成功提示:
HX-Trigger: {"toast":{"level":"success","message":"保存成功"}} - 失败提示:
HX-Trigger: {"toast":{"level":"error","message":"保存失败"}} - 跳转:
HX-Redirect: /property/{id}/
7.2 片段模板命名
| 场景 | 模板 |
|---|---|
| 列表表格 | templates/property/fragments/list_table.html |
| 跟进时间线 | templates/property/fragments/follow_timeline.html |
| 联系人面板 | templates/property/fragments/contact_panel.html |
| 相册宫格 | templates/property/fragments/photo_grid.html |
7.3 推荐前端触发方式
- 筛选提交:
hx-post="/property/fragments/list-table/" - 切 Tab:
hx-get="/property/{id}/fragments/tab/follow/" - 弹窗提交:
hx-post+hx-target当前弹窗容器,成功后触发父容器刷新
八、权限与数据范围设计
8.1 P0 必需权限项(最小集合)
与
DATA_MODEL_PERMISSION.md对齐;若permission_defs尚未落库,则按下列 code 补种子。
| 权限 code | 类型 | 说明 |
|---|---|---|
property.list.view.scope |
SCOPE | 查看房源范围 |
property.list.export.scope |
SCOPE | 导出房源范围 |
property.create.allow |
BOOLEAN | 新增房源 |
property.list.edit.allow |
BOOLEAN | 编辑房源基础信息 |
property.price.change.allow |
BOOLEAN | 调价 |
property.status.change.allow |
BOOLEAN | 改状态 |
property.follow.view.scope |
SCOPE | 查看跟进范围 |
property.follow.create.allow |
BOOLEAN | 新增跟进 |
property.contact.create.allow |
BOOLEAN | 新增联系人 |
property.contact.edit.allow |
BOOLEAN | 编辑联系人 |
property.photo.upload.allow |
BOOLEAN | 上传图片 |
property.photo.edit.allow |
BOOLEAN | 改分类/排序/封面 |
property.owner_phone.view.daily_limit |
INTEGER | 每日查看号码上限(0=不限制) |
8.2 Scope 与业务属性叠加
最终查询范围 = 权限 scope 过滤 ∩ 业务状态过滤 ∩ 房源属性规则(公盘/私盘/特盘/封盘)
- 权限系统不直接改写
properties.attribute - 房源属性由业务规则决定可见性,权限只决定“理论上可看范围”
8.3 查看号码审计
调用 /owner-phone/view/ 时必须:
- 校验
daily_limit - 解密返回明文(仅本次)
- 自动写入
follow_logs(sensitive_view) sensitive_view记录不可删除
九、异步任务与缓存策略
9.1 Celery 任务
| 任务 | 触发时机 | 说明 |
|---|---|---|
property_export_task |
创建导出任务后 | 按查询快照导出 Excel 到 R2 |
property_photo_postprocess_task |
图片上传 commit 后 | 生成缩略图、提取元数据 |
property_completeness_recalc_task |
调价/状态变更/跟进/图片更新后 | 异步重算维护完成度 |
所有任务参数必须包含
tenant_schema_name,任务开头显式切 schema。
9.2 Redis Key 规范
| Key | TTL | 说明 |
|---|---|---|
{schema}:property:list:query:{hash} |
60s | 热门筛选结果短缓存 |
{schema}:property:detail:{id} |
120s | 详情聚合缓存 |
{schema}:property:export:{job_id} |
24h | 导出任务状态 |
{schema}:property:owner_phone:view:{staff_id}:{date} |
24h | 每日查看号码计数 |
十、性能与可靠性约束
- 列表查询强制 Keyset 分页(禁止 OFFSET)。
- 所有筛选字段必须走已建索引(见
DATA_MODEL_PROPERTY.md)。 - 大列表默认返回精简字段,详情页再按 Tab 懒加载。
- 导出走异步任务,前端轮询任务状态,禁止同步导出。
- 批量写操作使用事务,失败要回滚并返回结构化错误。
十一、安全与合规
- 手机号、微信等敏感信息:入库加密、展示脱敏
- API 全链路 HTTPS
- 操作审计必须包含:操作者、时间、旧值/新值、来源 IP
- 文件上传白名单:
bmp/jpg/jpeg/png/gif/svg(P0 与 PRD 对齐) - 上传大小限制:20MB/文件
- 防刷:列表查询、号码查看、导出任务创建均需限流
十二、错误码约定(房源模块)
| code | HTTP | 场景 |
|---|---|---|
PROPERTY_NOT_FOUND |
404 | 房源不存在或无访问权限 |
PROPERTY_INVALID_STATE_TRANSITION |
400 | 非法状态流转 |
PROPERTY_PRICE_INVALID |
400 | 价格参数非法 |
PROPERTY_FOLLOW_CONTENT_TOO_SHORT |
400 | 跟进内容不足 6 字 |
PROPERTY_PHONE_VIEW_LIMIT_EXCEEDED |
429 | 查看号码超限 |
PROPERTY_EXPORT_JOB_NOT_READY |
409 | 导出未完成即下载 |
PROPERTY_PERMISSION_DENIED |
403 | 权限不足 |
十三、测试映射(P0)
| User Story | 最低测试覆盖 |
|---|---|
| US-PROPERTY-001 | 新增成功 / 必填校验失败 / 无权限403 |
| US-PROPERTY-002 | 关键词+组合筛选 / Keyset 分页 / 导出任务创建 |
| US-PROPERTY-003 | 详情加载 / 号码默认脱敏 / 查看号码审计 |
| US-PROPERTY-004 | 跟进写入成功 / 内容长度校验 / 时间线筛选 |
| US-PROPERTY-005 | 上传签名获取 / commit 落库 / 排序与封面设置 |
| US-PROPERTY-006 | 联系人新增编辑 / 同业主房源查询 |
| US-PROPERTY-007 | 调价成功并写历史 / 理由缺失失败 |
| US-PROPERTY-008 | 合法流转成功 / 非法流转失败 |
测试强制要求:
- 集成测试使用
TenantClient - HTMX 请求必须带
HTTP_HX_REQUEST=true - 权限三态:200 / 403 / 302
- Celery 在测试环境 eager 执行
十四、落地顺序建议(供开发阶段使用)
- 先搭建列表查询 + Scope 权限过滤(US-002)
- 再打通新增与详情主链路(US-001,003)
- 上线状态变更与调价(US-007,008)
- 补齐跟进、联系人、图片(US-004,005,006)
- 最后接入导出异步与性能压测
十五、与其他文档的同步规则
- 枚举变更:同步
DATA_MODEL/ENUMS.md - 权限 code 变更:同步
DATA_MODEL/DATA_MODEL_PERMISSION.md - API 变更:同步本文件与对应 PRD 验收条目
- 超出 P0 的能力(如地图找房、商业地产)只能写“预留”,不得提前实现