# 协同拜访记录去重 — 设计方案 ## 背景 当前机制:客户经理 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 时展示与现在无差异