一种基于坑位模版的分页方案

一. 概述

1.1 业务诉求

想象一个向用户展示数据的下拉列表,数据的来源有多种方式。支持在每一页都按固定的数据源类型及顺序展示。

1.2 业务场景

  • up主的主页展示带货商品列表
    商品来源有多种:up主自选、官方推荐的、根据up主风格AI推荐
  • 用户推荐视频列表
    视频来源有多种:用户关注、用户画像推荐、播放排行榜

二、方案

每一页的每个位置的数据,有数据源类型和顺序的差异,将每个配置定义为一个坑位。即每一页有pageSize个坑位。

2.1 后台配置

在后台定义每页所有坑位的数据源类型,每页坑位配置的合集定义为坑位模版:

struct{
	1:i32: minPageSize //每页最小的数量
	2:list: slotTypes //所有坑位的类型
}

一种基于坑位模版的分页方案_第1张图片

2.2 数据查询

  1. 坑位模版groupBy数据源类型,获取单页每种数据源的count
  2. 每一个数据源类型有一个分页查询接口,基于分页接口封装为游标(pageSize=坑位count)
  3. 按坑位模版的模型,分别从游标中获取元素充填:如果元素不够或元素被filter则后面的元素自动顶上,如果过滤后的所有数据源数量size

注意其中的细节:

  1. 分页参数offset: 包含还有数据dataSource和pageNo信息,第一次传空后续用上一次返回的offset
  2. 单页的数据大小: [minPageSize, minPageSize+pageSize]
  3. 某坑位元素被过滤后,尽管下一页还有数据,但该位置仍被后续其他类型数据占用。

一种基于坑位模版的分页方案_第2张图片

游标定义参考:

public class PageCursorList<T> {

	private int readCursor;

	@NonNull
	private final Function<Integer,List<T>> pageFetcher;

	private int pageNo = 1 ;

	private final int pageSize;

	protected List<T> list;

	public PageCursorList(int pageSize,Function<Integer,List<T>> pageFetcher){
		Preconditions.checkArgument(pageSize > 0,"pageSize must gt 0");
		this.pageSize = pageSize;
		this.pageFetcher = pageFetcher;
		//first page
		this.list = Optional.ofNullable(pageFetcher.apply(1)).orElse(Collections.emptyList());
	}

	public boolean hasNext() {
		boolean currentPageHasNext  = readCursor < list.size();
		if(currentPageHasNext || list.size() < pageSize){
			return currentPageHasNext;
		}
		nextPage();
		return readCursor < list.size();
	}

	public T getNext() {
		if (readCursor < list.size()) {
			return list.get(readCursor++);
		}
		return null;
	}

	private void nextPage(){
		pageNo++;
		this.list = Optional.ofNullable(pageFetcher.apply(pageNo)).orElse(Collections.emptyList());
		this.readCursor = 0;
	}
}

三、扩展

  1. 基于用户画像或标签,配置多套坑位模版,实现精准运营
  2. 配置多套坑位模版,实现AB

在用户查询时再套一层即可:

一种基于坑位模版的分页方案_第3张图片

你可能感兴趣的:(java,推荐算法)