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

16 KiB
Raw Blame History

云客员工聊天窗口对齐设计(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,组件拆分,旧页不动
二次审计修订(同日) 8 项全部修订:微信群表列改真机结构、统计明细按 tab 分型、语音通话复用 CallList 列、phone_call userId 转换写明、消息类型归一化+回填任务、通讯录批量操作、红包转账 msgid 定位、chat_group 补列(raw_json 提取)、左栏折叠

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
会话列表(群聊) 同上 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;各 tab 明细列不同(真机逐 tab 验证):新客户=客户微信(可点)/客户备注/员工微信/时间;红包转账=操作类型/客户微信/员工微信/金额/发生时间/操作(查看聊天记录带 msgid 定位消息);新的群=微信群信息/员工微信/时间;删除客户=客户微信/员工微信/发生时间;语音通话=复选框/通话人/通话类型/来源/呼入呼出/接通状态/通话录音/通话时间/操作(9 列,与 CallList 同构)

其他 UI 事实:

  • 右列默认日期窗近 14 天(今日00:00-14天 ~ 今日23:59:59),统计视图默认近 7 天
  • 类型下拉 10 类:文本/图片/语音消息/语音通话/视频通话/文件/小程序名片/链接/红包/转账
  • 「好友/群|聊天记录」下拉:聊天记录模式=左侧按全量消息关键词搜索
  • 置顶区含文件传输助手,可「折叠置顶聊天」
  • 已删除好友 tab 存在(实测 3 条老好友,最近联系时间可显示到 2025 年)
  • 资料卡 cursor=pointer 但点击无可见行为(不复刻点击)
  • 通讯录·微信群 tab 列(真机验证):复选框/群信息/数据来源/群主信息(昵称+微信号)/群成员数量/群成员好友数量/消息数量/操作(查看聊天记录→群聊消息+导出HTML聊天记录);搜索框仅「请输入群名称」
  • 通讯录批量操作:客户表/群表都有全选复选框 + 顶部「查询/导出/导出聊天记录(勾选后可批量)」
  • 左栏有「收起菜单」「收起侧边栏」折叠按钮
  • 云客全站昵称脱敏(A**2),我们不脱敏(镜像惯例,有意差异)

3. 架构(方案 A)

路由: /staff-chat/:ownerId/:tab(single|group|moments|contacts|stats)/:talkerId?
      默认 tab=single;talkerId 可空(账号级入口)
      tab=group 时 talkerId 语义为 roomId;红包转账明细跳转带 ?msgid= 定位消息

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 直接还原(git checkout),不迁移:ChatTimeline.vue 的 sidebarProfile/ownerPhone/ownerAvatar 三处、api/index.ts 的 delete_flag 参数——新页面自建,旧文件回到干净状态
  • talkerId 为空(账号级入口,从员工列表昵称点入):右列显示空态「选择左侧会话查看消息」(与现有 ChatTimeline 行为一致),中列正常展示会话列表
  • 入口改造: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;createTime 云客返回秒,入库前 ×1000 统一转 ms(否则渲染 1970、水位逻辑错位)
  • 接入 scrape_via_page.py 实体注册、full_sync_all.sh 加一步、调度 4h 增量(水位=本地 max(create_time),仅扫有新朋友圈的员工——云客接口无 per-员工增量标记,简化为全量浅扫第一页有新才继续)
  • 图片接入现有 media 本地化批处理(OSS URL 型)

4.2b chat_group 补列(通讯录·微信群 tab 数据,从 raw_json 提取)

真机验证云客群表需要:群主信息/群成员好友数量/消息数量。raw_json 已有这些键(ownerName/ownerWchatId/ownerAlias/ownerImage/friendCount/createTime),加实体列避免每行解析 JSON:

新列 来源 raw_json 键 说明
owner_name / owner_wxid / owner_alias / owner_avatar ownerName/ownerWchatId/ownerAlias/ownerImage 群主信息(上游脱敏,如实存)
friend_count friendCount 群成员中该员工好友数
create_time createTime 建群时间(ms,统计·新的群 tab 用)

一次性迁移脚本回填存量(幂等,复用 migrate 惯例),scraper upsert 持续写入。

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}(头部取自 wechat_account 表/nick_name + raw_json.headImageUrl,不在 moments_post 存)
GET /api/staff-stats 新 ?wechat_id=&start=&end= 计数;&kind= 明细行(除敏感词外全部 tab 都出明细行,本地表聚合)
GET /api/messages + msg_type 过滤 + q 全文关键词(聊天记录模式用)。msg_type→本地 type_raw 映射需先补全归一化(见 4.5)

4.4 消息类型归一化补全 + 存量回填(10 类过滤的数据前提)

本地实测 wechat 渠道 type 仅 4 种(text/image/image_legacy/unknown),unknown 约 13 万行:type_raw 21=31859(链接)、15=18393、9=9023、10/4/18/8/6/22/13/14/5/11/20 各数千数百、49=202(红包转账);语音消息(34)未出现。10 类下拉中 语音/视频/文件/小程序名片 4 类现状过滤必为 0。

任务:扩展 scripts/_normalize_msg_type 映射(21→link、49 拆红包/转账按 paymsgid/appmsg type、15/18→视频/语音通话候选——具体语义在 plan 第一阶段用 raw_json 样本逐一判别),跑存量回填(2026-08-20 type=2 回填先例,幂等可重跑)。回填后 10 类过滤才有真实数据。

4.5 统计口径(现有表本地聚合)

tab 口径
新的客户 customer.add_time 窗内(实测 62% 有值);缺失兜底=该对话首条消息时间
新的群 chat_group.create_time 窗内(新列,从 raw_json.createTime 回填;无值行不计入)
红包转账 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 存的是员工 userId(32位HEX) 非 wxId(实测确认)——必须先 wechat_account.user_id join 转换再查,直接用 wxId 查会 0 行;channel='voice_call' 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 + 搜索框(客户 tab:昵称/微信号/备注/手机号;群 tab:仅群名称)
  • 客户表 7 列:复选框/客户微信(头像+昵称+微信号)/数据来源/客户备注名/最近沟通时间/发送条数/接收条数/操作(查看聊天记录+导出HTML聊天记录)
  • 微信群表 8 列(真机):复选框/群信息(群名)/数据来源/群主信息(昵称+微信号,raw_json 提取)/群成员数量/群成员好友数量(friend_count)/消息数量/操作(查看聊天记录→群聊消息+导出HTML聊天记录)
  • 批量操作:全选复选框 + 顶部「查询/导出(CSV)/导出聊天记录(勾选后批量导出 HTML,串行调 /export/chat-html)」
  • 复用 /api/customers、/api/groups(owner 过滤已支持;groups 补 owner 精确+新列输出)

统计分析

  • 7 tab 带计数徽标,默认近 7 天窗
  • 明细列按 tab 分型(配置驱动 cols,真机验证):
    • 新的客户:客户微信(可点→聊天)/客户备注/员工微信/时间
    • 红包转账:操作类型(红包/转账)/客户微信(可点)/员工微信/金额/发生时间/操作(查看聊天记录带 msgid 定位滚动到该消息)
    • 新的群:微信群信息(可点→群聊)/员工微信/时间
    • 删除/拉黑客户:客户微信/员工微信/发生时间
    • 语音通话:复用 CallList 列定义(通话人/通话类型/来源/呼入呼出/接通状态/通话录音/通话时间/操作)
  • 敏感词空态占位

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. 后端:moments 表+scraper → chat_group 补列+回填 → 消息类型归一化映射判别+存量回填 → API(accounts/sessions/messages/groups 改动,moments/staff-stats 新增)
  2. 前端骨架:路由+左栏+单聊视图(含 msgid 定位消息能力)
  3. 群聊/通讯录(客户表+群表+批量操作)/统计分析(分型明细)视图
  4. 朋友圈视图
  5. 入口改造 + Playwright 走查 5 视图对照云客 + 提交