广告投放系统(二)

自家广告组件的演化历程。

2017年的时候,我们发布了自家小程序,当时小程序还不支持任何广告变现,所以上线之后一段时间并没有任何广告内容。

后来,随着小程序商业化的推进,小程序平台推出了原生的平台广告组件,因此我们也第一时间接入了下小程序的平台广告。

第一阶段:直接使用平台广告

由于当时也没有预期,因此这一阶段的接入很简单,就是页面指定的位置放置平台广告组件,比如:

1
2
3
<page>
<mp-ad></mp-ad>
</page>

其中 mp-ad 为平台广告组件。

一段时间之后,有商户找我们合作,希望在我们的应用中进行广告推广,这就意味着在平台广告之外,我们还需要支持自采广告。

第二阶段:增加自采广告

展示广告之前,先向后台请求广告对象:

1
2
3
4

const TYPE_MP = 0; //平台广告
const TYPE_SELF = 1; //自采广告
const ad = fetchAd();

在页面内部进行一个判断:

1
2
3
4
5
6
7
8

<page>
{{ if ad }}
<img src="{{ad.image_url}}" />
{{ else }}
<mp-ad></mp-ad>
{{ endif }}
</page>

发现有自采的机会之后,我们希望一个广告位能够同时放多个自采买的广告,于是就需要支持广告轮播。

第三阶段:支持轮播广告

这个需求出现之后,我们面临两个问题:

  1. 广告形式可能会更多,页面内的判断会变复杂,于是对广告组件进行封装。
  2. 以前的数据结构设计不合理,现在需要的是一个广告数组,但以前只返回单个广告对象。接口需要变动,而且这个变动还是不兼容的,于是增加字段。

创建应用内部的广告组件:ad-loader。结构如下(组合模式):

1
2
3
4
5
<ad-loader>
<mp-ad></mp-ad>
<self-ad></self-ad>
<else-ad></else-ad>
</ad-loader>

其中,self-ad 为自采广告组件,else-ad 为未来可能引入的其他类型广告组件。

修改广告接口,返回一个广告列表:

1
2

const {type, ads} = fetchAds();

self-ad 组件逻辑:

1
2
3
4
5
6
7
<view>
{{ if ads.length == 1 }}
<img src="{{ads[0]}}"></img>
{{ elif ads.length > 1 }}
<swiper src="{{ads}}" />
{{ endif }}
</view>

ad-loader 组件逻辑:

1
2
3
4
5
6
7
<view>
{{ if ads.length }}
<self-ad src="{{ads}}" />
{{ else }}
<mp-ad></mp-ad>
{{ endif }}
</view>

这么跑了一段时间之后,对比收益发现,原生平台广告的收益较高,因此我们再次修改了策略:先显示平台广告,如果没有平台广告,就显示自采广告。

于是广告加载逻辑变成:

  1. 页面加载完成之后,放置平台广告组件
  2. 如果平台广告加载失败,则移除平台广告组件,放置自采广告组件
  3. 如果自采广告加载失败,则移除自采广告组件,不显示广告

第四阶段:优先展示平台广告

由于 ad-loader 已经隔离了广告逻辑,所以只需要更改 ad-loader 组件即可,无需更改其他内容。

ad-loader :

1
2
3
4
<ad-loader>
<mp-ad loadError="onLoadError"></mp-ad>
<swipe-ad></swipe-ad>
</ad-loader>

ad-loader.js

1
2
3
4
5
showMpAd();
adDom.addEventListener('onLoadError', function(){
hideMpAd();
showSwipeAd();
});

第五阶段:修改广告优先级

这次希望部分指定广告可以在平台广告之前显示,接口又不满足了,需要增加字段。广告逻辑变为:

  1. 页面加载完成之后,判断自采广告优先级
  2. 如果自采广告优先级高,则放置自采广告组件,无视结果
  3. 如果自采广告优先级低,则放置平台广告组件
  4. 如果平台广告加载失败,则移除平台广告组件,放置自采广告组件
  5. 如果自采广告加载失败,则移除自采广告组件,不显示广告

代码:略

第六阶段:控制广告的展示与否

上面所有的逻辑都有一个共同的遗留问题,没法控制广告是否展示。所以增加一个判断:

如果没有获取到广告对象,则不展示广告(包括平台广告)。

第七阶段:独立控制每一种类型的广告

其实兜兜转转又回来了,新问题就是现在只想展示平台广告,却没法控制了,着实尴尬。

最终结果,接口和逻辑设计的时候,一定要保持简单独立,最后的措施还是:

  1. 返回一组广告列表,每个广告对象都带有广告组件的类型。
  2. 依次判断广告对象的展示结果,如果有广告展示成功,则完成广告逻辑。

讨论:广告类型应该是白名单机制还是黑名单机制?

白名单,只显示指定类型的广告,任何广告实例只处理自身可以处理的广告类型,如果没有任何广告实例可以处理,那么广告就不应该被显示。