后端:尝试引入 ES 服务

This commit is contained in:
YunaiV
2019-04-24 00:32:09 +08:00
parent 6fdec47430
commit 7fcb9da38e
29 changed files with 577 additions and 44 deletions

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.search.biz.constant;
/**
* ES 字段分析器的枚举类
*
* 关于 IK 分词,文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。
* 目前项目使用的 ES 版本是 6.7.1 ,可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载。
* 如果不知道怎么安装 ES ,可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单。
*/
public class FieldAnalyzer {
/**
* IK 最大化分词
*
* 会将文本做最细粒度的拆分
*/
public static final String IK_MAX_WORD = "ik_max_word";
/**
* IK 智能分词
*
* 会做最粗粒度的拆分
*/
public static final String IK_SMART = "ik_smart";
}

View File

@@ -0,0 +1,13 @@
package cn.iocoder.mall.search.biz.dao;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends ElasticsearchRepository<ESProductDO, Integer> {
@Deprecated
ESProductDO findByName(String name);
}

View File

@@ -0,0 +1,96 @@
package cn.iocoder.mall.search.biz.dataobject;
import cn.iocoder.mall.search.biz.constant.FieldAnalyzer;
import lombok.Data;
import lombok.experimental.Accessors;
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 DO
*/
@Document(indexName = "product", type = "spu", shards = 1, replicas = 0)
@Data
@Accessors(chain = true)
public class ESProductDO {
@Id
private Integer id;
// ========== 基本信息 =========
/**
* SPU 名字
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String name;
/**
* 卖点
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String sellPoint;
/**
* 描述
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String description;
/**
* 分类编号
*/
private Integer cid;
/**
* 分类名
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String categoryName;
/**
* 商品主图地数组
*/
private List<String> picUrls;
// ========== 其他信息 =========
/**
* 是否上架商品(是否可见)。
*
* true 为已上架
* false 为已下架
*/
private Boolean visible;
/**
* 排序字段
*/
private Integer sort;
// ========== Sku 相关字段 =========
/**
* 原价格,单位:分
*/
private Integer originalPrice;
/**
* 购买价格,单位:分。
*/
private Integer buyPrice;
/**
* 库存数量
*/
private Integer quantity;
// ========== 促销活动相关字段 =========
// 目前只促销单体商品促销,目前仅限制折扣。
/**
* 促销活动编号
*/
private Integer promotionActivityId;
/**
* 促销活动标题
*/
private String promotionActivityTitle;
/**
* 促销活动类型
*/
private Integer promotionActivityType;
}

View File

@@ -0,0 +1,79 @@
package cn.iocoder.mall.search.biz.service;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.order.api.CartService;
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO;
import cn.iocoder.mall.search.api.ProductSearchService;
import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO;
import cn.iocoder.mall.search.biz.dao.ProductRepository;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@com.alibaba.dubbo.config.annotation.Service(validation = "true")
public class ProductSearchServiceImpl implements ProductSearchService {
private static final Integer REBUILD_FETCH_PER_SIZE = 2;
@Autowired
private ProductRepository productRepository;
@Autowired
private ProductSpuService productSpuService;
@Autowired
private CartService cartService;
@Override
public CommonResult<Integer> rebuild() {
// TODO 芋艿,因为目前商品比较少,所以写的很粗暴。等未来重构
Integer lastId = null;
int rebuildCounts = 0;
while (true) {
CommonResult<List<ProductSpuDetailBO>> result = productSpuService.getProductSpuDetailListForSync(lastId, REBUILD_FETCH_PER_SIZE);
Assert.isTrue(result.isError(), "获得商品列表必然成功");
List<ProductSpuDetailBO> spus = result.getData();
rebuildCounts += spus.size();
// 存储到 ES 中
List<ESProductDO> products = spus.stream().map(new Function<ProductSpuDetailBO, ESProductDO>() {
@Override
public ESProductDO apply(ProductSpuDetailBO spu) {
return convert(spu);
}
}).collect(Collectors.toList());
// 设置新的 lastId ,或者结束
if (spus.size() < REBUILD_FETCH_PER_SIZE) {
break;
} else {
lastId = spus.get(spus.size() - 1).getId();
}
}
return CommonResult.success(rebuildCounts);
}
@SuppressWarnings("OptionalGetWithoutIsPresent")
private ESProductDO convert(ProductSpuDetailBO spu) {
// 获得最小价格的 SKU ,用于下面的价格计算
ProductSpuDetailBO.Sku sku = spu.getSkus().stream().min(Comparator.comparing(ProductSpuDetailBO.Sku::getPrice)).get();
// 价格计算
CommonResult<CalcSkuPriceBO> calSkuPriceResult = cartService.calcSkuPrice(sku.getId());
Assert.isTrue(calSkuPriceResult.isError(), String.format("SKU(%d) 价格计算不会出错", sku.getId()));
return new ESProductDO();
}
@Override
public CommonResult searchPage(ProductSearchPageDTO searchPageDTO) {
return null;
}
}

View File

@@ -0,0 +1,19 @@
#
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.88.10:9300
repositories:
enable: true
# dubbo
dubbo:
application:
name: search-service
registry:
address: zookeeper://127.0.0.1:2181
protocol:
port: -1
name: dubbo
scan:
base-packages: cn.iocoder.mall.search.service.biz

View File

@@ -0,0 +1,7 @@
package cn.iocoder.mall.search.biz;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.search"})
public class Application {
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.mall.search.biz.dao;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Test
public void testSave() {
// productRepository.deleteById(1);
ESProductDO product = new ESProductDO()
.setId(1)
.setName("你猜");
productRepository.save(product);
}
@Test
public void testFindByName() {
ESProductDO product = productRepository.findByName("锤子");
System.out.println(product);
}
}