|
@@ -47,6 +47,10 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
|
|
+import java.util.concurrent.Executors;
|
|
|
|
|
+import java.util.concurrent.Future;
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.function.Function;
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
@@ -656,6 +660,103 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按标题通过向量搜索接口查询视频列表,并发请求视频详情组装数据返回
|
|
|
|
|
+ */
|
|
|
|
|
+ private Page<VideoContentItemVO> getVideoContentListByTitleV2(VideoContentListParam param) {
|
|
|
|
|
+ Page<VideoContentItemVO> result = new Page<>(param.getPageNum(), param.getPageSize());
|
|
|
|
|
+ int pageSize = param.getPageSize() > 0 ? param.getPageSize() : 10;
|
|
|
|
|
+
|
|
|
|
|
+ // 调用向量搜索接口,topN 取 pageSize 的合理上限
|
|
|
|
|
+ int topN = Math.min(pageSize, 50);
|
|
|
|
|
+ JSONObject data = managerApiService.recallVideoWithScore(param.getTitle(), topN);
|
|
|
|
|
+ if (data == null) {
|
|
|
|
|
+ result.setTotalSize(0);
|
|
|
|
|
+ result.setObjs(new ArrayList<>());
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JSONArray items = data.getJSONArray("items");
|
|
|
|
|
+ if (items == null || items.isEmpty()) {
|
|
|
|
|
+ result.setTotalSize(0);
|
|
|
|
|
+ result.setObjs(new ArrayList<>());
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int total = data.getIntValue("total");
|
|
|
|
|
+ int effectiveTotalSize = Math.min(total, videoTitleSearchMaxCount);
|
|
|
|
|
+ result.setTotalSize(effectiveTotalSize);
|
|
|
|
|
+
|
|
|
|
|
+ // 分页截取
|
|
|
|
|
+ int offset = (param.getPageNum() - 1) * pageSize;
|
|
|
|
|
+ int end = Math.min(offset + pageSize, items.size());
|
|
|
|
|
+ if (offset >= items.size()) {
|
|
|
|
|
+ result.setObjs(new ArrayList<>());
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<JSONObject> pageItems = new ArrayList<>();
|
|
|
|
|
+ for (int i = offset; i < end; i++) {
|
|
|
|
|
+ pageItems.add(items.getJSONObject(i));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 提取 videoId 列表
|
|
|
|
|
+ List<Long> videoIds = pageItems.stream()
|
|
|
|
|
+ .map(item -> item.getLong("videoId"))
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .distinct()
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 并发请求视频详情
|
|
|
|
|
+ Map<Long, VideoDetail> videoDetailMap = new HashMap<>();
|
|
|
|
|
+ if (CollectionUtils.isNotEmpty(videoIds)) {
|
|
|
|
|
+ ExecutorService executor = Executors.newFixedThreadPool(videoIds.size() / 20 + 1);
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<Future<Map<Long, VideoDetail>>> futures = new ArrayList<>();
|
|
|
|
|
+ for (List<Long> partition : Lists.partition(videoIds, 20)) {
|
|
|
|
|
+ futures.add(executor.submit(() -> {
|
|
|
|
|
+ Set<Long> ids = new HashSet<>(partition);
|
|
|
|
|
+ return messageAttachmentService.getVideoDetail(ids);
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Future<Map<Long, VideoDetail>> future : futures) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Map<Long, VideoDetail> detailMap = future.get(10, TimeUnit.SECONDS);
|
|
|
|
|
+ if (detailMap != null) {
|
|
|
|
|
+ videoDetailMap.putAll(detailMap);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("getVideoContentListByTitle get video detail error", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ executor.shutdown();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 组装返回数据
|
|
|
|
|
+ List<VideoContentItemVO> voList = new ArrayList<>();
|
|
|
|
|
+ for (JSONObject item : pageItems) {
|
|
|
|
|
+ Long videoId = item.getLong("videoId");
|
|
|
|
|
+ VideoContentItemVO vo = new VideoContentItemVO();
|
|
|
|
|
+ vo.setVideoId(videoId);
|
|
|
|
|
+ vo.setScore(item.getDouble("score"));
|
|
|
|
|
+ // 优先从视频详情中获取标题、封面、视频地址
|
|
|
|
|
+ VideoDetail detail = videoDetailMap.get(videoId);
|
|
|
|
|
+ if (detail != null) {
|
|
|
|
|
+ vo.setTitle(detail.getTitle());
|
|
|
|
|
+ String cover = detail.getCover();
|
|
|
|
|
+ if (cover != null && cover.contains("/watermark")) {
|
|
|
|
|
+ cover = cover.substring(0, cover.indexOf("/watermark"));
|
|
|
|
|
+ }
|
|
|
|
|
+ vo.setCover(cover);
|
|
|
|
|
+ vo.setVideo(detail.getVideoPath());
|
|
|
|
|
+ voList.add(vo);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ result.setObjs(voList);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 将 manager 接口返回的 objs 数据转换为 VideoContentItemVO 列表
|
|
* 将 manager 接口返回的 objs 数据转换为 VideoContentItemVO 列表
|
|
|
*/
|
|
*/
|