|
|
@@ -47,6 +47,7 @@ import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
+import java.time.LocalDate;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
import java.util.concurrent.Executors;
|
|
|
@@ -699,8 +700,9 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 三路 1/1/1 严格穿插 + 跨路 video_id 去重。
|
|
|
- * 缺路顺位补;先到先得(顺序:prior > posterior > hot)。
|
|
|
+ * 三路随机穿插 + 跨路 video_id 去重。
|
|
|
+ * 每步在未耗尽的池中等概率随机选一个,从该池头部取下一条(池内仍保持需求强度优先、组内 score DESC 的次序)。
|
|
|
+ * 用 (userId ^ 当天日期) 作为种子,保证同一用户当天翻页顺序一致、刷新一致。
|
|
|
*/
|
|
|
private Page<VideoContentItemVO> getInterleavedPage(VideoContentListParam param, ContentPlatformAccount user) {
|
|
|
List<VideoContentItemVO> prior = fetchPriorCandidates(user, DEMAND_CANDIDATE_LIMIT);
|
|
|
@@ -716,13 +718,17 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
Set<Long> emittedIds = new HashSet<>();
|
|
|
List<VideoContentItemVO> merged = new ArrayList<>();
|
|
|
|
|
|
- int idx = 0;
|
|
|
+ long userSeed = user.getId() == null ? 0L : user.getId();
|
|
|
+ long seed = userSeed ^ LocalDate.now().toString().hashCode();
|
|
|
+ Random rng = new Random(seed);
|
|
|
+
|
|
|
while (!(exhausted[0] && exhausted[1] && exhausted[2])) {
|
|
|
- int cur = idx % 3;
|
|
|
- idx++;
|
|
|
- if (exhausted[cur]) {
|
|
|
- continue;
|
|
|
+ List<Integer> alive = new ArrayList<>(3);
|
|
|
+ for (int i = 0; i < 3; i++) {
|
|
|
+ if (!exhausted[i]) alive.add(i);
|
|
|
}
|
|
|
+ int cur = alive.get(rng.nextInt(alive.size()));
|
|
|
+
|
|
|
List<VideoContentItemVO> pool = pools.get(cur);
|
|
|
while (pointers[cur] < pool.size() && emittedIds.contains(pool.get(pointers[cur]).getVideoId())) {
|
|
|
pointers[cur]++;
|