前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >广告系统设计与实现(八) -广告检索系统的设计与实现 - 下

广告系统设计与实现(八) -广告检索系统的设计与实现 - 下

作者头像
chenchenchen
发布2019-09-02 17:18:59
1.8K0
发布2019-09-02 17:18:59
举报
文章被收录于专栏:chenchenchenchenchenchen

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/weixin_38004638/article/details/91974842

8.4 广告检索服务

媒体方发起广告请求,检索服务检索广告数据(条件匹配过程),返回响应

媒体方的请求包含的三个要素 媒体方的请求标识 mediaId 请求基本信息 RequestInfo: requestId,adSlots, App,Geo,Device 匹配信息 FeatureInfo:KeywordFeature, DistrictFeature,ItFeature, FeatureRelation

8.4.1 媒体方请求对象的定义

定义检索服务中媒体方发起的请求对象

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SearchRequest {
    // 媒体方的请求标识
    private String mediaId;
    // 请求基本信息(广告位信息AdSlot、终端信息App、设备信息Device、地域信息Geo)
    private RequestInfo requestInfo;
    // 匹配信息(关键字、兴趣、地域)
    private FeatureInfo featureInfo;

    /**
     * 请求信息
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class RequestInfo {
        //唯一请求ID
        private String requestId;
        //基本请求信息
        private List<AdSlot> adSlots;
        private App app;
        private Geo geo;
        private Device device;
    }
    /**
     * 匹配信息
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class FeatureInfo {
        //根据关键字、地域、兴趣筛选推广单元
        private KeywordFeature keywordFeature;
        private DistrictFeature districtFeature;
        private ItFeature itFeature;
        //默认全部匹配
        private FeatureRelation relation = FeatureRelation.AND;
    }
}

广告位信息AdSlot

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdSlot {
    // 广告位编码
    private String adSlotCode;
    // 广告位置类型
    private Integer positionType;
    // 宽和高
    private Integer width;
    private Integer height;
    // 广告物料类型: 图片, 视频
    private List<Integer> type;
    // 最低出价
    private Integer minCpm;
}

应用终端信息App

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class App {
    // 应用编码
    private String appCode;
    // 应用名称
    private String appName;
    // 应用包名
    private String packageName;
    // 请求页面 activity名称
    private String activityName;
}

设备信息Device

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Device {
    // 设备 id
    private String deviceCode;
    // mac地址
    private String mac;
    // 设备ip
    private String ip;
    // 机型编码
    private String model;
    // 分辨率尺寸
    private String displaySize;
    // 屏幕尺寸
    private String screenSize;
    // 设备序列号
    private String serialName;
}

地域信息Geo

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Geo {
    //纬度
    private Float latitude;
    //经度
    private Float longitude;
    //所在城市
    private String city;
    private String province;
}

8.4.2 检索服务响应对象的定义

检索服务中检索系统,根据媒体请求,返回响应

定义响应对象格式

匹配信息 FeatureInfo:KeywordFeature, DistrictFeature,ItFeature, FeatureRelation

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SearchResponse {
    public Map<String, List<Creative>> adSlot2Ads = new HashMap<>();
    /**
     * 返回创意信息
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Creative {
        private Long adId;
        private String adUrl;
        private Integer width;
        private Integer height;
        private Integer type;
        private Integer materialType;
        // 展示监测 url:监测上架需要展示的广告
        private List<String> showMonitorUrl = Arrays.asList("www.imooc.com", "www.imooc.com");
        // 点击监测 url:监测需要点击的广告,类似手机开屏广告
        private List<String> clickMonitorUrl = Arrays.asList("www.imooc.com", "www.imooc.com");
    }

    //广告创意索引对象CreativeObject -> 响应返回创意信息Creative
    public static Creative convert(CreativeObject object) {
        Creative creative = new Creative();
        creative.setAdId(object.getAdId());
        creative.setAdUrl(object.getAdUrl());
        creative.setWidth(object.getWidth());
        creative.setHeight(object.getHeight());
        creative.setType(object.getType());
        creative.setMaterialType(object.getMaterialType());
        return creative;
    }
}

广告二次筛选:地域DistrictFeature

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DistrictFeature {
    private List<ProvinceAndCity> districts;
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class ProvinceAndCity {
        private String province;
        private String city;
    }
}

广告二次筛选:兴趣ItFeature

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ItFeature {
    private List<String> its;
}

广告二次筛选:关键字KeywordFeature

代码语言:javascript
复制
@Data
@NoArgsConstructor
@AllArgsConstructor
public class KeywordFeature {
    private List<String> keywords;
}

广告筛选关系:全部匹配and,部分匹配or

代码语言:javascript
复制
public enum FeatureRelation {
    OR,AND
}

广告单元对象的位置类型字段信息

代码语言:javascript
复制
public class AdUnitConstants {
    public static class POSITION_TYPE {
        //开屏(打卡应用)
        public static final int KAIPING = 1;
        //贴片(视频播放前)
        public static final int TIEPIAN = 2;
        //中贴(视频播放中间)
        public static final int TIEPIAN_MIDDLE = 4;
        //暂停贴(暂停时广告)
        public static final int TIEPIAN_PAUSE = 8;
        //后贴(视频播放完)
        public static final int TIEPIAN_POST = 16;
    }
}

在AdUnit索引对象AdUnitObject中添加isAdSlotTypeOK方法,判断检索请求对象adSlotType 和 广告单元索引AdUnit ,两者的positionType是否匹配

代码语言:javascript
复制
public static boolean isAdSlotTypeOK(int adSlotType, int positionType) {
    switch (adSlotType) {
        case AdUnitConstants.POSITION_TYPE.KAIPING:return isKaiPing(positionType);
        case AdUnitConstants.POSITION_TYPE.TIEPIAN:return isTiePian(positionType);
        case AdUnitConstants.POSITION_TYPE.TIEPIAN_MIDDLE:return isTiePianMiddle(positionType);
        case AdUnitConstants.POSITION_TYPE.TIEPIAN_PAUSE:return isTiePianPause(positionType);
        case AdUnitConstants.POSITION_TYPE.TIEPIAN_POST:return isTiePianPost(positionType);
        default:return false;
    }
}

/**
 * 判断广告位置信息positionType是否匹配预定类型
 */
private static boolean isKaiPing(int positionType) {
    return (positionType & AdUnitConstants.POSITION_TYPE.KAIPING) > 0;
}
private static boolean isTiePian(int positionType) {
    return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN) > 0;
}
private static boolean isTiePianMiddle(int positionType) {
    return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN_MIDDLE) > 0;
}
private static boolean isTiePianPause(int positionType) {
    return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN_PAUSE) > 0;
}
private static boolean isTiePianPost(int positionType) {
    return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN_POST) > 0;
}

在AdUnitIndex中匹配推广单元中位置信息positionType

代码语言:javascript
复制
private static Map<Long, AdUnitObject> objectMap;
static {objectMap = new ConcurrentHashMap<>();}
//匹配推广单元中位置信息positionType
public Set<Long> match(Integer positionType) {
    Set<Long> adUnitIds = new HashSet<>();
    //遍历所有索引对象,添加匹配positionType成功的推广单元
    objectMap.forEach((k, v) -> {
        if (AdUnitObject.isAdSlotTypeOK(positionType, v.getPositionType())) {
            adUnitIds.add(k);
        }
    });return adUnitIds;
}

8.4.3 构造检索服务的响应对象

获取响应接口

代码语言:javascript
复制
public interface ISearch {
    //获取广告创意数据
    SearchResponse fetchAds(SearchRequest request);
}

检索服务的匹配过程 核心的思想是循环遍历媒体方请求的广告位,将匹配范围由大变小,越是能过滤更多的推广单元的条件匹配,越是先执行。对 于每一个广告位,匹配过程如下: 构造检索服务的响应对象,根据广告位置类型实现对推广单元的预筛选,根据匹配信息实现对推广单元的再筛选

通过推广单元获取关联的创意实现,填充检索服务响应对象

代码语言:javascript
复制
@Slf4j
@Service
public class SearchImpl implements ISearch {

    public SearchResponse fallback(SearchRequest request, Throwable e) {
        return null;
    }

    @Override
    @HystrixCommand(fallbackMethod = "fallback")
    public SearchResponse fetchAds(SearchRequest request) {

        // 请求的广告位信息
        List<AdSlot> adSlots = request.getRequestInfo().getAdSlots();

        // 响应对象Feature
        KeywordFeature keywordFeature = request.getFeatureInfo().getKeywordFeature();
        DistrictFeature districtFeature = request.getFeatureInfo().getDistrictFeature();
        ItFeature itFeature = request.getFeatureInfo().getItFeature();

        FeatureRelation relation = request.getFeatureInfo().getRelation();

        // 构造响应对象
        SearchResponse response = new SearchResponse();
        Map<String, List<SearchResponse.Creative>> adSlot2Ads = response.getAdSlot2Ads();

        //遍历AdSlot
        for (AdSlot adSlot : adSlots) {
            Set<Long> targetUnitIdSet;

            //根据广告位置类型一次筛选,获取初始推广单元AdUnit
            Set<Long> adUnitIdSet = DataTable.of(AdUnitIndex.class).match(adSlot.getPositionType());

            //根据(地域/兴趣/关键字)特征信息进行and/or二次筛选
            if (relation == FeatureRelation.AND) {
                filterKeywordFeature(adUnitIdSet, keywordFeature);
                filterDistrictFeature(adUnitIdSet, districtFeature);
                filterItTagFeature(adUnitIdSet, itFeature);
                targetUnitIdSet = adUnitIdSet;
            } else {
                targetUnitIdSet = getORRelationUnitIds(adUnitIdSet, keywordFeature, districtFeature, itFeature);
            }

            //根据广告单元ID集合获取广告单元索引对象
            List<AdUnitObject> unitObjects = DataTable.of(AdUnitIndex.class).fetch(targetUnitIdSet);
            //判断广告单元状态,筛选无效的广告单元
            filterAdUnitAndPlanStatus(unitObjects, CommonStatus.VALID);

            //根据广告单元ID集合获取创意ID集合
            List<Long> adIds = DataTable.of(CreativeUnitIndex.class).selectAds(unitObjects);
            //根据创意ID集合获取创意对象集合
            List<CreativeObject> creatives = DataTable.of(CreativeIndex.class).fetch(adIds);

            //根据广告位信息AdSlot 实现对创意对象的过滤
            filterCreativeByAdSlot(creatives, adSlot.getWidth(), adSlot.getHeight(), adSlot.getType());

            adSlot2Ads.put(adSlot.getAdSlotCode(), buildCreativeResponse(creatives));
        }
        log.info("fetchAds: {}-{}", JSON.toJSONString(request), JSON.toJSONString(response));
        return response;
    }

    /**
     * 广告筛选关系为部分匹配
     */
    private Set<Long> getORRelationUnitIds(Set<Long> adUnitIdSet, KeywordFeature keywordFeature,
                                           DistrictFeature districtFeature, ItFeature itFeature) {
        //判空
        if (CollectionUtils.isEmpty(adUnitIdSet)) { return Collections.emptySet(); }

        //将广告单元集合保存三个副本
        Set<Long> keywordUnitIdSet = new HashSet<>(adUnitIdSet);
        Set<Long> districtUnitIdSet = new HashSet<>(adUnitIdSet);
        Set<Long> itUnitIdSet = new HashSet<>(adUnitIdSet);

        //三个副本按照每个条件进行一次过滤
        filterKeywordFeature(keywordUnitIdSet, keywordFeature);
        filterDistrictFeature(districtUnitIdSet, districtFeature);
        filterItTagFeature(itUnitIdSet, itFeature);

        //返回三次过滤结果的并集合
        return new HashSet<>(
                CollectionUtils.union(
                        CollectionUtils.union(keywordUnitIdSet, districtUnitIdSet), itUnitIdSet
                )
        );
    }

    /**
     * 根据关键字二次筛选广告单元
     */
    private void filterKeywordFeature(Collection<Long> adUnitIds, KeywordFeature keywordFeature) {
        //判空
        if (CollectionUtils.isEmpty(adUnitIds)) { return; }

        //遍历adUnitIds,调用UnitKeywordIndex中的match方法,判断adUnit中是否含该关键字
        if (CollectionUtils.isNotEmpty(keywordFeature.getKeywords())) {
            CollectionUtils.filter(adUnitIds, adUnitId ->
                    DataTable.of(UnitKeywordIndex.class).match(adUnitId, keywordFeature.getKeywords()));
        }
    }

    /**
     * 根据地域二次筛选广告单元
     */
    private void filterDistrictFeature(Collection<Long> adUnitIds, DistrictFeature districtFeature) {
        //判空
        if (CollectionUtils.isEmpty(adUnitIds)) { return; }

        //遍历adUnitIds,调用UnitDistrictIndex中的match方法,判断adUnit中是否含该地域
        if (CollectionUtils.isNotEmpty(districtFeature.getDistricts())) {
            CollectionUtils.filter(adUnitIds, adUnitId ->
                    DataTable.of(UnitDistrictIndex.class).match(adUnitId, districtFeature.getDistricts()));
        }
    }

    /**
     * 根据兴趣二次筛选广告单元
     */
    private void filterItTagFeature(Collection<Long> adUnitIds, ItFeature itFeature) {
        //判空
        if (CollectionUtils.isEmpty(adUnitIds)) { return; }

        //遍历adUnitIds,调用UnitDistrictIndex中的match方法,判断adUnit中是否含该地域
        if (CollectionUtils.isNotEmpty(itFeature.getIts())) {
            CollectionUtils.filter(adUnitIds, adUnitId ->
                    DataTable.of(UnitItIndex.class).match(adUnitId, itFeature.getIts()));
        }
    }

    /**
     * 判断广告单元状态,筛选无效的广告单元
     */
    private void filterAdUnitAndPlanStatus(List<AdUnitObject> unitObjects, CommonStatus status) {
        //判空
        if (CollectionUtils.isEmpty(unitObjects)) { return; }

        CollectionUtils.filter(
                unitObjects, object -> object.getUnitStatus().equals(status.getStatus())
                && object.getAdPlanObject().getPlanStatus().equals(status.getStatus())
        );
    }

    /**
     * 通过广告位信息AdSlot 实现对创意对象的过滤
     */
    private void filterCreativeByAdSlot(List<CreativeObject> creatives, Integer width,
                                        Integer height, List<Integer> type) {
        //判空
        if (CollectionUtils.isEmpty(creatives)) { return; }

        //筛选状态有效,符合广告位定义的宽高和类型的创意对象
        CollectionUtils.filter(creatives, creative ->
                creative.getAuditStatus().equals(CommonStatus.VALID.getStatus())
                && creative.getWidth().equals(width)
                && creative.getHeight().equals(height)
                && type.contains(creative.getType())
        );
    }

    /**
     * 一个广告位对应一个广告创意
     * 实现过滤的广告创意对象,转换成检索系统返回响应中的创意信息
     * CreativeObject -> SearchResponse : Creative
     */
    private List<SearchResponse.Creative> buildCreativeResponse(List<CreativeObject> creatives) {
        //判空
        if (CollectionUtils.isEmpty(creatives)) { return Collections.emptyList(); }

        CreativeObject randomObject = creatives.get(Math.abs(new Random().nextInt()) % creatives.size());

        return Collections.singletonList(SearchResponse.convert(randomObject));
    }
}

8.4.5 完善广告检索服务入口

在SearchController中定义服务入口,根据请求,返回响应

代码语言:javascript
复制
@Autowired
private final ISearch search;

@PostMapping("/fetchAds")
public SearchResponse fetchAds(@RequestBody SearchRequest request){
    log.info("ad-search: fetchAds -> {}",JSON.toJSONString(request));
    return search.fetchAds(request);
}

在ad-gateway网关中resources/application.yml定义

代码语言:javascript
复制
#检索系统
search:
  path: /ad-search/**
  serviceId: eureka-client-ad-search
  strip-prefix: false
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年06月14日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 8.4 广告检索服务
    • 8.4.1 媒体方请求对象的定义
      • 8.4.2 检索服务响应对象的定义
        • 8.4.3 构造检索服务的响应对象
          • 8.4.5 完善广告检索服务入口
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档