现在很多 ToC 客户端,比如:滴滴、美团、携程等等,都有很多的弹窗,那后端怎么设计更合理、更方便、成本更低呢?

我这里说的弹窗是说一级页面的弹窗,比如客户端的首页、个人中心页面、订单页面等。这种一级页面一般都有专门的部门负责,其他业务方需要接入弹窗,需要通过这个部门来接入。这里定义两种角色:

  • 接入方,是需要在首页等页面投放弹窗的业务部门。
  • 公共,负责首页等公共页面的部门。

这里是我们的设计方案:

1 弹窗后端接口设计

1.1 弹窗类型很重要

首先要约束弹窗类型,

| 序号 | 弹窗类型 | 类型说明 |
| :- | :- | :- |
| 1 | 单图片 | 弹出一张图片,点击图片跳转 |
| 2 | 单Lottie | 弹出一个动图,点击动图或者按钮跳转 |
| 3 | 双Lottie | 弹出一个动图,点击或者自动跳转第二个动图,点击第二个动图跳转|
| 4 | 其他弹窗1 | 比如,需要前端拼接展示完整图片的类型 |
| 5 | 其他弹窗 | 需要不同组合字段组合弹窗资源的都算一种类型 |

1.2 弹窗接口设计

字段名称二级字段字段类型说明
status int成功失败状态码
message String成功失败信息
data
popNameString弹窗名称,可以用来埋点和弹窗区分
popInfoObject弹窗资源资源,每个类型弹窗里面字段不一致

比如图片弹窗 popInfo

字段名称字段类型说明
imgaeUrlString图片url
imageWidthint图片宽度
imageHeight 图片高度
jumpUrlString跳转url
popTypeint弹窗类型

其他类型弹窗按需设计字段即可

2 配置即弹窗

标准类型的弹窗要支持配置即可,不用重复开发,要支持配置,可以有两种类型:

  • 是所有人都出的弹窗,直接配置静态资源即可。
  • 种是根据各个接入方自己的条件判断是否出,弹窗资源接入方自己控制,这种需要接入方提供一个接口,这个接口是一个标准接口。

第一种方式比较简单,就不说了。直接说第二种,直接制定一个标准接口,接入方直接实现即可。

统一入参,仅供参考,按需设计即可:

字段名称字段类型说明
userIdString用户id
userNameString用户昵称
deviceIdString设备id
latString纬度
lonString经度

统一出参,这里以单图片弹窗为例:

字段名称二级字段字段类型说明
status int成功失败状态码
message String成功失败信息
data
imgaeUrlString图片url
imageWidthint图片宽度
imageHeightint图片高度
jumpUrlString跳转地址
其他通用字段String埋点等通用字段

接入的时候,公共方配置接入方的接口地址即可。

3 频控的两种技术方案

基本上除了某些 APP 之外,所有 APP 的弹窗都不会无限制的弹,都需要频控,否则可能导致用户体验的下降和用户的流失。

3.1 redis setex 弹出缓存自动过期

用户每次弹窗都使用redis 的 setex 设置过期时间,这个时间就是业务允许两次弹窗之间的最小间隔时间。

key 设计:popName + userId

优点:

  1. 所有弹窗频控后端可控。
  2. 有问题,可操作性强,可以操作redis,去除某些用户的频控限制。

缺点:

  1. 需要redis,造成架构复杂度上升,如果其他功能不需要使用 redis 的情况下,造成成本上涨。
  2. 用户量大的情况,比如上亿:redis 的key会非常大,造成redis压力。

3.2 前端缓存每个弹窗的最近一次弹出时间

前端存储每个用户弹窗的时间,请求后端的时候,把所有弹窗的上次弹出时间带给后端,由后端计算是否在频控时间范围内。

新弹窗弹出时间,由后端返给前端,前端存储,下次请求的时候带给后端。

优点:

  1. 不需要额外的存储,弹窗上次弹出时间存储在用户的客户端中。
  2. 架构简单。
  3. 问题好排查,直接看请求参数即可。

缺点:

  1. 用户卸载重装客户端的时候,会导致数据丢失,造成频控失效;
  2. 整个频控需要前后端配合才能完成,任何一方有问题,都有可能导致功能出问题。

4 怎么保证速度

异步执行所有弹窗,按顺序返回第一个有数据的弹窗,
参考:Reactor 之 多任务并发执行,结果按顺序返回第一个

欢迎大家提供更好的实现


程序员伍六七
201 声望597 粉丝