parent
05963d7c16
commit
92ce67245d
7
pom.xml
7
pom.xml
|
@ -243,6 +243,13 @@
|
||||||
<version>4.1.94.Final</version>
|
<version>4.1.94.Final</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Easy-Es -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.easyes</groupId>
|
||||||
|
<artifactId>easy-es-boot-starter</artifactId>
|
||||||
|
<version>1.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<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.VideoLikeMapper;
|
||||||
import com.guwan.backend.mapper.VideoMapper;
|
import com.guwan.backend.mapper.VideoMapper;
|
||||||
import com.guwan.backend.service.VideoService;
|
import com.guwan.backend.service.VideoService;
|
||||||
|
import com.guwan.backend.service.VideoSearchService;
|
||||||
import com.guwan.backend.util.MinioUtil;
|
import com.guwan.backend.util.MinioUtil;
|
||||||
import com.guwan.backend.util.SecurityUtil;
|
import com.guwan.backend.util.SecurityUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
@ -19,6 +20,8 @@ import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@ -28,6 +31,7 @@ public class VideoServiceImpl implements VideoService {
|
||||||
private final MinioUtil minioUtil;
|
private final MinioUtil minioUtil;
|
||||||
private final SecurityUtil securityUtil;
|
private final SecurityUtil securityUtil;
|
||||||
private final VideoLikeMapper videoLikeMapper;
|
private final VideoLikeMapper videoLikeMapper;
|
||||||
|
private final VideoSearchService videoSearchService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@ -58,6 +62,9 @@ public class VideoServiceImpl implements VideoService {
|
||||||
|
|
||||||
videoMapper.insert(video);
|
videoMapper.insert(video);
|
||||||
|
|
||||||
|
// 保存到ES
|
||||||
|
videoSearchService.saveOrUpdate(video);
|
||||||
|
|
||||||
return convertToDTO(video);
|
return convertToDTO(video);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("上传视频失败", e);
|
log.error("上传视频失败", e);
|
||||||
|
@ -107,6 +114,9 @@ public class VideoServiceImpl implements VideoService {
|
||||||
|
|
||||||
// 删除数据库记录
|
// 删除数据库记录
|
||||||
videoMapper.deleteById(id);
|
videoMapper.deleteById(id);
|
||||||
|
|
||||||
|
// 从ES中删除
|
||||||
|
videoSearchService.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -141,6 +151,9 @@ public class VideoServiceImpl implements VideoService {
|
||||||
if (video != null) {
|
if (video != null) {
|
||||||
video.setViewCount(video.getViewCount() + 1);
|
video.setViewCount(video.getViewCount() + 1);
|
||||||
videoMapper.updateById(video);
|
videoMapper.updateById(video);
|
||||||
|
|
||||||
|
// 更新ES中的观看次数
|
||||||
|
videoSearchService.updateViewCount(id, video.getViewCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +198,9 @@ public class VideoServiceImpl implements VideoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
videoMapper.updateById(video);
|
videoMapper.updateById(video);
|
||||||
|
|
||||||
|
// 更新ES中的点赞次数
|
||||||
|
videoSearchService.updateLikeCount(id, video.getLikeCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加新方法:检查用户是否已点赞
|
// 添加新方法:检查用户是否已点赞
|
||||||
|
@ -201,6 +217,16 @@ public class VideoServiceImpl implements VideoService {
|
||||||
return videoLikeMapper.selectCount(wrapper) > 0;
|
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) {
|
private VideoDTO convertToDTO(Video video) {
|
||||||
if (video == null) {
|
if (video == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -129,4 +129,15 @@ config:
|
||||||
# SRS配置
|
# SRS配置
|
||||||
srs:
|
srs:
|
||||||
server:
|
server:
|
||||||
url: http://localhost:1985 # SRS HTTP API地址
|
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