!188 优化初始化商品索引,增加编辑商品时,保留原商品的销量、好评率和评价量

Merge pull request !188 from OceansDeep/feature/pg
This commit is contained in:
OceansDeep 2022-06-09 08:46:56 +00:00 committed by Gitee
commit 712941038b
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
8 changed files with 98 additions and 28 deletions

View File

@ -0,0 +1,30 @@
package cn.lili.init;
import cn.lili.modules.search.service.EsGoodsIndexService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* @author paulG
* @since 2022/6/9
**/
@Component
@Slf4j
public class EsGoodsIndexInitRunner implements ApplicationRunner {
@Autowired
private EsGoodsIndexService esGoodsIndexService;
@Override
public void run(ApplicationArguments args) {
try {
esGoodsIndexService.initIndex();
} catch (Exception e) {
log.error("检测ES商品索引失败", e);
}
}
}

View File

@ -115,7 +115,6 @@ public class RedisConfig extends CachingConfigurerSupport {
@Bean(destroyMethod = "shutdown") @Bean(destroyMethod = "shutdown")
public RedissonClient redisson() { public RedissonClient redisson() {
Config config = new Config(); Config config = new Config();
if (redisProperties.getSentinel() != null && !redisProperties.getSentinel().getNodes().isEmpty()) { if (redisProperties.getSentinel() != null && !redisProperties.getSentinel().getNodes().isEmpty()) {
// 哨兵模式 // 哨兵模式
SentinelServersConfig sentinelServersConfig = config.useSentinelServers(); SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
@ -145,6 +144,7 @@ public class RedisConfig extends CachingConfigurerSupport {
if (CharSequenceUtil.isNotEmpty(redisProperties.getPassword())) { if (CharSequenceUtil.isNotEmpty(redisProperties.getPassword())) {
singleServerConfig.setPassword(redisProperties.getPassword()); singleServerConfig.setPassword(redisProperties.getPassword());
} }
singleServerConfig.setPingConnectionInterval(1000);
} }
return Redisson.create(config); return Redisson.create(config);

View File

@ -123,7 +123,7 @@ public abstract class BaseElasticsearchService {
" \"type\": \"keyword\"\n" + " \"type\": \"keyword\"\n" +
" },\n" + " },\n" +
" \"type\": {\n" + " \"type\": {\n" +
" \"type\": \"long\"\n" + " \"type\": \"integer\"\n" +
" },\n" + " },\n" +
" \"value\": {\n" + " \"value\": {\n" +
" \"type\": \"keyword\"\n" + " \"type\": \"keyword\"\n" +

View File

@ -14,6 +14,7 @@ import cn.lili.common.properties.RocketmqCustomProperties;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.SnowFlake; import cn.lili.common.utils.SnowFlake;
import cn.lili.modules.goods.entity.dos.Goods; import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dos.GoodsGallery;
import cn.lili.modules.goods.entity.dos.GoodsSku; import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO; import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams; import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
@ -166,8 +167,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
} else { } else {
skuList = new ArrayList<>(); skuList = new ArrayList<>();
for (Map<String, Object> map : goodsOperationDTO.getSkuList()) { for (Map<String, Object> map : goodsOperationDTO.getSkuList()) {
GoodsSku sku = null; GoodsSku sku = GoodsSkuBuilder.build(goods, map, goodsOperationDTO);
sku = GoodsSkuBuilder.build(goods, map, goodsOperationDTO);
renderGoodsSku(sku, goodsOperationDTO); renderGoodsSku(sku, goodsOperationDTO);
skuList.add(sku); skuList.add(sku);
//如果商品状态值不对则es索引移除 //如果商品状态值不对则es索引移除
@ -636,6 +636,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
// 商品销售模式渲染器 // 商品销售模式渲染器
salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderBatch(goodsSkuList, goodsOperationDTO)); salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderBatch(goodsSkuList, goodsOperationDTO));
for (GoodsSku goodsSku : goodsSkuList) { for (GoodsSku goodsSku : goodsSkuList) {
extendOldSkuValue(goodsSku);
this.renderImages(goodsSku); this.renderImages(goodsSku);
} }
} }
@ -647,22 +648,43 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
* @param goodsOperationDTO 商品操作DTO * @param goodsOperationDTO 商品操作DTO
*/ */
void renderGoodsSku(GoodsSku goodsSku, GoodsOperationDTO goodsOperationDTO) { void renderGoodsSku(GoodsSku goodsSku, GoodsOperationDTO goodsOperationDTO) {
extendOldSkuValue(goodsSku);
// 商品销售模式渲染器 // 商品销售模式渲染器
salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderSingle(goodsSku, goodsOperationDTO)); salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderSingle(goodsSku, goodsOperationDTO));
this.renderImages(goodsSku); this.renderImages(goodsSku);
} }
/**
* 将原sku的一些不会直接传递的值放到新的sku中
*
* @param goodsSku 商品sku
*/
private void extendOldSkuValue(GoodsSku goodsSku) {
if (CharSequenceUtil.isNotEmpty(goodsSku.getGoodsId())) {
GoodsSku oldSku = this.getGoodsSkuByIdFromCache(goodsSku.getId());
if (oldSku != null) {
goodsSku.setCommentNum(oldSku.getCommentNum());
goodsSku.setViewCount(oldSku.getViewCount());
goodsSku.setBuyCount(oldSku.getBuyCount());
goodsSku.setGrade(oldSku.getGrade());
}
}
}
/** /**
* 渲染sku图片 * 渲染sku图片
* *
* @param goodsSku * @param goodsSku sku
*/ */
void renderImages(GoodsSku goodsSku) { void renderImages(GoodsSku goodsSku) {
JSONObject jsonObject = JSONUtil.parseObj(goodsSku.getSpecs()); JSONObject jsonObject = JSONUtil.parseObj(goodsSku.getSpecs());
List<Map<String, String>> images = jsonObject.get("images", List.class); List<Map<String, String>> images = jsonObject.get("images", List.class);
if (images != null && !images.isEmpty()) { if (images != null && !images.isEmpty()) {
goodsSku.setThumbnail(goodsGalleryService.getGoodsGallery(images.get(0).get("url")).getThumbnail()); GoodsGallery goodsGallery = goodsGalleryService.getGoodsGallery(images.get(0).get("url"));
goodsSku.setSmall(goodsGalleryService.getGoodsGallery(images.get(0).get("url")).getSmall()); goodsSku.setBig(goodsGallery.getOriginal());
goodsSku.setOriginal(goodsGallery.getOriginal());
goodsSku.setThumbnail(goodsGallery.getThumbnail());
goodsSku.setSmall(goodsGallery.getSmall());
} }
} }

View File

@ -28,7 +28,7 @@ import java.util.Map;
* @author paulG * @author paulG
**/ **/
@Data @Data
@Document(indexName = "#{@elasticsearchProperties.indexPrefix}_" + EsSuffix.GOODS_INDEX_NAME) @Document(indexName = "#{@elasticsearchProperties.indexPrefix}_" + EsSuffix.GOODS_INDEX_NAME, createIndex = false)
@ToString @ToString
@NoArgsConstructor @NoArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
@ -215,6 +215,7 @@ public class EsGoodsIndex implements Serializable {
/** /**
* 销售模式 * 销售模式
*
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum * @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
*/ */
@Field(type = FieldType.Text) @Field(type = FieldType.Text)
@ -277,7 +278,7 @@ public class EsGoodsIndex implements Serializable {
* @see PromotionTypeEnum * @see PromotionTypeEnum
* value 促销活动实体信息 * value 促销活动实体信息
*/ */
@Field(type = FieldType.Nested) @Field(type = FieldType.Text)
@ApiModelProperty("商品促销活动集合JSONkey 为 促销活动类型value 为 促销活动实体信息 ") @ApiModelProperty("商品促销活动集合JSONkey 为 促销活动类型value 为 促销活动实体信息 ")
private String promotionMapJson; private String promotionMapJson;
@ -316,7 +317,7 @@ public class EsGoodsIndex implements Serializable {
/** /**
* 参数索引增加 * 参数索引增加
* *
* @param sku 商品sku信息 * @param sku 商品sku信息
* @param goodsParamDTOS 商品参数信息 * @param goodsParamDTOS 商品参数信息
*/ */
public EsGoodsIndex(GoodsSku sku, List<GoodsParamsDTO> goodsParamDTOS) { public EsGoodsIndex(GoodsSku sku, List<GoodsParamsDTO> goodsParamDTOS) {

View File

@ -20,7 +20,7 @@ import java.util.Map;
public interface EsGoodsIndexService { public interface EsGoodsIndexService {
/** /**
* 全局索引初始化 * 全局索引数据初始化
*/ */
void init(); void init();
@ -31,6 +31,12 @@ public interface EsGoodsIndexService {
*/ */
Map<String, Integer> getProgress(); Map<String, Integer> getProgress();
/**
* 全局索引初始化
*/
void initIndex();
/** /**
* 添加商品索引 * 添加商品索引
* *

View File

@ -66,6 +66,7 @@ import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -127,7 +128,7 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
Boolean flag = (Boolean) cache.get(CachePrefix.INIT_INDEX_FLAG.getPrefix()); Boolean flag = (Boolean) cache.get(CachePrefix.INIT_INDEX_FLAG.getPrefix());
//为空则默认写入没有任务 //为空则默认写入没有任务
if (flag == null) { if (flag == null) {
cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false); cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false, 10L, TimeUnit.MINUTES);
} }
//有正在初始化的任务则提示异常 //有正在初始化的任务则提示异常
if (Boolean.TRUE.equals(flag)) { if (Boolean.TRUE.equals(flag)) {
@ -200,6 +201,25 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
return map; return map;
} }
@Override
public void initIndex() {
//索引名称拼接
String indexName = this.getIndexName();
//索引初始化因为mapping结构问题
//但是如果索引已经自动生成过这里就不会创建索引设置mapping所以这里决定在初始化索引的同时将已有索引删除重新创建
boolean indexExist = this.indexExist(indexName);
log.info("检测 {} 索引结构是否存在:{}", indexName, indexExist);
if (!indexExist) {
log.info("初始化索引结构 {}", indexName);
//如果索引不存在则创建索引
createIndexRequest(indexName);
}
}
@Override @Override
public void addIndex(EsGoodsIndex goods) { public void addIndex(EsGoodsIndex goods) {
try { try {

View File

@ -11,10 +11,8 @@ import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
import cn.lili.modules.search.entity.dos.EsGoodsIndex; import cn.lili.modules.search.entity.dos.EsGoodsIndex;
import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo; import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo;
import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO; import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO;
import cn.lili.modules.search.entity.dto.HotWordsDTO;
import cn.lili.modules.search.entity.dto.ParamOptions; import cn.lili.modules.search.entity.dto.ParamOptions;
import cn.lili.modules.search.entity.dto.SelectorOptions; import cn.lili.modules.search.entity.dto.SelectorOptions;
import cn.lili.modules.search.service.EsGoodsIndexService;
import cn.lili.modules.search.service.EsGoodsSearchService; import cn.lili.modules.search.service.EsGoodsSearchService;
import com.alibaba.druid.util.StringUtils; import com.alibaba.druid.util.StringUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -44,7 +42,6 @@ import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
@ -73,9 +70,6 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
*/ */
@Autowired @Autowired
private ElasticsearchOperations restTemplate; private ElasticsearchOperations restTemplate;
@Autowired
private EsGoodsIndexService esGoodsIndexService;
/** /**
* 缓存 * 缓存
*/ */
@ -84,10 +78,6 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
@Override @Override
public SearchPage<EsGoodsIndex> searchGoods(EsGoodsSearchDTO searchDTO, PageVO pageVo) { public SearchPage<EsGoodsIndex> searchGoods(EsGoodsSearchDTO searchDTO, PageVO pageVo) {
boolean exists = restTemplate.indexOps(EsGoodsIndex.class).exists();
if (!exists) {
esGoodsIndexService.init();
}
if (CharSequenceUtil.isNotBlank(searchDTO.getKeyword())) { if (CharSequenceUtil.isNotBlank(searchDTO.getKeyword())) {
cache.incrementScore(CachePrefix.HOT_WORD.getPrefix(), searchDTO.getKeyword()); cache.incrementScore(CachePrefix.HOT_WORD.getPrefix(), searchDTO.getKeyword());
} }
@ -364,7 +354,7 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
this.commonSearch(filterBuilder, searchDTO); this.commonSearch(filterBuilder, searchDTO);
//智能推荐 //智能推荐
this.recommended(filterBuilder,searchDTO); this.recommended(filterBuilder, searchDTO);
//未上架的商品不显示 //未上架的商品不显示
filterBuilder.must(QueryBuilders.matchQuery("marketEnable", GoodsStatusEnum.UPPER.name())); filterBuilder.must(QueryBuilders.matchQuery("marketEnable", GoodsStatusEnum.UPPER.name()));
@ -406,30 +396,31 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
/** /**
* 商品推荐 * 商品推荐
*
* @param filterBuilder * @param filterBuilder
* @param searchDTO * @param searchDTO
*/ */
private void recommended(BoolQueryBuilder filterBuilder, EsGoodsSearchDTO searchDTO) { private void recommended(BoolQueryBuilder filterBuilder, EsGoodsSearchDTO searchDTO) {
String currentGoodsId = searchDTO.getCurrentGoodsId(); String currentGoodsId = searchDTO.getCurrentGoodsId();
if(CharSequenceUtil.isEmpty(currentGoodsId)) { if (CharSequenceUtil.isEmpty(currentGoodsId)) {
return; return;
} }
//排除当前商品 //排除当前商品
filterBuilder.mustNot(QueryBuilders.matchQuery("id",currentGoodsId)); filterBuilder.mustNot(QueryBuilders.matchQuery("id", currentGoodsId));
//查询当前浏览商品的索引信息 //查询当前浏览商品的索引信息
EsGoodsIndex esGoodsIndex = restTemplate.get(currentGoodsId, EsGoodsIndex.class); EsGoodsIndex esGoodsIndex = restTemplate.get(currentGoodsId, EsGoodsIndex.class);
if(esGoodsIndex==null) { if (esGoodsIndex == null) {
return; return;
} }
//推荐与当前浏览商品相同一个二级分类下的商品 //推荐与当前浏览商品相同一个二级分类下的商品
String categoryPath = esGoodsIndex.getCategoryPath(); String categoryPath = esGoodsIndex.getCategoryPath();
if(CharSequenceUtil.isNotEmpty(categoryPath)){ if (CharSequenceUtil.isNotEmpty(categoryPath)) {
//匹配二级分类 //匹配二级分类
String substring = categoryPath.substring(0, categoryPath.lastIndexOf(",")); String substring = categoryPath.substring(0, categoryPath.lastIndexOf(","));
filterBuilder.must(QueryBuilders.wildcardQuery("categoryPath",substring+"*")); filterBuilder.must(QueryBuilders.wildcardQuery("categoryPath", substring + "*"));
} }
} }