14 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(v1.4)
关联数据模型: DATA_MODEL/DATA_MODEL_CLIENT.md(本方案不重复 DDL)
关联契约规范: TECH_STACK/API_CONTRACT.md(全局 API 契约权威)
关联枚举字典: DATA_MODEL/ENUMS.md
最后更新: 2026-04-27
一、文档定位与边界
本文件聚焦客源模块的:
- 服务边界与模块协作
- API 端点设计(P0/P1 兼容)
- HTMX 局刷与页面分片约定
- 权限与数据范围控制
- 异步任务、缓存、性能与测试映射
不在本文件展开:数据表字段、DDL、索引、触发器。以
DATA_MODEL_CLIENT.md为唯一权威。
二、范围定义(以 TASK.md 为准)
2.1 P0 必须覆盖(Phase 1)
US-CLIENT-001 ~ US-CLIENT-017:
- 新增私客、列表筛选、详情、需求编辑、跟进、带看
- 联系人管理、等级/状态变更、转公客/转成交/转无效
- 相关员工管理
- 自动转公、重复检测
2.2 非 P0(仅预留端点,不实现复杂能力)
- AI 行为解读深度模型
- 新房推荐高级排序引擎
- 公客/成交客完整运营闭环(P1/P2)
三、模块架构边界
3.1 模块职责(apps/client)
- 客源主流程:新增、列表、详情、状态流转
- 客源关联流程:联系人、需求、跟进、带看、智能配房结果
- 质量控制:重复检测、自动转公、操作审计
3.2 外部依赖
| 依赖模块 | 依赖内容 | 依赖方式 |
|---|---|---|
apps/org |
员工、组织树、归属人/首录人 | FK + Service |
apps/property |
配房候选房源、带看房源关联 | 只读查询 + FK |
apps/permission |
角色与数据范围权限 | PermissionChecker + ScopeQueryBuilder |
apps/setting |
可配置枚举(来源、跟进目的)与查重规则 | TenantSettingsService |
core/encryption.py |
手机号/证件号加密与哈希 | 统一工具调用 |
core/cache.py |
列表缓存、重复计数缓存、任务进度 | Redis |
Celery |
自动转公、重复检测统计、配房重算、导出 | 异步任务 |
Cloudflare R2 |
跟进图片、带看附件 | 预签名上传 |
四、API 设计原则
- 页面路由与数据 API 分离:
- 页面:
/client/... - 数据:
/api/client/...
- 页面:
- 列表筛选、Tab 加载、弹窗提交优先 HTMX。
- 手机号明文永不落库;默认脱敏显示。
- 重复检测与自动转公走异步批处理 + 局部实时校验组合。
- 所有状态变更必须写操作日志(不可省略)。
五、端点清单(核心)
5.1 页面路由(SSR)
| 路径 | 方法 | 鉴权 | 说明 |
|---|---|---|---|
/client/list/ |
GET | 是 | 客源列表主页面(私客/公客/成交客 Tab 容器) |
/client/create/ |
GET | 是 | 新增私客页面 |
/client/{client_id}/ |
GET | 是 | 客源详情主页面 |
/client/{client_id}/edit/ |
GET | 是 | 编辑客源页面 |
/client/{client_id}/operation-logs/ |
GET | 是 | 客源操作日志页面 |
5.2 HTMX 片段端点
| 路径 | 方法 | 用途 | 返回 |
|---|---|---|---|
/client/fragments/list-table/ |
POST | 列表筛选/排序/分页局刷 | HTML |
/client/fragments/repeat-counters/ |
GET | 顶部重复统计局刷 | HTML |
/client/{id}/fragments/tab/{tab}/ |
GET | 详情 Tab 懒加载(需求/跟进/带看/配房) | HTML |
/client/{id}/fragments/contact-panel/ |
GET | 联系人面板局刷 | HTML |
/client/{id}/fragments/staff-panel/ |
GET | 相关员工面板局刷 | HTML |
/client/{id}/fragments/follow-timeline/ |
POST | 跟进筛选后局刷 | HTML |
fragment 端点必须校验
HX-Request=true,非 HTMX 请求返回 400。
5.3 JSON API(P0 必需)
| 端点 | 方法 | 权限 code(建议) | 说明 |
|---|---|---|---|
/api/client/ |
POST | client.private.create.allow |
新增私客(US-001) |
/api/client/list/query/ |
POST | client.private.view.scope |
私客列表筛选(US-002) |
/api/client/{id}/detail/ |
GET | client.private.view.scope |
详情聚合(US-004) |
/api/client/{id}/requirements/ |
PATCH | client.private.edit.allow |
编辑需求(US-005) |
/api/client/{id}/follow-logs/ |
POST | client.private.follow.create.allow |
写入跟进(US-006) |
/api/client/{id}/follow-logs/query/ |
POST | client.private.follow.view.scope |
跟进查询(US-006) |
/api/client/{id}/viewings/ |
POST | client.private.viewing.create.allow |
新增带看(US-007) |
/api/client/{id}/viewings/query/ |
POST | client.private.viewing.view.scope |
带看查询(US-007) |
/api/client/{id}/contacts/ |
POST | client.private.contact.create.allow |
新增联系人(US-008) |
/api/client/{id}/contacts/{contact_id}/ |
PATCH | client.private.contact.edit.allow |
编辑联系人(US-008) |
/api/client/{id}/grade/change/ |
POST | client.private.grade.change.allow |
改等级(US-009) |
/api/client/{id}/status/change/ |
POST | client.private.status.change.allow |
改状态(US-010) |
/api/client/{id}/transfer-public/ |
POST | client.private.transfer_public.allow |
手动转公客(US-011) |
/api/client/{id}/transfer-transacted/ |
POST | client.private.transfer_transacted.allow |
转成交(US-012) |
/api/client/{id}/mark-invalid/ |
POST | client.private.mark_invalid.allow |
转无效(US-013) |
/api/client/{id}/base-info/ |
PATCH | client.private.edit.allow |
编辑基础信息(US-014) |
/api/client/{id}/related-staff/ |
PATCH | client.private.related_staff.edit.allow |
修改首录/归属人(US-015) |
/api/client/duplicate/check/ |
POST | client.private.create.allow |
手机号实时重复检测(US-001/017) |
/api/client/duplicate/summary/ |
GET | client.private.view.scope |
重复统计(私客-成交、公客)(US-017) |
/api/client/matches/{id}/query/ |
GET | client.private.view.scope |
智能配房结果查询(US-020预留,P0可简版) |
/api/client/export/jobs/ |
POST | client.private.export.scope |
导出任务创建(US-002) |
/api/client/export/jobs/{job_id}/ |
GET | client.private.export.scope |
导出任务状态 |
/api/client/export/jobs/{job_id}/download/ |
GET | client.private.export.scope |
导出下载 |
六、关键 API 规范(请求/响应)
6.1 新增私客
POST /api/client/
{
"contacts": [
{
"name": "李雷",
"gender": "male",
"phone_country_code": "+86",
"phone": "13800000000",
"phone2": null,
"wechat": "lilei_wechat"
}
],
"base_info": {
"status": "buying",
"property_usage": "residential",
"grade": "B",
"source": "store_reception"
}
}
成功 201:
{
"id": "uuid",
"client_no": "KY202604270001",
"redirect_url": "/client/uuid/",
"message": "保存成功"
}
6.2 列表查询(Keyset)
POST /api/client/list/query/
{
"client_type": "private",
"tab": "buying",
"keyword": "1380000",
"filters": {
"grade": ["A", "B"],
"status": ["buying", "buy_rent"],
"budget": {"min": 200, "max": 500}
},
"sort": {"field": "last_follow_at", "order": "desc"},
"pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
}
6.3 手机号重复检测
POST /api/client/duplicate/check/
{
"phone_country_code": "+86",
"phone": "13800000000",
"scene": "create",
"client_id": null
}
响应:
{
"duplicated": true,
"scope": "dept",
"hits": [
{
"client_id": "uuid",
"client_type": "private",
"owner_name": "王店长",
"created_at": "2026-04-27T10:00:00+08:00"
}
]
}
6.4 状态与转化类接口(统一协议)
- 改状态:
/status/change/ - 转公:
/transfer-public/ - 转成交:
/transfer-transacted/ - 转无效:
/mark-invalid/
统一请求字段:
{
"reason": "客户需求变化",
"payload": {}
}
额外示例(转成交):
{
"reason": "已签约",
"payload": {
"transacted_date": "2026-04-27",
"transacted_price": "365.00",
"transacted_type": "bought",
"property_id": "uuid"
}
}
七、HTMX 交互约定
7.1 Header 约定
- 请求:
HX-Request: true - 成功:
HX-Trigger: {"toast":{"level":"success","message":"操作成功"}} - 失败:
HX-Trigger: {"toast":{"level":"error","message":"操作失败"}} - 跳转:
HX-Redirect: /client/{id}/
7.2 模板分片命名
| 场景 | 模板 |
|---|---|
| 列表 | templates/client/fragments/list_table.html |
| 重复计数条 | templates/client/fragments/repeat_counters.html |
| 跟进时间线 | templates/client/fragments/follow_timeline.html |
| 带看时间线 | templates/client/fragments/viewing_timeline.html |
| 联系人侧栏 | templates/client/fragments/contact_panel.html |
| 相关员工侧栏 | templates/client/fragments/staff_panel.html |
八、权限与数据范围
8.1 最小权限矩阵(P0)
| 能力 | 权限 code |
|---|---|
| 查看私客列表范围 | client.private.view.scope |
| 新增私客 | client.private.create.allow |
| 编辑私客信息 | client.private.edit.allow |
| 写跟进 | client.private.follow.create.allow |
| 查看跟进 | client.private.follow.view.scope |
| 管理联系人 | client.private.contact.create.allow / client.private.contact.edit.allow |
| 改等级/改状态 | client.private.grade.change.allow / client.private.status.change.allow |
| 转公/转成交/转无效 | client.private.transfer_public.allow / client.private.transfer_transacted.allow / client.private.mark_invalid.allow |
| 导出 | client.private.export.scope |
8.2 数据范围叠加逻辑
最终可见数据 = 权限 scope ∩ client_type 过滤 ∩ 业务状态过滤
- 禁止不带
client_type的全量查询 - 私客、公客、成交客必须分流查询
8.3 敏感信息查看审计
查看号码动作必须:
- 校验查看权限与日限额(若配置)
- 仅本次响应返回明文
- 自动写
client_follow_logs(log_type='sensitive_view') sensitive_view记录不可删除
九、异步任务与缓存策略
9.1 状态机与规则
private -> public(手动转公或自动转公)private -> transacted(转成交)private/public/transacted -> invalid(状态字段,不改变历史)
必须通过 service 校验状态机,禁止跳转。
最低规则(P0)
buying <-> buy_rentrenting <-> buy_rentbuying/renting/buy_rent -> pausedpaused -> buying/renting- 任意可跟进状态 ->
invalid(必须填写原因)
9.2 Celery 任务
| 任务 | 触发时机 | 说明 |
|---|---|---|
client_auto_transfer_public_task |
每小时 | 超时未跟进私客自动转公(US-016) |
client_duplicate_summary_task |
每日/按需 | 重复统计(US-017) |
client_match_recompute_task |
需求变更后 | 重新计算配房结果 |
client_export_task |
导出任务创建后 | 异步导出 Excel |
所有任务必须传入
tenant_schema_name并在任务开头切 schema。
9.3 Redis Key 规范
| Key | TTL | 说明 |
|---|---|---|
{schema}:client:list:query:{hash} |
60s | 热门列表筛选缓存 |
{schema}:client:repeat:summary |
300s | 顶部重复计数缓存 |
{schema}:client:detail:{id} |
120s | 详情聚合缓存 |
{schema}:client:export:{job_id} |
24h | 导出任务状态 |
十、性能与可靠性约束
- 客源列表、跟进时间线全部使用 Keyset 分页。
- 高频筛选列建立组合索引(见数据模型文档)。
- 手机号、证件号统一加密 + hash 索引;禁止明文。
- 附件上传限制:
bmp/jpg/jpeg/png/gif,20MB/文件。 - 导出、重复统计、自动转公均走异步,禁止阻塞请求线程。
十一、安全与合规
- 手机号、证件号默认脱敏显示,明文查看需权限与审计。
- 跟进/转化等关键动作必须记录操作者与来源。
- 重复检测接口不得泄露超出权限范围的完整客户信息。
十二、错误码建议
| code | HTTP | 场景 |
|---|---|---|
CLIENT_NOT_FOUND |
404 | 客源不存在或无权限 |
CLIENT_DUPLICATED_PHONE |
409 | 号码重复(按租户规则) |
CLIENT_INVALID_TRANSITION |
400 | 非法状态/类型流转 |
CLIENT_CONTACT_PHONE_INVALID |
400 | 联系人电话格式错误 |
CLIENT_PERMISSION_DENIED |
403 | 权限不足 |
CLIENT_EXPORT_JOB_NOT_READY |
409 | 导出未完成 |
十三、测试映射(P0)
| US | 最低覆盖 |
|---|---|
| US-CLIENT-001 | 新增成功/必填失败/重复检测提示 |
| US-CLIENT-002 | 组合筛选 + Keyset 分页 + 导出 |
| US-CLIENT-003 | 批量操作权限与审计 |
| US-CLIENT-004~006 | 详情/需求/跟进流程 |
| US-CLIENT-007~008 | 带看与联系人新增编辑 |
| US-CLIENT-009~013 | 等级/状态/转公/转成交/转无效 |
| US-CLIENT-014~015 | 编辑完整信息 + 相关员工变更 |
| US-CLIENT-016 | 自动转公定时任务 |
| US-CLIENT-017 | 重复统计任务与查询接口 |
测试落点:tests/integration/client/test_us_client.py
十四、落地顺序建议
- 列表查询 + scope 权限(US-002)
- 新增私客 + 重复检测(US-001/017)
- 详情 + 需求/跟进(US-004~006)
- 联系人/带看(US-007/008)
- 状态与转化(US-009~013)
- 自动任务与导出(US-016/017 + 导出)
十五、文档同步规则
- 枚举变更同步:
DATA_MODEL/ENUMS.md - 权限 code 变更同步:
DATA_MODEL/DATA_MODEL_PERMISSION.md - 新增配置项同步:
DATA_MODEL/DATA_MODEL_SETTING.md - API 变更同步:本文件 + 对应 PRD 验收条目