| | |
| | | import cn.lili.modules.member.entity.dos.Member; |
| | | import cn.lili.modules.member.service.FootprintService; |
| | | import cn.lili.modules.member.service.MemberService; |
| | | import cn.lili.modules.search.entity.dos.EsGoodsIndex; |
| | | import cn.lili.rocketmq.RocketmqSendCallbackBuilder; |
| | | import cn.lili.rocketmq.tags.CommentTagsEnum; |
| | | import cn.lili.rocketmq.tags.VideoTagsEnum; |
| | |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | import org.apache.commons.collections4.ListUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.apache.lucene.search.join.ScoreMode; |
| | | import org.apache.rocketmq.spring.core.RocketMQTemplate; |
| | | import org.elasticsearch.index.query.*; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.data.domain.PageRequest; |
| | | import org.springframework.data.domain.Pageable; |
| | | import org.springframework.data.domain.Sort; |
| | | import org.springframework.data.elasticsearch.core.ElasticsearchOperations; |
| | | import org.springframework.data.elasticsearch.core.SearchHits; |
| | | import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; |
| | | import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; |
| | | import org.springframework.stereotype.Service; |
| | | import lombok.RequiredArgsConstructor; |
| | | import cn.lili.utils.PageUtil; |
| | |
| | | private final RocketmqCustomProperties rocketmqCustomProperties; |
| | | private final RocketMQTemplate rocketMQTemplate; |
| | | private final ThumbsUpRecordService thumbsUpRecordService; |
| | | private final ElasticsearchOperations restTemplate; |
| | | |
| | | @Qualifier("videoEsServiceImpl") |
| | | private final EsService esService; |
| | | |
| | | |
| | | /** |
| | |
| | | // 5. 构建es中数据,mq异步处理 |
| | | VideoIndex videoIndex = new VideoIndex(); |
| | | BeanUtils.copyProperties(video, videoIndex); |
| | | videoIndex.setAuthorName(UserContext.getCurrentUser().getNickName()); |
| | | videoIndex.setAuthorAvatar(UserContext.getCurrentUser().getFace()); |
| | | videoIndex.setCoverFileKey(video.getCoverUrl()); |
| | | List<VideoGoodsDetailVO> esGoodsList = videoGoods.stream().map(goods -> { |
| | | VideoGoodsDetailVO vo = new VideoGoodsDetailVO(); |
| | |
| | | // 5. 更新es中的数据,mq异步处理 |
| | | VideoIndex videoIndex = new VideoIndex(); |
| | | BeanUtils.copyProperties(video, videoIndex); |
| | | videoIndex.setAuthorName(UserContext.getCurrentUser().getNickName()); |
| | | videoIndex.setAuthorAvatar(UserContext.getCurrentUser().getFace()); |
| | | videoIndex.setCoverFileKey(video.getCoverUrl()); |
| | | List<VideoGoodsDetailVO> esGoodsList = videoGoods.stream().map(goods -> { |
| | | VideoGoodsDetailVO vo = new VideoGoodsDetailVO(); |
| | |
| | | // 推荐算法: 1. 根据用户的收藏视频的标签 2. 根据用户关注的作者的其它视频 3. 根据用户的观看记录(观看时长较长的、重复观看次数较多的) 4. 基于相似用户的观看行为来给该用户推荐 |
| | | IPage<WxVideoVO> page = PageUtil.getPage(query, WxVideoVO.class); |
| | | switch (query.getVideoFrom()) { |
| | | case "recommend": |
| | | case "recommend":// 加载推荐视频 |
| | | baseMapper.recommendVideo(page, query); |
| | | break; |
| | | case "author": |
| | | case "author": // 加载视频主页我发布的视频 |
| | | AuthorVideoQuery query1 = new AuthorVideoQuery(); |
| | | BeanUtils.copyProperties(query, query1); |
| | | query1.setAuthorId(query.getAuthorId()); |
| | | baseMapper.getAuthorVideoPage(page, query1); |
| | | break; |
| | | case "collect": |
| | | case "collect": // 加载视频主页收藏视频 |
| | | AuthorVideoQuery query2 = new AuthorVideoQuery(); |
| | | BeanUtils.copyProperties(query, query2); |
| | | query2.setAuthorId(query.getAuthorId()); |
| | | baseMapper.getAuthorCollectVideoPage(page, query2); |
| | | break; |
| | | case "like": |
| | | case "like": // 加载视频主页点赞视频 |
| | | AuthorVideoQuery query3 = new AuthorVideoQuery(); |
| | | BeanUtils.copyProperties(query, query3); |
| | | query3.setAuthorId(query.getAuthorId()); |
| | | baseMapper.getAuthorLikeVideoPage(page, query3); |
| | | break; |
| | | case "search": // 加载es搜索视频 |
| | | VideoEsQuery query4 = new VideoEsQuery(); |
| | | BeanUtils.copyProperties(query, query4); |
| | | query4.setPageNumber((int) query.getPageNumber()); |
| | | query4.setPageSize((int) query.getPageSize()); |
| | | return this.esSearch(query4); |
| | | default: |
| | | break; |
| | | } |
| | |
| | | .in(Video::getId, chunk.stream().map(CollectTypeNumVO::getId).collect(Collectors.toList())) |
| | | .set(Video::getCollectNumJob, Boolean.FALSE) |
| | | .update(); |
| | | // 更新es的收藏数 |
| | | for (CollectTypeNumVO vo : chunk) { |
| | | Map<String, Object> fields = new HashMap<>(1); |
| | | fields.put("collectNum", vo.getCountNum()); |
| | | esService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, vo.getId(), fields); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | .in(Video::getId, chunk.stream().map(CollectTypeNumVO::getId).collect(Collectors.toList())) |
| | | .set(Video::getCommentNumJob, Boolean.FALSE) |
| | | .update(); |
| | | // 更新es的评论数 |
| | | for (CollectTypeNumVO vo : chunk) { |
| | | Map<String, Object> fields = new HashMap<>(1); |
| | | fields.put("commentNum", vo.getCountNum()); |
| | | esService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, vo.getId(), fields); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | .in(Video::getId, chunk.stream().map(CollectTypeNumVO::getId).collect(Collectors.toList())) |
| | | .set(Video::getThumbsUpNumJob, Boolean.FALSE) |
| | | .update(); |
| | | // 更新es的点赞数 |
| | | for (CollectTypeNumVO vo : chunk) { |
| | | Map<String, Object> fields = new HashMap<>(1); |
| | | fields.put("thumbsUpNum", vo.getCountNum()); |
| | | esService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, vo.getId(), fields); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Result esSearch(VideoEsQuery q) { |
| | | // 判断商品索引是否存在 |
| | | if (!restTemplate.indexOps(VideoIndex.class).exists()) { |
| | | return Result.ok(); |
| | | } |
| | | q.setPageNumber(q.getPageNumber() - 1); // 前端保持统一从第一页开始,但是es从0页开始,所以减一 |
| | | // 根据点赞数排序 |
| | | Pageable pageable = PageRequest.of(q.getPageNumber(), q.getPageSize(), Sort.by(Sort.Direction.DESC, "thumbsUpNum")); |
| | | |
| | | NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); |
| | | queryBuilder.withPageable(pageable); |
| | | |
| | | if (StringUtils.isNotBlank(q.getKeyword())) { |
| | | // 1. 构建主布尔查询 |
| | | BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); |
| | | |
| | | // 2. 添加标题匹配(非嵌套字段) |
| | | boolQuery.should(QueryBuilders.matchQuery("title", q.getKeyword())); |
| | | |
| | | // 3. 添加嵌套标签匹配 |
| | | NestedQueryBuilder tagQuery = QueryBuilders.nestedQuery( |
| | | "tagList", |
| | | QueryBuilders.matchQuery("tagList.tagName", q.getKeyword()), |
| | | ScoreMode.Total // 使用总分模式 |
| | | ); |
| | | boolQuery.should(tagQuery); |
| | | |
| | | // 4. 添加嵌套商品匹配 |
| | | NestedQueryBuilder goodsQuery = QueryBuilders.nestedQuery( |
| | | "goodsList", |
| | | QueryBuilders.matchQuery("goodsList.goodsName", q.getKeyword()), |
| | | ScoreMode.Total |
| | | ); |
| | | boolQuery.should(goodsQuery); |
| | | |
| | | // 5. 设置至少匹配一个条件(OR逻辑) |
| | | boolQuery.minimumShouldMatch(1); |
| | | |
| | | // 6. 状态为已发布的 |
| | | boolQuery.must(QueryBuilders.termQuery("status", VideoStatusEnum.PUBLISHED.getValue())); |
| | | queryBuilder.withQuery(boolQuery); |
| | | } else { |
| | | return Result.ok().data(new ArrayList<>()).total(0); |
| | | } |
| | | NativeSearchQuery query = queryBuilder.build(); |
| | | SearchHits<VideoIndex> searchHits = restTemplate.search(query, VideoIndex.class); |
| | | if (CollectionUtils.isEmpty(searchHits.getSearchHits())) { |
| | | return Result.ok().data(new ArrayList<>()).total(0); |
| | | } |
| | | List<VideoIndex> data = searchHits.stream().map(hit -> hit.getContent()).collect(Collectors.toList()); |
| | | List<String> videoIds = data.stream().map(VideoIndex::getId).collect(Collectors.toList()); |
| | | // 对象转换 |
| | | Map<String, List<SimpleMyCollectVO>> collectMap = myCollectService.getCollectsByVideoIds(videoIds) |
| | | .stream() |
| | | .collect(Collectors.groupingBy(SimpleMyCollectVO::getRefId)); |
| | | Map<String, List<SimpleMyThumbsUpVO>> thumbsUpMap = thumbsUpRecordService.getThumbssByVideoIds(videoIds) |
| | | .stream() |
| | | .collect(Collectors.groupingBy(SimpleMyThumbsUpVO::getRefId)); |
| | | List<String> subscribes = mySubscribeService.getSubscribesByUserId(UserContext.getCurrentUserId()); |
| | | List<WxVideoVO> vos = data.stream().map(videoIndex -> { |
| | | WxVideoVO wxVideoVO = new WxVideoVO(); |
| | | BeanUtils.copyProperties(videoIndex, wxVideoVO); |
| | | // 判断是否关注作者、是否点赞、是否收藏 |
| | | wxVideoVO.setCollected(CollectionUtils.isNotEmpty(collectMap.get(wxVideoVO.getId()))); |
| | | wxVideoVO.setThumbsUp(CollectionUtils.isNotEmpty(thumbsUpMap.get(wxVideoVO.getId()))); |
| | | if (UserContext.getCurrentUserId().equals(wxVideoVO.getAuthorId())) { |
| | | wxVideoVO.setSubscribeThisAuthor(Boolean.TRUE); |
| | | } else { |
| | | wxVideoVO.setSubscribeThisAuthor(subscribes.contains(wxVideoVO.getAuthorId())); |
| | | } |
| | | if (VideoContentTypeEnum.VIDEO.getValue().equals(wxVideoVO.getVideoContentType())) { |
| | | wxVideoVO.setCoverUrl(cosUtil.getPreviewUrl(wxVideoVO.getCoverFileKey())); |
| | | wxVideoVO.setVideoUrl(cosUtil.getPreviewUrl(wxVideoVO.getVideoFileKey())); |
| | | } else if (VideoContentTypeEnum.IMG.getValue().equals(wxVideoVO.getVideoContentType()) && StringUtils.isNotBlank(wxVideoVO.getVideoImgs())) { |
| | | wxVideoVO.setImgs(JSON.parseArray(wxVideoVO.getVideoImgs(), String.class).stream().map(fileKey -> cosUtil.getPreviewUrl(fileKey)).collect(Collectors.toList())); |
| | | wxVideoVO.setCoverUrl(wxVideoVO.getImgs().get(0)); |
| | | } |
| | | return wxVideoVO; |
| | | }).collect(Collectors.toList()); |
| | | return Result.ok().data(vos).total(searchHits.getTotalHits()); |
| | | } |
| | | } |