From 92ce67245d198dc9d4cbd8e4fd270b570b0a0c50 Mon Sep 17 00:00:00 2001 From: ovo Date: Sun, 8 Dec 2024 19:21:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 视频初步搭建 --- pom.xml | 7 + .../guwan/backend/config/EasyEsConfig.java | 9 ++ .../backend/es/document/VideoDocument.java | 58 ++++++++ .../backend/es/mapper/VideoEsMapper.java | 9 ++ .../backend/service/VideoSearchService.java | 133 ++++++++++++++++++ .../service/impl/VideoServiceImpl.java | 26 ++++ src/main/resources/application.yml | 13 +- 7 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/guwan/backend/config/EasyEsConfig.java create mode 100644 src/main/java/com/guwan/backend/es/document/VideoDocument.java create mode 100644 src/main/java/com/guwan/backend/es/mapper/VideoEsMapper.java create mode 100644 src/main/java/com/guwan/backend/service/VideoSearchService.java diff --git a/pom.xml b/pom.xml index b981831..8cbe2ac 100644 --- a/pom.xml +++ b/pom.xml @@ -243,6 +243,13 @@ 4.1.94.Final + + + cn.easyes + easy-es-boot-starter + 1.1.1 + + diff --git a/src/main/java/com/guwan/backend/config/EasyEsConfig.java b/src/main/java/com/guwan/backend/config/EasyEsConfig.java new file mode 100644 index 0000000..f63ad93 --- /dev/null +++ b/src/main/java/com/guwan/backend/config/EasyEsConfig.java @@ -0,0 +1,9 @@ +package com.guwan.backend.config; + +import cn.easyes.starter.register.EsMapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EsMapperScan("com.guwan.backend.es.mapper") +public class EasyEsConfig { +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/es/document/VideoDocument.java b/src/main/java/com/guwan/backend/es/document/VideoDocument.java new file mode 100644 index 0000000..36982ec --- /dev/null +++ b/src/main/java/com/guwan/backend/es/document/VideoDocument.java @@ -0,0 +1,58 @@ +package com.guwan.backend.es.document; + +import cn.easyes.annotation.IndexField; +import cn.easyes.annotation.IndexId; +import cn.easyes.annotation.IndexName; +import cn.easyes.annotation.Score; +import lombok.Data; +import java.time.LocalDateTime; + +@Data +@IndexName("videos") +public class VideoDocument { + + @IndexId + private Long id; + + @IndexField(fieldType = "text", analyzer = "ik_max_word", searchAnalyzer = "ik_smart") + private String title; + + @IndexField(fieldType = "text", analyzer = "ik_max_word", searchAnalyzer = "ik_smart") + private String description; + + @IndexField(fieldType = "keyword") + private String url; + + @IndexField(fieldType = "keyword") + private String coverUrl; + + @IndexField(fieldType = "long") + private Long duration; + + @IndexField(fieldType = "long") + private Long size; + + @IndexField(fieldType = "keyword") + private String status; + + @IndexField(fieldType = "long") + private Long userId; + + @IndexField(fieldType = "keyword") + private String username; + + @IndexField(fieldType = "date") + private LocalDateTime createdTime; + + @IndexField(fieldType = "integer") + private Integer viewCount; + + @IndexField(fieldType = "integer") + private Integer likeCount; + + @IndexField(fieldType = "keyword") + private String tags; + + @Score + private Float score; +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/es/mapper/VideoEsMapper.java b/src/main/java/com/guwan/backend/es/mapper/VideoEsMapper.java new file mode 100644 index 0000000..b38e301 --- /dev/null +++ b/src/main/java/com/guwan/backend/es/mapper/VideoEsMapper.java @@ -0,0 +1,9 @@ +package com.guwan.backend.es.mapper; + +import cn.easyes.core.conditions.interfaces.BaseEsMapper; +import com.guwan.backend.es.document.VideoDocument; +import org.springframework.stereotype.Repository; + +@Repository +public interface VideoEsMapper extends BaseEsMapper { +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/service/VideoSearchService.java b/src/main/java/com/guwan/backend/service/VideoSearchService.java new file mode 100644 index 0000000..0a53ad3 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/VideoSearchService.java @@ -0,0 +1,133 @@ +package com.guwan.backend.service; + +import cn.easyes.core.conditions.LambdaEsQueryWrapper; +import cn.easyes.core.conditions.LambdaEsUpdateWrapper; +import com.guwan.backend.dto.video.VideoDTO; +import com.guwan.backend.entity.Video; +import com.guwan.backend.es.document.VideoDocument; +import com.guwan.backend.es.mapper.VideoEsMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class VideoSearchService { + + private final VideoEsMapper videoEsMapper; + + /** + * 保存或更新视频文档 + */ + public void saveOrUpdate(Video video) { + VideoDocument document = convertToDocument(video); + videoEsMapper.insert(document); + } + + /** + * 删除视频文档 + */ + public void delete(Long id) { + videoEsMapper.deleteById(id); + } + + /** + * 更新视频观看次数 + */ + public void updateViewCount(Long id, Integer viewCount) { + LambdaEsUpdateWrapper wrapper = new LambdaEsUpdateWrapper<>(); + wrapper.eq(VideoDocument::getId, id) + .set(VideoDocument::getViewCount, viewCount); + videoEsMapper.update(null, wrapper); + } + + /** + * 更新视频点赞次数 + */ + public void updateLikeCount(Long id, Integer likeCount) { + LambdaEsUpdateWrapper wrapper = new LambdaEsUpdateWrapper<>(); + wrapper.eq(VideoDocument::getId, id) + .set(VideoDocument::getLikeCount, likeCount); + videoEsMapper.update(null, wrapper); + } + + /** + * 搜索视频 + */ + public List search(String keyword) { + // 构建查询条件 + LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); + wrapper.and(w -> w + .match(VideoDocument::getTitle, keyword) + .or() + .match(VideoDocument::getDescription, keyword) + .or() + .match(VideoDocument::getTags, keyword) + ); + + // 设置排序 + wrapper.orderByDesc(VideoDocument::getScore) + .orderByDesc(VideoDocument::getCreatedTime); + + // 执行查询 + List documents = videoEsMapper.selectList(wrapper); + + // 转换结果 + return documents.stream() + .map(this::convertToDTO) + .collect(Collectors.toList()); + } + + /** + * 推荐相似视频 + */ + public List findSimilar(Long id, int limit) { + // 获取当前视频 + VideoDocument current = videoEsMapper.selectById(id); + if (current == null) { + return List.of(); + } + + // 构建查询条件 + LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); + wrapper.and(w -> w + .match(VideoDocument::getTags, current.getTags()) + .or() + .match(VideoDocument::getTitle, current.getTitle()) + .or() + .match(VideoDocument::getDescription, current.getDescription()) + ); + + // 排除当前视频 + wrapper.ne(VideoDocument::getId, id); + + // 设置排序和限制 + wrapper.orderByDesc(VideoDocument::getScore) + .limit(limit); + + // 执行查询 + List documents = videoEsMapper.selectList(wrapper); + + // 转换结果 + return documents.stream() + .map(this::convertToDTO) + .collect(Collectors.toList()); + } + + private VideoDocument convertToDocument(Video video) { + VideoDocument document = new VideoDocument(); + BeanUtils.copyProperties(video, document); + return document; + } + + private VideoDTO convertToDTO(VideoDocument document) { + VideoDTO dto = new VideoDTO(); + BeanUtils.copyProperties(document, dto); + return dto; + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java index 1ca67da..a4fd690 100644 --- a/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java +++ b/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java @@ -10,6 +10,7 @@ import com.guwan.backend.entity.VideoLike; import com.guwan.backend.mapper.VideoLikeMapper; import com.guwan.backend.mapper.VideoMapper; import com.guwan.backend.service.VideoService; +import com.guwan.backend.service.VideoSearchService; import com.guwan.backend.util.MinioUtil; import com.guwan.backend.util.SecurityUtil; import lombok.RequiredArgsConstructor; @@ -19,6 +20,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + @Slf4j @Service @RequiredArgsConstructor @@ -28,6 +31,7 @@ public class VideoServiceImpl implements VideoService { private final MinioUtil minioUtil; private final SecurityUtil securityUtil; private final VideoLikeMapper videoLikeMapper; + private final VideoSearchService videoSearchService; @Override @Transactional @@ -58,6 +62,9 @@ public class VideoServiceImpl implements VideoService { videoMapper.insert(video); + // 保存到ES + videoSearchService.saveOrUpdate(video); + return convertToDTO(video); } catch (Exception e) { log.error("上传视频失败", e); @@ -107,6 +114,9 @@ public class VideoServiceImpl implements VideoService { // 删除数据库记录 videoMapper.deleteById(id); + + // 从ES中删除 + videoSearchService.delete(id); } @Override @@ -141,6 +151,9 @@ public class VideoServiceImpl implements VideoService { if (video != null) { video.setViewCount(video.getViewCount() + 1); videoMapper.updateById(video); + + // 更新ES中的观看次数 + videoSearchService.updateViewCount(id, video.getViewCount()); } } @@ -185,6 +198,9 @@ public class VideoServiceImpl implements VideoService { } videoMapper.updateById(video); + + // 更新ES中的点赞次数 + videoSearchService.updateLikeCount(id, video.getLikeCount()); } // 添加新方法:检查用户是否已点赞 @@ -201,6 +217,16 @@ public class VideoServiceImpl implements VideoService { return videoLikeMapper.selectCount(wrapper) > 0; } + @Override + public List searchVideos(String keyword) { + return videoSearchService.search(keyword); + } + + @Override + public List getSimilarVideos(Long id, int limit) { + return videoSearchService.findSimilar(id, limit); + } + private VideoDTO convertToDTO(Video video) { if (video == null) { return null; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c9f9643..bd7027d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -129,4 +129,15 @@ config: # SRS配置 srs: server: - url: http://localhost:1985 # SRS HTTP API地址 \ No newline at end of file + url: http://localhost:1985 # SRS HTTP API地址 + +easy-es: + enable: true + address: localhost:9200 + username: elastic # 如果有的话 + password: elastic # 如果有的话 + global-config: + process-index-mode: manual + print-dsl: true + distributed: false + response-log: true \ No newline at end of file