商城业务
- 前言
- 一、商品上架
- 1.1 商品 Mapping
- 1.2 商品信息保存到es
- 1.3 es数组的扁平化处理
- 1.4 构造基本数据
前言
本文继续记录B站谷粒商城项目视频 P128-135 的内容,做到知识点的梳理和总结的作用,接口文档地址:gulimall接口文档
一、商品上架
上架的商品才可以在网站展示及被检索。
1.1 商品 Mapping
分析:商品上架在 es 中是存 sku 还是 spu?
1)检索的时候输入名字,是需要按照 sku 的 title 进行全文检索的。
2)检索使用商品规格,规格是 spu 的公共属性,每个 spu 是一样的。
3)按照分类 id 进去的都是直接列出 spu 的,还可以切换。
4)我们如果将 sku 的全量信息保存到 es 中(包括 spu 属性)就太多量字段了。
5)我们如果将 spu 以及他包含的 sku 信息保存到 es 中,也可以方便检索。但是 sku 属于 spu 的级联对象,在 es 中需要 nested 模型,这种性能差点。
6)但是存储与检索我们必须性能折中。
7)如果我们分拆存储,spu 和 attr 一个索引,sku 单独一个索引可能涉及的问题。
检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些 spu 的所有关联属性,再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就 10000*4 = 4MB;并发情况下假设 1000 检索请求,那就是 4GB 的数据,,传输阻塞时间会很长,业务更加无法继续。
所以,我们如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数据库范式。
1.2 商品信息保存到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"
}
}
}
}
}
}
index:
默认 true,如果为 false,表示该字段不会被索引,但是检索结果里面有,但字段本身不能当做检索条件。
doc_values:
默认 true,设置为 false,表示不可以做排序、聚合以及脚本操作,这样更节省磁盘空间。
还可以通过设定 doc_values 为 true,index 为 false 来让字段不能被搜索但可以用于排序、聚
合以及脚本操作。
1.3 es数组的扁平化处理
只需要修改 user 的类型即可:
PUT my-index
{
"mappings": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
执行查询操作,返回0条数据。
1.4 构造基本数据
商品上架接口: POST /product/spuinfo/{spuId}/up
SpuInfoController 层代码
/**
* 商品上架
* @param spuId
* @return
*/
@PostMapping("/{spuId}/up")
public R spuUp(@PathVariable Long spuId){
spuInfoService.productUp(spuId);
return R.ok();
}
sku 在 es 存储的数据
@Data
public class SkuEsModel {
private Long skuId;
private Long spuId;
private String skuTitle;
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;
}
}
业务层代码
@Override
public void productUp(Long spuId) {
//1.查出当前spuId对应的所有sku信息,品牌的名字
List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);
List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
//TODO 4.查询当前sku的所有可以被检索的规格属性
List<ProductAttrValueEntity> baseAttrs = attrValueService.baseAttrListForSpu(spuId);
List<Long> attrIds = baseAttrs.stream().map(attrValue -> {
return attrValue.getAttrId();
}).collect(Collectors.toList());
List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);
Set<Long> ids = new HashSet<>(searchAttrIds);
List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {
return ids.contains(item.getAttrId());
}).map(item -> {
SkuEsModel.Attrs attr = new SkuEsModel.Attrs();
BeanUtils.copyProperties(item, attr);
return attr;
}).collect(Collectors.toList());
List<SkuEsModel> collect = skus.stream().map(sku -> {
// 组装存储到es的数据
SkuEsModel skuEsModel = new SkuEsModel();
BeanUtils.copyProperties(sku, skuEsModel);
skuEsModel.setSkuPrice(sku.getPrice());
skuEsModel.setSkuImg(sku.getSkuDefaultImg());
//TODO 2.热度评分 0
skuEsModel.setHotScore(0L);
//TODO 3.查询品牌和分类信息
BrandEntity brand = brandService.getById(sku.getBrandId());
skuEsModel.setBrandName(brand.getName());
skuEsModel.setBrandImg(brand.getLogo());
CategoryEntity category = categoryService.getById(sku.getCatalogId());
skuEsModel.setCatalogName(category.getName());
//设置检索属性
skuEsModel.setAttrs(attrsList);
return skuEsModel;
}).collect(Collectors.toList());
//TODO 5.将数据发送给es进行保存 gulimall-search
//TODO 1.发送远程调用,库存系统是否有库存
R skusHasStock = wareFeignService.getSkusHasStock(skuIdList);
//2.封装每个sku的信息
}