根据三个维度继续过滤

在上一节中我们实现了根据流量信息过滤的代码,但是我们的条件有可能是多条件一起传给我们的检索服务的,本节我们继续实现根据推广单元的三个维度条件的过滤。

  • SearchImpl类中添加过滤方法
public class SearchImpl implements ISearch {
    @Override
    public SearchResponse fetchAds(SearchRequest request) {
        ...
            // 根据三个维度过滤
            if (featureRelation == FeatureRelation.AND) {
                filterKeywordFeature(adUnitIdSet, keywordFeature);
                filterHobbyFeature(adUnitIdSet, hobbyFeatrue);
                filterDistrictFeature(adUnitIdSet, districtFeature);

                targetUnitIdSet = adUnitIdSet;
            } else {
                getOrRelationUnitIds(adUnitIdSet, keywordFeature, hobbyFeatrue, districtFeature);
            }
        }
        return null;
    }
  • 定义三个方法实现过滤
/**
     * 获取三个维度各自满足时的广告id
     */
    private Set<Long> getOrRelationUnitIds(Set<Long> adUnitIdsSet,
                                           KeywordFeature keywordFeature,
                                           HobbyFeatrue hobbyFeatrue,
                                           DistrictFeature districtFeature) {
        if (CollectionUtils.isEmpty(adUnitIdsSet)) return Collections.EMPTY_SET;

        // 我们在处理的时候,需要对副本进行处理,大家可以考虑一下为什么需要这么做?
        Set<Long> keywordUnitIdSet = new HashSet<>(adUnitIdsSet);
        Set<Long> hobbyUnitIdSet = new HashSet<>(adUnitIdsSet);
        Set<Long> districtUnitIdSet = new HashSet<>(adUnitIdsSet);

        filterKeywordFeature(keywordUnitIdSet, keywordFeature);
        filterHobbyFeature(hobbyUnitIdSet, hobbyFeatrue);
        filterDistrictFeature(districtUnitIdSet, districtFeature);

        // 返回它们的并集
        return new HashSet<>(
                CollectionUtils.union(
                        CollectionUtils.union(keywordUnitIdSet, hobbyUnitIdSet),
                        districtUnitIdSet
                )
        );
    }

    /**
     * 根据传递的关键词过滤
     */
    private void filterKeywordFeature(Collection<Long> adUnitIds, KeywordFeature keywordFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) return;
        if (CollectionUtils.isNotEmpty(keywordFeature.getKeywords())) {
            // 如果存在需要过滤的关键词,查找索引实例对象进行过滤处理
            CollectionUtils.filter(
                    adUnitIds,
                    adUnitId -> IndexDataTableUtils.of(UnitKeywordIndexAwareImpl.class)
                                                   .match(adUnitId, keywordFeature.getKeywords())
            );
        }
    }

    /**
     * 根据传递的兴趣信息过滤
     */
    private void filterHobbyFeature(Collection<Long> adUnitIds, HobbyFeatrue hobbyFeatrue) {
        if (CollectionUtils.isEmpty(adUnitIds)) return;
        // 如果存在需要过滤的兴趣,查找索引实例对象进行过滤处理
        if (CollectionUtils.isNotEmpty(hobbyFeatrue.getHobbys())) {
            CollectionUtils.filter(
                    adUnitIds,
                    adUnitId -> IndexDataTableUtils.of(UnitHobbyIndexAwareImpl.class)
                                                   .match(adUnitId, hobbyFeatrue.getHobbys())
            );
        }
    }

    /**
     * 根据传递的地域信息过滤
     */
    private void filterDistrictFeature(Collection<Long> adUnitIds, DistrictFeature districtFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) return;
        // 如果存在需要过滤的地域信息,查找索引实例对象进行过滤处理
        if (CollectionUtils.isNotEmpty(districtFeature.getProvinceAndCities())) {
            CollectionUtils.filter(
                    adUnitIds,
                    adUnitId -> {
                        return IndexDataTableUtils.of(UnitDistrictIndexAwareImpl.class)
                                                  .match(adUnitId, districtFeature.getProvinceAndCities());
                    }
            );
        }
    }
根据推广单元id获取推广创意

我们知道,推广单元和推广创意的关系是多对多,从上文我们查询到了推广单元ids,接下来我们实现根据推广单元id获取推广创意的代码,let's code.
首先,我们需要在com.sxzhongf.ad.index.creative_relation_unit.CreativeRelationUnitIndexAwareImpl 关联索引中查到推广创意的ids

 /**
     * 通过推广单元id获取推广创意id
     */
    public List<Long> selectAdCreativeIds(List<AdUnitIndexObject> unitIndexObjects) {
        if (CollectionUtils.isEmpty(unitIndexObjects)) return Collections.emptyList();

        //获取要返回的广告创意ids
        List<Long> result = new ArrayList<>();
        for (AdUnitIndexObject unitIndexObject : unitIndexObjects) {
            //根据推广单元id获取推广创意
            Set<Long> adCreativeIds = unitRelationCreativeMap.get(unitIndexObject.getUnitId());
            if (CollectionUtils.isNotEmpty(adCreativeIds)) result.addAll(adCreativeIds);
        }

        return result;
    }

然后得到了推广创意的id list后,我们在创意索引实现类com.sxzhongf.ad.index.creative.CreativeIndexAwareImpl中定义根据ids查询创意的方法。

/**
 * 根据ids获取创意list
 */
public List<CreativeIndexObject> findAllByIds(Collection<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) return Collections.emptyList();
    List<CreativeIndexObject> result = new ArrayList<>();

    for (Long id : ids) {
        CreativeIndexObject object = get(id);
        if (null != object)
            result.add(object);
    }

    return result;
}

自此,我们已经得到了想要的推广单元和推广创意,因为推广单元包含了推广计划,所以我们想要的数据已经全部可以获取到了,接下来,我们还得过滤一次当前我们查询到的数据的状态,因为有的数据,我们可能已经进行过逻辑删除了,因此还需要判断获取的数据是否有效。在SearchImpl类中实现。

  /**
   * 根据状态信息过滤数据
   */
  private void filterAdUnitAndPlanStatus(List<AdUnitIndexObject> unitIndexObjects, CommonStatus status) {
      if (CollectionUtils.isEmpty(unitIndexObjects)) return;

      //同时判断推广单元和推广计划的状态
      CollectionUtils.filter(
              unitIndexObjects,
              unitIndexObject -> unitIndexObject.getUnitStatus().equals(status.getStatus()) &&
                      unitIndexObject.getAdPlanIndexObject().getPlanStatus().equals(status.getStatus())
      );
  }

SearchImpl中我们实现广告创意的查询.

...

//获取 推广计划 对象list
List<AdUnitIndexObject> unitIndexObjects = IndexDataTableUtils.of(AdUnitIndexAwareImpl.class).fetch(adUnitIdSet);
//根据状态过滤数据
filterAdUnitAndPlanStatus(unitIndexObjects, CommonStatus.VALID);
//获取 推广创意 id list
List<Long> creativeIds = IndexDataTableUtils.of(CreativeRelationUnitIndexAwareImpl.class)
                                            .selectAdCreativeIds(unitIndexObjects);
//根据 推广创意ids获取推广创意
List<CreativeIndexObject> creativeIndexObjects = IndexDataTableUtils.of(CreativeIndexAwareImpl.class)
...
根据广告位adslot 实现对创意数据的过滤

因为我们的广告位是有不同的大小,不同的类型,因此,我们在获取到所有符合我们查询维度以及流量类型的条件后,还需要针对不同的广告位来展示不同的广告创意信息。

/**
* 根据广告位类型以及参数获取展示的合适广告信息
*
* @param creativeIndexObjects 所有广告创意
* @param width                广告位width
* @param height               广告位height
*/
private void filterCreativeByAdSlot(List<CreativeIndexObject> creativeIndexObjects,
                                  Integer width,
                                  Integer height,
                                  List<Integer> type) {
  if (CollectionUtils.isEmpty(creativeIndexObjects)) return;

  CollectionUtils.filter(
          creativeIndexObjects,
          creative -> {
              //审核状态必须是通过
              return creative.getAuditStatus().equals(CommonStatus.VALID.getStatus())
                      && creative.getWidth().equals(width)
                      && creative.getHeight().equals(height)
                      && type.contains(creative.getType());
          }
  );
}
  • 组建搜索返回对象
    正常业务场景中,同一个广告位可以展示多个广告信息,也可以只展示一个广告信息,这个需要根据具体的业务场景来做不同的处理,本次为了演示方便,会从返回的创意列表中随机选择一个创意广告信息进行展示,当然大家也可以根据业务类型,设置不同的优先级或者权重值来进行广告选择。
/**
 * 从创意列表中随机获取一条创意广告返回出去
 *
 * @param creativeIndexObjects 创意广告list
 */
private List<SearchResponse.Creative> buildCreativeResponse(List<CreativeIndexObject> creativeIndexObjects) {
    if (CollectionUtils.isEmpty(creativeIndexObjects)) return Collections.EMPTY_LIST;

    //随机获取一个广告创意,也可以实现优先级排序,也可以根据权重值等等,具体根据业务
    CreativeIndexObject randomObject = creativeIndexObjects.get(
            Math.abs(new Random().nextInt()) % creativeIndexObjects.size()
    );
    //List<SearchResponse.Creative> result = new ArrayList<>();
    //result.add(SearchResponse.convert(randomObject));

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

完整的请求过滤实现方法:

@Service
@Slf4j
public class SearchImpl implements ISearch {
    @Override
    public SearchResponse fetchAds(SearchRequest request) {

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

        //获取三个Feature信息
        KeywordFeature keywordFeature = request.getFeatureInfo().getKeywordFeature();
        HobbyFeatrue hobbyFeatrue = request.getFeatureInfo().getHobbyFeatrue();
        DistrictFeature districtFeature = request.getFeatureInfo().getDistrictFeature();
        //Feature关系
        FeatureRelation featureRelation = request.getFeatureInfo().getRelation();

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

        for (AdSlot adSlot : adSlotList) {
            Set<Long> targetUnitIdSet;
            //根据流量类型从缓存中获取 初始 广告信息
            Set<Long> adUnitIdSet = IndexDataTableUtils.of(
                    AdUnitIndexAwareImpl.class
            ).match(adSlot.getPositionType());

            // 根据三个维度过滤
            if (featureRelation == FeatureRelation.AND) {
                filterKeywordFeature(adUnitIdSet, keywordFeature);
                filterHobbyFeature(adUnitIdSet, hobbyFeatrue);
                filterDistrictFeature(adUnitIdSet, districtFeature);

                targetUnitIdSet = adUnitIdSet;
            } else {
                targetUnitIdSet = getOrRelationUnitIds(adUnitIdSet, keywordFeature, hobbyFeatrue, districtFeature);
            }
            //获取 推广计划 对象list
            List<AdUnitIndexObject> unitIndexObjects = IndexDataTableUtils.of(AdUnitIndexAwareImpl.class)
                                                                          .fetch(targetUnitIdSet);
            //根据状态过滤数据
            filterAdUnitAndPlanStatus(unitIndexObjects, CommonStatus.VALID);

            //获取 推广创意 id list
            List<Long> creativeIds = IndexDataTableUtils.of(CreativeRelationUnitIndexAwareImpl.class)
                                                        .selectAdCreativeIds(unitIndexObjects);
            //根据 推广创意ids获取推广创意
            List<CreativeIndexObject> creativeIndexObjects = IndexDataTableUtils.of(CreativeIndexAwareImpl.class)
                                                                                .fetch(creativeIds);

            //根据 广告位adslot 实现对创意数据的过滤
            filterCreativeByAdSlot(creativeIndexObjects, adSlot.getWidth(), adSlot.getHeight(), adSlot.getType());

            //一个广告位可以展示多个广告,也可以仅展示一个广告,具体根据业务来定
            adSlotRelationAds.put(
                    adSlot.getAdSlotCode(),
                    buildCreativeResponse(creativeIndexObjects)
            );
        }

        return response;
    }
    ...
检索服务对外提供
  • 暴露API接口
    上文中,我们实现了检索服务的核心逻辑,接下来,我们需要对外暴露我们的广告检索服务接口,在SearchController中提供:

        @PostMapping("/fetchAd")
        public SearchResponse fetchAdCreative(@RequestBody SearchRequest request) {
            log.info("ad-serach: fetchAd ->{}", JSON.toJSONString(request));
            return search.fetchAds(request);
        }
  • 实现API网关配置

    zuul:
    routes:
        sponsor: #在路由中自定义服务路由名称
        path: /ad-sponsor/**
        serviceId: mscx-ad-sponsor #微服务name
        strip-prefix: false
        search: #在路由中自定义服务路由名称
        path: /ad-search/**
        serviceId: mscx-ad-search #微服务name
        strip-prefix: false
    prefix: /gateway/api
    strip-prefix: true #不对 prefix: /gateway/api 设置的路径进行截取,默认转发会截取掉配置的前缀

[Spring cloud 一步步实现广告系统] 18. 查询返回广告创意的更多相关文章

  1. SpringCloud(9)使用Spring Cloud OAuth2保护微服务系统

    一.简介 OAth2是一个标准的授权协议. 在认证与授权的过程中,主要包含以下3种角色. 服务提供方 Authorization Server. 资源持有者 Resource Server. 客户端 ...

  2. Spring Cloud Config(配置中心)

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 一.简介 Spring Cloud Config为分布式系统中的外部配置提供服务器和客 ...

  3. 微服务网关 Spring Cloud Gateway

    1.  为什么是Spring Cloud Gateway 一句话,Spring Cloud已经放弃Netflix Zuul了.现在Spring Cloud中引用的还是Zuul 1.x版本,而这个版本是 ...

  4. Spring Cloud 开门见山

    Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,为开发者提供了在分布式系统(配置管理,服务发现,熔断,路由,微代理,控制总线,一次性toke ...

  5. Spring Cloud 学习笔记(一)——入门、特征、配置

    [TOC] 0 放在前面 0.1 参考文档 http://cloud.spring.io/spring-cloud-static/Brixton.SR7/ https://springcloud.cc ...

  6. Spring Cloud教程合集

    Spring Cloud系列终于搞完啦! 这一系列是笔者的学习笔记,原书之前也给小伙伴们推荐过 <Spring Cloud微服务实战> 原书采用了较老的Brixton版,笔者在学习的过程中 ...

  7. Spring Cloud之——Config(配置中心)

    Spring Cloud Config(配置中心) 大家好,有一段时间没有写技术博客了.由于工作上的事情,这方面很难分配时间.近几年随着服务化的兴起,一批服务化的框架应运而生,像dubbo,thrif ...

  8. 优秀的 Spring Cloud 开源软件

    Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据监控等,都可以用 ...

  9. Spring Cloud与微服务构建:Spring Cloud简介

    Spring Cloud简介 微服务因该具备的功能 微服务可以拆分为"微"和"服务"二字."微"即小的意思,那到底多小才算"微&q ...

  10. Spring Cloud与微服务构建:微服务简介

    Spring Cloud与微服务构建:微服务简介 单体架构及其不足 1.单体架构简介 在软件设计中,经常提及和使用经典的3曾模型,即表示层.业务逻辑层和数据访问层. 表示层:用于直接和用户交互,也成为 ...

随机推荐

  1. 嵌套 QQ、微博 通讯工具到HTML中

    自己QQ.微博的实例: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Defa ...

  2. 1、图解Oracle Logminer配置使用

    LogMiner配置使用手册 1 Logminer简介 1.1 LogMiner介绍 Oracle LogMiner 是Oracle公司从产品8i以后提供的一个实际非常有用的分析工具,使用该工具可以轻 ...

  3. 将java.util.Date类型转换成json时,使用JsonValueProcessor将date转换成希望的类型

    问题描述: java里面时间类型转换成json数据就成这样了: "createTime":{"date":30,"day":3," ...

  4. @有两个含义:1,在参数里,以表明该变量为伪参数 ,在本例中下文里将用@name变量代入当前代码中2,在字串中,@的意思就是后面的字串以它原本的含义显示,如果不

    @有两个含义:1,在参数里,以表明该变量为伪参数 ,在本例中下文里将用@name变量代入当前代码中 2,在字串中,@的意思就是后面的字串以它原本的含义显示,如果不加@那么需要用一些转义符\来显示一些特 ...

  5. pyenv

    export PYTHON_BUILD_MIRROR_URL="http://pyenv.qiniudn.com/pythons/"

  6. keepalived: Compile &amp; startup

    first get keepalived source from git: git clone https://github.com/acassen/keepalived then unzip and ...

  7. 后端开发者的Vue学习之路(四)

    目录 上节内容回顾: npm 介绍 安装 常用命令: 补充: 基于npm的Hello World 项目结构分析 用法迁移 小提醒 ES6语法 知识补充 单文件组件 使用注意: 路由 开启路由 定义路由 ...

  8. HttpContextAccessor不会出现线程同步问题?

    我有一段比较常规的.net core mvc代码,就是StartUp函数中注册HttpContextAccessor到系统DI工厂中,如图: 然后调用方是service层,这个service层是使用a ...

  9. linux server 产生大量 Too many open files CLOSE_WAIT激增

    情景描述:系统产生大量“Too many open files” 原因分析:在服务器与客户端通信过程中,因服务器发生了socket未关导致的closed_wait发生,致使监听port打开的句柄数到了 ...

  10. indexOf()定义和用法

    indexOf()定义和用法 indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置. 语法 stringObject.indexOf(searchvalue,fromindex) ...