从前端看售后业务
时间:2023-03-14 22:28:04
科技观察
作者:靳泽一入职一年多,大部分时间负责售后前端开发商业。本文主要从前端角度分享售后业务的业务和系统,本文内容如下:从业务入手前端技术架构从业务到技术写在最后从业务作为电子商务企业,售后服务不仅是一笔交易的结束,更是下一笔交易的开始。良好的售后服务可以提升用户体验和口碑,提高用户满意度。转售售后项目主要有两部分:用户端售后页面,客服端后台系统,转售售后系统是一个比较复杂的业务,主要包括用户售后的所有环节-散货销售申请完成。下图是一个简单的售后流程示意图:除了业务流程,售后业务的复杂性还有以下挑战:它涉及多个操作环境:包括转转、找好机、挑货,以及小程序、H5页面等多项业务。例如:3C、图书、游戏、奢侈品、虚拟商品等售后类型:面对维保等复杂业务,如何适应多终端环境,兼容各种业务场景和各种售后类型,退换货,更快更好的应对业务变化,是我们在系统开发时需要思考的问题。前端技术架构我们来看看转转售后系统的前端技术架构图:转转有比较完整的前端技术架构。如上图所示,售后系统的开发完全按照其系统架构进行开发,包括UI协同:BlueLake,可视化辅助平台技术栈:Vue(客户端),React(后台)工具库&组件库:zz-ui、zant-ui、多终端适配器native-adapter、call-appcaller、zz-util工具库等项目改造:apollo配置平台、zz-cli脚手架、umi-cli开发调试:whistle代理、zApi接口管理平台监控平台:哨兵异常监控、性能分析平台、乐高埋点……从业务到技术日常业务开发我主要讲两个方面:用户端页面设计、后台系统,用户端页面设计,以及售后场景的多样性。这样一来,虽然用户端的很多页面都是相似的,但是需要展示不同的客户端、不同的业务、不同的状态。提供给用户的信息各不相同。当需要使用同一套页面来兼容多终端、多业务场景时,使用大量的if-else进行业务逻辑处理,或者自己开发一套页面来做,是非常麻烦的每个场景。面对品类扩容、业务下线等业务变化,对项目的影响和变化会比较大,会大大增加测试回归的成本。因此,我们使用更多的配置方式来解决这个问题。当用户点击进入售后页面时,我们需要根据不同业务在售后的不同状态节点,跳转到不同的售后页面。这里我们需要先进入一个空白的转账页面,在这个页面调用接口查询,根据接口返回链接进入不同的页面,前端不需要做太多的判断,当其他业务需要跳转到售后页面,只需要提供一个转账页面的链接即可。售后类型选择页面,我们会针对不同的产品品类、业务线、客户、申请截止时间,在后台配置页面需要显示的售后服务类型。并关联不同售后类型下的原因配置。在售后申请页面,针对不同的售后类型和售后服务,需要用户填写的信息和表单的交互逻辑是不同的。页面如下:我们使用数据驱动的视图来完成页面逻辑和表单渲染。首先与后台定义表单设计,根据不同场景在Apollo配置平台上配置各种售后申请表单。表单配置示例如下图所示:"formInfo":[{"tip":"",//提示信息"title":"Receiptstatus",//表单名称"placeholder":"",//占位符信息"type":"",//组件类型(比如需要区分输入框是普通输入框还是textarea)"componentRef":"refname",//组件ref值/形式keyvalue(unique)"componentName":"componentName",//组件名称(同一个表单中可能出现多个同名组件)"options":[{"id":"1","name":"Ihavereceivedthegoods","nonRequiredComponentNames":"unlock",////联动信息:选择当前选项后需要隐藏的组件(配置componentRef)"isDefault":"",//是否是默认值"children":[],//subcomponent"requiredFields":[//linkagecomponentrefandoption{"requiredRef":"reasonId","requiredOptions":""}]}],"rules":[//表单组件验证规则{"name":"isRequire","value":"1","message":"Receiptstatusisrequired","messageType":"alert"}]}]配置信息包含表单渲染需要的所有信息和规则,一些文案展示信息比如用户须知也会一起配置。在前端项目中,将页面拆分成组件,根据从界面获取的配置信息渲染页面。代码如下://通用组件
在售后申请页面,我们需要做好到达信息,我们会在后台配置用户端信息展示的配置,可以让不同业务、不同状态的售后服务向用户展示相应的信息。在配置平台,可以根据业务类型配置用户侧展示的流量信息、售后节点信息、推送等。最后,由于配置较多,为了方便使用,开发了配置验证工具。通过配置验证工具,可以对以上所有配置进行验证,提高效率和配置的准确性。这种多种配置的组合对于转售后成熟稳定的售后流程具有诸多优势。对于一些简单的页面信息和表单逻辑修改,产品可以直接修改配置信息来完成。当需要开发上线扩展类目时,我们只需要添加单独的组件,按照相同的模式配置表单,从后端查询返回即可,大大减少了前端的工作量发展。后端系统采用React+Hooks+unstated-next技术栈,全面拥抱Function组件的写法。林语堂说:“懒惰使人进步”。为了更快更好地完成日常工作,我们有更多的时间去“钓鱼”。我们需要提高开发效率,在最短的时间内完成工作。为了方便使用系统的售后人员工作,系统中的表格以如下方式展示。对于本系统中很多重复筛选的表单+表格,我们会通过Forms、表格、弹窗、视图、自定义hook进行封装,基于useAntdTable实现组件封装,在售后系统中对组件进行封装,在原有功能的基础上,结合售后业务逻辑封装自定义Hooks,格式化接口输入输出,分页逻辑过滤表单逻辑,刷新页面等逻辑,实现筛选表单在逻辑上的复用售后体系;使用时只需要传入相应的配置信息,将返回的tableprops和filterprops传递给相应的表单和表单组件即可实现表单页面。可配置开发。exportdefault(requestApi,option={})=>{const{title,filterConfig=[],wrapperParams={},getColumns,...options}=option//...省略部分代码constgetTableData=useCallback((params,formData)=>{const{current,pageSize,sorter:s={},filters:f={}}=params//处理getTableData返回的表的属性和方法//过滤掉filterformNull,undefinednullvalues,''和0不会过滤constfilterNullObj=objFilter(formData||{},(_,value)=>value!==null&&value!==undefined)//......省略部分代码returnrequestApi({...wrapperParams,...p,...filterNullObj}).then((res={})=>({total:+res.totalCount||0,list:res.dataList||res.ticketDownloadTasks||[]}))},[requestApi,wrapperParams])constresult=useAntdTable(getTableData,{defaultPageSize:5,form,...options})const{refresh}=result常量{提交}=结果。搜索缺点tcolumns=useMemo(()getColumns&&getColumns(refresh,wrapperParams),[wrapperParams,refresh,getColumns])result.search={...result.search,//返回过滤框的配置信息filterConfig:typeoffilterConfig==='函数'?filterConfig(submit):filterConfig,form}returnresult}封装filterform比较简单。通过遍历filterConfig配置信息传递给Form.Item,内部封装表单联动、搜索、重置等功能逻辑,使得开发变得可配置。如果业务场景比较多,没有复杂的联动,会继续优化,使用内置的组件类型,通过后端驱动筛选表单。对于售后人员来说,“时间就是生命,效率就是金钱”,效率是他们衡量售后系统好坏的最重要标准。多向工作人员展示需要的信息,并加入一些自动化设计,减少售后人员的操作,提高效率。在售后详情页,通过多个标签展示更多信息,不同岗位的售后工程师通过不同入口进入详情时,会直接定位到相应标签下。另外修改了tab的操作方法。当鼠标悬停在选项卡上时会切换,点击时会刷新当前选项卡信息,方便工程师在详情页查看频繁操作的效率。对于售后收货人员来说,操作收货本质上是一个比较同质化的流水线操作,但是在输入框的聚焦、选择、搜索、清空、点击按钮等人机交互会降低他们的整体审核效率..基于此问题内置关注+自动请求,简化接收人员操作。主要有以下需求:专注于进入页面专注于切换回浏览器标签-专注于切换系统标签/重新滚动到可视化区域-专注于请求数据/表单提交后-专注于请求模式标记模式-防抖200msAutomaticallyrequestorpressEnter自动请求输入模式-本身不会自动请求只有当回车键被按下组件包如下:constFastInput=React.forwardRef(({supportBatch,...otherProps},ref)=>{const[mode,setMode]=useState('print')//打印打标机模式编辑手动输入模式//......省略部分代码//聚焦并选择当前输入boxconstselectAll=usePersistFn((){inputRef.current.focus({cursor:'all'})})//监听输入框是否可见,如果不可见->可见,需要聚焦并选中textconstinViewPort=useInViewport(wrapRef)const{run:printUpdateForm,flush,cancel}=useDebounceFn((value)=>{otherProps.onSubmit?otherProps.onSubmit(value):otherProps.onChange(value)//每次提交方法后,再次全选,减少用户操作;inputRef.current.focus({cursor:'all'})},{wait:200})//手写模式,回车更新表单consteditUpdateForm=(value)=>{//同上,调用提交方法,并选择//??......省略部分代码}//监听浏览器选项卡是否可见-切换到选项卡自动聚焦并选择useEffect((){inputRef.current.focus()constrevalidate=(){if(!isDocumentVisible())returnselectAll()}if(typeofwindow!=='undefined'&&window.addEventListener){window.addEventListener('visibilitychange',revalidate,false复制代码)}return(){window.removeEventListener('visibilitychange',revalidate,false)}},[selectAll])//监听输入框时,在可见区域。当移动到可见区域时,会自动聚焦并选择useUpdateEffect((){if(inViewPort)selectAll()},[inViewPort])//只有标记模式会通过防抖通知父元素constonChange=usePersistFn((e)=>{//......省略部分代码if(mode==='print'){printUpdateForm(values)}})//监听回车键-当前反在标记模式下立即调用shake;输入模式下,直接通知父元素更新constonPressEnter=usePersistFn((e)=>{if(mode==='print'){flush()}if(mode==='edit'){editUpdateForm(e.target.value)}})//切换模式;取消当前防抖consttransform=(mode)=>{if(mode==='print'){cancel()}inputRef.current.focus()setMode(mode)}return(
)})写在最后,虽然目前的转售流程比较完整稳定,但是还是有一些业务痛点,需要我们持续思考和优化:售后是电商企业的“护城河”。在售后业务中,满意度是一个非常重要的指标。如何提高满意度也成为一个非常重要的问题。除了做好日常开发,保证项目质量,我们还需要思考如何通过技术手段提升用户体验,分析用户行为,寻找差评原因。涉及多个业务和多种类型的售后后,售后流程变得非常复杂。当多个流程的耦合导致业务发生变化时,测试回归的工作就很麻烦了。因此,在设计和开发系统时,还需要考虑如何通过技术设计来降低测试成本。如何提高后台系统的运行效率作为业务发展,我们需要衡量业务发展与技术创新的关系。技术服务于企业创造价值。完全脱离业务的技术创新是“耍流氓”。在完成基础业务开发的同时,我们需要思考如何通过技术手段解决业务痛点,从而达到技术创新的目的。