生产不再内置 nginx/`__cos__`;镜像用 serve -s,HTTPS 由 Traefik+cert-manager;面单预览走 stream。 Co-authored-by: Cursor <cursoragent@cursor.com>
26 KiB
在线客服模块设计方案
文档状态:产品 + 架构 + 表结构 + 接口(先评审,再开发)
编写日期:2026-07-26
关联页面:H5「我的 / 联系客服」、AI 顾问(/assistant、/h5/assistant)、坐席工作台(新建)
关联能力:专属顾问(Customer.CustomerService→/customer/service-advisor)、AI 对话(/xhs/ai/chat)、站内信(/system/notice/*)、微信注册绑定
关联文档:docs/H5客户中心设计方案.md、docs/前端页面设计规范.md、docs/rbac系统设计-060801.md、docs/数据权限设计.md
目标读者:产品、前端、后端、运营、测试
0 阅读指引
| 角色 | 优先阅读 |
|---|---|
| 产品 / 运营 | §1~§3、§5、§11 |
| 前端 | §4~§6、§8、§10 |
| 后端 / DBA | §4、§7~§9、§10 |
| 测试 | §5、§11、§12 |
分期对照(务必先对齐)
| 阶段 | 名称 | 是否改实时通道 | 交付物 |
|---|---|---|---|
| P0 | AI + 微信兜底 | 否 | 入口打通、顾问二维码、复制单号 |
| P1 | 站内轻量 IM(主推) | 是(SignalR) | 会话 / 消息 / 坐席工作台 / 转人工 |
| P2 | 触达与渠道扩展 | 可选 | 离线通知、公众号模板、企微等 |
本文 默认按 P0 → P1 落地;P2 仅列边界,不进首版开发范围。
1 背景与目标
1.1 为什么要做
客户高频问题(多少钱、到哪了、出问题找谁)已在 H5 客户中心与 AI 顾问中部分覆盖,但「找人」仍停在:
| 现状 | 缺口 |
|---|---|
AI 顾问 HTTP 一问一答(/xhs/ai/chat) |
无法转人工、无会话留痕给坐席 |
GET /customer/service-advisor 展示微信二维码 |
系统外沟通,订单上下文靠客户口述 |
| H5「联系客服」规划为入口 | 尚未形成可运营的客服闭环 |
| 问题件站内信 / 邮件通知 | 非双向即时对话 |
物流售后场景需要:带着运单上下文、可留痕、可考核、可回退到微信 的客服能力,而不是官网浮窗式售前聊天。
1.2 建设目标
- 三层服务:AI 一线 → 站内人工 → 微信兜底,按能力成熟度渐进上线。
- 上下文优先:会话可挂
OrderNo/ 订单状态摘要,减少「请发单号」。 - 归属清晰:优先路由到客户已绑定的
CustomerService专属客服。 - 双端一致:客户侧(H5 / PC 顾问壳)与坐席侧(中后台)共用同一会话模型。
- 可运营:会话状态、未读、转接基础能力可统计;首版不做大屏 SLA。
1.3 设计原则
- 不推翻现有顾问模型:
Customer.CustomerService+ 顾问二维码继续作为兜底与身份来源。 - AI 与人工同壳:复用
AiAdvisorShell/ H5 AI Tab,增加「转人工」而非另起一套聊天 UI(客户侧)。 - 实时用 SignalR:与 .NET 8 栈一致;多实例用 Redis backplane(已有 FreeRedis)。
- 身份隔离:客户会话强制
CustomerId隔离;坐席受 RBAC + 数据权限约束。 - 先业务后推送:P1 先保证在线双方实时;离线触达(站内信 / 公众号)可后置。
1.4 非目标(P0 / P1 不做)
- 语音 / 视频通话
- 全渠道聚合(企微、抖音、美洽等)作为主通道
- 机器人知识库编排、意图平台
- 坐席大屏、复杂 SLA / 质检评分体系
- 客户侧管理端 H5(坐席仅 PC 中后台)
- 改变订单状态机、计费、现有 AI Provider 配置模型
1.5 方案选型结论
| 方案 | 结论 |
|---|---|
| 仅 AI + 微信二维码 | P0 必做,成本最低 |
| 自研 SignalR 轻量 IM | P1 主路径,订单上下文与归属可深嵌 OMS |
| 第三方在线客服(美洽 / 环信等) | 不作为主路径;仅适合官网匿名售前且不绑 OMS 时另议 |
2 角色与场景
2.1 角色
| 角色 | 登录形态 | 核心诉求 |
|---|---|---|
| 客户(含子客户) | loginType=1 |
带着单号问人、尽快得到答复 |
| 专属客服 | loginType=0,对应 Customer.CustomerService |
只看自己客户、处理跟单问题 |
| 客服主管 / 值班 | loginType=0 + 权限 |
认领公共队列、转接、查看范围更大(受数据权限) |
| AI 顾问 | 系统机器人 | 一线解答;无法解决时引导转人工 |
2.2 高频场景(按紧迫度)
1. 订单异常 / 理赔 → 要人立刻接
2. 查价 / 渠道选择搞不清 → 先问 AI,再转人工
3. 「货到哪了」→ AI / 自助轨迹优先;仍不懂再人工
4. 充值、账号问题 → 人工或微信
5. 坐席不在线 → 微信二维码 + 复制单号(绝不让客户卡死)
2.3 成功标准(客户感受)
| 场景 | 成功标准 |
|---|---|
| 联系客服 | 30 秒内看到「AI / 在线客服 / 微信」之一可用 |
| 转人工 | 单号与最近提问自动带入,无需重述 |
| 坐席侧 | 打开会话即见客户卡片 + 最近订单摘要 |
| 无人在线 | 明确提示并给出顾问微信,不出现空白等待 |
3 产品分期
3.1 P0 — AI + 微信兜底(约 1~2 周)
目标:H5 / AI 壳「找得到人」。
| 项 | 说明 |
|---|---|
| 入口 | H5「我的 → 联系客服」;订单异常「问顾问 / 联系客服」 |
| 能力 | 拉 service-advisor;展示姓名 / 微信号 / 二维码;一键复制单号 |
| 可选 | AI 页底部固定「联系专属客服」抽屉(复用现有 Advisor 抽屉) |
| 不做 | 会话表、SignalR、坐席工作台 |
3.2 P1 — 站内轻量 IM(主交付)
目标:客户 ↔ 坐席站内实时对话 + AI 转人工。
| 项 | 说明 |
|---|---|
| 客户侧 | 会话列表 + 聊天窗(嵌入 AI 壳旁或独立页);文本 + 图片 |
| 坐席侧 | 收件箱(待接入 / 进行中 / 已结束)+ 聊天 + 右侧客户/订单上下文 |
| 实时 | SignalR Hub;断线可 HTTP 补拉历史 |
| 分配 | 优先专属客服;离线进公共队列认领 |
| 兜底 | 无坐席在线 → 降级 P0 微信卡片 |
| 通知 | 未读角标;可选写站内信(noticeType=message) |
3.3 P2 — 触达与渠道(延后)
- 坐席离线时公众号 / 订阅消息提醒客户「有新回复」
- 客户离线时站内信 / 邮件提醒坐席(或反向)
- 企微 / 微信客服作为外部渠道,会话仍落本库
- 快捷回复库、会话评价、简易报表
- 转接多人、技能组、自动分配负载均衡
4 总体架构
4.1 逻辑架构
┌─────────────┐ ┌──────────────┐ ┌────────────────────┐
│ H5 / PC 客户 │ │ 中后台坐席台 │ │ AI(现有 XhsAi) │
│ 聊天 + 入口 │ │ Inbox + 上下文│ │ HTTP /xhs/ai/chat │
└──────┬──────┘ └──────┬───────┘ └─────────┬──────────┘
│ REST + SignalR │ REST + SignalR │ 转人工时写入消息
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ SimpleAdmin.Api │
│ CsConversationService(HTTP) + CsChatHub(SignalR) │
└─────────────┬───────────────────────────────┬───────────────┘
│ │
▼ ▼
MySQL(会话/消息) Redis(在线状态 + SignalR backplane)
│
▼(可选)
RabbitMQ / Notice(离线提醒,P1 末或 P2)
4.2 与现有模块关系
| 现有能力 | 关系 |
|---|---|
Customer.CustomerService |
默认 Assignee 解析源 |
/customer/service-advisor |
P0 展示;P1 无人在线时降级 |
AiAdvisorShell / /xhs/ai/chat |
一线;「转人工」创建会话并可选灌入最近 AI 对话 |
OmsFileUploadHelper |
聊天图片上传(新 folder:cs) |
NoticeService |
可选:新消息站内信 |
| 数据权限 | 坐席列表/认领范围按客服数据权限过滤 |
| Traefik Ingress | P1 须允许 WebSocket Upgrade(SignalR) |
4.3 技术选型
| 点 | 选型 | 说明 |
|---|---|---|
| 实时 | ASP.NET Core SignalR | 官方、易鉴权、易水平扩展 |
| 前端 SDK | @microsoft/signalr |
勿复用 hiprint 的 socket.io-client |
| Backplane | Redis | 多 Api 实例必开 |
| 落库 | SqlSugar + MySQL | 与 OMS 一致 |
| 在线状态 | Redis KV | cs:agent:{userId},心跳续期 |
| 附件 | 本地 wwwroot/uploads/cs/{yyyyMM}/ |
图片 ≤ 5MB,与现规则一致 |
4.4 服务端部署注意
AddSignalR().AddStackExchangeRedis(...)或与现有 FreeRedis 兼容的 backplane 方案(实现阶段选定一种,避免双 Redis 客户端混乱)。- Traefik / Ingress 对 Hub 路径(建议
/hubs/或 SignalR 约定路径)开启 WebSocket;超时按长连接调大。前后端分域名时,浏览器直连 api.*,前端静态容器无需反代 Hub。 - SPA 回退由前端容器
serve -s处理;见规范 §十五。
5 核心流程
5.1 P0:联系专属客服
客户点「联系客服」
→ GET /customer/service-advisor
→ 有微信:展示二维码 + 复制微信号
→ 无配置:提示「暂未分配专属客服,请联系管理员」
→ 若从订单进入:提供「复制单号」
5.2 P1:客户发起 / 继续会话
客户进入客服页
→ 若有进行中会话:直接进入
→ 否则创建会话(可选带 orderNo、source、首条留言)
→ 分配:
1) 解析 Customer.CustomerService → 目标坐席
2) 坐席在线:状态=Open,推送给该坐席
3) 坐席离线:状态=Queued,进入公共队列
4) 无人可服务且队列过久(可配置,默认立即):展示微信降级卡片
→ 双方通过 Hub 收发;每条消息落库
5.3 AI → 转人工
客户在 AI 顾问对话中点「转人工」
→ 创建(或复用进行中)会话
→ 将最近 N 条(建议 10)AI 对话写入 CsMessage
SenderType = Bot / Customer
→ 系统消息:「客户从 AI 顾问转入,关联运单:XXX」
→ 进入 §5.2 分配逻辑
→ UI 切换到人工会话视图(同一壳内 Tab 或抽屉)
5.4 坐席接单与结束
坐席打开 Inbox
→ 我的进行中 + 公共待接入(有权限)
→ 认领 Queued 会话 → Assignee=自己,Status=Open
→ 回复文本/图片;可查看右侧订单摘要
→ 结束会话 → Status=Closed(客户可再开新会话)
→ 转接(P1 可做简化版):指定同事 → 改 Assignee + 系统消息
5.5 断线与补拉
SignalR 断线 → 客户端自动重连
→ 重连成功后 HTTP 拉取 cursor 之后的消息(按 MessageId / CreateTime)
→ 不依赖「推送一定到达」作为唯一真相;数据库为真相源
6 页面、路由与权限
6.1 菜单与路由(坐席 / 管理端)
客户服务(新建顶级或挂在「客户」下,评审定)
└── 在线客服 /customerService/inbox
前端:src/router/routes/newModules/customerService.ts
| path | name | component | permission |
|---|---|---|---|
inbox |
CsInbox |
views/customerService/inbox/index.vue |
CustomerService.Inbox.Query |
布局:左会话列表 + 中聊天区 + 右上下文(非表格主列表页;工具按钮仍遵循 32px / 主色变量)。列表类配置页(快捷回复等)P2 再按 table-toolbar 规范做。
6.2 客户侧入口
| 入口 | 路由 / 位置 | P0 | P1 |
|---|---|---|---|
| H5 我的 → 联系客服 | /h5/me 列表项 → 抽屉或 /h5/cs |
微信卡片 | 会话页 |
| H5 / PC AI 顾问 | AiAdvisorShell |
顾问微信抽屉 | 「转人工」 |
| 订单异常 | 订单卡片操作 | 复制单号 + 联系客服 | 带 orderNo 建会话 |
| PC 顶栏(可选) | 未读角标进 Inbox(仅坐席) | — | 有 |
建议客户会话页:
| path | 说明 |
|---|---|
/h5/cs |
H5 客服会话(P1) |
/assistant 内嵌人工面板 |
PC 与 AI 同壳切换(P1) |
6.3 RBAC 权限码
格式:{Module}.{Resource}.{Action}。
| 权限码 | 说明 |
|---|---|
CustomerService.Inbox |
在线客服模块 |
CustomerService.Inbox.Query |
查看收件箱 / 会话详情 / 历史消息 |
CustomerService.Inbox.Reply |
发送消息、上传附件 |
CustomerService.Inbox.Claim |
认领公共队列 |
CustomerService.Inbox.Close |
结束会话 |
CustomerService.Inbox.Transfer |
转接(P1 简化或 P1.1) |
CustomerService.Inbox.ViewAll |
跨专属查看(主管;仍受数据权限) |
客户门户接口:无上述 Permission;仅 loginType=1 + 当前 CustomerId 隔离。
6.4 后端 Service 与前缀
| 类 | 路由前缀 | 说明 |
|---|---|---|
CsConversationService |
/cs/conversation/* |
会话 CRUD / 列表 / 认领 / 关闭 |
CsMessageService |
/cs/message/* |
历史分页、(可选)HTTP 发消息兜底 |
CsAgentService |
/cs/agent/* |
坐席心跳、在线状态 |
CsChatHub |
/hubs/cs |
SignalR |
上传:POST /cs/upload/image(内部走 OmsFileUploadHelper,folder=cs)。
7 数据模型
表前缀建议
t_cs_;字段 PascalCase;审计字段对齐 OMS 公共规范(Id、CreateTime、UpdateTime、IsDeleted等,以项目现有基类为准)。
7.1 t_cs_conversation 会话
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | bigint PK | |
| CustomerId | int | 主客户 Id(子客户发起也归主客户,或记 SubUserId,见下) |
| SubUserId | int? | 子客户 RBAC 用户 Id(可空) |
| AssigneeUserId | int? | 当前坐席用户 Id |
| Status | varchar(20) | Queued / Open / Closed |
| Source | varchar(32) | h5 / assistant / order / ai_transfer … |
| OrderNo | varchar(64)? | 关联运单号(展示用,不强绑订单表 FK) |
| Subject | varchar(200)? | 首条摘要 / 标题 |
| LastMessageAt | datetime? | 排序用 |
| LastMessagePreview | varchar(200)? | 列表预览 |
| CustomerUnread | int | 客户未读数 |
| AgentUnread | int | 坐席未读数 |
| ClosedAt | datetime? | |
| CloseReason | varchar(64)? | agent / customer / system |
| ExtJson | text? | 扩展(AI 会话 Id 等) |
索引建议:
(CustomerId, Status, LastMessageAt)(AssigneeUserId, Status, LastMessageAt)(Status, LastMessageAt)— 公共队列
7.2 t_cs_message 消息
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | bigint PK | |
| ConversationId | bigint | |
| SenderType | varchar(16) | Customer / Agent / Bot / System |
| SenderId | int? | 客户用户或坐席用户 Id |
| MsgType | varchar(16) | text / image / system |
| Content | text | 文本或图片 URL |
| PayloadJson | text? | 结构化扩展 |
| ClientMsgId | varchar(64)? | 客户端去重 |
| CreateTime | datetime |
索引:(ConversationId, Id)、(ConversationId, CreateTime);ClientMsgId 唯一(可按会话维度)。
7.3 坐席在线(可不建表)
Redis:
cs:agent:online:{userId} = 1 TTL 60s(心跳 20s 续)
cs:agent:conn:{connectionId} → userId
7.4 快捷回复(P2)
t_cs_quick_reply:Title、Content、Sort、Enabled — 本期不建。
7.5 状态机
创建
│
▼
Queued ──认领/专属在线自动接入──► Open ──结束──► Closed
│ │
└──────── 客户取消/超时 ────────┴──► Closed
规则:
- 同一
CustomerId同时最多 1 个Queued|Open会话(再进线则复用)。 Closed后新开会话。- 转接只改
AssigneeUserId,不改CustomerId。
8 接口与 Hub 协议
8.1 HTTP — 客户侧(需客户登录)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /cs/conversation/my-active |
当前进行中会话,无则 null |
| POST | /cs/conversation/open |
创建或返回进行中;body: orderNo?, source, firstMessage? |
| GET | /cs/conversation/my-list |
历史会话分页 |
| GET | /cs/message/list |
conversationId + beforeId? + pageSize |
| POST | /cs/message/send |
HTTP 兜底发消息(与 Hub 等价落库) |
| POST | /cs/upload/image |
上传图片 |
| POST | /cs/conversation/close |
客户结束(可选) |
open 响应需带:conversation、assigneeOnline、fallbackAdvisor(无人在线时附带原 service-advisor 结构)。
8.2 HTTP — 坐席侧(需内部登录 + Permission)
| 方法 | 路径 | 权限 |
|---|---|---|
| GET | /cs/conversation/inbox |
Query;query: tab=mine|queue|closed |
| GET | /cs/conversation/detail |
Query;含客户摘要 |
| POST | /cs/conversation/claim |
Claim |
| POST | /cs/conversation/close |
Close |
| POST | /cs/conversation/transfer |
Transfer |
| GET | /cs/message/list |
Query |
| POST | /cs/message/send |
Reply |
| POST | /cs/upload/image |
Reply |
| POST | /cs/agent/heartbeat |
Query(或连上 Hub 即心跳) |
| GET | /cs/conversation/context |
Query;customerId + 最近订单摘要 |
Inbox 数据范围:
mine:AssigneeUserId == 当前用户queue:Status=Queued且当前用户数据权限覆盖该客户(或ViewAll)- 无
ViewAll时,默认还可看到Customer.CustomerService映射到自己的会话
8.3 上下文接口(右侧栏)
GET /cs/conversation/context?customerId=&orderNo? 返回:
{
"customer": { "id": 0, "name": "", "code": "", "group": "", "balance": 0 },
"advisorKey": "",
"recentOrders": [
{ "orderNo": "", "status": "", "statusName": "", "updatedAt": "" }
],
"focusOrder": { "orderNo": "", "status": "", "trackSummary": "" }
}
复用现有订单查询能力,只读摘要,不在客服模块复制订单逻辑。
8.4 SignalR Hub /hubs/cs
鉴权:JWT(query access_token 或 Header,按现有前端封装统一)。
客户端 → 服务端
| 方法 | 参数 | 说明 |
|---|---|---|
JoinConversation |
conversationId |
加入组 cs:conv:{id};校验参与权 |
LeaveConversation |
conversationId |
|
SendMessage |
{ conversationId, msgType, content, clientMsgId } |
落库并广播 |
Typing |
conversationId |
可选,P1.1 |
AgentHeartbeat |
— | 坐席续在线 |
服务端 → 客户端
| 事件 | 载荷 | 说明 |
|---|---|---|
message |
消息 DTO | 新消息 |
conversationUpdated |
会话摘要 | 列表刷新 |
assigned |
会话 Id + 坐席信息 | 客户侧提示已接入 |
closed |
会话 Id | |
fallbackAdvisor |
顾问 DTO | 无人在线降级 |
error |
{ code, message } |
业务错误 |
组设计:
cs:conv:{conversationId}— 会话内消息cs:agent:{userId}— 定向推给坐席cs:customer:{customerId}— 定向推给该客户所有连接cs:queue— 有Claim权限的坐席收「新排队」事件(实现可用权限过滤代替大组广播)
8.5 消息发送统一语义
无论 HTTP 还是 Hub:
- 校验会话状态为
Open(或Queued时允许客户继续留言) ClientMsgId去重- 写入
t_cs_message - 更新会话预览、未读、时间
- 通过 Hub 推送;对方离线则累加未读,P1 可写一条站内信(开关配置)
9 分配与权限细节
9.1 专属客服解析
复用 CustomerServiceAdvisorResolver 思路:
- 读
t_customer.CustomerService(工号 / 用户名 / 业务键,与现网一致) - 映射到 RBAC
UserId - 若映射失败:会话进
Queued,不阻塞客户留言
9.2 在线判定
- 坐席页打开并成功连接 Hub 后开始心跳
- TTL 过期视为离线
- 创建会话时若专属坐席离线 →
Queued+ 返回fallbackAdvisor
9.3 子客户
- 会话
CustomerId= 主客户 Id SubUserId= 发起人- 主客户下其他子账号是否可见:P1 默认 仅发起人 + 主账号 可见(实现时用当前用户与主从关系判断);避免子账号互相窥私聊
9.4 数据权限
坐席 queue / ViewAll 必须走现有数据权限过滤器(docs/数据权限设计.md),禁止仅靠前端藏按钮。
10 前端交互要点
10.1 坐席 Inbox(PC)
┌──────────────┬─────────────────────┬──────────────────┐
│ Tab:我的/ │ 聊天区 │ 客户卡片 │
│ 排队/已结束 │ 气泡 + 输入框 │ 最近订单 │
│ 会话列表 │ 系统消息/AI 记录 │ 聚焦运单摘要 │
│ 未读角标 │ 结束 / 转接 │ 打开订单详情链 │
└──────────────┴─────────────────────┴──────────────────┘
- 主色使用
var(--app-primary-color) - 按钮高度 32px;危险操作(结束)二次确认
- 权限:
hasPermission('CustomerService.Inbox.Reply')等
10.2 客户侧(H5)
- P0:底部抽屉展示顾问二维码(与 AI 壳现抽屉一致)
- P1:独立
/h5/cs全屏聊天气泡;顶栏显示接入状态(排队中 / 某某客服已接入 / 请添加微信) - 与 AI Tab 关系:AI 页提供「转人工」;人工页提供「回 AI 顾问」
10.3 AI 同壳
- 保留现有 AI 消息流
- 转人工后:上方可折叠「AI 对话记录」,下方进入人工输入
- 或整页切换,但 必须带上 order 上下文条(与
useAskAdvisor一致)
10.4 前端工程目录(建议)
src/api/cs/
src/views/customerService/inbox/
src/views/h5/customer-center/cs/
src/components/CsChat/ # 气泡、输入框、连接状态(客户/坐席复用)
src/composables/useCsHub.ts
11 配置项与开关
| 配置键 | 默认 | 说明 |
|---|---|---|
Cs:Enabled |
false | 总开关;false 时入口仅 P0 微信 |
Cs:SignalREnabled |
true | 可降级为纯 HTTP 轮询(应急) |
Cs:QueueFallbackSeconds |
0 | 0=创建即若专属离线展示微信;>0 为等待秒数 |
Cs:AiTransferHistoryCount |
10 | 转入时灌入 AI 消息条数 |
Cs:OfflineNoticeEnabled |
false | 新消息写站内信 |
Cs:MaxImageSizeMb |
5 | 附件上限 |
配置可落 t_system_other_config 或 appsettings,实现阶段二选一,保持与现网配置风格一致。
12 验收标准
12.1 P0
- H5「联系客服」可展示专属顾问姓名 / 微信 / 二维码
- 从异常订单可复制单号
- 未分配顾问时有明确空态,不白屏
- AI 壳仍可打开顾问抽屉
12.2 P1
- 客户可创建会话、发文本/图片,刷新后历史仍在
- 专属客服在线时自动进入「我的」列表并实时收到消息
- 专属离线时进排队;有权限坐席可认领
- 无人可服务时返回微信降级卡片
- AI「转人工」带最近对话与 orderNo
- 坐席右侧能看到客户与最近订单摘要
- 结束会话后客户再进线为新会话
- 双实例部署下消息不丢(Redis backplane)
- Traefik / Ingress WebSocket 升级验证通过
- 权限码控制认领 / 回复 / 结束;客户无法调坐席接口
12.3 非功能
- 消息
ClientMsgId重复提交不双写 - 客户只能读写本客户会话
- 图片上传超限有错误提示
- Hub 断线重连后消息可补齐
13 实施顺序(建议)
| 序号 | 任务 | 阶段 |
|---|---|---|
| 1 | P0 入口与顾问卡片打通(H5 + 异常订单) | P0 |
| 2 | DDL:t_cs_conversation / t_cs_message + 仓储 |
P1 |
| 3 | HTTP 会话/消息/上传 API + 权限码种子 | P1 |
| 4 | 坐席 Inbox 静态联调(可先轮询) | P1 |
| 5 | SignalR Hub + 前端 useCsHub + Traefik WS |
P1 |
| 6 | 分配逻辑 + 在线心跳 + 微信降级 | P1 |
| 7 | AI 转人工 + 上下文侧栏 | P1 |
| 8 | 未读角标 +(可选)站内信 | P1 |
| 9 | 转接 / 快捷回复 / 公众号触达 | P2 |
DDL 脚本路径(落地时):oms0414-back/sql/migrate_cs_module.sql
权限种子:随 RBAC 菜单 SQL 或现有权限导入方式一并提交。
14 风险与对策
| 风险 | 对策 |
|---|---|
| 坐席不用系统、仍只回微信 | P0 微信永远保留;P1 强调「转人工带单号」提升系统内处理价值 |
| SignalR 经网关失败 | Ingress 允许 Upgrade + 验收清单;可临时 SignalREnabled=false HTTP 轮询 |
| 多实例串消息 | 强制 Redis backplane,禁止单机假设 |
| 客户把客服当即时 IM 闲聊 | 入口文案强调「物流咨询」;系统消息提示工作时间(可配置) |
| AI 灌入消息过长 | 限制条数与单条长度;仅转人工时写入 |
CustomerService 键与 UserId 映射不稳定 |
收敛解析规则并在客户编辑页校验「可映射」 |
15 开放问题(评审需拍板)
- 菜单挂载:独立「客户服务」还是挂在现有「客户」菜单下?
- 子客户会话可见性:是否允许主账号查看所有子账号会话?
- 工作时间:是否首版就做「非工作时间仅微信」?
- P1 是否包含转接:还是 P1 只做认领 + 结束?
- 站内信提醒:P1 默认开还是关?
- 客户 PC 端:是否必须与 H5 同步上线人工会话,还是 H5 优先?
16 附录:与 H5 设计方案的衔接
| H5 文档点 | 本文落点 |
|---|---|
| 我的 → 联系客服 | §6.2 / P0~P1 |
| 异常 → 问 AI / 联系客服 | §5.1、§5.3 |
| 复制单号给人工客服 | P0 保留;P1 升级为自动带 orderNo |
| AI 顾问为随身客服 | 仍为一线;人工为升级路径,不替换 AI Tab |
文档维护:评审结论请回写 §15;开发启动后将「文档状态」改为「开发中」,并补充实际 DDL 文件名与权限种子脚本路径。