Files
qiji/docs/superpowers/specs/2026-07-30-visit-dedup-design.md
2026-07-30 09:55:08 +08:00

157 lines
5.8 KiB
Markdown
Raw Permalink 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.
# 协同拜访记录去重 — 设计方案
## 背景
当前机制:客户经理 A 创建拜访记录并选择协同人员 B 时,后端为 B 生成一条空内容的草稿副本。B 填写后,同一客户、同一天出现两条独立拜访记录,导致周报重复展示、亮灯表重复计数。
## 设计目标
1. 周报中同一客户同一次拜访合并展示为一张卡片
2. 亮灯表不重复计数
3. 每个参与人的填报进度不受影响(各自计入)
4. 每个参与人能独立编辑自己的副本
5. 历史数据兼容,不做强制迁移
## 方案概要
### 1. 数据库:新增 `visit_group_id`
```sql
ALTER TABLE visits ADD COLUMN visit_group_id UUID;
CREATE INDEX ix_visits_visit_group_id ON visits(visit_group_id);
```
- 单人拜访:`visit_group_id = NULL`
- 多人协同拜访:同一次拜访的所有记录共享同一个 `visit_group_id`
### 2. 后端:POST /visits/ 改造
**原逻辑:** 为每个协同人生成一条空内容草稿(`communication_content=""`, `photos=[]`, `companions=[]`
**新逻辑:** 为每个协同人生成一条完整副本:
| 副本字段 | 值 | 说明 |
|----------|-----|------|
| `customer_id` | 同主记录 | |
| `visit_date` | 同主记录 | |
| `visit_method` | 同主记录 | |
| `time_range` | 同主记录 | |
| `visitor_name` | 同主记录 | 实际拜访人不变 |
| `visitor_phone` | 同主记录 | |
| `communication_content` | 原内容 + `(协同XXX` | 如:`了解项目进度...(协同韦佶秀)` |
| `customer_demand` | 同主记录 | |
| `companions` | `[主记录创建人]` | 互换:协同人看到主访人 |
| `companion_names` | 同主记录 | |
| `photos` | 同主记录 | |
| `manager_id` | 协同人的 UUID | 归属到协同人名下 |
| `visit_group_id` | 同主记录 | **同一 UUID,关联所有副本** |
**前端表现(创建人韦佶秀视角):**
```
相关人员选择了 [韦伦]
→ 保存后韦伦收到一条完整拜访记录
→ 韦伦记录内容末尾自动追加"(协同韦佶秀)"
```
### 3. 后端:PUT /visits/{id} 不变
协同人编辑自己的副本时不影响主记录,各自独立。
### 4. 仪表盘统计:按 visit_group_id 去重
`get_dashboard_stats` 中的 `week_visits` 改为:
```python
# 原:count 所有 visit 行
select count(distinct coalesce(visit_group_id, id))
from visits
where visit_date between monday and sunday
```
每个 visit_group_id 只算一次,跳过 NULL(单人拜访用 id 做 fallback key)。
### 5. 填报进度:不变
`get_reporting_progress``manager_id` 分组计数,每个参与人各算一次,不受去重影响。
### 6. 亮灯表:按 (customer_id, visit_date, visit_group_id) 去重
同一客户同一天同一次拜访只亮一盏灯。
### 7. 周报展示:按 visit_group_id 合并
`get_weekly_report` 查询时,同一个 `visit_group_id` 的记录合并成一条输出:
```json
{
"customer_name": "污水处理厂",
"visit_date": "2026-07-30",
"visit_method": "上门",
"time_range": "09:00-10:30",
"visit_group_id": "abc-123",
"participants": [
{
"manager_name": "韦佶秀",
"manager_id": "uuid-1",
"role": "primary",
"communication_content": "了解污水处理新项目...",
"customer_demand": "专线扩容至100M...",
"photos": ["photo_001", "photo_002"]
},
{
"manager_name": "韦伦",
"manager_id": "uuid-2",
"role": "companion",
"communication_content": "了解污水处理新项目...(协同韦佶秀)",
"customer_demand": "专线扩容至100M...",
"photos": ["photo_001", "photo_002"]
}
]
}
```
**前端周报卡片展示:**
```
┌─────────────────────────────────────────────┐
│ 污水处理厂 2026-07-30 上门 │
│ 09:00-10:30 │
│ │
│ ▸ 韦佶秀(主访) │
│ 了解污水处理新项目建设进度,客户计划新增... │
│ 客户需求:专线扩容至100M,新增视频监控... │
│ 📷 2张 │
│ │
│ ▸ 韦伦(协同) │
│ 了解污水处理新项目建设进度...(协同韦佶秀) │
│ 客户需求:专线扩容至100M... │
│ 📷 2张 │
└─────────────────────────────────────────────┘
```
### 8. 前端:协同记录编辑提示
协同人打开自己的副本编辑时,沟通内容区域上方显示一行提示:
> 💡 此记录为协同韦佶秀拜访「污水处理厂」的副本。你可以补充或修改内容,不会影响主记录。
## 改动清单
| 层 | 文件 | 改动 |
|----|------|------|
| DB | migration | `ALTER TABLE visits ADD visit_group_id UUID` |
| 后端 | `models/visit.py` | 新增 `visit_group_id` 字段 |
| 后端 | `schemas/visit.py` | `VisitOut` 新增 `visit_group_id` |
| 后端 | `api/visits.py` POST | 副本改为完整复制 + `(协同XXX)` 后缀 + 互换 companions + 设置 visit_group_id |
| 后端 | `services/dashboard.py` | `get_dashboard_stats` 的 week_visits 改为 distinct 去重 |
| 后端 | `services/dashboard.py` | `get_weekly_report` 合并同 visit_group_id 记录 |
| 后端 | `services/light_board.py` | 亮灯计数去重 |
| 前端 | 周报 `WeeklyReport.vue` | 拜访卡片支持 participants 展开/折叠 |
| 前端 | 移动端 `VisitForm.vue` | 协同记录编辑时显示提示条 |
## 兼容性
- 历史数据 `visit_group_id = NULL`,行为与现在完全一致
- 前端 `participants` 字段回退:单 participant 时展示与现在无差异