From d49707858ddd3ba73ded357bdaf8044c8b4b9c40 Mon Sep 17 00:00:00 2001 From: xiangpei <xiangpei@timesnew.cn> Date: 星期一, 30 六月 2025 20:41:17 +0800 Subject: [PATCH] 视频es --- framework/src/main/java/cn/lili/modules/lmk/service/EsService.java | 70 ++++++ framework/src/main/java/cn/lili/elasticsearch/EsSuffix.java | 5 framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoGoodsDetailVO.java | 8 framework/src/main/resources/mapper/lmk/VideoMapper.xml | 64 +++++ framework/src/main/resources/es/video.json | 49 ++++ framework/src/main/java/cn/lili/modules/search/repository/EsVideoIndexRepository.java | 15 + framework/src/main/java/cn/lili/modules/lmk/domain/es/VideoIndex.java | 94 ++++++++ manager-api/src/main/java/cn/lili/controller/lmk/VideoController.java | 13 + framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java | 75 ++++++ framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java | 10 framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoEsServiceImpl.java | 213 +++++++++++++++++++ framework/src/main/java/cn/lili/modules/lmk/domain/vo/SimpleVideoTagVO.java | 5 12 files changed, 618 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/cn/lili/elasticsearch/EsSuffix.java b/framework/src/main/java/cn/lili/elasticsearch/EsSuffix.java index 9b6e4a9..72bd99c 100644 --- a/framework/src/main/java/cn/lili/elasticsearch/EsSuffix.java +++ b/framework/src/main/java/cn/lili/elasticsearch/EsSuffix.java @@ -14,6 +14,11 @@ public static final String GOODS_INDEX_NAME = "goods"; /** + * 瑙嗛绱㈠紩鍚庣紑 + */ + public static final String VIDEO_INDEX_NAME = "video"; + + /** * 鏃ュ織绱㈠紩鍚庣紑 */ public static final String LOGS_INDEX_NAME = "logs"; diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/es/VideoIndex.java b/framework/src/main/java/cn/lili/modules/lmk/domain/es/VideoIndex.java new file mode 100644 index 0000000..b38db0a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/es/VideoIndex.java @@ -0,0 +1,94 @@ +package cn.lili.modules.lmk.domain.es; + +import cn.lili.elasticsearch.EsSuffix; +import cn.lili.modules.lmk.domain.vo.SimpleVideoTagVO; +import cn.lili.modules.lmk.domain.vo.VideoGoodsDetailVO; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; + +import java.util.List; + +/** + * es鐨勮棰慸ocument锛堝瓨鍌ㄥ崟鍏冿級 + * + * @author锛歺p + * @date锛�2025/6/30 14:50 + */ +@Data +@Document(indexName = "#{@elasticsearchProperties.indexPrefix}_" + EsSuffix.VIDEO_INDEX_NAME, createIndex = false) +public class VideoIndex { + + /** 瑙嗛id */ + @Id + private String id; + + /** 瑙嗛鏍囬 */ + @Field(type = FieldType.Text, searchAnalyzer = "ik_max_word") + private String title; + + /** 浣滆�卛d */ + @Field(type = FieldType.Keyword) + private String authorId; + + /** 浣滆�呭悕绉� */ + @Field(type = FieldType.Text, searchAnalyzer = "ik_max_word") + private String authorName; + + /** 浣滆�呭ご鍍� */ + @Field(type = FieldType.Keyword) + private String authorAvatar; + + /** 灏侀潰 */ + @Field(type = FieldType.Keyword) + private String coverFileKey; + + /** 瑙嗛鍦板潃 */ + @Field(type = FieldType.Keyword) + private String videoFileKey; + + /** + * 瑙嗛鍐呭绫诲瀷锛氳棰戙�佸浘鐗� + * @see cn.lili.modules.lmk.enums.general.VideoContentTypeEnum + */ + @Field(type = FieldType.Keyword) + private String videoContentType; + + /** + * 瑙嗛绫诲瀷锛氳棰戙�佸ぇ鍋ュ悍銆佺鍘� + * @see cn.lili.modules.lmk.enums.general.VideoTypeEnum + */ + @Field(type = FieldType.Keyword) + private String videoType; + + /** 鍥鹃泦-json鏁扮粍 */ + @Field(type = FieldType.Keyword) + private String videoImgs; + + /** 瑙嗛鏍囩 */ + @Field(type = FieldType.Nested) + private List<SimpleVideoTagVO> tagList; + + /** 瑙嗛鏃堕暱锛氱 */ + @Field(type = FieldType.Keyword) + private Long videoDuration; + + /** 瑙嗛濉厖妯″紡 */ + @Field(type = FieldType.Keyword) + private String videoFit; + + /** 瑙嗛鐘舵�� */ + @Field(type = FieldType.Keyword) + private String status; + + /** 鍟嗗搧淇℃伅 */ + @Field(type = FieldType.Nested) + private List<VideoGoodsDetailVO> goodsList; + + /** 鏄惁鎺ㄨ崘瑙嗛 */ + @Field(type = FieldType.Keyword) + private boolean recommend = false; + +} diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/SimpleVideoTagVO.java b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/SimpleVideoTagVO.java index 87c6671..a613316 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/SimpleVideoTagVO.java +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/SimpleVideoTagVO.java @@ -6,6 +6,8 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.beans.BeanUtils; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; import org.springframework.lang.NonNull; /** @@ -19,13 +21,16 @@ public class SimpleVideoTagVO { @ApiModelProperty("鏍囩id") + @Field(type = FieldType.Keyword) private String id; /** 鏍囩鍚嶇О */ @ApiModelProperty("鏍囩鍚嶇О") + @Field(type = FieldType.Text, searchAnalyzer = "ik_max_word") private String tagName; @ApiModelProperty(hidden = true) + @Field(type = FieldType.Keyword) private String videoId; diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoGoodsDetailVO.java b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoGoodsDetailVO.java index a98f5dc..c5d4acc 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoGoodsDetailVO.java +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoGoodsDetailVO.java @@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; /** * @author锛歺p @@ -14,20 +16,26 @@ public class VideoGoodsDetailVO { @ApiModelProperty("鍟嗗搧id") + @Field(type = FieldType.Keyword) private String goodsId; @ApiModelProperty("鍟嗗搧skuid") + @Field(type = FieldType.Keyword) private String id; @ApiModelProperty("鍟嗗搧鍚嶇О") + @Field(type = FieldType.Text, searchAnalyzer = "ik_max_word") private String goodsName; @ApiModelProperty("浠锋牸") + @Field(type = FieldType.Keyword) private String price; @ApiModelProperty("缂╃暐鍥�") + @Field(type = FieldType.Keyword) private String thumbnail; @ApiModelProperty("鍟嗗搧鏁伴噺") + @Field(type = FieldType.Keyword) private Integer goodsNum; } diff --git a/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java b/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java index c8a6340..380e9e5 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java +++ b/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java @@ -1,6 +1,7 @@ package cn.lili.modules.lmk.mapper; import cn.lili.modules.lmk.domain.entity.Video; +import cn.lili.modules.lmk.domain.es.VideoIndex; import cn.lili.modules.lmk.domain.query.*; import cn.lili.modules.lmk.domain.vo.*; import com.baomidou.mybatisplus.core.mapper.BaseMapper; @@ -130,4 +131,13 @@ * @return */ List<VideoGoodsDetailVO> getVideoGoods(@Param("id") String videoId); + + /** + * es鍚屾鏌ヨ瑙嗛鏁版嵁 + * + * @param start 寮�濮嬩綅缃� + * @param end 缁撴潫浣嶇疆 + * @return + */ + List<VideoIndex> getEsPage(@Param("start") int start, @Param("end") int end); } diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/EsService.java b/framework/src/main/java/cn/lili/modules/lmk/service/EsService.java new file mode 100644 index 0000000..ea42513 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/lmk/service/EsService.java @@ -0,0 +1,70 @@ +package cn.lili.modules.lmk.service; + +import java.util.Map; + +/** + * es澶勭悊 + * + * @author锛歺p + * @date锛�2025/6/30 14:47 + */ +public interface EsService { + + /** + * 鑾峰彇绱㈠紩鐨勫畬鏁村悕绉� + * + * @param indexName + * @return + */ + String getIndexFullName(String indexName); + + /** + * 鍒涘缓绱㈠紩 + * + * @param indexName 绱㈠紩鍚嶇О + * @param mappingJsonPath json鏂囦欢浣嶇疆锛岀浉瀵逛簬resource鐩綍锛屼緥濡傦細/es/video.json + */ + void createIndex(String indexName, String mappingJsonPath); + + /** + * 閲嶅缓绱㈠紩锛屽苟閲嶆柊娣诲姞绱㈠紩鏁版嵁 + * + * @param indexName 绱㈠紩鍚嶇О + * @param mappingJsonPath json鏂囦欢浣嶇疆锛岀浉瀵逛簬resource鐩綍锛屼緥濡傦細/es/video.json + */ + void recreateIndex(String indexName, String mappingJsonPath); + + /** + * 娣诲姞/淇敼 鏂囨。锛屽鏋滄槸淇敼锛屽垯鏄暣鏉℃暟鎹洿鏂� + * + * @param indexName 绱㈠紩鍚嶇О + * @param id es涓婚敭锛屽彲浼犱笟鍔′富閿� + * @param data 鏁版嵁瀵硅薄 + */ + void addOrUpdateDocument(String indexName, String id, Object data); + + /** + * 鏇存柊鏌愪簺瀛楁鐨勫�� + * + * @param indexName 绱㈠紩鍚嶇О + * @param id 鏁版嵁id + * @param updateList 鏇存柊鍝簺瀛楁锛宬ey 瀛楁 value瑕佷慨鏀圭殑鍊� + */ + void updateSomeField(String indexName, String id, Map<String, Object> updateList); + + /** + * 鍒犻櫎鏂囨。 + * @param indexName 绱㈠紩鍚嶇О + * @param id es涓婚敭锛屽彲浼犱笟鍔′富閿� + */ + void deleteDocument(String indexName, String id); + + /** + * 绱㈠紩鏄惁瀛樺湪 + * + * @param indexName + * @return + */ + boolean indexExist(String indexName); + +} diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoEsServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoEsServiceImpl.java new file mode 100644 index 0000000..7d9a4d8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoEsServiceImpl.java @@ -0,0 +1,213 @@ +package cn.lili.modules.lmk.service.impl; + +import cn.lili.elasticsearch.BaseElasticsearchService; +import cn.lili.elasticsearch.EsSuffix; +import cn.lili.elasticsearch.config.ElasticsearchProperties; +import cn.lili.modules.lmk.domain.entity.Video; +import cn.lili.modules.lmk.domain.es.VideoIndex; +import cn.lili.modules.lmk.enums.general.VideoStatusEnum; +import cn.lili.modules.lmk.mapper.VideoMapper; +import cn.lili.modules.lmk.service.EsService; +import cn.lili.modules.search.repository.EsVideoIndexRepository; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.delete.DeleteRequest; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.indices.CreateIndexRequest; +import org.elasticsearch.client.indices.CreateIndexResponse; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.springframework.data.elasticsearch.core.document.Document; +import org.springframework.data.elasticsearch.core.query.UpdateQuery; +import org.springframework.stereotype.Service; +import org.springframework.util.FileCopyUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; + +/** + * 瑙嗛es + * + * @author锛歺p + * @date锛�2025/6/30 15:54 + */ +@Slf4j +@RequiredArgsConstructor +@Service("videoEsServiceImpl") +public class VideoEsServiceImpl extends BaseElasticsearchService implements EsService { + + private final ElasticsearchProperties elasticsearchProperties; + private final VideoMapper videoMapper; + private final EsVideoIndexRepository esVideoIndexRepository; + + @Override + public String getIndexFullName(String indexName) { + return elasticsearchProperties.getIndexPrefix() + "_" + indexName; + } + + @Override + public void createIndex(String indexName, String mappingJsonPath) { + if (! indexName.startsWith(elasticsearchProperties.getIndexPrefix())) { + indexName = this.getIndexFullName(indexName); + } + if (this.indexExist(indexName)) { + throw new RuntimeException(String.format("绱㈠紩锛�%s宸茬粡瀛樺湪锛屾棤娉曞垱寤�", indexName)); + } + CreateIndexRequest request = new CreateIndexRequest(indexName); + + // 1. 閰嶇疆绱㈠紩 + request.settings(Settings.builder() + .put("index.number_of_shards", elasticsearchProperties.getIndex().getNumberOfShards()) + .put("index.number_of_replicas", elasticsearchProperties.getIndex().getNumberOfReplicas()) + .put("index.max_result_window", 100000) //鏈�澶ф煡璇㈢粨鏋滄暟 + .put("index.mapping.total_fields.limit", 2000)); + // 2. 閰嶇疆mapping + String mapping; + try (InputStream inputStream = this.getClass().getResourceAsStream(mappingJsonPath)) { + byte[] bytes = FileCopyUtils.copyToByteArray(inputStream); + mapping = new String(bytes, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(String.format("璇诲彇es鏄犲皠json鏂囦欢锛�%s寮傚父", mappingJsonPath), e); + } + request.mapping(mapping, XContentType.JSON); + // 3. 鍒涘缓绱㈠紩 + try { + CreateIndexResponse createIndexResponse = client.indices().create(request, COMMON_OPTIONS); + } catch (IOException e) { + throw new RuntimeException(String.format("es鍒涘缓绱㈠紩澶辫触锛�%s", indexName), e); + } + } + + @Override + public void recreateIndex(String indexName, String mappingJsonPath) { + indexName = this.getIndexFullName(indexName); + // 1. 濡傛灉绱㈠紩瀛樺湪锛屽厛鍒犻櫎绱㈠紩锛屽啀鍒涘缓绱㈠紩 + if (this.indexExist(indexName)) { + DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName); + try { + AcknowledgedResponse deleteRes = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT); + this.createIndex(indexName, mappingJsonPath); + } catch (IOException e) { + log.error("鍒犻櫎绱㈠紩澶辫触", e); + throw new RuntimeException("鍒犻櫎绱㈠紩澶辫触"); + } + } else { + this.createIndex(indexName, mappingJsonPath); + } + // 2. 澶氱嚎绋嬫煡璇㈣棰戞暟鎹紝鏋勫缓鏂囨。瀵硅薄 + Long totalVideo = new LambdaQueryChainWrapper<>(videoMapper) + .eq(Video::getStatus, VideoStatusEnum.PUBLISHED.getValue()) + .count(); + int totalThreads = (int) Math.ceil((double) totalVideo / 200); // 璁$畻闇�瑕佸灏戜釜绾跨▼ + CountDownLatch latch = new CountDownLatch(totalThreads); + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( + 4, + 10, + 10, + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(4), + Executors.defaultThreadFactory(), + new ThreadPoolExecutor.AbortPolicy()); + BlockingQueue<VideoIndex> dataList = new LinkedBlockingQueue<>(); + for (int page = 0; page < totalThreads; page++) { + final int currentPage = page; + threadPoolExecutor.execute(() -> { + try { + List<VideoIndex> pageData = videoMapper.getEsPage(currentPage * 200, 200); + dataList.addAll(pageData); + } catch (Exception e) { + log.error("绗瑊}椤垫暟鎹煡璇㈠け璐�", currentPage, e); + } finally { + latch.countDown(); // 绾跨▼鎵ц瀹屾垚 -1 + } + }); + } + try { + latch.await(); // 绛夊緟鎵�鏈夌嚎绋嬫墽琛屽畬鎴� + // 3. 娣诲姞es鏁版嵁 +// BulkRequest bulkRequest = new BulkRequest(); +// String finalIndexName = indexName; +// dataList.forEach(data -> { +// IndexRequest indexRequest = new IndexRequest(finalIndexName) +// .id(data.getId()) +// .source(data); +// bulkRequest.add(indexRequest); +// }); +// client.bulk(bulkRequest, RequestOptions.DEFAULT); + esVideoIndexRepository.saveAll(dataList); + } catch (InterruptedException e) { + log.error("澶氱嚎绋嬭鍙栬棰戞暟鎹紓甯�", e); + } finally { + threadPoolExecutor.shutdown(); + } + } + + @Override + public void addOrUpdateDocument(String indexName, String id, Object data) { + indexName = this.getIndexFullName(indexName); + IndexRequest request = new IndexRequest(indexName); + request.id(id).source(data); + try { + client.index(request, RequestOptions.DEFAULT); + } catch (IOException e) { + throw new RuntimeException("es鏂囨。娣诲姞/淇敼澶辫触", e); + } + } + + @Override + public void updateSomeField(String indexName, String id, Map<String, Object> updateList) { + indexName = this.getIndexFullName(indexName); + // 鏋勫缓鏇存柊璇锋眰 + UpdateRequest request = new UpdateRequest(indexName, id); + + try { + // 鏋勫缓鏇存柊鍐呭 + XContentBuilder builder = XContentFactory.jsonBuilder().startObject(); + for (Map.Entry<String, Object> entry : updateList.entrySet()) { + builder.field(entry.getKey(), entry.getValue()); + } + builder.endObject(); + + request.doc(builder); // 璁剧疆閮ㄥ垎鏇存柊鍐呭 + + // 鍙�夐厤缃� + request.retryOnConflict(2); // 鍐茬獊閲嶈瘯娆℃暟 +// request.fetchSource(true); // 杩斿洖鏇存柊鍚庣殑鏂囨。 + + client.update(request, RequestOptions.DEFAULT); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void deleteDocument(String indexName, String id) { + indexName = this.getIndexFullName(indexName); + DeleteRequest request = new DeleteRequest(indexName, id); + try { + client.delete(request, RequestOptions.DEFAULT); + } catch (IOException e) { + throw new RuntimeException("鍒犻櫎es鏂囨。澶辫触锛�" + id, e); + } + } + + @Override + public boolean indexExist(String indexName) { + if (!indexName.startsWith(elasticsearchProperties.getIndexPrefix())) { + indexName = this.getIndexFullName(indexName); + } + return super.indexExist(indexName); + } +} diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java index 116f4f6..980f329 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java @@ -3,8 +3,10 @@ import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.security.context.UserContext; +import cn.lili.elasticsearch.EsSuffix; import cn.lili.modules.lmk.constant.RedisKeyExpireConstant; import cn.lili.modules.lmk.domain.entity.*; +import cn.lili.modules.lmk.domain.es.VideoIndex; import cn.lili.modules.lmk.domain.form.*; import cn.lili.modules.lmk.domain.query.*; import cn.lili.modules.lmk.domain.vo.*; @@ -26,6 +28,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; import cn.lili.utils.PageUtil; @@ -61,6 +64,10 @@ private final VideoGoodsService videoGoodsService; private final KitchenTypeService kitchenTypeService; private final Cache cache; + + @Qualifier("videoEsServiceImpl") + private final EsService videoEsService; + /** * 娣诲姞 @@ -107,8 +114,16 @@ * @return */ @Override + @Transactional(rollbackFor = Exception.class) public Result removeById(String id) { baseMapper.deleteById(id); + new LambdaUpdateChainWrapper<>(videoGoodsService.getBaseMapper()) + .eq(VideoGoods::getVideoId, id) + .remove(); + new LambdaUpdateChainWrapper<>(videoTagRefService.getBaseMapper()) + .eq(VideoTagRef::getVideoId, id) + .remove(); + videoEsService.deleteDocument(EsSuffix.VIDEO_INDEX_NAME, id); return Result.ok("鍒犻櫎鎴愬姛"); } @@ -175,6 +190,7 @@ } baseMapper.insert(video); // 2.澶勭悊鏍囩 + List<SimpleVideoTagVO> esTagList = new ArrayList<>(2); List<VideoTagRef> videoTagRefs = form.getTags().stream().map(tag -> { VideoTagRef videoTagRef = new VideoTagRef(); videoTagRef.setVideoId(video.getId()); @@ -194,14 +210,19 @@ } else { videoTagRef.setVideoTagId(tag.getId()); } + SimpleVideoTagVO esTag = new SimpleVideoTagVO(); + esTag.setVideoId(video.getId()); + esTag.setTagName(tag.getTagName()); + esTag.setId(tag.getId()); + esTagList.add(esTag); return videoTagRef; }).collect(Collectors.toList()); videoTagRefService.saveBatch(videoTagRefs); // 3. 淇濆瓨瑙嗛鏂囦欢淇℃伅 lmkFileService.addByForm(form.getFileInfo()); // 4. 澶勭悊閫夋嫨鐨勫晢鍝� + List<VideoGoods> videoGoods = new ArrayList<>(2); if (CollectionUtils.isNotEmpty(form.getGoodsList())) { - List<VideoGoods> videoGoods = new ArrayList<>(2); for (int i = 0; i < form.getGoodsList().size(); i++) { VideoGoods e = new VideoGoods(); e.setVideoId(video.getId()); @@ -209,10 +230,22 @@ e.setGoodsSkuId(form.getGoodsList().get(i).getGoodsSkuId()); e.setGoodsNum(form.getGoodsList().get(i).getGoodsNum()); e.setOrderNum(i); - videoGoods.add(e); + videoGoodsService.save(e); } videoGoodsService.saveBatch(videoGoods); } + // 5. 鏋勫缓es涓暟鎹� + VideoIndex videoIndex = new VideoIndex(); + BeanUtils.copyProperties(video, videoIndex); + videoIndex.setCoverFileKey(video.getCoverUrl()); + List<VideoGoodsDetailVO> esGoodsList = videoGoods.stream().map(goods -> { + VideoGoodsDetailVO vo = new VideoGoodsDetailVO(); + BeanUtils.copyProperties(goods, vo); + return vo; + }).collect(Collectors.toList()); + videoIndex.setGoodsList(esGoodsList); + videoIndex.setTagList(esTagList); + videoEsService.addOrUpdateDocument(EsSuffix.VIDEO_INDEX_NAME, video.getId(), videoIndex); return Result.ok("鍙戝竷鎴愬姛锛岃棰戝鏍镐腑~"); } @@ -236,6 +269,7 @@ new LambdaUpdateChainWrapper<>(videoTagRefService.getBaseMapper()) .eq(VideoTagRef::getVideoId, video.getId()) .remove(); + List<SimpleVideoTagVO> esTagList = new ArrayList<>(2); List<VideoTagRef> videoTagRefs = form.getTags().stream().map(tag -> { VideoTagRef videoTagRef = new VideoTagRef(); videoTagRef.setVideoId(video.getId()); @@ -255,6 +289,11 @@ } else { videoTagRef.setVideoTagId(tag.getId()); } + SimpleVideoTagVO esTag = new SimpleVideoTagVO(); + esTag.setVideoId(video.getId()); + esTag.setTagName(tag.getTagName()); + esTag.setId(tag.getId()); + esTagList.add(esTag); return videoTagRef; }).collect(Collectors.toList()); videoTagRefService.saveBatch(videoTagRefs); @@ -264,8 +303,8 @@ new LambdaUpdateChainWrapper<>(videoGoodsService.getBaseMapper()) .eq(VideoGoods::getVideoId, video.getId()) .remove(); + List<VideoGoods> videoGoods = new ArrayList<>(2); if (CollectionUtils.isNotEmpty(form.getGoodsList())) { - List<VideoGoods> videoGoods = new ArrayList<>(2); for (int i = 0; i < form.getGoodsList().size(); i++) { VideoGoods e = new VideoGoods(); e.setVideoId(video.getId()); @@ -277,6 +316,18 @@ } videoGoodsService.saveBatch(videoGoods); } + // 5. 鏇存柊es涓殑鏁版嵁 + VideoIndex videoIndex = new VideoIndex(); + BeanUtils.copyProperties(video, videoIndex); + videoIndex.setCoverFileKey(video.getCoverUrl()); + List<VideoGoodsDetailVO> esGoodsList = videoGoods.stream().map(goods -> { + VideoGoodsDetailVO vo = new VideoGoodsDetailVO(); + BeanUtils.copyProperties(goods, vo); + return vo; + }).collect(Collectors.toList()); + videoIndex.setGoodsList(esGoodsList); + videoIndex.setTagList(esTagList); + videoEsService.addOrUpdateDocument(EsSuffix.VIDEO_INDEX_NAME, video.getId(), videoIndex); return Result.ok("鍙戝竷鎴愬姛锛岃棰戝鏍镐腑~"); } @@ -305,6 +356,9 @@ .eq(Video::getId, form.getId()) .set(Video::getRecommend, form.getRecommend()) .update(); + Map<String, Object> fields = new HashMap<>(2); + fields.put("recommend", form.getRecommend()); + videoEsService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, form.getId(), fields); return Result.ok("璁剧疆鎴愬姛"); } @@ -327,6 +381,10 @@ if (form.getResult()) { video.setStatus(VideoStatusEnum.PUBLISHED.getValue()); video.setAuditPassTime(new Date()); + + Map<String, Object> fields = new HashMap<>(2); + fields.put("status", VideoStatusEnum.PUBLISHED.getValue()); + videoEsService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, video.getId(), fields); } else { video.setStatus(VideoStatusEnum.REJECT.getValue()); } @@ -337,21 +395,32 @@ @Override public Result up(String id) { + // 1. 鏇存柊鏁版嵁搴� new LambdaUpdateChainWrapper<>(baseMapper) .eq(Video::getId, id) .set(Video::getStatus, VideoStatusEnum.PUBLISHED.getValue()) .update(); + // 2. 鏇存柊es + Map<String, Object> fields = new HashMap<>(2); + fields.put("status", VideoStatusEnum.PUBLISHED.getValue()); + videoEsService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, id, fields); return Result.ok("涓婃灦鎴愬姛"); } @Override public Result down(VideoDownForm form) { + // 1. 鏇存柊鏁版嵁搴� new LambdaUpdateChainWrapper<>(baseMapper) .eq(Video::getId, form.getId()) .set(Video::getStatus, VideoStatusEnum.DISABLE.getValue()) .update(); + // 2. 鏇存柊es + Map<String, Object> fields = new HashMap<>(2); + fields.put("status", VideoStatusEnum.DISABLE.getValue()); + videoEsService.updateSomeField(EsSuffix.VIDEO_INDEX_NAME, form.getId(), fields); // TODO 灏嗕笅鏋跺師鍥犱互閫氱煡鐨勬柟寮忓憡鐭ョ敤鎴� + return Result.ok("涓嬫灦鎴愬姛"); } diff --git a/framework/src/main/java/cn/lili/modules/search/repository/EsVideoIndexRepository.java b/framework/src/main/java/cn/lili/modules/search/repository/EsVideoIndexRepository.java new file mode 100644 index 0000000..1819a0d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/repository/EsVideoIndexRepository.java @@ -0,0 +1,15 @@ +package cn.lili.modules.search.repository; + +import cn.lili.modules.lmk.domain.es.VideoIndex; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +/** + * 瑙嗛绱㈠紩 + * + * @author paulG + * @since 2020/10/15 + **/ +public interface EsVideoIndexRepository extends ElasticsearchRepository<VideoIndex, String> { + +} diff --git a/framework/src/main/resources/es/video.json b/framework/src/main/resources/es/video.json new file mode 100644 index 0000000..bda78cd --- /dev/null +++ b/framework/src/main/resources/es/video.json @@ -0,0 +1,49 @@ +{ + "properties": { + "id": {"type": "keyword"}, + "title": { + "type": "text", + "analyzer": "ik_max_word" + }, + "authorId": {"type": "keyword"}, + "authorName": { + "type": "text", + "analyzer": "ik_max_word" + }, + "authorAvatar": {"type": "keyword"}, + "recommend": {"type": "boolean"}, + "coverFileKey": {"type": "keyword"}, + "videoFileKey": {"type": "keyword"}, + "videoContentType": {"type": "keyword"}, + "videoType": {"type": "keyword"}, + "videoImgs": {"type": "keyword"}, + "tagList": { + "type": "nested", + "properties": { + "id": {"type": "keyword"}, + "videoId": {"type": "keyword"}, + "tagName": { + "type": "text", + "analyzer": "ik_max_word" + } + } + }, + "videoDuration": {"type": "keyword"}, + "videoFit": {"type": "keyword"}, + "status": {"type": "keyword"}, + "goodsList": { + "type": "nested", + "properties": { + "id": {"type": "keyword"}, + "goodsId": {"type": "keyword"}, + "price": {"type": "keyword"}, + "thumbnail": {"type": "keyword"}, + "goodsNum": {"type": "keyword"}, + "goodsName": { + "type": "text", + "analyzer": "ik_max_word" + } + } + } + } +} diff --git a/framework/src/main/resources/mapper/lmk/VideoMapper.xml b/framework/src/main/resources/mapper/lmk/VideoMapper.xml index 248411a..c6e1579 100644 --- a/framework/src/main/resources/mapper/lmk/VideoMapper.xml +++ b/framework/src/main/resources/mapper/lmk/VideoMapper.xml @@ -545,4 +545,68 @@ <if test="query.authorId != null and query.authorId != ''">AND LV.author_id = #{query.authorId}</if> <if test="query.status != null and query.status != ''">AND LV.status = #{query.status}</if> </select> + + <resultMap id="EsResultMap" type="cn.lili.modules.lmk.domain.es.VideoIndex"> + <id column="id" property="id"/> + <result column="author_id" property="authorId" /> + <result column="authorName" property="authorName" /> + <result column="authorAvatar" property="authorAvatar" /> + <result column="cover_url" property="coverFileKey" /> + <result column="video_file_key" property="videoFileKey" /> + <result column="video_fit" property="videoFit" /> + <result column="video_duration" property="videoDuration" /> + <result column="title" property="title" /> + <result column="status" property="status" /> + <result column="recommend" property="recommend" /> + <result column="video_content_type" property="videoContentType" /> + <result column="video_type" property="videoType" /> + <result column="video_imgs" property="videoImgs" /> + <collection property="goodsList" column="id" select="getVideoGoods" ofType="cn.lili.modules.lmk.domain.vo.VideoGoodsDetailVO"/> + <collection property="tagList" column="id" select="getVideoTags" ofType="cn.lili.modules.lmk.domain.vo.SimpleVideoTagVO"/> + </resultMap> + + <select id="getVideoTags" resultType="cn.lili.modules.lmk.domain.vo.SimpleVideoTagVO"> + SELECT + LVT.id, + LVT.tag_name as tagName + FROM + lmk_video_tag_ref LVTR + INNER JOIN lmk_video_tag LVT ON LVTR.video_tag_id = LVT.id AND LVT.delete_flag = 0 + WHERE + LVTR.video_id = #{id} + </select> + + <select id="getEsPage" parameterType="int" resultMap="EsResultMap"> + SELECT + LV.author_id, + LV.cover_url, + LV.video_fit, + LV.video_duration, + LV.video_file_key, + LV.title, + LV.goods_view_num, + LV.goods_order_num, + LV.recommend, + LV.status, + LV.play_num, + LV.comment_num, + LV.collect_num, + LV.weight, + LV.audit_pass_time, + LV.update_time, + LV.create_time, + LV.video_content_type, + LV.video_type, + LV.video_imgs, + LV.id, + LM.nick_name as authorName, + LM.face as authorAvatar + FROM + lmk_video LV + LEFT JOIN li_member LM ON LV.author_id = LM.id + WHERE + LV.delete_flag = 0 AND LV.status = '1' + LIMIT #{start}, #{end} + </select> + </mapper> diff --git a/manager-api/src/main/java/cn/lili/controller/lmk/VideoController.java b/manager-api/src/main/java/cn/lili/controller/lmk/VideoController.java index b496f0d..1202217 100644 --- a/manager-api/src/main/java/cn/lili/controller/lmk/VideoController.java +++ b/manager-api/src/main/java/cn/lili/controller/lmk/VideoController.java @@ -1,5 +1,6 @@ package cn.lili.controller.lmk; +import cn.lili.elasticsearch.EsSuffix; import cn.lili.group.Update; import cn.lili.group.Add; import cn.lili.modules.lmk.domain.form.VideoAuditingForm; @@ -7,6 +8,8 @@ import cn.lili.modules.lmk.domain.form.VideoRecommendForm; import cn.lili.modules.lmk.domain.form.WxVideoForm; import cn.lili.modules.lmk.domain.query.ManagerVideoQuery; +import cn.lili.modules.lmk.service.EsService; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.validation.annotation.Validated; import lombok.RequiredArgsConstructor; import java.util.List; @@ -31,6 +34,9 @@ public class VideoController { private final VideoService videoService; + + @Qualifier("videoEsServiceImpl") + private final EsService esService; @PostMapping @ApiOperation(value = "娣诲姞", notes = "娣诲姞") @@ -97,4 +103,11 @@ public Result down(@RequestBody @Validated VideoDownForm form) { return videoService.down(form); } + + @PostMapping("/recreate/es/index") + @ApiOperation(value = "閲嶅缓es绱㈠紩", notes = "閲嶅缓es绱㈠紩") + public Result recreateEsIndex() { + esService.recreateIndex(EsSuffix.VIDEO_INDEX_NAME, "/es/video.json"); + return Result.ok(); + } } -- Gitblit v1.8.0