|
|
@@ -606,6 +606,13 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
return gzhPlanMapper.selectByExample(example);
|
|
|
}
|
|
|
|
|
|
+ private static final int RECOMMEND_CANDIDATE_LIMIT = 500;
|
|
|
+ private static final String DEMAND_STRATEGY_PRIOR = "先验需求";
|
|
|
+ private static final String DEMAND_STRATEGY_POSTERIOR = "后验需求";
|
|
|
+ private static final String SOURCE_PRIOR = "prior";
|
|
|
+ private static final String SOURCE_POSTERIOR = "posterior";
|
|
|
+ private static final String SOURCE_HOT = "hot";
|
|
|
+
|
|
|
@Override
|
|
|
public Page<VideoContentItemVO> getVideoContentList(VideoContentListParam param) {
|
|
|
ContentPlatformAccount user = LoginUserContext.getUser();
|
|
|
@@ -613,25 +620,140 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
if (StringUtils.hasText(param.getTitle())) {
|
|
|
return getVideoContentListByTitle(param);
|
|
|
}
|
|
|
- Page<VideoContentItemVO> result = new Page<>(param.getPageNum(), param.getPageSize());
|
|
|
- int offset = (param.getPageNum() - 1) * param.getPageSize();
|
|
|
- String dt = planMapperExt.getVideoMaxDt();
|
|
|
- String datastatDt = planMapperExt.getVideoDatastatMaxDt();
|
|
|
- int count = planMapperExt.getVideoCount(param, dt, videoMinScore);
|
|
|
- result.setTotalSize(count);
|
|
|
- if (count == 0) {
|
|
|
+ String source = param.getSource();
|
|
|
+ if (SOURCE_PRIOR.equalsIgnoreCase(source)) {
|
|
|
+ return getSingleSourcePage(param, user, SOURCE_PRIOR);
|
|
|
+ }
|
|
|
+ if (SOURCE_POSTERIOR.equalsIgnoreCase(source)) {
|
|
|
+ return getSingleSourcePage(param, user, SOURCE_POSTERIOR);
|
|
|
+ }
|
|
|
+ if (SOURCE_HOT.equalsIgnoreCase(source)) {
|
|
|
+ return getSingleSourcePage(param, user, SOURCE_HOT);
|
|
|
+ }
|
|
|
+ return getInterleavedPage(param, user);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单一来源分页:从对应候选池取最多 N 条,按候选池顺序分页。
|
|
|
+ */
|
|
|
+ private Page<VideoContentItemVO> getSingleSourcePage(VideoContentListParam param, ContentPlatformAccount user, String source) {
|
|
|
+ List<VideoContentItemVO> candidates;
|
|
|
+ if (SOURCE_PRIOR.equals(source)) {
|
|
|
+ candidates = fetchPriorCandidates(user, RECOMMEND_CANDIDATE_LIMIT);
|
|
|
+ } else if (SOURCE_POSTERIOR.equals(source)) {
|
|
|
+ candidates = fetchPosteriorCandidates(user, RECOMMEND_CANDIDATE_LIMIT);
|
|
|
+ } else {
|
|
|
+ candidates = fetchHotCandidates(param, user, RECOMMEND_CANDIDATE_LIMIT);
|
|
|
+ }
|
|
|
+ for (VideoContentItemVO v : candidates) {
|
|
|
+ v.setSource(source);
|
|
|
+ }
|
|
|
+ return paginateCandidates(param, candidates);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 三路 1/1/1 严格穿插 + 跨路 video_id 去重。
|
|
|
+ * 缺路顺位补;先到先得(顺序:prior > posterior > hot)。
|
|
|
+ */
|
|
|
+ private Page<VideoContentItemVO> getInterleavedPage(VideoContentListParam param, ContentPlatformAccount user) {
|
|
|
+ List<VideoContentItemVO> prior = fetchPriorCandidates(user, RECOMMEND_CANDIDATE_LIMIT);
|
|
|
+ List<VideoContentItemVO> posterior = fetchPosteriorCandidates(user, RECOMMEND_CANDIDATE_LIMIT);
|
|
|
+ List<VideoContentItemVO> hot = fetchHotCandidates(param, user, RECOMMEND_CANDIDATE_LIMIT);
|
|
|
+ for (VideoContentItemVO v : prior) v.setSource(SOURCE_PRIOR);
|
|
|
+ for (VideoContentItemVO v : posterior) v.setSource(SOURCE_POSTERIOR);
|
|
|
+ for (VideoContentItemVO v : hot) v.setSource(SOURCE_HOT);
|
|
|
+
|
|
|
+ List<List<VideoContentItemVO>> pools = Arrays.asList(prior, posterior, hot);
|
|
|
+ int[] pointers = new int[3];
|
|
|
+ boolean[] exhausted = new boolean[3];
|
|
|
+ Set<Long> emittedIds = new HashSet<>();
|
|
|
+ List<VideoContentItemVO> merged = new ArrayList<>();
|
|
|
+
|
|
|
+ int idx = 0;
|
|
|
+ while (!(exhausted[0] && exhausted[1] && exhausted[2])) {
|
|
|
+ int cur = idx % 3;
|
|
|
+ idx++;
|
|
|
+ if (exhausted[cur]) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ List<VideoContentItemVO> pool = pools.get(cur);
|
|
|
+ while (pointers[cur] < pool.size() && emittedIds.contains(pool.get(pointers[cur]).getVideoId())) {
|
|
|
+ pointers[cur]++;
|
|
|
+ }
|
|
|
+ if (pointers[cur] < pool.size()) {
|
|
|
+ VideoContentItemVO item = pool.get(pointers[cur]++);
|
|
|
+ emittedIds.add(item.getVideoId());
|
|
|
+ merged.add(item);
|
|
|
+ } else {
|
|
|
+ exhausted[cur] = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return paginateCandidates(param, merged);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Page<VideoContentItemVO> paginateCandidates(VideoContentListParam param, List<VideoContentItemVO> all) {
|
|
|
+ int pageSize = param.getPageSize();
|
|
|
+ int pageNum = param.getPageNum();
|
|
|
+ Page<VideoContentItemVO> result = new Page<>(pageNum, pageSize);
|
|
|
+ result.setTotalSize(all.size());
|
|
|
+ if (all.isEmpty()) {
|
|
|
result.setObjs(new ArrayList<>());
|
|
|
return result;
|
|
|
}
|
|
|
+ int from = Math.min((pageNum - 1) * pageSize, all.size());
|
|
|
+ int to = Math.min(pageNum * pageSize, all.size());
|
|
|
+ result.setObjs(new ArrayList<>(all.subList(from, to)));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VideoContentItemVO> fetchPriorCandidates(ContentPlatformAccount user, int limit) {
|
|
|
+ return fetchDemandCandidates(user, DEMAND_STRATEGY_PRIOR, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VideoContentItemVO> fetchPosteriorCandidates(ContentPlatformAccount user, int limit) {
|
|
|
+ return fetchDemandCandidates(user, DEMAND_STRATEGY_POSTERIOR, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VideoContentItemVO> fetchDemandCandidates(ContentPlatformAccount user, String demandStrategy, int limit) {
|
|
|
+ String dt = demandVideoMapperExt.getMaxDt();
|
|
|
+ if (!StringUtils.hasText(dt)) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ String crowdSegment = user.getChannel();
|
|
|
+ // 超量拉取,再按 video_id 去重保留首条(即得分最高的一条)
|
|
|
+ List<ContentPlatformDemandVideo> rows = demandVideoMapperExt.selectForRecommend(dt, crowdSegment, demandStrategy, limit * 3);
|
|
|
+ if (CollectionUtils.isEmpty(rows)) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ LinkedHashMap<Long, ContentPlatformDemandVideo> distinct = new LinkedHashMap<>();
|
|
|
+ for (ContentPlatformDemandVideo row : rows) {
|
|
|
+ if (row.getVideoId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!distinct.containsKey(row.getVideoId())) {
|
|
|
+ distinct.put(row.getVideoId(), row);
|
|
|
+ if (distinct.size() >= limit) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return buildDemandVideoContentItemVOList(new ArrayList<>(distinct.values()));
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VideoContentItemVO> fetchHotCandidates(VideoContentListParam param, ContentPlatformAccount user, int limit) {
|
|
|
+ String dt = planMapperExt.getVideoMaxDt();
|
|
|
+ String datastatDt = planMapperExt.getVideoDatastatMaxDt();
|
|
|
+ if (!StringUtils.hasText(dt)) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
String sort = getVideoContentListSort(param.getSort());
|
|
|
String type = getVideoContentListType(param.getType());
|
|
|
String channel = getVideoContentListChannel(param.getSort(), user.getChannel());
|
|
|
String strategy = param.getSort() == 3 ? "recommend" : "normal";
|
|
|
List<ContentPlatformVideo> videoList = planMapperExt.getVideoList(param, dt, datastatDt, type, channel, strategy,
|
|
|
- videoMinScore, offset, param.getPageSize(), sort);
|
|
|
- List<VideoContentItemVO> list = buildVideoContentItemVOList(videoList, type, "sum", user.getChannel(), datastatDt);
|
|
|
- result.setObjs(list);
|
|
|
- return result;
|
|
|
+ videoMinScore, 0, limit, sort);
|
|
|
+ List<VideoContentItemVO> result = buildVideoContentItemVOList(videoList, type, "sum", user.getChannel(), datastatDt);
|
|
|
+ return result == null ? new ArrayList<>() : result;
|
|
|
}
|
|
|
|
|
|
/**
|