chore: sync local project changes
This commit is contained in:
@@ -1942,6 +1942,134 @@
|
||||
|
||||
---
|
||||
|
||||
## 6. 技术考量
|
||||
|
||||
### 6.0 核心技术设计
|
||||
|
||||
#### 6.0.1 客源状态机
|
||||
|
||||
客源在生命周期内的状态流转是系统的核心业务逻辑,必须在后端通过状态机严格控制,禁止跨状态直接跳转。
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ ▼
|
||||
[录入] ──► 私客(求购/求租/暂缓)──► 公客 ──► 成交客
|
||||
│ │
|
||||
│ 手动转公/自动掉公 │ 转私客(若有权限)
|
||||
│ │
|
||||
▼ ▼
|
||||
无效客 无效客
|
||||
```
|
||||
|
||||
| 状态 | 允许流转目标 | 触发方式 |
|
||||
|------|------------|---------|
|
||||
| 私客(求购) | 求租、暂缓、公客、成交客、无效 | 手动操作 / 掉公规则 |
|
||||
| 私客(求租) | 求购、暂缓、公客、成交客、无效 | 手动操作 / 掉公规则 |
|
||||
| 私客(暂缓) | 求购、求租、公客、无效 | 手动操作 |
|
||||
| 公客 | 无效 | 手动操作(暂不支持公转私) |
|
||||
| 成交客 | — | 终态,不可流转 |
|
||||
| 无效客 | — | 终态,不可流转 |
|
||||
|
||||
**实现要求**:
|
||||
- 所有状态变更须通过后端 API 的状态机校验,前端不直接修改状态字段
|
||||
- 每次状态变更自动写入 `ClientStatusChangeLog` 审计表(变更人、变更前后状态、时间、理由)
|
||||
- 状态变更失败须返回明确错误码,前端展示友好提示
|
||||
|
||||
---
|
||||
|
||||
#### 6.0.2 重复客源检测算法
|
||||
|
||||
重复检测是客源管理的核心数据质量保障。采用**异步检测 + 前端提示**策略,不阻塞录入流程。
|
||||
|
||||
**检测触发时机**:
|
||||
1. 录入时:提交表单后,后端异步触发检测,结果以橙色提示横幅展示
|
||||
2. 编辑手机号时:号码变更保存后重新触发
|
||||
3. 批量导入后:全量扫描,生成重复报告
|
||||
|
||||
**去重匹配规则(按优先级)**:
|
||||
|
||||
| 优先级 | 匹配维度 | 匹配逻辑 | 说明 |
|
||||
|--------|---------|---------|------|
|
||||
| P0 | 手机号精确匹配 | `phone_normalized = normalize(input_phone)` | 去除空格、+86、国际区号后精确比对 |
|
||||
| P1 | 手机号模糊匹配 | 去除格式差异后匹配 | 处理 `135****8888` 与 `13500008888` 等录入差异 |
|
||||
| P2 | 姓名 + 意向商圈组合 | 同名 + 相同意向区域 | 辅助人工判断,不自动合并 |
|
||||
|
||||
**去重处理规则**:
|
||||
- **私客与私客重复**:提示「该手机号已存在于 [员工姓名] 名下私客,请确认是否为同一客户」,由操作人决定是否继续录入或转为该员工协作方
|
||||
- **私客与公客重复**:提示「该手机号已在公客池中,请确认业务归属后操作」
|
||||
- **合并客户**:合并以**首录时间更早**的记录为主数据,跟进记录、带看记录全部合并至主记录,被合并记录保留为「已合并」历史副本,不可恢复
|
||||
|
||||
**数据模型**:
|
||||
```python
|
||||
class ClientDuplicateLog(TenantModel):
|
||||
source_client = ForeignKey(Client, related_name='duplicate_source')
|
||||
target_client = ForeignKey(Client, related_name='duplicate_target')
|
||||
match_type = CharField(choices=['phone_exact', 'phone_fuzzy', 'name_area'])
|
||||
detected_at = DateTimeField(auto_now_add=True)
|
||||
resolved_by = ForeignKey(User, null=True)
|
||||
resolution = CharField(choices=['merged', 'ignored', 'pending'])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6.0.3 私客掉公(自动转公)机制
|
||||
|
||||
掉公规则由运营配置,初期建议默认值如下(待业务确认后写入系统配置模块):
|
||||
|
||||
| 触发条件 | 默认值 | 是否可配置 |
|
||||
|---------|--------|----------|
|
||||
| 私客无跟进天数 | 30 天 | 是(系统配置模块) |
|
||||
| 触发检测频率 | 每日凌晨 02:00 Celery 定时任务 | 否 |
|
||||
| 提前预警天数 | 到期前 3 天,系统消息通知归属人 | 是 |
|
||||
|
||||
**掉公流程**:
|
||||
```
|
||||
Celery Beat(每日 02:00)
|
||||
└─► 扫描:last_follow_up_at < NOW() - 掉公天数 AND status='private'
|
||||
└─► 批量更新状态为 'public'
|
||||
└─► 写入 ClientStatusChangeLog(operator='system')
|
||||
└─► 触发消息推送:「您的 N 个客源已自动转入公客池」
|
||||
```
|
||||
|
||||
**边界情况**:
|
||||
- 暂缓状态的私客**不参与**掉公计算
|
||||
- 成交客、无效客**不参与**掉公计算
|
||||
- 掉公后归属人变更为 `NULL`,首录人保留
|
||||
|
||||
---
|
||||
|
||||
#### 6.0.4 敏感号码权限与留痕
|
||||
|
||||
手机号码为核心敏感数据,访问须满足以下要求:
|
||||
|
||||
| 场景 | 展示方式 | 查看权限 | 留痕要求 |
|
||||
|------|---------|---------|---------|
|
||||
| 列表页 | `+86 135****8888` 打码 | 无需额外权限 | 不留痕 |
|
||||
| 详情页联系人面板 | `+86 135****8888` 打码 | 无需额外权限 | 不留痕 |
|
||||
| 点击「查看号码」 | 展示完整号码 10 秒后重新打码 | 需「查看客源完整号码」权限 | **必须留痕**:写入操作日志 |
|
||||
| 编辑联系人 | 打码显示 + 「查看号码」链接 | 查看需权限,编辑需「编辑客源联系人」权限 | 查看留痕 |
|
||||
|
||||
**实现要求**:
|
||||
- 号码存储使用字段级加密(AES-256),与房源模块同等安全标准
|
||||
- 查看完整号码的 API 须独立端点(不含在标准详情接口内),方便审计
|
||||
- 操作日志异步写入,不影响查看响应时间
|
||||
|
||||
---
|
||||
|
||||
#### 6.0.5 列表性能策略
|
||||
|
||||
当前截图显示公客池已有 28,878 条,私客总量持续增长,列表性能须在设计阶段预防:
|
||||
|
||||
| 策略 | 说明 |
|
||||
|------|------|
|
||||
| 服务端分页 | 所有列表接口默认 20 条/页,禁止前端全量加载 |
|
||||
| 筛选条件下推 | 所有筛选条件传至 Django ORM,生成 SQL WHERE 子句,不做内存过滤 |
|
||||
| 索引设计 | 联合索引:`(tenant_id, status,归属人, 最后跟进时间)`;手机号哈希索引(辅助去重查询) |
|
||||
| 慢查询监控 | 超过 500ms 的查询写入慢日志,上线前需通过 EXPLAIN ANALYZE 验证核心列表查询 |
|
||||
| 智能配房批量计算 | 不在请求链路内实时计算,通过 Celery 定时或客源更新时异步触发,结果缓存至 Redis |
|
||||
|
||||
---
|
||||
|
||||
### 6.1 依赖关系
|
||||
|
||||
| 系统/模块 | 依赖原因 | 优先级风险 |
|
||||
|
||||
Reference in New Issue
Block a user