本文介绍了大转转客服工作台的设计思考和技术实现,包括系统整体介绍、客服IM与第三屏数据通信、第三屏多页签、客服会话缓存、全埋点统计等方面的内容。
客服IM采用iframe内嵌的独立系统,主要负责当前会话的实时通讯、历史会话的消息展示、与第三屏数据通信的能力。第三屏需要提供解决问题的相关信息,这些信息需要和进线的用户相关,通过postMessage进行数据通信。
为了实现客服在与多个用户进行沟通时,保持会话数据的交互状态不变,需要实现会话缓存功能。通过使用LRU缓存算法和动态挂载真实dom的方式实现会话数据的缓存和替换。
为了评估客服操作的系统使用情况和操作费力度,通过全埋点的方式进行数据统计。通过监听点击事件、dom变化、请求接口等行为数据进行埋点上报。
包括优化客服IM的开发成本和性能、提供排查问题的信息、提供统一且便利的交互操作、适应不同业务场景的诉求、拥抱人工智能技术提升客服的工作效率等。
前言
大转转客服工作台的实践总结,涵盖了系统整体介绍、客服 IM 与第三屏数据通信、第三屏多页签、客服会话缓存、全埋点统计等方面的技术实现和设计思考。今日前端早读课文章由公号 @大转转 FE 授权分享。
正文从这开始~~
客服工作台是什么?它能做解决什么问题?系统设计很复杂吗?带着这些问题,我们一起揭开转转客服工作台的面纱。
-
系统整体介绍
-
客服 IM 与第三屏数据通信
-
第三屏多页签
-
客服会话缓存
-
全埋点统计
-
最后的思考
随着我司业务的拓展,用户咨询或反馈问题的场景和诉求也越来越多,客服团队不断壮大。客服工作台是客服团队用来解答和处理用户问题的操作平台。客服团队分为一线和二线,其中一线客服(后面统称
在线客服
)主要接待用户通过客服入口的进线咨询,二线客服主要通过信息查询、电话外呼等方式处理工单流转进一步解决用户问题。本文提到的客服工作台特指为在线客服服务的系统。
【第3160期】客服发送一条消息背后的技术和思考
系统整体介绍
在线客服包括两部分,图中的左侧两屏属于客服与用户的聊天区域(后面统称客服 IM),其中第一屏展示客服的部分重要服务指标(满意度、首解率和接待量)、客服基本信息(昵称 / 头像 / 状态 / 时长)、当前会话和历史会话列表,第二屏为具体某个会话的聊天内容,而图中的右侧为当前会话对应的所有重要信息(后面统称第三屏),包括用户信息、订单信息、工单信息、售后信息、知识库等。
客服 IM
客服 IM 采用 iframe 内嵌的独立系统,主要负责当前会话的实时通讯、历史会话的消息展示、与第三屏数据通信的能力,后面会介绍客服 IM 与第三屏数据通信的消息分类和实现。
【第2910期】前端视角下的转转客服通信过程
第三屏
第三屏是在线客服工作台至关重要的部分,因为它承载了大部分协助客服解答用户问题的信息。在系统实现层面,第三屏有两个核心的问题要解决 —— 多页签和会话缓存。多页签满足客服打开不同页面,并完成信息查阅和操作的需要,会话缓存是客服在同时处理多个进线且需要来回切换会话时,缓存不同会话状态的技术。
同时为了分析系统设计对于客服费力度(评价使用某个产品、服务来解决问题的困难程度)的影响,需要对在线客服工作台做用户行为的采集和分析。
客服 IM 与第三屏数据通信
上面提到第三屏需要提供解决问题的相关信息,但这些信息需要和进线的用户相关,比如用户信息、用户进线时咨询的订单或商品、用户命中的售后单或工单等。那么就需要 iframe(客服 IM)和外层系统(工单系统)通过 postMessage 进行数据通信。
以工单系统收到消息为例,看下第三屏的代码设计。
第三屏部分将数据通信和视图进行了隔离,Message 为通信层,IframeImpage 为视图层。通信层对 iframe 的 postMessage 消息进行监听收发,并将这些消息存储在 model 中共享和管理数据。
// 在线客服页面的入口
import React from 'react'
import Message from './message'
import IframeImPage from './IframePage'
import type { IframeImPagePropsType } from './@types'
// Message为通信层,IframeImpage为视图层
const IframeIndex: React.FC<IframeImPagePropsType> = (props) => {
return (
<Message {...props}>
<IframeImPage location={props?.location} />
Message>
)
}
export default IframeIndex
// Message
import React, { useRef, useEffect, useState } from 'react'
import { useModel } from 'umi'
import { PostMsg } from '@/utils/PostMsg'
import { useDebounceFn } from 'ahooks'
import type { CovCoreInfoType } from '@/models/@types'
import type {
IframeImPagePropsType,
PcimPostMsgContent
} from './@types'
const IframeImPage: React.FC<IframeImPagePropsType> = ({ children, location, history }) => {
const {
...
setCovCoreInfo,
...
} = useModel<'imWorkPlateform'>('imWorkPlateform')
// 监听postMessage消息
const chatWrap = useRef<PostMsg>(new PostMsg(handleOpenChatWrap, 'createWrap'))
// 避免客服连续快速切换--防抖200ms
const { run: handleOpenChatWrap } = useDebounceFn(handleOpenChatWrapFn, {
wait: 200
})
// 点击用户会话,打开对应的第三屏
const handleOpenChatWrapFn = async(content: PcimPostMsgContent): Promise<void> => {
const {
convId,
contactUid,
...
} = content
... ...
const targetUrl: string = `/home?uid=${contactUid}***`
// 在第三屏打开指定路由页面
handleAddChangeTab({
url: targetUrl,
covCoreInfoData: {
convId,
contactUid,
...
}
})
// model保存会话信息
setCovCoreInfo({
convId,
contactUid,
...
})
}
useEffect(() => {
...
return () => {
// 注销postMessage监听
chatWrap.current?.destroyPostMsg && chatWrap.current?.destroyPostMsg()
}
})
return <div>{children ? children : null}div>
}
第三屏多页签
针对 umi 多页签的实现,我们采用的是 antd 的 Tabs 组件,而具体需要渲染哪些页面,则需要通过自定义路由实现。目前多页签实现的功能如下:
从下面代码实现中可以看到,我们仿照 umi 写了一份路由对象和解析渲染的方法,把即将打开的页面地址,与路由中的 path 进行匹配,拿到对应的 component,交由 TabPannel 进行组件渲染。
//自定义路由
const baseConfig = [
{
title: '首页',
component: (params: UserInfoParamsType) => import('../components/home/index'),
path: '/home',
keepAlivePermanentTab: true
},
{
title: '推荐信息',
component: (params: UserInfoParamsType) => import('../components/NewHome/index'),
path: '/newhome',
keepAlivePermanentTab: true
},
...
]
// 多页签实现RouterView.tsx
const RouterView: React.FC<RouteViewParamsType> = (props) => {
return (
<div>
<Tabs>
{panels.map((curPathKey, idx) => {
return (
<Tabs.TabPane>
// View是动态获取的路由中的组件
<Bundle component={View} />
Tabs.TabPane>
)
})}
Tabs>
div>
}
// Bundle.tsx
import React from 'react'