CsSdk 智能客服
智能客服 SDK(带界面版):提供 <re-cs-chat /> 开箱即用组件与无 UI 核心层 createCsSdk,支持 SSE 流式对话、转人工、满意度评价与 WebSocket 坐席推送
演示
当前版本:v0.2.1(协议版本
sdk_version0.1.1)。
基于 uni-app x / UTS 实现的智能客服 SDK,提供无 UI 核心层(
createCsSdk)与开箱即用的界面组件(<re-cs-chat />),支持 App(Android / iOS / HarmonyOS)、H5、微信小程序 三端。 聊天协议与 Web 版@reglow/customer-serviceSDK 完全同源(/api/v1/cs/*,SSE 流式 + WebSocket 坐席推送),同一后端可直接复用。
特性
- 协议同源:
GET /cs/sdk-config(公开配置/FAQ/二维码/联系方式)、POST /cs/chat(SSE 流式对话)、GET /cs/history(历史恢复)、POST /cs/transfer(转人工)、POST /cs/rating(满意度评价)、POST /cs/upload(图片上传)、WS /cs/ws/user(坐席实时消息)。 - 带界面组件:
<re-cs-chat />两种形态——float(默认,内置悬浮球 + 工具菜单 + 全屏弹窗聊天)/page(整页在线客服)。含消息气泡 / 图片 / 流式追加 / 正在输入 / 转人工 / 满意度评价、FAQ 搜索与提问、历史消息恢复,界面功能对照 Web 版sdk_template.html。 - 无 UI 核心层:
createCsSdk(config)返回CsSdk,纯逻辑可独立用于自定义界面。 - SSE 流式:H5 端
fetch + ReadableStream逐行解析data:增量实时输出;App / 小程序端自动降级为一次性拉取完整 SSE 文本后顺序回调(见下方「SSE 流式的端差异」)。 - WebSocket 推送:
uni.connectSocket+ 30s 心跳ping+ 断线指数退避重连(最多 5 次)。 - 转人工排队(0.2.1):转人工支持坐席并发容量(默认 5)与 FIFO 派单——无在线坐席时保持 AI 模式并提示「当前人工坐席忙」;在线坐席已满时进入排队,展示位次并每 10s 轮询
GET /cs/queue-status,同时接收 WSqueue_update推送实时刷新位次。 - 会话持久化:
session_id通过uni.setStorageSync持久化,二次进入自动loadHistory恢复。 - 极简 Markdown:客服气泡支持
**加粗**、*斜体*、行内`code`、换行与代码块等宽显示。 - 隐私友好:
optOut()持久化退出标记,退出后不再发起任何聊天请求。
安装
- 将
uni_modules/reglow-cs-sdk目录复制到项目的uni_modules/下(HBuilderX 插件市场安装亦同)。 - 依赖:本插件依赖
uni_modules/reglow-review(easycom 引用其re-icon/re-button/re-empty/re-dialog/re-fab基础组件),需一并安装reglow-review。
快速接入(带界面)
<re-cs-chat> 支持两种形态(mode),按需选用:
① 悬浮版(默认 mode="float")—— 一行接入
内置悬浮球 + 工具菜单(在线客服 / 常见问题 / 扫码咨询 / 联系方式 / 反馈)与全屏弹窗聊天,无需自行拼装:
② 页面版(mode="page")—— 独立「在线客服」整页
宿主在任意入口(个人中心 / 订单页等)跳转到独立客服页;该页内放 mode="page",聊天面板撑满整页(无悬浮球 / 弹窗):
关于微信开发者工具:工具对「自定义组件内部
position: fixed」渲染不可靠。页面版不受影响(无 fixed,全端可用);悬浮版的悬浮球 / 弹窗若在开发者工具中显示异常,请以真机 / H5 为准。
Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
appId | string | 必填 | 客服应用 ID,对应协议 app_id |
apiBase | string | '' | 接口前缀。留空时使用 sdk-config 返回的 api_base,仍为空则取 /api/v1。传 /api/v1(同源)或 https://api.xxx.com/api/v1(跨域)均可自动归一 |
visitorName | string | '' | 访客昵称,随消息与转人工提交 |
primaryColor | string | '' | 主题色,缺省使用 sdk-config.primary_color,再缺省为 #4f46e5 |
mode | string | 'float' | 形态:float(悬浮球 + 菜单 + 全屏弹窗)/ page(整页在线客服) |
showFab | boolean | true | 是否显示悬浮球入口(仅 float) |
visible | boolean | false | 面板是否可见(受控,仅 float,配合 v-model:visible) |
panel | string | 'chat' | 当前面板:chat / faq / qr / contact / feedback |
Events
| 名称 | 载荷 | 说明 |
|---|---|---|
update:visible | boolean | 面板显隐变化(点遮罩 / 关闭时回写,用于 v-model:visible,仅 float) |
ready | { appName, faqTitle, faqEnabled, hasQr, hasContact, primaryColor } | SDK 配置就绪回调 |
组件方法(可选,命令式)
| 方法 | 说明 |
|---|---|
open(panel?: string) | 打开指定面板(默认 chat) |
close() | 关闭面板 |
getSdk() / getConfig() | 取底层 SDK / 服务端配置 |
组件默认挂载后即创建 SDK 并拉取公开配置;面板打开(或页面版进入)时自动连接 WS 并恢复历史消息。
无 UI 接入(createCsSdk)
CsSdk API 与事件
方法
| 方法 | 说明 |
|---|---|
init(): Promise<void> | 拉取 GET /cs/sdk-config 并合并配置(幂等;重复调用安全) |
send(text: string): void | 发送文本,调 POST /cs/chat 并解析 SSE |
sendImage(imageUrl: string): void | 发送图片消息(image_url 字段) |
requestTransfer(): Promise<boolean> | 请求转人工(POST /cs/transfer);返回 false 表示当前无在线坐席(保持 AI 模式),排队中返回 true 并自动开始轮询位次 |
getQueueStatus(): Promise<CsQueueInfo | null> | 查询排队状态(GET /cs/queue-status?session_id=) |
rate(rating: number, comment?: string): Promise<boolean> | 满意度评价 1~5(POST /cs/rating) |
loadHistory(): Promise<Array<ChatMessage>> | 拉取 GET /cs/history 恢复历史 |
connectWS(): void | 建立用户端 WebSocket(有 session_id 才生效) |
disconnect(): void | 断开 WS 并停止心跳 / 重连 |
optOut(): void | 持久化退出标记,此后不再发起请求 |
optIn(): void | 恢复 |
setVisitorName(name: string): void | 动态设置访客昵称 |
getSessionId(): string | 当前会话 ID |
getConfig(): CsSdkConfig | null | 服务端公开配置(init 后可用) |
isStreaming(): boolean | 是否正在等待 SSE 回复 |
事件(onEvent(event: CsSdkEvent))
event.type | 载荷 | 触发时机 |
|---|---|---|
ready | content(欢迎语) | init() 完成,配置就绪 |
session | sessionId | 首条消息后服务端下发会话 ID(已持久化) |
stream | content(增量) | SSE 文本增量(H5 实时;App/小程序一次性到达) |
done | - | 一轮回复结束 |
message | message(ChatMessage) | 完整消息(预留,流式回复由 stream + done 表达) |
transfer | content(提示文案)、agentName、status、queuePosition、waitingTotal、onlineAgents | 命中转人工(SSE transfer_required 或手动 requestTransfer)。status=0 无在线坐席保持 AI;status=1 且有 agentId 已接入;status=1 且无 agentId 进入排队 |
queue_update | content(位次提示)、queuePosition、waitingTotal | 排队位次变化(WS queue_update 推送或 10s 轮询),同一位次只提示一次 |
agent_online | agentName | WS 推送坐席在线 |
session_closed | content | WS 推送人工会话结束 |
error | content | 请求失败 / SSE error 事件 |
CsSdkEvent 结构:{ type, content, sessionId, agentName, message, status, queuePosition, waitingTotal, onlineAgents }(无值字段为空串 / null / 0)。
聊天协议(与 Web 版同源)
| 接口 | 方法 | 说明 |
|---|---|---|
{base}/cs/sdk-config?app_id= | GET | 返回 {app_id, app_name, welcome_message, api_base, primary_color, qr_code:{title,desc,url}, contact:{phone,email,online_url,address,business_hours}, faq_enabled, faq_title, faqs:[{id,title,content}]} |
{base}/cs/chat | POST | body {app_id, session_id?, message?, image_url?, visitor_name?};响应 SSE,每行 data: {json},事件序:session → content(可多次)→ handled_by_agent / transfer_required → done / error |
{base}/cs/history?session_id= | GET | list[{id, session_id, role, content, content_type, image_url, created_at}] |
{base}/cs/transfer | POST | body {app_id, session_id?, agent_key?, visitor_name?} → {session_id, status, agent_id, queue_position, waiting_total, online_agents, message} |
{base}/cs/queue-status?session_id= | GET | 查询排队状态 → {status, queue_position, waiting_total, online_agents} |
{base}/cs/rating | POST | body {session_id, rating(1-5), comment?} |
{base}/cs/upload | POST | multipart file(≤5MB,jpg/png/gif/webp)→ {url} |
{base}/cs/ws/user?session_id= | WS | 服务端推送 agent_message / agent_online / queue_update / session_closed;客户端每 30s 发 ping 保活 |
{base} 为归一化后的接口前缀,即 apiBase 以 /api/v1 结尾时为 apiBase,否则为 apiBase + '/api/v1';默认 /api/v1。
SSE
transfer_required事件同步携带status/queue_position,AI 自动触发转人工时同样区分「忙 / 排队 / 已接入」;进入排队后 SDK 每 10s 轮询排队状态,坐席接入、会话结束、退出服务或断开连接时自动停止轮询。
SSE 流式的端差异(重要)
完整实时流式(逐字增量)仅在 H5 端可用:
#ifdef H5下使用fetch + ReadableStream.getReader()逐行解析data:前缀的 SSE 帧,通过onEvent('stream')实时增量回调,UI 呈现打字机效果。
App(Android/iOS/HarmonyOS)与微信小程序:
uni.request不支持流式读取,SDK 自动降级为「一次性拉取」——uni.request(dataType: 'text',超时 120s)等待整个 SSE 响应结束后,一次性解析全部data:帧并按原顺序触发stream → done回调。界面上表现为:发送后显示「正在输入…」,等待完整回复后一次性展示,功能不缺失、仅非实时。
若需要 App / 小程序端实时流式,可自行改造:将
sse.uts中#ifndef H5分支替换为原生 Socket 代理(由后端 / 网关把 SSE 转推给客户端),SDK 其余部分无需改动;或业务侧改为「发送后按需轮询/cs/history」。
依赖说明
- 依赖
reglow-review(package.json的uni_modules.dependencies已声明):界面组件以 easycom 复用其re-icon/re-button/re-empty/re-dialog/re-fab基础组件。 re-chat家族(re-chat/re-chat-sender/re-chat-message/re-chat-content)的消息模型仅支持纯文本(content: string),无法承载客服图片消息 / 系统提示 / 评分卡片等混合内容,故本插件消息列表与输入区以自绘为主(对照 Web 版气泡样式),基础组件复用上述re-*;如你的场景只用纯文本,也可自行换用re-chat家族。- 不改动
reglow-review与后端任何代码。
Demo
- 悬浮版页面:
pages/cs/cs-sdk(「智能客服 Demo」,一行接入<re-cs-chat />,右下角悬浮球体验)。 - 页面版页面:
pages/cs/cs-page(「在线客服」整页,<re-cs-chat mode="page" />),由 Demo 页入口跳转进入。
隐私说明
optOut()持久化退出标记,退出后send / sendImage / requestTransfer / rate / connectWS / loadHistory均不再发起请求。- SDK 不采集输入框内容之外的任何本地数据;访客昵称由业务侧主动传入。
