|
@@ -804,38 +804,45 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 通用 N 池轮转穿插(确定性,无随机):
|
|
|
|
|
- * - 池按传入顺序轮转,每池取 1 条,跳过已耗尽的池继续下一轮
|
|
|
|
|
|
|
+ * 通用 N 池轮转穿插:
|
|
|
|
|
+ * - 池列表随机打乱后,按新顺序轮转,每池取 1 条,跳过已耗尽的池继续下一轮
|
|
|
* - 跨池 video_id / 标题去重;某池耗尽后自动从轮转中移除
|
|
* - 跨池 video_id / 标题去重;某池耗尽后自动从轮转中移除
|
|
|
*/
|
|
*/
|
|
|
private List<VideoContentItemVO> interleaveMultiPools(List<List<VideoContentItemVO>> pools) {
|
|
private List<VideoContentItemVO> interleaveMultiPools(List<List<VideoContentItemVO>> pools) {
|
|
|
int n = pools.size();
|
|
int n = pools.size();
|
|
|
|
|
+ // 随机打乱池顺序,轮转遍历
|
|
|
|
|
+ List<Integer> order = new ArrayList<>(n);
|
|
|
|
|
+ for (int i = 0; i < n; i++) order.add(i);
|
|
|
|
|
+ Collections.shuffle(order);
|
|
|
|
|
+
|
|
|
int[] pointers = new int[n];
|
|
int[] pointers = new int[n];
|
|
|
boolean[] exhausted = new boolean[n];
|
|
boolean[] exhausted = new boolean[n];
|
|
|
for (int i = 0; i < n; i++) {
|
|
for (int i = 0; i < n; i++) {
|
|
|
- if (pools.get(i) == null || pools.get(i).isEmpty()) exhausted[i] = true;
|
|
|
|
|
|
|
+ List<VideoContentItemVO> p = pools.get(order.get(i));
|
|
|
|
|
+ if (p == null || p.isEmpty()) exhausted[order.get(i)] = true;
|
|
|
}
|
|
}
|
|
|
Set<Long> seenIds = new HashSet<>();
|
|
Set<Long> seenIds = new HashSet<>();
|
|
|
Set<String> seenTitles = new HashSet<>();
|
|
Set<String> seenTitles = new HashSet<>();
|
|
|
List<VideoContentItemVO> out = new ArrayList<>();
|
|
List<VideoContentItemVO> out = new ArrayList<>();
|
|
|
|
|
|
|
|
- int cur = 0;
|
|
|
|
|
|
|
+ int pos = 0;
|
|
|
while (true) {
|
|
while (true) {
|
|
|
// 跳过已耗尽的池
|
|
// 跳过已耗尽的池
|
|
|
- int started = cur;
|
|
|
|
|
- while (exhausted[cur]) {
|
|
|
|
|
- cur = (cur + 1) % n;
|
|
|
|
|
- if (cur == started) return out; // 全部耗尽
|
|
|
|
|
|
|
+ int started = pos;
|
|
|
|
|
+ while (exhausted[order.get(pos)]) {
|
|
|
|
|
+ pos = (pos + 1) % n;
|
|
|
|
|
+ if (pos == started) return out; // 全部耗尽
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- List<VideoContentItemVO> pool = pools.get(cur);
|
|
|
|
|
- while (pointers[cur] < pool.size()) {
|
|
|
|
|
- VideoContentItemVO v = pool.get(pointers[cur]++);
|
|
|
|
|
|
|
+ int idx = order.get(pos);
|
|
|
|
|
+ List<VideoContentItemVO> pool = pools.get(idx);
|
|
|
|
|
+ while (pointers[idx] < pool.size()) {
|
|
|
|
|
+ VideoContentItemVO v = pool.get(pointers[idx]++);
|
|
|
if (tryEmit(v, seenIds, seenTitles, out)) break;
|
|
if (tryEmit(v, seenIds, seenTitles, out)) break;
|
|
|
}
|
|
}
|
|
|
- if (pointers[cur] >= pool.size()) exhausted[cur] = true;
|
|
|
|
|
|
|
+ if (pointers[idx] >= pool.size()) exhausted[idx] = true;
|
|
|
|
|
|
|
|
- cur = (cur + 1) % n;
|
|
|
|
|
|
|
+ pos = (pos + 1) % n;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|