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

423 lines
14 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.4
**关联数据模型**: `DATA_MODEL/DATA_MODEL_CLIENT.md`(本方案不重复 DDL
**关联契约规范**: `TECH_STACK/API_CONTRACT.md`(全局 API 契约权威)
**关联枚举字典**: `DATA_MODEL/ENUMS.md`
**最后更新**: 2026-04-27
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、文档定位与边界
本文件聚焦客源模块的:
1. 服务边界与模块协作
2. API 端点设计P0/P1 兼容)
3. HTMX 局刷与页面分片约定
4. 权限与数据范围控制
5. 异步任务、缓存、性能与测试映射
> **不在本文件展开**数据表字段、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 设计原则
1. 页面路由与数据 API 分离:
- 页面:`/client/...`
- 数据:`/api/client/...`
2. 列表筛选、Tab 加载、弹窗提交优先 HTMX。
3. 手机号明文永不落库;默认脱敏显示。
4. 重复检测与自动转公走异步批处理 + 局部实时校验组合。
5. 所有状态变更必须写操作日志(不可省略)。
---
## 五、端点清单(核心)
## 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 APIP0 必需)
| 端点 | 方法 | 权限 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/`
```json
{
"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`
```json
{
"id": "uuid",
"client_no": "KY202604270001",
"redirect_url": "/client/uuid/",
"message": "保存成功"
}
```
## 6.2 列表查询Keyset
`POST /api/client/list/query/`
```json
{
"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/`
```json
{
"phone_country_code": "+86",
"phone": "13800000000",
"scene": "create",
"client_id": null
}
```
响应:
```json
{
"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/`
统一请求字段:
```json
{
"reason": "客户需求变化",
"payload": {}
}
```
额外示例(转成交):
```json
{
"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 敏感信息查看审计
查看号码动作必须:
1. 校验查看权限与日限额(若配置)
2. 仅本次响应返回明文
3. 自动写 `client_follow_logs(log_type='sensitive_view')`
4. `sensitive_view` 记录不可删除
---
## 九、异步任务与缓存策略
## 9.1 状态机与规则
- `private -> public`(手动转公或自动转公)
- `private -> transacted`(转成交)
- `private/public/transacted -> invalid`(状态字段,不改变历史)
必须通过 service 校验状态机,禁止跳转。
### 最低规则P0
- `buying <-> buy_rent`
- `renting <-> buy_rent`
- `buying/renting/buy_rent -> paused`
- `paused -> 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 | 导出任务状态 |
---
## 十、性能与可靠性约束
1. 客源列表、跟进时间线全部使用 Keyset 分页。
2. 高频筛选列建立组合索引(见数据模型文档)。
3. 手机号、证件号统一加密 + hash 索引;禁止明文。
4. 附件上传限制:`bmp/jpg/jpeg/png/gif`20MB/文件。
5. 导出、重复统计、自动转公均走异步,禁止阻塞请求线程。
---
## 十一、安全与合规
1. 手机号、证件号默认脱敏显示,明文查看需权限与审计。
2. 跟进/转化等关键动作必须记录操作者与来源。
3. 重复检测接口不得泄露超出权限范围的完整客户信息。
---
## 十二、错误码建议
| 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`
---
## 十四、落地顺序建议
1. 列表查询 + scope 权限US-002
2. 新增私客 + 重复检测US-001/017
3. 详情 + 需求/跟进US-004~006
4. 联系人/带看US-007/008
5. 状态与转化US-009~013
6. 自动任务与导出US-016/017 + 导出)
---
## 十五、文档同步规则
- 枚举变更同步:`DATA_MODEL/ENUMS.md`
- 权限 code 变更同步:`DATA_MODEL/DATA_MODEL_PERMISSION.md`
- 新增配置项同步:`DATA_MODEL/DATA_MODEL_SETTING.md`
- API 变更同步:本文件 + 对应 PRD 验收条目