数据源真机核实(wxStatsByTypes/wxChatInfo);从 message 表聚合 5 数字+客户明细; 复用 YkStaffDetailModal;口径近似(跟进等)。姓名drawer/主badge = Phase B。 Co-Authored-By: Claude <noreply@anthropic.com>
8.4 KiB
微信员工 沟通数/及时回复 5 数字 modal(Phase A)设计
日期:2026-08-07|分支:
feat/yunke-data-mirror|路线图 #2 Phase A 前置:Plan 1(StaffList 微信员工列表已对齐云客,含YkStaffDetailModal通用件、数字→modal 模式);message 表已同步(含owner_account_id/talker_id/is_send/timestamp)。 数据来源:全本地 message 表聚合(用户定,符合 yunke-verify-data-in-browser 全本地原则;数字口径近似云客——已与用户确认接受)。
1. 目标与范围
目标:StaffList(微信员工列表)每行的「沟通数」「及时回复」复合列里 5 个数字可点 → 弹 modal 展示对应客户列表,对齐云客。数据从本地 message 表实时聚合(按 员工微信 × 客户 × 日期窗),运行时不碰云客。
5 个数字(云客 /cms/wechat/wechatlist 微信员工列表,真机核实 2026-08-07):
- 沟通数:沟通人数 / 有效沟通数 / 跟进客户数
- 及时回复:员工最后回复数 / 未回复客户数
在本 plan(Phase A):
- 后端:新聚合 endpoint
GET /api/staff-chat-stats(per 员工微信 + 日期窗 → 5 个汇总数 + 客户明细行)。 - 前端:StaffList 5 个数字可点 → 复用
YkStaffDetailModal弹对应客户子集 + 日期选择器(默认近 3 天)。
不在本 plan(Phase B / 后续):
- 员工姓名 → 员工详情(降级 drawer)、主 badge → 聊天窗口(Phase B,小前端)。
- modal 内「客户微信/员工微信 → 聊天记录」二级可点(Phase B/后)。
- WA 员工表同类数字 modal(WA 员工表数字 modal 待复核,本 plan 只做微信员工)。
- 与云客数字精确对齐(口径近似,见 §4)。
2. 数据来源(真机核实)
云客微信员工列表两个接口(2026-08-07 真机):
- 行的 5 数字 ←
POST /wechatweb/statistics/wxStatsByTypes(per 员工聚合,固定窗)。 - 数字 modal 内容 ←
POST /wechatweb/statistics/wxChatInfo(body{weChatId, userId, ymdS, ymdE, page, size};5 个 modal 共用此 endpoint,仅筛选/展示不同;modal 带日期选择器 = 动态窗)。响应每条 = 一个客户 +sendNum/receiveNum/noResponse/responseCount/saleSendLastNum/customerSendLastNum等字段。
我们的方案(用户定):不抓 wxStatsByTypes/wxChatInfo,从本地 message 表实时聚合(动态日期窗没法预抓,且全本地原则)。message 表字段齐(smoke.db 实测 wechat 消息 owner_account_id/talker_id/is_send/timestamp 全非空)。
3. 数据模型 / 聚合
无新表。聚合在查询时算(message 表按 owner_account_id + 日期窗 GROUP BY talker_id)。
每个客户(talker_id)的聚合字段(窗口内):
send_count=is_send=1消息数(员工发)receive_count=is_send=0消息数(客户发)interaction_days= 不同timestamp日期数last_is_send= 窗口内最后一条消息的is_send(1=员工最后发,0=客户最后发)last_time= 最后一条时间- 客户身份:
talker_id→ 关联customer表取friend_nickname/friend_remark/real_name(left join,缺失则用 talker_id)。
建议索引:message(owner_account_id, timestamp) 加速(若不存在,plan 里加 Alembic 迁移;先确认现有索引)。
4. 5 数字口径(近似,从聚合字段派生)
| 数字 | 口径(本系统) | 云客原口径 |
|---|---|---|
沟通人数 chat_count |
窗口内 ≥1 条消息的去重客户数 | 同(去重沟通客户) |
有效沟通数 effective_count |
双向客户数(send_count≥1 且 receive_count≥1) |
双向有效沟通 |
跟进客户数 follow_count |
窗口内 interaction_days≥2 的客户数(近似,跟进精确口径云客未公开) |
跟进(精确口径未知) |
员工最后回复数 last_reply_count |
last_is_send=1 的客户数(员工最后发) |
员工最后回复 |
未回复客户数 no_reply_count |
last_is_send=0 的客户数(客户最后发、员工未回) |
未回复客户 |
近似声明:跟进客户数口径为合理近似(云客精确标准未公开);其余 4 个与云客语义一致,但数字可能因日期窗/数据完整性略有出入。modal 内显示口径说明(小字)。
5. API
GET /api/staff-chat-stats(新 router,镜像现有 list endpoints 风格)
- query:
owner_account_id(必填,员工微信号=wxid)、start?/end?(ms-epoch,默认近 3 天)。 - 响应:
{
"owner_account_id": "wxid_...",
"start": ..., "end": ...,
"summary": { "chat_count": N, "effective_count": N, "follow_count": N, "last_reply_count": N, "no_reply_count": N },
"customers": [
{ "talker_id": "...", "friend_name": "...", "send_count": N, "receive_count": N,
"interaction_days": N, "last_is_send": 0|1, "last_time": <ms> }
]
}
- 前端
getStaffChatStats({owner_account_id, start, end})。 - 聚合一条 SQL(GROUP BY talker_id)+ 5 个汇总在 Python 侧从 customers 派生(或 SQL 同时算)。
6. 前端(StaffList + modal)
- StaffList 每行(员工微信)「沟通数」「及时回复」复合列:把现有静态数字(若有)改/补成可点 5 个数字。每行加载时调
getStaffChatStats({owner_account_id: row.<account>, start, end})(默认近 3 天)填 summary。- 性能:每页 N 行 × 1 次聚合;首版顺序调用可接受(消息表有索引),或后续优化为批量 endpoint。
- 点数字 → 复用
YkStaffDetailModal(Plan 1 通用件,支持自定义列 + 数据源)弹客户子集:- 沟通人数 → customers 全集
- 有效沟通 → 过滤
send≥1 && receive≥1 - 跟进 → 过滤
interaction_days≥2 - 最后回复 → 过滤
last_is_send=1 - 未回复 → 过滤
last_is_send=0 - modal 列(对齐云客):序号 / 客户微信(昵称+号) / [有效沟通多 发送数/接收数 列] / 员工信息 / 员工微信。modal 内客户微信可点→(Phase B)。
- modal 顶部日期选择器(el-date-picker,默认近 3 天)→ 改 window 重查。
- modal 标题对齐云客(沟通人数(已去重) / 有效沟通数(已去重) / 跟进客户数 / 员工最后回复数 / 未回复客户数)+ 近似口径小字说明。
7. 关键决策(已与用户确认 2026-08-07)
- 全本地消息聚合(不抓 wxStatsByTypes/wxChatInfo):从 message 表算。
- 口径近似:跟进客户数用合理近似;数字可能与云客略出入,用户接受。
- 范围 Phase A:只做 5 数字 modal;姓名 drawer + 主badge + modal 内二级可点 → Phase B。
- 默认窗近 3 天(对齐云客 modal 默认 ymdS/ymdE)。
- 无新表:聚合在查询时算(动态窗不宜预存)。
8. 风险
- 数据完整性:smoke.db 微信消息稀疏(4 员工×6 客户×399 条),数字小但机制成立;真实部署数据全后数字才有意义。
- 跟进口径近似:与云客可能不一致(已声明)。
- 性能:每行一次聚合;大消息量下需索引
(owner_account_id, timestamp)(plan 确认/加)。 - StaffList 行 ↔ owner_account_id 映射:确认 StaffList 行有员工微信号字段(plan 核实 Plan 1 的 row shape)。
9. 文件清单(预期)
后端:
- 新
app/api/routers/staff_chat_stats.py(/api/staff-chat-stats聚合查询) - 改
app/api/main.py(注册 router) - 改
app/api/schemas.py(StaffChatStatsOut/StaffChatCustomerOut) - (若需)Alembic 加
message(owner_account_id, timestamp)索引 - 新测试
tests/test_api_staff_chat_stats.py(种消息 → 聚合 5 数字 + 客户行正确)
前端:
- 改
yunkes/src/api/index.ts+types.ts:getStaffChatStats/StaffChatStats类型 - 改
yunkes/src/views/StaffList.vue:5 数字可点 + 挂 modal - 改/复用
yunkes/src/components/YkStaffDetailModal.vue:支持日期选择器 + 5 套列配置 + 客户子集过滤
10. 验收
- 后端测试全绿(含
test_api_staff_chat_stats.py:种若干 message → summary 5 数字 + customers 字段正确;日期窗过滤生效)。 vue-tsc -bexit 0。- 浏览器:StaffList 微信员工行 5 数字显示且可点 → modal 弹对应客户子集;切日期窗重查;列对齐云客。
- 数据来自本地 message(network 只打
/api/staff-chat-stats,不碰云客)。