parent
05963d7c16
commit
92ce67245d
7
pom.xml
7
pom.xml
|
@ -243,6 +243,13 @@
|
|||
<version>4.1.94.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Easy-Es -->
|
||||
<dependency>
|
||||
<groupId>cn.easyes</groupId>
|
||||
<artifactId>easy-es-boot-starter</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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<VideoDocument> {
|
||||
}
|
|
@ -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<VideoDocument> wrapper = new LambdaEsUpdateWrapper<>();
|
||||
wrapper.eq(VideoDocument::getId, id)
|
||||
.set(VideoDocument::getViewCount, viewCount);
|
||||
videoEsMapper.update(null, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新视频点赞次数
|
||||
*/
|
||||
public void updateLikeCount(Long id, Integer likeCount) {
|
||||
LambdaEsUpdateWrapper<VideoDocument> wrapper = new LambdaEsUpdateWrapper<>();
|
||||
wrapper.eq(VideoDocument::getId, id)
|
||||
.set(VideoDocument::getLikeCount, likeCount);
|
||||
videoEsMapper.update(null, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索视频
|
||||
*/
|
||||
public List<VideoDTO> search(String keyword) {
|
||||
// 构建查询条件
|
||||
LambdaEsQueryWrapper<VideoDocument> 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<VideoDocument> documents = videoEsMapper.selectList(wrapper);
|
||||
|
||||
// 转换结果
|
||||
return documents.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 推荐相似视频
|
||||
*/
|
||||
public List<VideoDTO> findSimilar(Long id, int limit) {
|
||||
// 获取当前视频
|
||||
VideoDocument current = videoEsMapper.selectById(id);
|
||||
if (current == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
LambdaEsQueryWrapper<VideoDocument> 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<VideoDocument> 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;
|
||||
}
|
||||
}
|
|
@ -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<VideoDTO> searchVideos(String keyword) {
|
||||
return videoSearchService.search(keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VideoDTO> getSimilarVideos(Long id, int limit) {
|
||||
return videoSearchService.findSimilar(id, limit);
|
||||
}
|
||||
|
||||
private VideoDTO convertToDTO(Video video) {
|
||||
if (video == null) {
|
||||
return null;
|
||||
|
|
|
@ -130,3 +130,14 @@ config:
|
|||
srs:
|
||||
server:
|
||||
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
|
Loading…
Reference in New Issue