|
@@ -642,6 +642,8 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
return Arrays.asList(
|
|
return Arrays.asList(
|
|
|
// 策略1: 人群需求*场景已看
|
|
// 策略1: 人群需求*场景已看
|
|
|
new PriorPoolConfig(PriorDimensionEnum.PREMIUM, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.SCENE, true),
|
|
new PriorPoolConfig(PriorDimensionEnum.PREMIUM, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.SCENE, true),
|
|
|
|
|
+ new PriorPoolConfig(PriorDimensionEnum.GROWTH, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.SCENE, true),
|
|
|
|
|
+ new PriorPoolConfig(PriorDimensionEnum.DISTRIBUTION, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.SCENE, true),
|
|
|
// 策略2: 人群需求*特征点*向量匹配
|
|
// 策略2: 人群需求*特征点*向量匹配
|
|
|
new PriorPoolConfig(PriorDimensionEnum.PREMIUM, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.VECTOR_SIMILARITY, false),
|
|
new PriorPoolConfig(PriorDimensionEnum.PREMIUM, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.VECTOR_SIMILARITY, false),
|
|
|
new PriorPoolConfig(PriorDimensionEnum.GROWTH, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.VECTOR_SIMILARITY, false),
|
|
new PriorPoolConfig(PriorDimensionEnum.GROWTH, DemandTypeEnum.STANDARD, DemandMatchMethodEnum.VECTOR_SIMILARITY, false),
|
|
@@ -692,10 +694,7 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
}
|
|
}
|
|
|
String ghName = param.getGhName();
|
|
String ghName = param.getGhName();
|
|
|
if (StringUtils.hasText(ghName)) {
|
|
if (StringUtils.hasText(ghName)) {
|
|
|
- String dt = demandVideoMapperExt.getMaxDt(null);
|
|
|
|
|
- if (StringUtils.hasText(dt)) {
|
|
|
|
|
- return DemandChannelEnum.fromValue(demandVideoMapperExt.selectChannelNameByGh(dt, ghName));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return DemandChannelEnum.fromValue(demandVideoMapperExt.selectChannelNameByGh(ghName));
|
|
|
}
|
|
}
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
@@ -768,6 +767,9 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
if (VideoContentSource.HOT == source) {
|
|
if (VideoContentSource.HOT == source) {
|
|
|
return getHotSourcePaged(param, user);
|
|
return getHotSourcePaged(param, user);
|
|
|
}
|
|
}
|
|
|
|
|
+ // 杠杆1: channel + dt 每请求只解析一次,传入各池,避免 fan-out 各池重复查 channel/dt
|
|
|
|
|
+ DemandChannelEnum channel = resolveChannelName(param);
|
|
|
|
|
+ String dt = demandVideoMapperExt.getMaxDt(channel == null ? null : channel.getValue());
|
|
|
List<VideoContentItemVO> list;
|
|
List<VideoContentItemVO> list;
|
|
|
if (VideoContentSource.PRIOR == source) {
|
|
if (VideoContentSource.PRIOR == source) {
|
|
|
// 人群需求池并行拉取, 按 PRIOR_POOL_CONFIGS 配置
|
|
// 人群需求池并行拉取, 按 PRIOR_POOL_CONFIGS 配置
|
|
@@ -777,8 +779,8 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
List<Future<List<VideoContentItemVO>>> futures = new ArrayList<>(poolCount);
|
|
List<Future<List<VideoContentItemVO>>> futures = new ArrayList<>(poolCount);
|
|
|
for (PriorPoolConfig cfg : PRIOR_POOL_CONFIGS) {
|
|
for (PriorPoolConfig cfg : PRIOR_POOL_CONFIGS) {
|
|
|
futures.add(executor.submit(cfg.scene
|
|
futures.add(executor.submit(cfg.scene
|
|
|
- ? () -> fetchPriorSceneCandidates(param, user, DEMAND_CANDIDATE_LIMIT, cfg.dimension, cfg.demandType, cfg.matchMethod)
|
|
|
|
|
- : () -> fetchPriorPool(param, user, DEMAND_CANDIDATE_LIMIT, cfg.dimension, cfg.demandType, cfg.matchMethod)));
|
|
|
|
|
|
|
+ ? () -> fetchPriorSceneCandidates(param, user, DEMAND_CANDIDATE_LIMIT, channel, dt, cfg.dimension, cfg.demandType, cfg.matchMethod)
|
|
|
|
|
+ : () -> fetchPriorPool(param, user, DEMAND_CANDIDATE_LIMIT, channel, dt, cfg.dimension, cfg.demandType, cfg.matchMethod)));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int timeoutSeconds = 30;
|
|
int timeoutSeconds = 30;
|
|
@@ -795,7 +797,7 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
executor.shutdown();
|
|
executor.shutdown();
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- list = fetchPosteriorCandidates(param, user, DEMAND_CANDIDATE_LIMIT);
|
|
|
|
|
|
|
+ list = fetchPosteriorCandidates(param, user, DEMAND_CANDIDATE_LIMIT, channel, dt);
|
|
|
}
|
|
}
|
|
|
for (VideoContentItemVO v : list) {
|
|
for (VideoContentItemVO v : list) {
|
|
|
v.setSource(source.getValue());
|
|
v.setSource(source.getValue());
|
|
@@ -805,21 +807,15 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 通用 N 池轮转穿插:
|
|
* 通用 N 池轮转穿插:
|
|
|
- * - 场景已看视频池(索引0)固定首位,其余池随机打乱后按序轮转,每池取 1 条
|
|
|
|
|
- * - TODO: 场景已看池扩充多 dimension 后去掉首位固定,全部随机打乱
|
|
|
|
|
|
|
+ * - 池列表随机打乱后,按新顺序轮转,每池取 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);
|
|
List<Integer> order = new ArrayList<>(n);
|
|
|
- if (n > 0) order.add(0); // 场景已看视频池固定首位
|
|
|
|
|
- if (n > 1) {
|
|
|
|
|
- List<Integer> tail = new ArrayList<>(n - 1);
|
|
|
|
|
- for (int i = 1; i < n; i++) tail.add(i);
|
|
|
|
|
- Collections.shuffle(tail);
|
|
|
|
|
- order.addAll(tail);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ 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];
|
|
@@ -927,18 +923,21 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
private Page<VideoContentItemVO> getInterleavedPage(VideoContentListParam param, ContentPlatformAccount user) {
|
|
private Page<VideoContentItemVO> getInterleavedPage(VideoContentListParam param, ContentPlatformAccount user) {
|
|
|
int priorCount = PRIOR_POOL_CONFIGS.size();
|
|
int priorCount = PRIOR_POOL_CONFIGS.size();
|
|
|
int totalCount = priorCount + 2; // + posterior + hot
|
|
int totalCount = priorCount + 2; // + posterior + hot
|
|
|
|
|
+ // 杠杆1: channel + dt 每请求只解析一次,传入各 demand 池(hot 池用自己的 dt,不受影响)
|
|
|
|
|
+ DemandChannelEnum channel = resolveChannelName(param);
|
|
|
|
|
+ String dt = demandVideoMapperExt.getMaxDt(channel == null ? null : channel.getValue());
|
|
|
ExecutorService executor = Executors.newFixedThreadPool(totalCount);
|
|
ExecutorService executor = Executors.newFixedThreadPool(totalCount);
|
|
|
try {
|
|
try {
|
|
|
// prior 池并行拉取
|
|
// prior 池并行拉取
|
|
|
List<Future<List<VideoContentItemVO>>> futures = new ArrayList<>(totalCount);
|
|
List<Future<List<VideoContentItemVO>>> futures = new ArrayList<>(totalCount);
|
|
|
for (PriorPoolConfig cfg : PRIOR_POOL_CONFIGS) {
|
|
for (PriorPoolConfig cfg : PRIOR_POOL_CONFIGS) {
|
|
|
futures.add(executor.submit(cfg.scene
|
|
futures.add(executor.submit(cfg.scene
|
|
|
- ? () -> fetchPriorSceneCandidates(param, user, DEMAND_CANDIDATE_LIMIT, cfg.dimension, cfg.demandType, cfg.matchMethod)
|
|
|
|
|
- : () -> fetchPriorPool(param, user, DEMAND_CANDIDATE_LIMIT, cfg.dimension, cfg.demandType, cfg.matchMethod)));
|
|
|
|
|
|
|
+ ? () -> fetchPriorSceneCandidates(param, user, DEMAND_CANDIDATE_LIMIT, channel, dt, cfg.dimension, cfg.demandType, cfg.matchMethod)
|
|
|
|
|
+ : () -> fetchPriorPool(param, user, DEMAND_CANDIDATE_LIMIT, channel, dt, cfg.dimension, cfg.demandType, cfg.matchMethod)));
|
|
|
}
|
|
}
|
|
|
// posterior + hot
|
|
// posterior + hot
|
|
|
Future<List<VideoContentItemVO>> fPosterior = executor.submit(
|
|
Future<List<VideoContentItemVO>> fPosterior = executor.submit(
|
|
|
- () -> fetchPosteriorCandidates(param, user, DEMAND_CANDIDATE_LIMIT));
|
|
|
|
|
|
|
+ () -> fetchPosteriorCandidates(param, user, DEMAND_CANDIDATE_LIMIT, channel, dt));
|
|
|
Future<List<VideoContentItemVO>> fHot = executor.submit(
|
|
Future<List<VideoContentItemVO>> fHot = executor.submit(
|
|
|
() -> fetchHotCandidates(param, user, HOT_CANDIDATE_LIMIT));
|
|
() -> fetchHotCandidates(param, user, HOT_CANDIDATE_LIMIT));
|
|
|
futures.add(fPosterior);
|
|
futures.add(fPosterior);
|
|
@@ -987,9 +986,8 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
* 3. 输出顺序按 sceneSumRov DESC,相同再按 total_rov DESC 兜底
|
|
* 3. 输出顺序按 sceneSumRov DESC,相同再按 total_rov DESC 兜底
|
|
|
*/
|
|
*/
|
|
|
private List<VideoContentItemVO> fetchPriorSceneCandidates(VideoContentListParam param, ContentPlatformAccount user, int limit,
|
|
private List<VideoContentItemVO> fetchPriorSceneCandidates(VideoContentListParam param, ContentPlatformAccount user, int limit,
|
|
|
|
|
+ DemandChannelEnum channel, String dt,
|
|
|
PriorDimensionEnum dimension, DemandTypeEnum demandType, DemandMatchMethodEnum matchMethod) {
|
|
PriorDimensionEnum dimension, DemandTypeEnum demandType, DemandMatchMethodEnum matchMethod) {
|
|
|
- DemandChannelEnum channel = resolveChannelName(param);
|
|
|
|
|
- String dt = demandVideoMapperExt.getMaxDt(channel == null ? null : channel.getValue());
|
|
|
|
|
if (!StringUtils.hasText(dt)) {
|
|
if (!StringUtils.hasText(dt)) {
|
|
|
return new ArrayList<>();
|
|
return new ArrayList<>();
|
|
|
}
|
|
}
|
|
@@ -1050,9 +1048,8 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
* 6. 截断到 limit
|
|
* 6. 截断到 limit
|
|
|
*/
|
|
*/
|
|
|
private List<VideoContentItemVO> fetchPriorPool(VideoContentListParam param, ContentPlatformAccount user, int limit,
|
|
private List<VideoContentItemVO> fetchPriorPool(VideoContentListParam param, ContentPlatformAccount user, int limit,
|
|
|
|
|
+ DemandChannelEnum channel, String dt,
|
|
|
PriorDimensionEnum dimension, DemandTypeEnum demandType, DemandMatchMethodEnum matchMethod) {
|
|
PriorDimensionEnum dimension, DemandTypeEnum demandType, DemandMatchMethodEnum matchMethod) {
|
|
|
- DemandChannelEnum channel = resolveChannelName(param);
|
|
|
|
|
- String dt = demandVideoMapperExt.getMaxDt(channel == null ? null : channel.getValue());
|
|
|
|
|
if (!StringUtils.hasText(dt)) {
|
|
if (!StringUtils.hasText(dt)) {
|
|
|
return new ArrayList<>();
|
|
return new ArrayList<>();
|
|
|
}
|
|
}
|
|
@@ -1135,9 +1132,8 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
* 按 demand_content_id 分组,组按 total_rov DESC、组内 score DESC 取前 K;
|
|
* 按 demand_content_id 分组,组按 total_rov DESC、组内 score DESC 取前 K;
|
|
|
* 跨组用 video_id + 归一化标题去重,截到 limit。
|
|
* 跨组用 video_id + 归一化标题去重,截到 limit。
|
|
|
*/
|
|
*/
|
|
|
- private List<VideoContentItemVO> fetchPosteriorCandidates(VideoContentListParam param, ContentPlatformAccount user, int limit) {
|
|
|
|
|
- DemandChannelEnum channel = resolveChannelName(param);
|
|
|
|
|
- String dt = demandVideoMapperExt.getMaxDt(channel == null ? null : channel.getValue());
|
|
|
|
|
|
|
+ private List<VideoContentItemVO> fetchPosteriorCandidates(VideoContentListParam param, ContentPlatformAccount user, int limit,
|
|
|
|
|
+ DemandChannelEnum channel, String dt) {
|
|
|
if (!StringUtils.hasText(dt)) {
|
|
if (!StringUtils.hasText(dt)) {
|
|
|
return new ArrayList<>();
|
|
return new ArrayList<>();
|
|
|
}
|
|
}
|
|
@@ -1502,6 +1498,7 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
if (Objects.isNull(item.getRecommendScore())) {
|
|
if (Objects.isNull(item.getRecommendScore())) {
|
|
|
item.setRecommendScore(recommendTypeVideoScoreMap.get(video.getVideoId()));
|
|
item.setRecommendScore(recommendTypeVideoScoreMap.get(video.getVideoId()));
|
|
|
}
|
|
}
|
|
|
|
|
+ item.setCategory(defaultString(video.getCategory()));
|
|
|
item.setExperimentId("hot");
|
|
item.setExperimentId("hot");
|
|
|
result.add(item);
|
|
result.add(item);
|
|
|
}
|
|
}
|
|
@@ -1730,6 +1727,11 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
String pageUrl = messageAttachmentService.getPage(loginUser.getChannel(), carrierId,
|
|
String pageUrl = messageAttachmentService.getPage(loginUser.getChannel(), carrierId,
|
|
|
"dyyqw", "企微", QwPlanTypeEnum.from(param.getType()).getDescription(),
|
|
"dyyqw", "企微", QwPlanTypeEnum.from(param.getType()).getDescription(),
|
|
|
"位置1", videoParam.getVideoId(), videoParam.getExperimentId());
|
|
"位置1", videoParam.getVideoId(), videoParam.getExperimentId());
|
|
|
|
|
+ // 判断是否为特殊首层,追加 isSpecialLayer 参数
|
|
|
|
|
+ String layerType = redisUtils.getString("cp:qw_hz_layer_type:" + loginUser.getChannel());
|
|
|
|
|
+ if ("特殊".equals(layerType)) {
|
|
|
|
|
+ pageUrl = pageUrl + "%26isSpecialLayer%3D1";
|
|
|
|
|
+ }
|
|
|
//pageUrl = videoMultiService.setVideoMultiTitleCoverPagePath(videoParam.getVideoId(), pageUrl,
|
|
//pageUrl = videoMultiService.setVideoMultiTitleCoverPagePath(videoParam.getVideoId(), pageUrl,
|
|
|
// videoParam.getTitle(), videoParam.getCover());
|
|
// videoParam.getTitle(), videoParam.getCover());
|
|
|
String rootSourceId = MessageUtil.getRootSourceId(pageUrl);
|
|
String rootSourceId = MessageUtil.getRootSourceId(pageUrl);
|
|
@@ -2104,6 +2106,7 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
item.setDemandContentTopic(video.getDemandContentTopic());
|
|
item.setDemandContentTopic(video.getDemandContentTopic());
|
|
|
item.setPointType(video.getPointType());
|
|
item.setPointType(video.getPointType());
|
|
|
item.setStandardElement(video.getStandardElement());
|
|
item.setStandardElement(video.getStandardElement());
|
|
|
|
|
+ item.setCategory(defaultString(video.getCategory()));
|
|
|
item.setCategoryName(video.getCategoryName());
|
|
item.setCategoryName(video.getCategoryName());
|
|
|
item.setExperimentId(video.getExperimentId());
|
|
item.setExperimentId(video.getExperimentId());
|
|
|
item.setSim(video.getSim());
|
|
item.setSim(video.getSim());
|
|
@@ -2121,6 +2124,10 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private static String defaultString(String value) {
|
|
|
|
|
+ return StringUtils.hasText(value) ? value : "";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public Page<XcxPlanItemVO> xcxPlanList(XcxPlanListParam param) {
|
|
public Page<XcxPlanItemVO> xcxPlanList(XcxPlanListParam param) {
|
|
|
ContentPlatformAccount loginAccount = LoginUserContext.getUser();
|
|
ContentPlatformAccount loginAccount = LoginUserContext.getUser();
|