Files
yunke/docs/superpowers/specs/2026-08-25-yunke-staff-chat-window-design.md
T

171 lines
11 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.
# 云客员工聊天窗口对齐设计(StaffChat 五视图)
- 日期:2026-08-25
- 状态:已批准(用户逐节确认)
- 范围:微信员工列表 → 员工信息列昵称点击进入的聊天窗口页,对齐云客全部 5 个视图(单聊/群聊/朋友圈/通讯录/统计分析),含功能与数据
## 1. 背景与目标
现有 `ChatTimeline.vue`(双栏:会话侧栏+消息流)与云客员工聊天窗口差距大。云客真机(2026-08-25 Playwright 实测,员工 `wxid_wo97u1u2x11w22`)该页面是**三栏五视图**结构:左侧员工资料卡+导航,中间会话列表,右侧内容区按导航切换五种视图。
目标:新建独立页面完整对齐(布局/交互/数据口径),并与云客保持一致。旧 `ChatTimeline` 保留给 WhatsApp 渠道,不动。
### 用户决策记录(2026-08-25 逐项确认)
| 决策点 | 结论 |
|---|---|
| 范围 | 全部 5 个视图(含用户未提的统计分析) |
| 渠道 | 只做微信版;WA 保持现有页面 |
| 删除聊天记录按钮 | **不复刻**(镜像数据删了只有重扫能找回) |
| 敏感词统计 | tab 占位空态(待 C1 敏感词功能接入,见 2026-08-24 数据分析方案) |
| 实现方案 | 方案 A:独立新页 `StaffChat.vue`,组件拆分,旧页不动 |
## 2. 云客真机事实(对齐依据)
页面 URL:`/cms/wechat/salewechat/message/wechat-window/{tab}/{wxId}/{friendId}`,tab 路径段 1=单聊 2=群聊;朋友圈/通讯录/统计分析是独立路由但共享左栏。
### 2.1 布局
```
┌─────────┬──────────────┬────────────────────────┐
│ 员工资料卡 │ 好友/群|聊天记录▼ │ 对话标题 │
│ 昵称 │ [搜索框] │ [近14天窗][关键词][类型▼]│
│ 真名·电话 │ ┌──────────┐ │ [导出聊天记录] │
│ 微信号 │ │聊天好友│已删除│ │ 消息流(加载更多+日期分隔) │
│─────────│ └──────────┘ │ │
│ 单聊/群聊/ │ 📌置顶区(可折叠)│ │
│ 朋友圈/ │ 会话卡列表 │ │
│ 通讯录/ │ (头像+今日N │ │
│ 统计分析 │ +相对时间) │ │
└─────────┴──────────────┴────────────────────────┘
```
### 2.2 接口事实(真机抓包)
| 视图 | 接口 | 关键参数/字段 |
|---|---|---|
| 员工资料卡 | `GET /wechatweb/wxFriends/getUserAndFriendInfo?wechatId=&friendId=` | `saleName` 昵称、`realName` 真名、`salePhone`、`alias`/`wechatId`、`HeadImageUrl` 头像 |
| 会话列表(单聊) | `POST /wechatweb/wxFriends/talkerList` form | `type=1, isDel=0|1, pageNumber, pageSize=20, wechatId`;行:`friendWechatId/nickName(脱敏)/remarkName/messageCount(今日)/headUrl/lastChatTime/showHead(-1=置顶)/memberCount`;`topCount` 置顶数、`hasMore` 翻页 |
| 会话列表(群聊) | 同上 | `type=2`(无 isDel);群名不脱敏 |
| 朋友圈 | `POST /wechatweb/wechatFriendster/getFriendsterInfo` form | `startTime/endTime(空), pageNum, pageSize=10, wechatId`;行:`id/snsId/friendsterName/friendsterWXIdImg/content/createTime(秒)/type(1图文/28纯文字)/praise[]{name,praiseImg}/comment[]/imgUrlsList[](OSS URL)/praiseNum`;头部 `saleNickName/headImageUrl/saleAlias/bgImg`;`hashNext` 翻页 |
| 通讯录 | `POST /wechatweb/wxFriends/friend_list/es` | 即逐员工客户列表(已抓过);表格列:客户微信(昵称+微信号)/数据来源/客户备注名/最近沟通时间/发送条数/接收条数/操作(查看聊天记录+导出HTML聊天记录) |
| 统计计数 | `POST /wechatweb/wechatcount/getSaleWechatIdInfoCount` | `{addFriend, addGroup, redAndTrans, sensitiveCount, delFriend, blackFriend, weChatVoiceCount}` |
| 统计明细 | `POST /wechatweb/wxFriends/getAddFriendOrGroup` form | `pageNum, pageSize, friendTypeYunke=1, wechatId, startdate, enddate`;行:客户微信(可点→聊天)/客户备注/员工微信/时间 |
其他 UI 事实:
- 右列默认日期窗**近 14 天**(`今日00:00-14天 ~ 今日23:59:59`),统计视图默认**近 7 天**
- 类型下拉 10 类:文本/图片/语音消息/语音通话/视频通话/文件/小程序名片/链接/红包/转账
- 「好友/群|聊天记录」下拉:聊天记录模式=左侧按全量消息关键词搜索
- 置顶区含文件传输助手,可「折叠置顶聊天」
- 已删除好友 tab 存在(实测 3 条老好友,最近联系时间可显示到 2025 年)
- 资料卡 cursor=pointer 但点击无可见行为(不复刻点击)
## 3. 架构(方案 A)
```
路由: /staff-chat/:ownerId/:tab(single|group|moments|contacts|stats)/:talkerId?
默认 tab=single;talkerId 可空(账号级入口)
StaffChat.vue (页面骨架)
├── StaffChatSide.vue 左栏: 员工资料卡 + 5 导航按钮
├── tab=single|group → SessionPane.vue (中列) + ChatMain.vue (右列)
├── tab=moments → MomentsPane.vue
├── tab=contacts → ContactsPane.vue
└── tab=stats → StatsPane.vue
```
- `SessionPane`/`ChatMain` 从 `ChatSidebar`/`ChatTimeline` 复制改造(非原地重构);`MessageBubble`、`buildMessageSections` 直接复用
- `ChatTimeline.vue`/`ChatSidebar.vue` 不动,WA 继续用;未提交 WIP(`sidebarProfile` 传参、`delete_flag` query 类型)随本设计消化:ChatTimeline 还原 WIP,`delete_flag` 参数由新 sessions API 取代
- 入口改造:`StaffList.vue` 微信行昵称/主badge → `/staff-chat/{account_id}/single`;modal 内点客户微信带日期窗 query;`CustomerList.vue` 微信行「查看聊天记录」同步切新页
## 4. 数据层
### 4.1 新表 `moments_post`
| 列 | 类型 | 说明 |
|---|---|---|
| id | VARCHAR(64) PK | 云客 data.id |
| wechat_id | VARCHAR(128) idx | 归属员工 |
| sns_id | VARCHAR(64) | 云客 snsId |
| author_name / author_wxid / author_avatar | VARCHAR | 发布者 |
| content | TEXT | 文案 |
| type_raw | VARCHAR(16) | 1=图文 28=纯文字 |
| create_time | BIGINT idx | 发布时间(ms) |
| praise_json / comment_json / image_urls | JSON | 点赞/评论/图片 |
| raw_json + fetched_at/updated_at | | 惯例字段 |
### 4.2 新抓取实体 `moments`(页面 fetch 方式)
- 浏览器 `fetch POST getFriendsterInfo`,`pageSize=100` 逐员工翻页到 `hashNext=false`
- 接入 `scrape_via_page.py` 实体注册、`full_sync_all.sh` 加一步、调度 4h 增量(水位=本地 max(create_time),仅扫有新朋友圈的员工——云客接口无 per-员工增量标记,简化为全量浅扫第一页有新才继续)
- 图片接入现有 media 本地化批处理(OSS URL 型)
### 4.3 API 改动
| API | 改动 |
|---|---|
| `GET /api/accounts` | `AccountOut` + `avatar`(从 `raw_json.headImageUrl` 提取,WA 侧无此键则 null) |
| `GET /api/sessions` | + `is_del` 参数(join `customer.delete_flag`,NULL 视为 0);行 + `pinned`(`talker_id='filehelper'`) |
| `GET /api/moments` 新 | `?wechat_id=&page=&page_size=` 帖子 desc + 头部 `{nickname, avatar, alias}` |
| `GET /api/staff-stats` 新 | `?wechat_id=&start=&end=` 计数;`&kind=` 明细行 |
| `GET /api/messages` | + `msg_type` 过滤(映射云客 10 类)+ `q` 全文关键词(聊天记录模式用) |
### 4.4 统计口径(现有表本地聚合)
| tab | 口径 |
|---|---|
| 新的客户 | `customer.add_time` 窗内(实测 62% 有值);缺失兜底=该对话首条消息时间 |
| 新的群 | `chat_group` 建群时间(无则跳过该 tab 计数,显示现存量) |
| 红包转账 | `message.type_raw=49 AND (raw_json.paymsgid 非空 OR text 含 appmsg type 2001/2000)`,合并一类;明细金额从 XML `<feedesc>` 提取,失败显 `—` |
| 删除客户 | `customer.delete_flag=1`(实测 53 行;无时间字段→计数=现存量,时间列显最近 updated) |
| 拉黑客户 | `customer.is_block=1`(实测 8 行,同上) |
| 语音通话 | `phone_call.user_id=员工 AND start_call_time 窗内` |
| 敏感词 | 占位空态「词库未配置,待 C1 敏感词功能接入」 |
## 5. 各视图细节(对齐云客)
### 单聊/群聊
- 中列顶部「好友/群 | 聊天记录」下拉双模式 + 搜索框(昵称/wxid 或全量消息关键词)
- 单聊有 `聊天好友|已删除好友` tab(群聊无)→ sessions `is_del`
- 置顶区:filehelper 固定置顶 + 可折叠;置顶不参与今日红点
- 会话卡:头像+昵称+`今日N`红点+相对时间(今天 `HH:mm`/昨天/今年 `M月D日`/往年全日期)
- 右列:默认近 14 天窗(清空=全部)、关键词、类型下拉 10 类、导出按钮(复用 `/export/chat-html`)
- 消息流:加载更多/上一页 + 日期分隔条;点会话切路由 talkerId
- 已删除好友会话照常显示历史消息
### 朋友圈
- feed 卡片:作者头像+名 → 内容 → 九宫格图片(点击预览)→ 时间 → 点赞人列表
- 滚动翻页;空态提示先跑 moments 抓取;缺图占位框
### 通讯录
- `微信客户|微信群` tab + 搜索(昵称/微信号/备注/手机号)
- 客户表 7 列:客户微信(头像+昵称+微信号)/数据来源/客户备注名/最近沟通时间/发送条数/接收条数/操作(查看聊天记录+导出HTML聊天记录)
- 微信群 tab:群名/群主/成员数/最近群消息时间/查看群聊
- 复用 `/api/customers`、`/api/groups`(owner 过滤已支持)
### 统计分析
- 7 tab 带计数徽标,默认近 7 天窗
- 明细 4 列:客户微信(可点→聊天)/客户备注/员工微信/时间
- 敏感词空态占位
## 6. 错误处理
- 每视图独立 loading/error(`el-alert`),互不影响
- 翻页失败→显示已加载+重试;图片失效→占位框;缺字段→`—`
- 统计某类无数据→空表格+计数 0;非法 tab→重定向 single
- 与现有页面一致的后端未起/超时错误条
## 7. 测试
- 后端 pytest:`test_api_moments.py`(分页/排序/空库)、`test_api_staff_stats.py`(7 类口径+窗口边界+add_time 兜底+红包判定)、sessions 增补(is_del/置顶)、accounts 增补(avatar)、scraper 归一化单测(fixture=真机 JSON)
- 前端 vitest:路由跳转、tab 切换、相对时间格式化
- 验收:本地前后端 + Playwright 走查 5 视图,与云客真机对比
## 8. 实施顺序
1. 后端:表+scraper → API(accounts/sessions/messages 改动,moments/staff-stats 新增)
2. 前端骨架:路由+左栏+单聊视图
3. 群聊/通讯录/统计分析视图
4. 朋友圈视图
5. 入口改造 + 走查 + 提交