xiangpei
4 天以前 d49707858ddd3ba73ded357bdaf8044c8b4b9c40
视频es
7个文件已修改
5个文件已添加
621 ■■■■■ 已修改文件
framework/src/main/java/cn/lili/elasticsearch/EsSuffix.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/es/VideoIndex.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/vo/SimpleVideoTagVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoGoodsDetailVO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/EsService.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoEsServiceImpl.java 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/search/repository/EsVideoIndexRepository.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/resources/es/video.json 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/resources/mapper/lmk/VideoMapper.xml 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager-api/src/main/java/cn/lili/controller/lmk/VideoController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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";
framework/src/main/java/cn/lili/modules/lmk/domain/es/VideoIndex.java
New file
@@ -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的视频document(存储单元)
 *
 * @author:xp
 * @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;
    /** 作者id */
    @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;
}
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;
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:xp
@@ -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;
}
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);
}
framework/src/main/java/cn/lili/modules/lmk/service/EsService.java
New file
@@ -0,0 +1,70 @@
package cn.lili.modules.lmk.service;
import java.util.Map;
/**
 * es处理
 *
 * @author:xp
 * @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 更新哪些字段,key 字段  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);
}
framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoEsServiceImpl.java
New file
@@ -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:xp
 * @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);
    }
}
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("下架成功");
    }
framework/src/main/java/cn/lili/modules/search/repository/EsVideoIndexRepository.java
New file
@@ -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> {
}
framework/src/main/resources/es/video.json
New file
@@ -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"
        }
      }
    }
  }
}
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>
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();
    }
}