公司客户门户的故事
在公司客户门户的设计与实现过程的来龙去脉
背景
我们公司是一个潜在客户获取(Lead Generation)的公司,主要针对公司客户。但是我们公司Account Manager和客户沟通的主要还是考电话和邮件,效率很低。因此我们需要开发一个客户门户,让客户可以在上面查看各种数据,也可以在上面提交信息,提升信息透明度,提升效率。
介绍
客户门户(Client Portal),是用来访问信息和文件的电子平台。举个例子,我们公司做的类似于淘宝,但是没有订单查询和管理,如果你想查询昨天买的东西寄到哪里了,或去年的订单,你就需要打电话或发邮件给客服,很难受。客户门户就是可以把我们客户关系管理系统(CRM)里面的数据整合在一起,客户可以在上面查询。
前情
去年夏天,我们公司就开始了这个项目,但是可能因为我们开发比较忙,或他们高估了这个项目的复杂度,或是自己心里也没底不知道应该做什么样的,所以在网上找了专卖客户门户模板的公司买了一套和我们现在CRM有对接的模板,还外包了一家 UI 公司,协助处理部分 JS 和 CSS 的定制开发。因为不需要什么开发,我也没有参与太多,但据我了解,钱没少花,时间也没有省,因为和外包公司沟通一如既往的不容易,前期也要写各种需求,并且结果也不好,因为是用的模板,和我们公司的数据结构并没有非常适合,操作起来奇蠢无比,模板的限制超级多,客户也没啥兴趣用。最后这个项目就不了了之了,估计一个月都不会有一个人登录。
准备
虽然找的两个外包的成果很垃圾,但是感觉负责人还是学到了一些东西,比如我们需要哪些页,大概需要什么内容,所以新的客户门户的设计阶段还是很顺利,基本就是复现加改进已知的缺陷。
前端
如上一段所说,尤其是前端,需要有哪几个页面,内容大概是什么样的,基本上都已经有大概。这几年流行的前端框架就是Vue和React,我上学的时候之前用Vue做过几个项目,用React过一两次,不过最近React变得越来越流行了,所以我决定用React来做这个项目。在此之中,复习和更多练习了React的各种特性,Hooks,Router等等,感觉等有机会可以写一写,做一些学习笔记。至于UI,我们公司有个同事虽然职位里没有设计,但是也会点,基本就是他说自然语言,我写CSS,也是把UI做得差不多了。 我们的成品有Dashborad,包括一些统计数字和最近两周的重要信息;3个关键模组(以及关联模组)的列表页,页面上就是一个大表格,其中有一个模组页里包括一个modal可以更新数据;几个几乎是静态的页面;1个change log页;当然还是登录,设置/重置密码和catch all的错误页面。继我们公司一贯传统,做之前完全没有人做规划,做完了才发现有些页面是多余的,有些页面是缺失的,最后还是要花时间去改。有几天每天早上catch up时候都要加需求,这真有点头疼。 写起来几乎都是用copilot辅助写的,效率还是很高的。因为需求是随着开发进展逐渐增加,经常在初始写一个ts文件的时候,因为需求的功能不多,我没有太多的把组件拆分成独立的文件,这导致后面有的文件行数会有点多,copilot在编辑的时候会有点慢,或者编辑出错。不过好在在我的引导下,copilot可以帮我拆分成独立的文件,感觉这是一个之后可以思考的点。 我们是使用Firebase Hosting来做静态文件的托管,CI/CD是用Bitbucket的pipeline来做的。
鉴权
因为我们的客户都是大公司,所以安全性尤为重要。之外我们找过印度外包写过另外一方面业务的客户门户,是他们自己写的鉴权,把用户名和加密密码存在数据库里,感觉非常智障,并且时而出问题,还有一些安全性问题。所以这次我选择了集成的解决方案。因为在Firebase已经有Project,所以直接用Firebase Authentication来做身份验证。他们的sdk非常不错,直接把官方文档里的例子改改就行。并且他们支持我们之前就比较希望有的一次性链接登录,用户只需收到一封邮件,点击链接就可以登录了。
Firebase Authentication有一个小问题是不允许修改重置密码邮件和一次性链接登录邮件的内容如这一篇Stack Overflow。所以我就自己写了Cloud Function来做这个功能,接受一个用户邮箱,用admin.auth().generatePasswordResetLink()
或admin.auth().generateSignInWithEmailLink()
来生成链接。不过这里生成的链接是project-name.firebaseapp.com,我直接加了一个字符串替换,替换成我们的域名,他们SDK做的还挺好,可以支持这么操作,这样客户看到的所有的域名都是同一个。
另一个遇到的小问题是generatePasswordResetLink
最好也要设置url参数。重置密码页面其实是Firebase提供的,如果不写url参数,用户设置完密码,页面完全不会引导到登陆界面,因为这个页面是Firebase提供的,所以也不会有我们的导航栏。我得到的反馈是,客户设置完密码后,页面就说设置成功了,没别的,客户也不知道该做什么,他就在邮件里重新点了一次链接,结果提示这个链接已经使用过了,所以他也不知道该怎么进入门户。实话实话这不是很合理嘛,可能我们的客户真的对电脑一窍不通叭。
PS在我写这篇文章的时候,据我尝试,Firebase Authentication已经支持重置密码邮件的自定义内容,但是一次性链接登录邮件还是不支持的。
后端
流程其实还挺难,因为这次的客户门户,并不是门户里每一页或每一个表对应一个CRM的模组,并且我们的CRM是买的SaaS,并不能直接链接到MySQL之类的数据库,只能用提供的Restful API来访问数据。后端需要做的就是把这些API整合在一起,做一个中间层,给前端提供一个统一的API。 我最终选择了Nest.js来做后端。我们用到的优点包括:
- 它使用TypeScript编写,有静态检查之类的ts的众所周知的好处
- 它的模块化设计,可以把不同的功能拆分成不同的模块,方便维护和扩展
- 支持中间件和守卫,做权限控制时比较方便
- Nest.js的社区比较好,并且copilot对它的支持也很好
- 基于 Node.js,启动速度快,适合构建高并发 API 网关或中间层
开发的时候感觉有点像Spring Boot,都是用装饰器来做路由和依赖注入,都是模块化设计。不过后面项目比较急,在写抽象和测试的时候有点懒,很多地方没有写,需要之后有时间再补上。有时候因为可能一个CRM模块只需要一种请求,就偷懒干脆写在别的模块里了,后面如果需求增加,就疯狂后悔,这样不好,不能这样啦。尤其是在项目上线后再加的需求,这样还需要再把之前的endpoint给重定向到新的模块里。有时候不写接口或DTO,直接any
或@Body()
,虽然没有造成问题,但浪费了ts的好处。
使用者的反馈
上线后,是由Account Manager来给客户介绍的,他们后来告诉我客户都非常喜欢,说比之前的强太多了,各种信息一目了然,大大降低沟通成本。并且在新的客户入驻(on board)的时候,也快了很多,只需要在系统里设置几下,就可以直接触发给客户发邀请邮件。 客户还提出建议增加一些新的页面、表格和小功能,我按照他们的建议做了一些新的页面,客户也很满意。
下一步优化计划
- 补齐接口层面的 DTO、抽象类型定义
- 清理临时塞进去的逻辑
- 强化客户视角的使用体验,包括登录、导航、出错提示、流程引导等
- 优化UI,尤其是表格的UI,比如引入合适的UI组件
- 补上测试用例
- 整理项目技术文档 & 开发说明
不过这些都是后话了,感觉从我们公司的角度上说,最重要的项目尽快上线,这点其实我有点烦恼,几乎不管写啥项目,流程都是这样,然后快写完,需要做一些优化的时候,我就要被叫道新的项目上去。反正就尽可能找时间把上面的这些优化写完吧。
评论