一、商品上架功能
ElasticSearch实现商城系统中全文检索的流程。
1.商品ES模型
商品的映射关系
PUT product
{
"mappings": {
"properties": {
"skuId": {
"type": "long"
},
"spuId": {
"type": "keyword"
},
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": {
"type": "keyword"
},
"skuImg": {
"type": "keyword",
"index": "false",
"doc_values": "false"
},
"saleCount": {
"type": "long"
},
"hasStock": {
"type": "boolean"
},
"hotScore": {
"type": "long"
},
"brandId": {
"type": "long"
},
"catalogId": {
"type": "long"
},
"brandName": {
"type": "keyword",
"index": "false",
"doc_values": "false"
},
"brandImg": {
"type": "keyword",
"index": "false",
"doc_values": "false"
},
"catalogName": {
"type": "keyword",
"index": "false",
"doc_values": "false"
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword",
"index": "false",
"doc_values": "false"
},
"attrValue": {
"type": "keyword"
}
}
}
}
}
}
2.netsted数据类型
如上的例子,netsted类型,避免了我们properties属性里的三个字段值同类型,他们的内容不会错乱匹配
参考官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/nested.html
3.实现上架功能
3.1 创建ESModel
点击上架功能传递spuId到后台,我们需要根据SpuID查询对应的信息,然后封装到自定义的Model对象中,然后将该对象传递给mall-search服务,所以我们需要先定义这样一个Model对象
@Data
public class SkuESModel {
private Long skuId;
private Long spuId;
private String subTitle;
private BigDecimal skuPrice;
private String skuImg;
private Long saleCount;
private Boolean hasStock;
private Long hotScore;
private Long brandId;
private Long catalogId;
private String brandName;
private String brandImg;
private String catalogName;
private List<Attrs> attrs;
@Data
public static class Attrs{
private Long attrId;
private String attrName;
private String attrValue;
}
}
3.2 上架逻辑实现
controller层
/**
* spu信息
*
*/
@RestController
@RequestMapping("product/spuinfo")
public class SpuInfoController {
@Autowired
private SpuInfoService spuInfoService;
/**
* app/product/spuinfo/6/up
* 商品的上架功能
* 传递过来一个spuID
* 我们就需要根据SPUID查询出需要存储在ElasticSearch中的数据
* 然后把数据存储到ELasticSearch中,并修改该SPU的状态为上架
*/
@PostMapping("/{spuId}/up")
public R spuUp(@PathVariable("spuId") Long spuId){
spuInfoService.up(spuId);
return R.ok();
}
}
核心service层方法实现
调用了一些同moudle下的其他实现类方法忽略
/**
* 实现商品上架--》商品相关数据存储到ElasticSearch中
* 1.根据SpuID查询出相关的信息
* 封装到对应的对象中
* 2.将封装的数据存储到ElasticSearch中--》调用mall-search的远程接口
* 3.更新SpuID对应的状态--》上架
*
* @param spuId
*/
@Override
public void up(Long spuId) {
// 1.根据spuId查询相关的信息 封装到SkuESModel对象中
List<SkuESModel> skuEs = new ArrayList<>();
// 根据spuID找到对应的SKU信息
List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);
// 对应的规格参数 根据spuId来查询规格参数信息
List<SkuESModel.Attrs> attrsModel = getAttrsModel(spuId);
// 需要根据所有的skuId获取对应的库存信息---》远程调用
List<Long> skuIds = skus.stream().map(sku -> {
return sku.getSkuId();
}).collect(Collectors.toList());
Map<Long, Boolean> skusHasStockMap = getSkusHasStock(skuIds);
// 2.远程调用mall-search的服务,将SukESModel中的数据存储到ES中
List<SkuESModel> skuESModels = skus.stream().map(item -> {
SkuESModel model = new SkuESModel();
// 先实现属性的复制
BeanUtils.copyProperties(item,model);
model.setSubTitle(item.getSkuTitle());
model.setSkuPrice(item.getPrice());
// hasStock 是否有库存 --》 库存系统查询 一次远程调用获取所有的skuId对应的库存信息
if(skusHasStockMap == null){
model.setHasStock(true);
}else{
model.setHasStock(skusHasStockMap.get(item.getSkuId()));
}
// hotScore 热度分 --> 默认给0即可
model.setHotScore(0l);
// 品牌和类型的名称
BrandEntity brand = brandService.getById(item.getBrandId());
CategoryEntity category = categoryService.getById(item.getCatalogId());
model.setBrandName(brand.getName());
model.setBrandImg(brand.getLogo());
model.setCatalogName(category.getName());
// 需要存储的规格数据
model.setAttrs(attrsModel);
return model;
}).collect(Collectors.toList());
// 将SkuESModel中的数据存储到ES中
R r = searchFeginService.productStatusUp(skuESModels);
// 3.更新SPUID对应的状态
// 根据对应的状态更新商品的状态
log.info("----->ES操作完成:{}" ,r.getCode());
System.out.println("-------------->"+r.getCode());
if(r.getCode() == 0){
// 远程调用成功 更新商品的状态为 上架
baseMapper.updateSpuStatusUp(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());
}else{
// 远程调用失败
}
}
Feign远程调用了其他微服务module
在当前需要调用远程微服务的模块中创建接口,通过注解@FeignClient(“mall-search”)找到对应被调用的微服务,接着将被调用微服务的controller层的所需方法名,形参路径等都全部复制过来,就能完成远程调用
@FeignClient("mall-search")
public interface SearchFeginService {
@PostMapping("/search/save/product")
public R productStatusUp(@RequestBody List<SkuESModel> skuESModels);
}
被调用的微服务模块Controller层
/**
*
* 存储商城数据到ElasticSearch的服务
*/
@Slf4j
@RequestMapping("/search/save")
@RestController
public class ElasticSearchSaveController {
@Autowired
private ElasticSearchSaveService service;
/**
* 存储商品上架信息到ElasticSearch服务的接口
* @return
*/
@PostMapping("/product")
public R productStatusUp(@RequestBody List<SkuESModel> skuESModels){
Boolean b = false;
try {
b = service.productStatusUp(skuESModels);
} catch (IOException e) {
// e.printStackTrace();
log.error("ElasticSearch商品上架错误:{}",e);
return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());
}
if(b){
return R.ok();
}
return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());
}